generateCertificate() occurs at CertificateException
I’m developing my Android app. I’m trying to generate an X509Certificate instance from my certificate file stream but get CertificateException
, here’s my simple code:
import java.security.cert.CertificateException;
import java.security.cert.CertificateFactory;
import java.security.cert.X509Certificate;
...
public class CertMgr {
my certification file 'mycert.p12' located in my app internal storage
File certFile = GET_CERT();
X509Certificate cert = null;
try {
FileInputStream fis = new FileInputStream(certFile);
BufferedInputStream bis = new BufferedInputStream(fis);
CertificateFactory cf = CertificateFactory.getInstance("X.509");
if(bis.available() > 0){
I got CertificateException here, see the stack trace
cert = (X509Certificate) cf.generateCertificate(bis); line nr 150
}
}catch(...)
{...}
...
}
Stack trace:
javax.security.cert.CertificateException: org.apache.harmony.security.asn1.ASN1Exception: ASN.1 Sequence: mandatory value is missing at [4]
11-11 17:30:20.731: W/System.err(11529): at javax.security.cert.X509Certificate.getInstance(X509Certificate.java:94)
11-11 17:30:20.731: W/System.err(11529): at javax.security.cert.X509Certificate.getInstance(X509Certificate.java:213)
11-11 17:30:20.731: W/System.err(11529): at com.my.app.CertMgr.getCert(CertMgr.java:150)
Can someone explain to me why this exception is occurring?
P.S.: This is the class I use from the Android SDK X509Certificate , CertificateFactory
If you’re curious about why I’m doing this, the reason is that I want my app to be able to install the certificate (mycert.p12) via the following code (if the code above is working):
Intent installIntent = KeyChain.createInstallIntent();
installIntent.putExtra(KeyChain.EXTRA_CERTIFICATE, cert.getEncoded());
installIntent.putExtra(KeyChain.EXTRA_NAME, MY_CERT);
startActivityForResult(installIntent, INSTALL_KEYCHAIN_CODE);
Solution
You are trying to read the PKCS#12 data structure because it is an X509 certificate. PKCS The #12 standard specifies a data structure that can bundle multiple certificates and private keys, optionally protected with a password.
In order to read PKCS#12 data, you need to load it using KeyStore. This code fragment shows how to list all entries for the PCKS#12 file:
KeyStore keyStore = KeyStore.getInstance("PKCS12");
File p12File = GET_CERT();
FileInputStream fis = new FileInputStream(p12File);
BufferedInputStream bis = new BufferedInputStream(fis);
keyStore.load(bis, password.toCharArray()); password is the PKCS#12 password. If there is no password, just pass null
Enumeration<String> aliases = keyStore.aliases();
while (aliases.hasMoreElements()) {
String alias = aliases.nextElement();
/* Do something with the keystore entry */
}
A KeyStore entry can be a private key, a chain with or without an associated certificate (that is, a sequence of certificates from the root certificate to the certificate corresponding to the private key), or a trusted certificate. You can determine the entry type through the KeyStore.isKeyEntry and KeyStore.isCertificateEntry
methods.
Based on the KeyChain Intent you provided, it seems that you want to add a new trusted root CA certificate to the key chain. So I think you should list the certificate entries for the PKCS#12 file.
Edit (November 12, 2013).
How to get a trusted certificate from keystore:
String alias = aliases.nextElement();
if (keyStore.isCertificateEntry(alias)) { // keep only trusted cert entries
Certificate caCert = keyStore.getCertificate(alias)
byte[] extraCertificate = caCert.getEncoded();
Intent installIntent = KeyChain.createInstallIntent();
installIntent.putExtra(KeyChain.EXTRA_CERTIFICATE, extraCertificate);
installIntent.putExtra(KeyChain.EXTRA_NAME, MY_CERT);
startActivityForResult(installIntent, INSTALL_KEYCHAIN_CODE);
}