On my android device, I manually installed a certificate which goal is to allow me access to a specific website.
When I look in Security -- User certs, I see can see my certificate.
My website is displayed into a webview, so I have to use the following code
@Override
public void onReceivedClientCertRequest(WebView view, final ClientCertRequest request) {
if (mCertificates == null || mPrivateKey == null) {
loadCertificateAndPrivateKey();
}
request.proceed(mPrivateKey, mCertificates);
}
In my loadCertificateAndPrivateKey() function, I am to get certificate like this
KeyStore ks = KeyStore.getInstance("AndroidCAStore");
if (ks != null)
{
ks.load(null, null);
Enumeration<String> aliases = ks.aliases();
while (aliases.hasMoreElements())
{
String alias = (String) aliases.nextElement();
java.security.cert.X509Certificate cert = (java.security.cert.X509Certificate) ks.getCertificate(alias);
if(cert.getIssuerDN().getName().contains("TOTO")){
mCertificates = new X509Certificate[1];
mCertificates[0] = (X509Certificate)cert;
mPrivateKey = ??;
}
}
But I don't know how to get the private key...
I tried this way, but key object is null (the same code works when I load my certificate directly from my app)
Key key = keyStore.getKey(alias, password.toCharArray());
if (key instanceof PrivateKey) {
mPrivateKey = (PrivateKey)key;
}
So I have few questions :
- Is it the correct way to retrieve the certificate ?
- Why is it not possible to retrieve private key with the certificate's password ?
- How to retrieve this privateKey ?
Tkanks for your answers !
I found the way to do that, hope that it'll help somebody.
I followed this link that explain how to implement the entire processus.
Finally, I've got the code bolow.
First, define a KeyChainAliasCallback and call the KainChain.choosePrivateKeyAlias that display an activity to have the user authorization to access certificates store.
KeyChainAliasCallback keyChainAliasCallback = new KeyChainAliasCallback() {
@Override
public void alias(@Nullable String s) {
Log.d(TAG, "selected alias = " + s);
asyncTask.execute();
}
};
KeyChain.choosePrivateKeyAlias(this, keyChainAliasCallback, null, null, null, -1, CERT_ALIAS);
Then, you're able to retrieve private key and certificates chains from the internal store (you have to use an async function)
AsyncTask<Void, Void, Boolean> asyncTask = new AsyncTask<Void, Void, Boolean>() {
private Exception error;
@Override
protected Boolean doInBackground(Void... arg) {
try {
PrivateKey pk = KeyChain.getPrivateKey(mContext, CERT_ALIAS);
X509Certificate[] chain = KeyChain.getCertificateChain(mContext, CERT_ALIAS);
byte[] data = "foobar".getBytes("ASCII");
Signature sig = Signature.getInstance("SHA1withRSA");
sig.initSign(pk);
sig.update(data);
byte[] signed = sig.sign();
PublicKey pubk = chain[0].getPublicKey();
sig.initVerify(pubk);
sig.update(data);
boolean valid = sig.verify(signed);
Log.d(TAG, "signature is valid: " + valid);
if(valid) {
mPrivateKey = pk;
mCertificates = chain;
}
return valid;
} catch (Exception e) {
e.printStackTrace();
error = e;
return null;
}
}
@Override
protected void onPostExecute(Boolean valid) {
if (error != null) {
Toast.makeText(mContext, "Error: " + error.getMessage(),
Toast.LENGTH_LONG).show();
return;
} else {
Toast.makeText(mContext, "Signature is valid: " + valid,
Toast.LENGTH_SHORT).show();
mWebView.loadUrl("https://blablabla.com");
}
}
};
Then, you're able to give certificate with this function :
@Override
public void onReceivedClientCertRequest(WebView view, final ClientCertRequest request) {
request.proceed(mPrivateKey, mCertificates);
}
Last thing I have to find is how to display the choosePrivateKeyAlias dialog just one time (the first).