android: Added JNI method to retrieve user certificate and private key

To simplify things the private key, the user certificate and the CA
certificates are all put into the same list.
This commit is contained in:
Tobias Brunner
2012-08-31 18:24:46 +02:00
parent 38e866c3dd
commit 3aba33868b
3 changed files with 110 additions and 14 deletions
@@ -199,6 +199,33 @@ failed:
return FALSE;
}
/**
* Converts the given Java array of byte arrays (byte[][]) to a linked list
* of chunk_t objects.
*/
static linked_list_t *convert_array_of_byte_arrays(JNIEnv *env,
jobjectArray jarray)
{
linked_list_t *list;
jsize i;
list = linked_list_create();
for (i = 0; i < (*env)->GetArrayLength(env, jarray); ++i)
{
chunk_t *chunk;
jbyteArray jbytearray;
chunk = malloc_thing(chunk_t);
list->insert_last(list, chunk);
jbytearray = (*env)->GetObjectArrayElement(env, jarray, i);
*chunk = chunk_alloc((*env)->GetArrayLength(env, jbytearray));
(*env)->GetByteArrayRegion(env, jbytearray, 0, chunk->len, chunk->ptr);
(*env)->DeleteLocalRef(env, jbytearray);
}
return list;
}
METHOD(charonservice_t, get_trusted_certificates, linked_list_t*,
private_charonservice_t *this)
{
@@ -206,7 +233,6 @@ METHOD(charonservice_t, get_trusted_certificates, linked_list_t*,
jmethodID method_id;
jobjectArray jcerts;
linked_list_t *list;
jsize i;
androidjni_attach_thread(&env);
@@ -222,21 +248,39 @@ METHOD(charonservice_t, get_trusted_certificates, linked_list_t*,
{
goto failed;
}
list = linked_list_create();
for (i = 0; i < (*env)->GetArrayLength(env, jcerts); ++i)
list = convert_array_of_byte_arrays(env, jcerts);
androidjni_detach_thread();
return list;
failed:
androidjni_exception_occurred(env);
androidjni_detach_thread();
return NULL;
}
METHOD(charonservice_t, get_user_certificate, linked_list_t*,
private_charonservice_t *this)
{
JNIEnv *env;
jmethodID method_id;
jobjectArray jencodings;
linked_list_t *list;
androidjni_attach_thread(&env);
method_id = (*env)->GetMethodID(env,
android_charonvpnservice_class,
"getUserCertificate", "()[[B");
if (!method_id)
{
chunk_t *ca_cert;
jbyteArray jcert;
ca_cert = malloc_thing(chunk_t);
list->insert_last(list, ca_cert);
jcert = (*env)->GetObjectArrayElement(env, jcerts, i);
*ca_cert = chunk_alloc((*env)->GetArrayLength(env, jcert));
(*env)->GetByteArrayRegion(env, jcert, 0, ca_cert->len, ca_cert->ptr);
(*env)->DeleteLocalRef(env, jcert);
goto failed;
}
(*env)->DeleteLocalRef(env, jcerts);
jencodings = (*env)->CallObjectMethod(env, this->vpn_service, method_id, NULL);
if (!jencodings)
{
goto failed;
}
list = convert_array_of_byte_arrays(env, jencodings);
androidjni_detach_thread();
return list;
@@ -323,6 +367,7 @@ static void charonservice_init(JNIEnv *env, jobject service, jobject builder)
.update_status = _update_status,
.bypass_socket = _bypass_socket,
.get_trusted_certificates = _get_trusted_certificates,
.get_user_certificate = _get_user_certificate,
.get_vpnservice_builder = _get_vpnservice_builder,
},
.attr = android_attr_create(),
@@ -85,6 +85,17 @@ struct charonservice_t {
*/
linked_list_t *(*get_trusted_certificates)(charonservice_t *this);
/**
* Get the configured user certificate chain and private key via JNI
*
* The first item in the returned list is the private key, followed by the
* user certificate and any remaining elements of the certificate chain.
*
* @return list of DER encoded objects (as chunk_t*),
* NULL on failure
*/
linked_list_t *(*get_user_certificate)(charonservice_t *this);
/**
* Get the current vpnservice_builder_t object
*
@@ -21,6 +21,7 @@ import java.io.File;
import java.net.InetAddress;
import java.net.NetworkInterface;
import java.net.SocketException;
import java.security.PrivateKey;
import java.security.cert.CertificateEncodingException;
import java.security.cert.X509Certificate;
import java.util.ArrayList;
@@ -42,6 +43,8 @@ import android.net.VpnService;
import android.os.Bundle;
import android.os.IBinder;
import android.os.ParcelFileDescriptor;
import android.security.KeyChain;
import android.security.KeyChainException;
import android.util.Log;
public class CharonVpnService extends VpnService implements Runnable
@@ -54,6 +57,7 @@ public class CharonVpnService extends VpnService implements Runnable
private Thread mConnectionHandler;
private VpnProfile mCurrentProfile;
private volatile String mCurrentCertificateAlias;
private volatile String mCurrentUserCertificateAlias;
private VpnProfile mNextProfile;
private volatile boolean mProfileUpdated;
private volatile boolean mTerminate;
@@ -203,6 +207,7 @@ public class CharonVpnService extends VpnService implements Runnable
/* store this in a separate (volatile) variable to avoid
* a possible deadlock during deinitialization */
mCurrentCertificateAlias = mCurrentProfile.getCertificateAlias();
mCurrentUserCertificateAlias = mCurrentProfile.getUserCertificateAlias();
setProfile(mCurrentProfile);
setError(ErrorState.NO_ERROR);
@@ -421,6 +426,41 @@ public class CharonVpnService extends VpnService implements Runnable
return certs.toArray(new byte[certs.size()][]);
}
/**
* Function called via JNI to get a list containing the DER encoded private key
* and DER encoded certificates of the user selected certificate chain (beginning
* with the user certificate).
*
* Since this method is called from a thread of charon's thread pool we are safe
* to call methods on KeyChain directly.
*
* @return list containing the private key and certificates (first element is the key)
* @throws InterruptedException
* @throws KeyChainException
* @throws CertificateEncodingException
*/
private byte[][] getUserCertificate() throws KeyChainException, InterruptedException, CertificateEncodingException
{
ArrayList<byte[]> encodings = new ArrayList<byte[]>();
String alias = mCurrentUserCertificateAlias;
PrivateKey key = KeyChain.getPrivateKey(getApplicationContext(), alias);
if (key == null)
{
return null;
}
encodings.add(key.getEncoded());
X509Certificate[] chain = KeyChain.getCertificateChain(getApplicationContext(), alias);
if (chain == null || chain.length == 0)
{
return null;
}
for (X509Certificate cert : chain)
{
encodings.add(cert.getEncoded());
}
return encodings.toArray(new byte[encodings.size()][]);
}
/**
* Initialization of charon, provided by libandroidbridge.so
*