Merge branch 'android-cert-import'
[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_MD5:
72 method = "MD5withRSA";
73 break;
74 case SIGN_RSA_EMSA_PKCS1_SHA1:
75 method = "SHA1withRSA";
76 break;
77 case SIGN_RSA_EMSA_PKCS1_SHA224:
78 method = "SHA224withRSA";
79 break;
80 case SIGN_RSA_EMSA_PKCS1_SHA256:
81 method = "SHA256withRSA";
82 break;
83 case SIGN_RSA_EMSA_PKCS1_SHA384:
84 method = "SHA384withRSA";
85 break;
86 case SIGN_RSA_EMSA_PKCS1_SHA512:
87 method = "SHA512withRSA";
88 break;
89 default:
90 break;
91 }
92 break;
93 case KEY_ECDSA:
94 switch (scheme)
95 {
96 case SIGN_ECDSA_256:
97 method = "SHA256withECDSA";
98 break;
99 case SIGN_ECDSA_384:
100 method = "SHA384withECDSA";
101 break;
102 case SIGN_ECDSA_521:
103 method = "SHA512withECDSA";
104 break;
105 default:
106 break;
107 }
108 break;
109 default:
110 break;
111 }
112 if (!method)
113 {
114 DBG1(DBG_LIB, "signature scheme %N not supported via JNI",
115 signature_scheme_names, scheme);
116 return FALSE;
117 }
118
119 androidjni_attach_thread(&env);
120 /* we use java.security.Signature to create the signature without requiring
121 * access to the actual private key */
122 method_id = (*env)->GetStaticMethodID(env, this->signature_class,
123 "getInstance", "(Ljava/lang/String;)Ljava/security/Signature;");
124 if (!method_id)
125 {
126 goto failed;
127 }
128 jmethod = (*env)->NewStringUTF(env, method);
129 if (!jmethod)
130 {
131 goto failed;
132 }
133 jsignature = (*env)->CallStaticObjectMethod(env, this->signature_class,
134 method_id, jmethod);
135 if (!jsignature)
136 {
137 goto failed;
138 }
139 method_id = (*env)->GetMethodID(env, this->signature_class, "initSign",
140 "(Ljava/security/PrivateKey;)V");
141 if (!method_id)
142 {
143 goto failed;
144 }
145 (*env)->CallVoidMethod(env, jsignature, method_id, this->key);
146 if (androidjni_exception_occurred(env))
147 {
148 goto failed;
149 }
150 method_id = (*env)->GetMethodID(env, this->signature_class, "update",
151 "([B)V");
152 if (!method_id)
153 {
154 goto failed;
155 }
156 jdata = byte_array_from_chunk(env, data);
157 (*env)->CallVoidMethod(env, jsignature, method_id, jdata);
158 if (androidjni_exception_occurred(env))
159 {
160 goto failed;
161 }
162 method_id = (*env)->GetMethodID(env, this->signature_class, "sign",
163 "()[B");
164 if (!method_id)
165 {
166 goto failed;
167 }
168 jsigarray = (*env)->CallObjectMethod(env, jsignature, method_id);
169 if (!jsigarray)
170 {
171 goto failed;
172 }
173 if (this->pubkey->get_type(this->pubkey) == KEY_ECDSA)
174 {
175 chunk_t encoded, parse, r, s;
176 size_t len = 0;
177
178 switch (scheme)
179 {
180 case SIGN_ECDSA_256:
181 len = 32;
182 break;
183 case SIGN_ECDSA_384:
184 len = 48;
185 break;
186 case SIGN_ECDSA_521:
187 len = 66;
188 break;
189 default:
190 break;
191 }
192
193 /* we get an ASN.1 encoded sequence of integers r and s */
194 parse = encoded = chunk_from_byte_array(env, jsigarray);
195 if (asn1_unwrap(&parse, &parse) != ASN1_SEQUENCE ||
196 asn1_unwrap(&parse, &r) != ASN1_INTEGER ||
197 asn1_unwrap(&parse, &s) != ASN1_INTEGER)
198 {
199 chunk_free(&encoded);
200 goto failed;
201 }
202 r = chunk_skip_zero(r);
203 s = chunk_skip_zero(s);
204 if (r.len > len || s.len > len)
205 {
206 chunk_free(&encoded);
207 goto failed;
208 }
209
210 /* concatenate r and s (forced to the defined length) */
211 *signature = chunk_alloc(2*len);
212 memset(signature->ptr, 0, signature->len);
213 memcpy(signature->ptr + (len - r.len), r.ptr, r.len);
214 memcpy(signature->ptr + len + (len - s.len), s.ptr, s.len);
215 chunk_free(&encoded);
216 }
217 else
218 {
219 *signature = chunk_from_byte_array(env, jsigarray);
220 }
221 androidjni_detach_thread();
222 return TRUE;
223
224 failed:
225 DBG1(DBG_LIB, "failed to build %N signature via JNI",
226 signature_scheme_names, scheme);
227 androidjni_exception_occurred(env);
228 androidjni_detach_thread();
229 return FALSE;
230 }
231
232 METHOD(private_key_t, get_type, key_type_t,
233 private_private_key_t *this)
234 {
235 return this->pubkey->get_type(this->pubkey);
236 }
237
238 METHOD(private_key_t, decrypt, bool,
239 private_private_key_t *this, encryption_scheme_t scheme,
240 chunk_t crypto, chunk_t *plain)
241 {
242 DBG1(DBG_LIB, "private key decryption is currently not supported via JNI");
243 return FALSE;
244 }
245
246 METHOD(private_key_t, get_keysize, int,
247 private_private_key_t *this)
248 {
249 return this->pubkey->get_keysize(this->pubkey);
250 }
251
252 METHOD(private_key_t, get_public_key, public_key_t*,
253 private_private_key_t *this)
254 {
255 return this->pubkey->get_ref(this->pubkey);
256 }
257
258 METHOD(private_key_t, get_encoding, bool,
259 private_private_key_t *this, cred_encoding_type_t type,
260 chunk_t *encoding)
261 {
262 return FALSE;
263 }
264
265 METHOD(private_key_t, get_fingerprint, bool,
266 private_private_key_t *this, cred_encoding_type_t type, chunk_t *fp)
267 {
268 return this->pubkey->get_fingerprint(this->pubkey, type, fp);
269 }
270
271 METHOD(private_key_t, get_ref, private_key_t*,
272 private_private_key_t *this)
273 {
274 ref_get(&this->ref);
275 return &this->public;
276 }
277
278 METHOD(private_key_t, destroy, void,
279 private_private_key_t *this)
280 {
281 if (ref_put(&this->ref))
282 {
283 JNIEnv *env;
284
285 androidjni_attach_thread(&env);
286 if (android_sdk_version == ANDROID_JELLY_BEAN)
287 { /* there is a bug in JB that causes a SIGSEGV if the key object is
288 * garbage collected so we intentionally leak the reference to it */
289 DBG1(DBG_LIB, "intentionally leaking private key reference due to "
290 "a bug in the framework");
291 }
292 else
293 {
294 (*env)->DeleteGlobalRef(env, this->key);
295 }
296 (*env)->DeleteGlobalRef(env, this->signature_class);
297 androidjni_detach_thread();
298 this->pubkey->destroy(this->pubkey);
299 free(this);
300 }
301 }
302
303 /*
304 * See header
305 */
306 private_key_t *android_private_key_create(jobject key, public_key_t *pubkey)
307 {
308 JNIEnv *env;
309 private_private_key_t *this;
310
311 INIT(this,
312 .public = {
313 .get_type = _get_type,
314 .sign = _sign,
315 .decrypt = _decrypt,
316 .get_keysize = _get_keysize,
317 .get_public_key = _get_public_key,
318 .belongs_to = private_key_belongs_to,
319 .equals = private_key_equals,
320 .get_fingerprint = _get_fingerprint,
321 .has_fingerprint = private_key_has_fingerprint,
322 .get_encoding = _get_encoding,
323 .get_ref = _get_ref,
324 .destroy = _destroy,
325 },
326 .ref = 1,
327 .pubkey = pubkey,
328 );
329
330 if (!pubkey)
331 {
332 free(this);
333 return NULL;
334 }
335
336 /* in ICS we could simply call getEncoded and use the PKCS#8/DER encoded
337 * private key, since JB that's not possible as there is no direct access
338 * to private keys anymore (as these could now be hardware backed) */
339 androidjni_attach_thread(&env);
340 this->key = (*env)->NewGlobalRef(env, key);
341 this->signature_class = (*env)->NewGlobalRef(env, (*env)->FindClass(env,
342 "java/security/Signature"));
343 androidjni_detach_thread();
344 return &this->public;
345 }