Merge branch 'android-eap-tls'
[strongswan.git] / src / frontends / android / jni / libandroidbridge / backend / android_private_key.c
1 /*
2 * Copyright (C) 2012-2014 Tobias Brunner
3 * Hochschule fuer Technik Rapperswil
4 *
5 * This program is free software; you can redistribute it and/or modify it
6 * under the terms of the GNU General Public License as published by the
7 * Free Software Foundation; either version 2 of the License, or (at your
8 * option) any later version. See <http://www.fsf.org/copyleft/gpl.txt>.
9 *
10 * This program is distributed in the hope that it will be useful, but
11 * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
12 * or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
13 * for more details.
14 */
15
16 #include "android_private_key.h"
17
18 #include "../android_jni.h"
19 #include <utils/debug.h>
20 #include <asn1/asn1.h>
21
22 typedef struct private_private_key_t private_private_key_t;
23
24 /**
25 * Private data of a android_private_key_t object.
26 */
27 struct private_private_key_t {
28
29 /**
30 * Public interface
31 */
32 private_key_t public;
33
34 /**
35 * reference to the Java PrivateKey object
36 */
37 jobject key;
38
39 /**
40 * Java class used to build signatures
41 */
42 jclass signature_class;
43
44 /**
45 * public key that belongs to this private key
46 */
47 public_key_t *pubkey;
48
49 /**
50 * reference count
51 */
52 refcount_t ref;
53 };
54
55 METHOD(private_key_t, sign, bool,
56 private_private_key_t *this, signature_scheme_t scheme,
57 chunk_t data, chunk_t *signature)
58 {
59 JNIEnv *env;
60 jmethodID method_id;
61 const char *method = NULL;
62 jstring jmethod;
63 jobject jsignature;
64 jbyteArray jdata, jsigarray;
65
66 switch (this->pubkey->get_type(this->pubkey))
67 {
68 case KEY_RSA:
69 switch (scheme)
70 {
71 case SIGN_RSA_EMSA_PKCS1_NULL:
72 method = "NONEwithRSA";
73 break;
74 case SIGN_RSA_EMSA_PKCS1_MD5:
75 method = "MD5withRSA";
76 break;
77 case SIGN_RSA_EMSA_PKCS1_SHA1:
78 method = "SHA1withRSA";
79 break;
80 case SIGN_RSA_EMSA_PKCS1_SHA224:
81 method = "SHA224withRSA";
82 break;
83 case SIGN_RSA_EMSA_PKCS1_SHA256:
84 method = "SHA256withRSA";
85 break;
86 case SIGN_RSA_EMSA_PKCS1_SHA384:
87 method = "SHA384withRSA";
88 break;
89 case SIGN_RSA_EMSA_PKCS1_SHA512:
90 method = "SHA512withRSA";
91 break;
92 default:
93 break;
94 }
95 break;
96 case KEY_ECDSA:
97 switch (scheme)
98 {
99 case SIGN_ECDSA_WITH_SHA1_DER:
100 method = "SHA1withECDSA";
101 break;
102 case SIGN_ECDSA_WITH_SHA256_DER:
103 case SIGN_ECDSA_256:
104 method = "SHA256withECDSA";
105 break;
106 case SIGN_ECDSA_WITH_SHA384_DER:
107 case SIGN_ECDSA_384:
108 method = "SHA384withECDSA";
109 break;
110 case SIGN_ECDSA_WITH_SHA512_DER:
111 case SIGN_ECDSA_521:
112 method = "SHA512withECDSA";
113 break;
114 default:
115 break;
116 }
117 break;
118 default:
119 break;
120 }
121 if (!method)
122 {
123 DBG1(DBG_LIB, "signature scheme %N not supported via JNI",
124 signature_scheme_names, scheme);
125 return FALSE;
126 }
127
128 androidjni_attach_thread(&env);
129 /* we use java.security.Signature to create the signature without requiring
130 * access to the actual private key */
131 method_id = (*env)->GetStaticMethodID(env, this->signature_class,
132 "getInstance", "(Ljava/lang/String;)Ljava/security/Signature;");
133 if (!method_id)
134 {
135 goto failed;
136 }
137 jmethod = (*env)->NewStringUTF(env, method);
138 if (!jmethod)
139 {
140 goto failed;
141 }
142 jsignature = (*env)->CallStaticObjectMethod(env, this->signature_class,
143 method_id, jmethod);
144 if (!jsignature)
145 {
146 goto failed;
147 }
148 method_id = (*env)->GetMethodID(env, this->signature_class, "initSign",
149 "(Ljava/security/PrivateKey;)V");
150 if (!method_id)
151 {
152 goto failed;
153 }
154 (*env)->CallVoidMethod(env, jsignature, method_id, this->key);
155 if (androidjni_exception_occurred(env))
156 {
157 goto failed;
158 }
159 method_id = (*env)->GetMethodID(env, this->signature_class, "update",
160 "([B)V");
161 if (!method_id)
162 {
163 goto failed;
164 }
165 jdata = byte_array_from_chunk(env, data);
166 (*env)->CallVoidMethod(env, jsignature, method_id, jdata);
167 if (androidjni_exception_occurred(env))
168 {
169 goto failed;
170 }
171 method_id = (*env)->GetMethodID(env, this->signature_class, "sign",
172 "()[B");
173 if (!method_id)
174 {
175 goto failed;
176 }
177 jsigarray = (*env)->CallObjectMethod(env, jsignature, method_id);
178 if (!jsigarray)
179 {
180 goto failed;
181 }
182 if (this->pubkey->get_type(this->pubkey) == KEY_ECDSA)
183 {
184 chunk_t encoded, parse, r, s;
185 size_t len = 0;
186
187 switch (scheme)
188 {
189 case SIGN_ECDSA_256:
190 len = 32;
191 break;
192 case SIGN_ECDSA_384:
193 len = 48;
194 break;
195 case SIGN_ECDSA_521:
196 len = 66;
197 break;
198 default:
199 break;
200 }
201 if (len)
202 {
203 /* we get an ASN.1 encoded sequence of integers r and s */
204 parse = encoded = chunk_from_byte_array(env, jsigarray);
205 if (asn1_unwrap(&parse, &parse) != ASN1_SEQUENCE ||
206 asn1_unwrap(&parse, &r) != ASN1_INTEGER ||
207 asn1_unwrap(&parse, &s) != ASN1_INTEGER)
208 {
209 chunk_free(&encoded);
210 goto failed;
211 }
212 r = chunk_skip_zero(r);
213 s = chunk_skip_zero(s);
214 if (r.len > len || s.len > len)
215 {
216 chunk_free(&encoded);
217 goto failed;
218 }
219
220 /* concatenate r and s (forced to the defined length) */
221 *signature = chunk_alloc(2*len);
222 memset(signature->ptr, 0, signature->len);
223 memcpy(signature->ptr + (len - r.len), r.ptr, r.len);
224 memcpy(signature->ptr + len + (len - s.len), s.ptr, s.len);
225 chunk_free(&encoded);
226 }
227 else
228 {
229 *signature = chunk_from_byte_array(env, jsigarray);
230 }
231 }
232 else
233 {
234 *signature = chunk_from_byte_array(env, jsigarray);
235 }
236 androidjni_detach_thread();
237 return TRUE;
238
239 failed:
240 DBG1(DBG_LIB, "failed to build %N signature via JNI",
241 signature_scheme_names, scheme);
242 androidjni_exception_occurred(env);
243 androidjni_detach_thread();
244 return FALSE;
245 }
246
247 METHOD(private_key_t, get_type, key_type_t,
248 private_private_key_t *this)
249 {
250 return this->pubkey->get_type(this->pubkey);
251 }
252
253 METHOD(private_key_t, decrypt, bool,
254 private_private_key_t *this, encryption_scheme_t scheme,
255 chunk_t crypto, chunk_t *plain)
256 {
257 DBG1(DBG_LIB, "private key decryption is currently not supported via JNI");
258 return FALSE;
259 }
260
261 METHOD(private_key_t, get_keysize, int,
262 private_private_key_t *this)
263 {
264 return this->pubkey->get_keysize(this->pubkey);
265 }
266
267 METHOD(private_key_t, get_public_key, public_key_t*,
268 private_private_key_t *this)
269 {
270 return this->pubkey->get_ref(this->pubkey);
271 }
272
273 METHOD(private_key_t, get_encoding, bool,
274 private_private_key_t *this, cred_encoding_type_t type,
275 chunk_t *encoding)
276 {
277 return FALSE;
278 }
279
280 METHOD(private_key_t, get_fingerprint, bool,
281 private_private_key_t *this, cred_encoding_type_t type, chunk_t *fp)
282 {
283 return this->pubkey->get_fingerprint(this->pubkey, type, fp);
284 }
285
286 METHOD(private_key_t, get_ref, private_key_t*,
287 private_private_key_t *this)
288 {
289 ref_get(&this->ref);
290 return &this->public;
291 }
292
293 METHOD(private_key_t, destroy, void,
294 private_private_key_t *this)
295 {
296 if (ref_put(&this->ref))
297 {
298 JNIEnv *env;
299
300 androidjni_attach_thread(&env);
301 if (android_sdk_version == ANDROID_JELLY_BEAN)
302 { /* there is a bug in JB that causes a SIGSEGV if the key object is
303 * garbage collected so we intentionally leak the reference to it */
304 DBG1(DBG_LIB, "intentionally leaking private key reference due to "
305 "a bug in the framework");
306 }
307 else
308 {
309 (*env)->DeleteGlobalRef(env, this->key);
310 }
311 (*env)->DeleteGlobalRef(env, this->signature_class);
312 androidjni_detach_thread();
313 this->pubkey->destroy(this->pubkey);
314 free(this);
315 }
316 }
317
318 /*
319 * See header
320 */
321 private_key_t *android_private_key_create(jobject key, public_key_t *pubkey)
322 {
323 JNIEnv *env;
324 private_private_key_t *this;
325
326 INIT(this,
327 .public = {
328 .get_type = _get_type,
329 .sign = _sign,
330 .decrypt = _decrypt,
331 .get_keysize = _get_keysize,
332 .get_public_key = _get_public_key,
333 .belongs_to = private_key_belongs_to,
334 .equals = private_key_equals,
335 .get_fingerprint = _get_fingerprint,
336 .has_fingerprint = private_key_has_fingerprint,
337 .get_encoding = _get_encoding,
338 .get_ref = _get_ref,
339 .destroy = _destroy,
340 },
341 .ref = 1,
342 .pubkey = pubkey,
343 );
344
345 if (!pubkey)
346 {
347 free(this);
348 return NULL;
349 }
350
351 /* in ICS we could simply call getEncoded and use the PKCS#8/DER encoded
352 * private key, since JB that's not possible as there is no direct access
353 * to private keys anymore (as these could now be hardware backed) */
354 androidjni_attach_thread(&env);
355 this->key = (*env)->NewGlobalRef(env, key);
356 this->signature_class = (*env)->NewGlobalRef(env, (*env)->FindClass(env,
357 "java/security/Signature"));
358 androidjni_detach_thread();
359 return &this->public;
360 }