Merge branch 'debian-testing'
[strongswan.git] / src / frontends / android / jni / libandroidbridge / backend / android_private_key.c
1 /*
2 * Copyright (C) 2012 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
21 typedef struct private_private_key_t private_private_key_t;
22
23 /**
24 * Private data of a android_private_key_t object.
25 */
26 struct private_private_key_t {
27
28 /**
29 * Public interface
30 */
31 private_key_t public;
32
33 /**
34 * reference to the Java PrivateKey object
35 */
36 jobject key;
37
38 /**
39 * Java class used to build signatures
40 */
41 jclass signature_class;
42
43 /**
44 * public key that belongs to this private key
45 */
46 public_key_t *pubkey;
47
48 /**
49 * reference count
50 */
51 refcount_t ref;
52 };
53
54 METHOD(private_key_t, sign, bool,
55 private_private_key_t *this, signature_scheme_t scheme,
56 chunk_t data, chunk_t *signature)
57 {
58 JNIEnv *env;
59 jmethodID method_id;
60 const char *method;
61 jstring jmethod;
62 jobject jsignature;
63 jbyteArray jdata, jsigarray;
64
65 switch (scheme)
66 {
67 case SIGN_RSA_EMSA_PKCS1_MD5:
68 method = "MD5withRSA";
69 break;
70 case SIGN_RSA_EMSA_PKCS1_SHA1:
71 method = "SHA1withRSA";
72 break;
73 case SIGN_RSA_EMSA_PKCS1_SHA224:
74 method = "SHA224withRSA";
75 break;
76 case SIGN_RSA_EMSA_PKCS1_SHA256:
77 method = "SHA256withRSA";
78 break;
79 case SIGN_RSA_EMSA_PKCS1_SHA384:
80 method = "SHA384withRSA";
81 break;
82 case SIGN_RSA_EMSA_PKCS1_SHA512:
83 method = "SHA512withRSA";
84 break;
85 default:
86 DBG1(DBG_LIB, "signature scheme %N not supported via JNI",
87 signature_scheme_names, scheme);
88 return FALSE;
89 }
90
91 androidjni_attach_thread(&env);
92 /* we use java.security.Signature to create the signature without requiring
93 * access to the actual private key */
94 method_id = (*env)->GetStaticMethodID(env, this->signature_class,
95 "getInstance", "(Ljava/lang/String;)Ljava/security/Signature;");
96 if (!method_id)
97 {
98 goto failed;
99 }
100 jmethod = (*env)->NewStringUTF(env, method);
101 if (!jmethod)
102 {
103 goto failed;
104 }
105 jsignature = (*env)->CallStaticObjectMethod(env, this->signature_class,
106 method_id, jmethod);
107 if (!jsignature)
108 {
109 goto failed;
110 }
111 method_id = (*env)->GetMethodID(env, this->signature_class, "initSign",
112 "(Ljava/security/PrivateKey;)V");
113 if (!method_id)
114 {
115 goto failed;
116 }
117 (*env)->CallVoidMethod(env, jsignature, method_id, this->key);
118 if (androidjni_exception_occurred(env))
119 {
120 goto failed;
121 }
122 method_id = (*env)->GetMethodID(env, this->signature_class, "update",
123 "([B)V");
124 if (!method_id)
125 {
126 goto failed;
127 }
128 jdata = byte_array_from_chunk(env, data);
129 (*env)->CallVoidMethod(env, jsignature, method_id, jdata);
130 if (androidjni_exception_occurred(env))
131 {
132 goto failed;
133 }
134 method_id = (*env)->GetMethodID(env, this->signature_class, "sign",
135 "()[B");
136 if (!method_id)
137 {
138 goto failed;
139 }
140 jsigarray = (*env)->CallObjectMethod(env, jsignature, method_id);
141 if (!jsigarray)
142 {
143 goto failed;
144 }
145 *signature = chunk_from_byte_array(env, jsigarray);
146 androidjni_detach_thread();
147 return TRUE;
148
149 failed:
150 DBG1(DBG_LIB, "failed to build %N signature via JNI",
151 signature_scheme_names, scheme);
152 androidjni_exception_occurred(env);
153 androidjni_detach_thread();
154 return FALSE;
155 }
156
157 METHOD(private_key_t, get_type, key_type_t,
158 private_private_key_t *this)
159 {
160 return KEY_RSA;
161 }
162
163 METHOD(private_key_t, decrypt, bool,
164 private_private_key_t *this, encryption_scheme_t scheme,
165 chunk_t crypto, chunk_t *plain)
166 {
167 DBG1(DBG_LIB, "private key decryption is currently not supported via JNI");
168 return FALSE;
169 }
170
171 METHOD(private_key_t, get_keysize, int,
172 private_private_key_t *this)
173 {
174 return this->pubkey->get_keysize(this->pubkey);
175 }
176
177 METHOD(private_key_t, get_public_key, public_key_t*,
178 private_private_key_t *this)
179 {
180 return this->pubkey->get_ref(this->pubkey);
181 }
182
183 METHOD(private_key_t, get_encoding, bool,
184 private_private_key_t *this, cred_encoding_type_t type,
185 chunk_t *encoding)
186 {
187 return FALSE;
188 }
189
190 METHOD(private_key_t, get_fingerprint, bool,
191 private_private_key_t *this, cred_encoding_type_t type, chunk_t *fp)
192 {
193 return this->pubkey->get_fingerprint(this->pubkey, type, fp);
194 }
195
196 METHOD(private_key_t, get_ref, private_key_t*,
197 private_private_key_t *this)
198 {
199 ref_get(&this->ref);
200 return &this->public;
201 }
202
203 METHOD(private_key_t, destroy, void,
204 private_private_key_t *this)
205 {
206 if (ref_put(&this->ref))
207 {
208 JNIEnv *env;
209
210 androidjni_attach_thread(&env);
211 if (android_sdk_version == ANDROID_JELLY_BEAN)
212 { /* there is a bug in JB that causes a SIGSEGV if the key object is
213 * garbage collected so we intentionally leak the reference to it */
214 DBG1(DBG_LIB, "intentionally leaking private key reference due to "
215 "a bug in the framework");
216 }
217 else
218 {
219 (*env)->DeleteGlobalRef(env, this->key);
220 }
221 (*env)->DeleteGlobalRef(env, this->signature_class);
222 androidjni_detach_thread();
223 this->pubkey->destroy(this->pubkey);
224 free(this);
225 }
226 }
227
228 /*
229 * See header
230 */
231 private_key_t *android_private_key_create(jobject key, public_key_t *pubkey)
232 {
233 JNIEnv *env;
234 private_private_key_t *this;
235
236 INIT(this,
237 .public = {
238 .get_type = _get_type,
239 .sign = _sign,
240 .decrypt = _decrypt,
241 .get_keysize = _get_keysize,
242 .get_public_key = _get_public_key,
243 .belongs_to = private_key_belongs_to,
244 .equals = private_key_equals,
245 .get_fingerprint = _get_fingerprint,
246 .has_fingerprint = private_key_has_fingerprint,
247 .get_encoding = _get_encoding,
248 .get_ref = _get_ref,
249 .destroy = _destroy,
250 },
251 .ref = 1,
252 .pubkey = pubkey,
253 );
254
255 if (!pubkey)
256 {
257 free(this);
258 return NULL;
259 }
260
261 /* in ICS we could simply call getEncoded and use the PKCS#8/DER encoded
262 * private key, since JB that's not possible as there is no direct access
263 * to private keys anymore (as these could now be hardware backed) */
264 androidjni_attach_thread(&env);
265 this->key = (*env)->NewGlobalRef(env, key);
266 this->signature_class = (*env)->NewGlobalRef(env, (*env)->FindClass(env,
267 "java/security/Signature"));
268 androidjni_detach_thread();
269 return &this->public;
270 }