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;
|
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*,
|
METHOD(charonservice_t, get_trusted_certificates, linked_list_t*,
|
||||||
private_charonservice_t *this)
|
private_charonservice_t *this)
|
||||||
{
|
{
|
||||||
@@ -206,7 +233,6 @@ METHOD(charonservice_t, get_trusted_certificates, linked_list_t*,
|
|||||||
jmethodID method_id;
|
jmethodID method_id;
|
||||||
jobjectArray jcerts;
|
jobjectArray jcerts;
|
||||||
linked_list_t *list;
|
linked_list_t *list;
|
||||||
jsize i;
|
|
||||||
|
|
||||||
androidjni_attach_thread(&env);
|
androidjni_attach_thread(&env);
|
||||||
|
|
||||||
@@ -222,21 +248,39 @@ METHOD(charonservice_t, get_trusted_certificates, linked_list_t*,
|
|||||||
{
|
{
|
||||||
goto failed;
|
goto failed;
|
||||||
}
|
}
|
||||||
list = linked_list_create();
|
list = convert_array_of_byte_arrays(env, jcerts);
|
||||||
for (i = 0; i < (*env)->GetArrayLength(env, jcerts); ++i)
|
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;
|
goto failed;
|
||||||
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);
|
|
||||||
}
|
}
|
||||||
(*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();
|
androidjni_detach_thread();
|
||||||
return list;
|
return list;
|
||||||
|
|
||||||
@@ -323,6 +367,7 @@ static void charonservice_init(JNIEnv *env, jobject service, jobject builder)
|
|||||||
.update_status = _update_status,
|
.update_status = _update_status,
|
||||||
.bypass_socket = _bypass_socket,
|
.bypass_socket = _bypass_socket,
|
||||||
.get_trusted_certificates = _get_trusted_certificates,
|
.get_trusted_certificates = _get_trusted_certificates,
|
||||||
|
.get_user_certificate = _get_user_certificate,
|
||||||
.get_vpnservice_builder = _get_vpnservice_builder,
|
.get_vpnservice_builder = _get_vpnservice_builder,
|
||||||
},
|
},
|
||||||
.attr = android_attr_create(),
|
.attr = android_attr_create(),
|
||||||
|
|||||||
@@ -85,6 +85,17 @@ struct charonservice_t {
|
|||||||
*/
|
*/
|
||||||
linked_list_t *(*get_trusted_certificates)(charonservice_t *this);
|
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
|
* Get the current vpnservice_builder_t object
|
||||||
*
|
*
|
||||||
|
|||||||
@@ -21,6 +21,7 @@ import java.io.File;
|
|||||||
import java.net.InetAddress;
|
import java.net.InetAddress;
|
||||||
import java.net.NetworkInterface;
|
import java.net.NetworkInterface;
|
||||||
import java.net.SocketException;
|
import java.net.SocketException;
|
||||||
|
import java.security.PrivateKey;
|
||||||
import java.security.cert.CertificateEncodingException;
|
import java.security.cert.CertificateEncodingException;
|
||||||
import java.security.cert.X509Certificate;
|
import java.security.cert.X509Certificate;
|
||||||
import java.util.ArrayList;
|
import java.util.ArrayList;
|
||||||
@@ -42,6 +43,8 @@ import android.net.VpnService;
|
|||||||
import android.os.Bundle;
|
import android.os.Bundle;
|
||||||
import android.os.IBinder;
|
import android.os.IBinder;
|
||||||
import android.os.ParcelFileDescriptor;
|
import android.os.ParcelFileDescriptor;
|
||||||
|
import android.security.KeyChain;
|
||||||
|
import android.security.KeyChainException;
|
||||||
import android.util.Log;
|
import android.util.Log;
|
||||||
|
|
||||||
public class CharonVpnService extends VpnService implements Runnable
|
public class CharonVpnService extends VpnService implements Runnable
|
||||||
@@ -54,6 +57,7 @@ public class CharonVpnService extends VpnService implements Runnable
|
|||||||
private Thread mConnectionHandler;
|
private Thread mConnectionHandler;
|
||||||
private VpnProfile mCurrentProfile;
|
private VpnProfile mCurrentProfile;
|
||||||
private volatile String mCurrentCertificateAlias;
|
private volatile String mCurrentCertificateAlias;
|
||||||
|
private volatile String mCurrentUserCertificateAlias;
|
||||||
private VpnProfile mNextProfile;
|
private VpnProfile mNextProfile;
|
||||||
private volatile boolean mProfileUpdated;
|
private volatile boolean mProfileUpdated;
|
||||||
private volatile boolean mTerminate;
|
private volatile boolean mTerminate;
|
||||||
@@ -203,6 +207,7 @@ public class CharonVpnService extends VpnService implements Runnable
|
|||||||
/* store this in a separate (volatile) variable to avoid
|
/* store this in a separate (volatile) variable to avoid
|
||||||
* a possible deadlock during deinitialization */
|
* a possible deadlock during deinitialization */
|
||||||
mCurrentCertificateAlias = mCurrentProfile.getCertificateAlias();
|
mCurrentCertificateAlias = mCurrentProfile.getCertificateAlias();
|
||||||
|
mCurrentUserCertificateAlias = mCurrentProfile.getUserCertificateAlias();
|
||||||
|
|
||||||
setProfile(mCurrentProfile);
|
setProfile(mCurrentProfile);
|
||||||
setError(ErrorState.NO_ERROR);
|
setError(ErrorState.NO_ERROR);
|
||||||
@@ -421,6 +426,41 @@ public class CharonVpnService extends VpnService implements Runnable
|
|||||||
return certs.toArray(new byte[certs.size()][]);
|
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
|
* Initialization of charon, provided by libandroidbridge.so
|
||||||
*
|
*
|
||||||
|
|||||||
Reference in New Issue
Block a user