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:
@@ -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
|
||||
*
|
||||
|
||||
Reference in New Issue
Block a user