Hey devs, I'm working on a proof of concept app for generating an RSA keypair in an app, generating a X509 CSR based on the new keypair, then exporting it to be signed by some CA. As a part o f the CSR generation process the whole CSR must be signed by the key itself.
I've been following along with the Keystore page on the documentation site (http://developer.android.com/training/articles/keystore.html#SigningAndVerifyingData) and reached a snag. The generation of the CSR is handled by spongycastle, which in turn uses a custom build class (called keystoreContentSigner) to sign the completed CSR with the keys stored in the keystore. According to the above referenced documentation, the call to ks.getEntry(keyAlias, null) should return an object typecasted to the Keystore.Entry class, which you then cast into a PrivateKeyEntry which you then call against using its .getPrivateKey method. On my test phone (Nexus 6P) this flow doesn't work. It excepts during the call to initSign, and reports the type of the object return from ks.getEntry as android.security.keystore.AndroidKeyStoreRSAPrivateKey, which isn't referenced anywhere I can find. This type can't be casted to anything usable in the current format, is there some new way to perform cryptographic operations in Marshmallow, or is there something I'm missing? KeystoreContentSigner class: package au.com.taylornetworks.tapid; import org.spongycastle.asn1.x509.AlgorithmIdentifier; import org.spongycastle.operator.ContentSigner; import org.spongycastle.operator.DefaultSignatureAlgorithmIdentifierFinder; import java.io.ByteArrayOutputStream; import java.io.OutputStream; import java.security.KeyStore; import java.security.Signature; import java.security.interfaces.RSAPrivateKey; public class keystoreContentSigner implements ContentSigner{ private AlgorithmIdentifier algorithmIdentifier; private String algorithmText; private ByteArrayOutputStream dataStream; private String keyAlias; public keystoreContentSigner(String keyAlias) { algorithmText = "SHA512withRSA"; algorithmIdentifier = new DefaultSignatureAlgorithmIdentifierFinder().find(algorithmText); dataStream = new ByteArrayOutputStream(); this.keyAlias = keyAlias; } public void setAlgorithm(String algorithmText) { this.algorithmText = algorithmText; algorithmIdentifier = new DefaultSignatureAlgorithmIdentifierFinder().find(algorithmText); } @Override public OutputStream getOutputStream() { return dataStream; } @Override public AlgorithmIdentifier getAlgorithmIdentifier() { return algorithmIdentifier; } public byte[] getSignature() { byte[] data; byte[] signature = null; KeyStore ks; try { ks = KeyStore.getInstance("AndroidKeyStore"); ks.load(null); data = dataStream.toByteArray(); dataStream.flush(); Signature s = Signature.getInstance(algorithmText); KeyStore.Entry entry = ks.getEntry(keyAlias, null); s.initSign((RSAPrivateKey) entry); s.update(data); signature = s.sign(); } catch (Exception e) { e.printStackTrace(); } return signature; } } PkiManager class: Enter package au.com.taylornetworks.tapid; import android.security.keystore.*; import java.io.IOException; import java.security.*; import java.security.cert.CertificateException; import org.spongycastle.asn1.misc.MiscObjectIdentifiers; import org.spongycastle.asn1.misc.NetscapeCertType; import org.spongycastle.asn1.x500.X500Name; import org.spongycastle.asn1.x509.BasicConstraints; import org.spongycastle.asn1.x509.ExtendedKeyUsage; import org.spongycastle.asn1.x509.Extension; import org.spongycastle.asn1.x509.ExtensionsGenerator; import org.spongycastle.asn1.x509.KeyPurposeId; import org.spongycastle.asn1.x509.KeyUsage; import org.spongycastle.operator.ContentSigner; import org.spongycastle.operator.OperatorCreationException; import org.spongycastle.pkcs.*; import org.spongycastle.pkcs.jcajce.JcaPKCS10CertificationRequestBuilder; public class pkiManager { static private String keystoreName = "AndroidKeyStore"; KeyStore keyStore; PKCS10CertificationRequest csr; String keyAlias; String username; public pkiManager(String keyAlias) throws KeyStoreException, CertificateException, NoSuchAlgorithmException, IOException { Security.insertProviderAt(new org.spongycastle.jce.provider.BouncyCastleProvider(), 1); username = "Default User"; keyStore = KeyStore.getInstance(keystoreName); keyStore.load(null); this.keyAlias = keyAlias; } public PublicKey generateKeypair() throws NoSuchProviderException, NoSuchAlgorithmException, InvalidAlgorithmParameterException { KeyPairGenerator generator; KeyGenParameterSpec.Builder paramBuilder; generator = KeyPairGenerator.getInstance(KeyProperties.KEY_ALGORITHM_RSA, keystoreName); paramBuilder = new KeyGenParameterSpec.Builder(keyAlias,KeyProperties.PURPOSE_SIGN); paramBuilder.setDigests(KeyProperties.DIGEST_SHA256, KeyProperties.DIGEST_SHA512); paramBuilder.setKeySize(2048); generator.initialize(paramBuilder.build()); KeyPair keyPair = generator.generateKeyPair(); return keyPair.getPublic(); } public void generateCSR(PublicKey publicKey) throws IOException, OperatorCreationException { // Create principal & set key usage and other extensions X500Name principal = new X500Name("CN=" + username +", O=TapID"); KeyUsage keyUsage = new KeyUsage(KeyUsage.digitalSignature | KeyUsage.nonRepudiation | KeyUsage.keyEncipherment | KeyUsage.dataEncipherment | KeyUsage.keyAgreement); ExtendedKeyUsage extKeyUsage = new ExtendedKeyUsage(KeyPurposeId.id_kp_clientAuth); NetscapeCertType netCertType = new NetscapeCertType(NetscapeCertType.objectSigning | NetscapeCertType.smime | NetscapeCertType.sslClient); // Setup signer for CSR ContentSigner signer = new keystoreContentSigner(keyAlias); // Configure CSR builder PKCS10CertificationRequestBuilder csrBuilder = new JcaPKCS10CertificationRequestBuilder(principal, publicKey); csrBuilder.addAttribute(MiscObjectIdentifiers.netscapeCertType, netCertType); // Configure certificate extensions ExtensionsGenerator extGen = new ExtensionsGenerator(); extGen.addExtension(Extension.basicConstraints, true, new BasicConstraints(false)); extGen.addExtension(Extension.keyUsage, false, keyUsage); extGen.addExtension(Extension.extendedKeyUsage, false, extKeyUsage); // Build certificate request csr = csrBuilder.build(signer); } public String getCSR() { return csr.toString(); } public void setName(String username) { this.username = username; } } here... The app activity code is basically just a simple button which generates a keypair and spits out the PEM encoded CSR. -- You received this message because you are subscribed to the Google Groups "Android Developers" group. To unsubscribe from this group and stop receiving emails from it, send an email to [email protected]. To post to this group, send email to [email protected]. Visit this group at https://groups.google.com/group/android-developers. To view this discussion on the web visit https://groups.google.com/d/msgid/android-developers/c60b1fb6-8260-48b6-8f61-0dce1b056ca0%40googlegroups.com. For more options, visit https://groups.google.com/d/optout.

