I've created a key pair, stored the private key in the AndroidKeystore.
Generated a CSR using the Public key from the key pair
Exported the CSR to the CA, and got it to generate a cert
Installed the cert on the phone
Now, I'm trying to associate the private key from #1 with the cert that was installed at step #4.
Have looked all day, cannot find a mechanism to do it. Android documentation suggests using the KeyStore.setKeyEntry method. I intend to use it but am unable to read the cert that was installed.
I can display all certs in the AndroidCAStore, and I do see this newly installed cert in the "User certificates" section on the phone but am unable to read it programatically. Any ideas ?
Trying to do pretty much the same thing. The key association with the signed cert also works using keystore.setEntry. What I can't get to work is this user cert then to appear in the "IPSec User certificate" listbox when trying to add a VPN. Running on Marshmallow 6.0.1
My logic looks like this
keyStore = KeyStore.getInstance("AndroidKeyStore");
keyGen = KeyPairGenerator.getInstance("EC", "AndroidKeyStore");
keyGen.initialize(
new KeyGenParameterSpec.Builder(
"myAlias",
KeyProperties.PURPOSE_SIGN)
.setAlgorithmParameterSpec(new ECGenParameterSpec("secp384r1"))
.setDigests(KeyProperties.DIGEST_SHA256,
KeyProperties.DIGEST_SHA384,
KeyProperties.DIGEST_SHA512)
.setBlockModes(KeyProperties.BLOCK_MODE_GCM)
.setUserAuthenticationRequired(false)
.setKeyValidityStart(now)
.setKeyValidityForOriginationEnd(nowPlusThreeYears)
.build());
keyPair = keyGen.generateKeyPair();
privateKey = (PrivateKey) keyStore.getKey("myAlias", null);
publicKey = keyStore.getCertificate("myAlias").getPublicKey();
Certificate[] chain = keyStore.getCertificateChain("myAlias");
keyStore.setKeyEntry("myAlias", privateKey, null, chain);
StringBuilder x500PrincipalBuilder = new StringBuilder("CN=");
x500PrincipalBuilder.append("My Company");
x500PrincipalBuilder.append(", ");
x500PrincipalBuilder.append("L=");
x500PrincipalBuilder.append("My Location");
x500PrincipalBuilder.append(", ");
x500PrincipalBuilder.append("ST=");
x500PrincipalBuilder.append("CA");
x500PrincipalBuilder.append(", ");
x500PrincipalBuilder.append("O=");
x500PrincipalBuilder.append("My Org");
x500PrincipalBuilder.append(", ");
x500PrincipalBuilder.append("OU=");
x500PrincipalBuilder.append("my ou");
x500PrincipalBuilder.append(", ");
x500PrincipalBuilder.append("C=");
x500PrincipalBuilder.append("US");
Signature ecdsaSignature = Signature.getInstance("SHA384withECDSA");
ecdsaSignature.initSign(keyPair.getPrivate());
byte[] strByte = new byte[0];
strByte = x500PrincipalBuilder.toString().getBytes(UTF8_CHARSET);
ecdsaSignature.update(strByte);
X500Principal x500Principal = new X500Principal(x500PrincipalBuilder.toString());
PKCS10CertificationRequestBuilder p10Builder = new JcaPKCS10CertificationRequestBuilder(
x500Principal, keyPair.getPublic());
JcaContentSignerBuilder csBuilder = new JcaContentSignerBuilder(signatureGenerator.getSigningAlgorithm());
ContentSigner signer = csBuilder.build(keyPair.getPrivate());
org.bouncycastle.pkcs.PKCS10CertificationRequest csr = p10Builder.build(signer);
PemObject pemObject = new PemObject("CERTIFICATE REQUEST", csr.getEncoded());
StringWriter str = new StringWriter();
PEMWriter pemWriter = new PEMWriter(str);
pemWriter.writeObject(pemObject);
pemWriter.close();
str.close();
// write the csr to a file
// get it signed by the CA (Microsoft CA)
// Push the cert to the phone
// Install the cert
Certificate clientCertSignedByCA = CertificateFactory.getInstance("X.509").generateCertificate(new FileInputStream("/path/to/cert.cer));
KeyStore.Entry entry = keyStore.getEntry("myAlias", null);
PrivateKey privateKey = (PrivateKey) keyStore.getKey("myAlias", null);
Certificate[] chain = new Certificate[1];
chain[0] = clientCertSignedByCA;
keyStore.setKeyEntry("myAlias", privateKey, null, chain);
Related
I have a app in Android which communicates with a server that Signs a certificate Signing Request and returns the signed certificate so that I can be used by the client in further calls.
Recently we are trying to move to Flutter (Dart) and I'm trying to replicate the creation of the CSR, but I'm a little lost since I'm relatively new with Dart.
I would appreciate if you could point me in the right direction.
Here's what the function that creates the CSR looks like:
public String createPKI(String mdnString) {
try {
KeyPairGenerator keyPairGenerator = KeyPairGenerator.getInstance("EC");
//KeyPairGenerator keyPairGenerator = KeyPairGenerator.getInstance("ECDSA", "SC");
ECGenParameterSpec ecGenSpec = new ECGenParameterSpec("prime256v1");
keyPairGenerator.initialize(ecGenSpec, new SecureRandom());
KeyPair kp = keyPairGenerator.generateKeyPair();
publicKey = kp.getPublic();
privateKey = kp.getPrivate();
PKCS10CertificationRequest kpGen = new PKCS10CertificationRequest("SHA256withECDSA", new X509Name(String.format("UID=%s", mdnString)), publicKey, null, privateKey); //mdnString is a phone number including the international code.
String certRequest = Base64.encodeToString(kpGen.getEncoded(), Base64.DEFAULT);
certRequest = certRequest.replace("\n", "");
return certRequest;
} catch(Exception e) {
if (Constants.DEBUG) {
Log.v("Exception", e.getMessage());
}
return null;
}
}
The string that I get from this function is sent to the server so it can be signed and then the cert is returned in the response.
Any help would be appreciated.
Look into the basic utils package: https://pub.dev/packages/basic_utils.
It contains several utility classes, including X509Utils and CryptoUtils, which you can use to generate your keypair and CSR. The snippet below should provide some reference:
import 'package:basic_utils/basic_utils.dart';
...
const Map<String, String> X509_SUBJECT = {
'CN': '...',
'C': '...',
'ST': '...',
'L': '...',
'O': '...',
'OU': '...',
};
AsymmetricKeyPair keyPair = CryptoUtils.generateEcKeyPair();
X509Utils.generateRsaCsrPem(X509_SUBJECT, keypair.privateKey, keypair.publicKey);
Android API 28 running on a Pixel 3 gives the option to require User Presence for keys generated inside the Android KeyStore. But how do I actually test for user presence when creating a signature with that key? Am I missing something in the docs?
KeyGenParameterSpec.Builder keyGenSpec = new KeyGenParameterSpec.Builder("alias", KeyProperties.PURPOSE_SIGN)
.setDigests(KeyProperties.DIGEST_SHA256)
.setKeySize(256)
.setIsStrongBoxBacked(true)
.setUserPresenceRequired(true);
KeyPairGenerator keyPairGenerator = KeyPairGenerator.getInstance("EC", "AndroidKeyStore");
keyPairGenerator.initialize(keyGenSpec.build());
keyPairGenerator.generateKeyPair();
KeyStore keyStore = KeyStore.getInstance("AndroidKeyStore");
keyStore.load(null, null);
PrivateKey privateKey = (PrivateKey) keyStore.getKey("alias", null);
Signature signature = Signature.getInstance("SHA256withECDSA");
signature.initSign(privateKey);
signature.update("Hello".getBytes(Charset.defaultCharset()));
byte[] sign = signature.sign();
That code throws android.security.KeyStoreException: -69 which translates to PROOF_OF_PRESENCE_REQUIRED. I've also tried to wrap the signature process in an BiometricPrompt, but to no avail.
I'm trying to store a short piece of text inside Android's Secure element using the following code:
KeyStore ks = KeyStore.getInstance("AndroidKeyStore");
ks.load(null);
PasswordProtection keyStorePP = new KeyStore.PasswordProtection(null);
SecretKeyFactory factory = SecretKeyFactory.getInstance("PBEwithHmacSHA1");
SecretKey generatedSecret = factory.generateSecret(newPBEKeySpec(mySecureText.toCharArray()));
ks.setEntry("alias", new KeyStore.SecretKeyEntry(generatedSecret), keyStorePP);
The code above gives the following exception:
08-17 14:39:32.832: W/System.err(11713): java.security.KeyStoreException: Unsupported protection parameter class: java.security.KeyStore$PasswordProtection. Supported: android.security.keystore.KeyProtection
Is there something I'm doing wrong or just AndroidKeyStore does not support storing strings?
Last thing, if I'll switch to BKS the code will execute.
Thanks.
The error tells you to change this:
PasswordProtection keyStorePP = new KeyStore.PasswordProtection(null);
To this
PasswordProtection keyStorePP = new KeyStore.KeyProtection();
KeyProtection cant set null password.
char[] password = {'p','a','s','s'};
KeyStore.ProtectionParameter protParam =
new KeyStore.PasswordProtection(password);
ks.setEntry("alias", new KeyStore.SecretKeyEntry(generatedSecret), keyStorePP);
I got this to work below. This is how you specify new KeyProtection:
import android.security.keystore.KeyProtection;
import android.security.keystore.KeyProperties;
...............
KeyStore keyStore = KeyStore.getInstance("AndroidKeyStore");
keyStore.load(null, null);
SecureRandom secureRandom = new SecureRandom(mySecureText.getBytes());
KeyGenerator keyGenerator = KeyGenerator.getInstance(KeyProperties.KEY_ALGORITHM_AES);
keyGenerator.init(256, secureRandom);
SecretKeySpec keySpec = new SecretKeySpec(keyGenerator.generateKey().getEncoded(), KeyProperties.KEY_ALGORITHM_AES);
keyStore.setEntry(alias, new SecretKeyEntry(keySpec),
new KeyProtection.Builder(KeyProperties.PURPOSE_ENCRYPT | KeyProperties.PURPOSE_DECRYPT)
.setBlockModes(KeyProperties.BLOCK_MODE_GCM)
.setEncryptionPaddings(KeyProperties.ENCRYPTION_PADDING_NONE)
.setRandomizedEncryptionRequired(false)
.build()
);
In my app we are using RSA key, that the app generate (using android key store) on the first launch. From unknown reason, the app failed to retrieved the key from the key store on some of the devices. I've checked the logs, and I could not find a correlation between this bug to a specific OS version or to a specific device model. Also, I know for sure the app tried to read it only after the key created. So - my question is that: As far as I know, android key store should be persistent. What can cause such a bug?
Bellow are relevant code samples.
Key generation:
try {
KeyPairGenerator generator = KeyPairGenerator.getInstance("RSA", keyStore.getProvider());
if (android.os.Build.VERSION.SDK_INT >= android.os.Build.VERSION_CODES.M){
KeyGenParameterSpec spec;
spec = new KeyGenParameterSpec.Builder(alias, KeyProperties.PURPOSE_DECRYPT | KeyProperties.PURPOSE_ENCRYPT| KeyProperties.PURPOSE_SIGN| KeyProperties.PURPOSE_VERIFY)
.setDigests(KeyProperties.DIGEST_SHA256, KeyProperties.DIGEST_SHA512)
.setEncryptionPaddings(KeyProperties.ENCRYPTION_PADDING_RSA_PKCS1)
.setSignaturePaddings(KeyProperties.SIGNATURE_PADDING_RSA_PKCS1)
.setKeySize(2048)
.build();
generator.initialize(spec);
} else {
Calendar start = new GregorianCalendar();
Calendar end = new GregorianCalendar();
end.add(Calendar.YEAR, 500);
KeyPairGeneratorSpec spec = new KeyPairGeneratorSpec.Builder(context)
.setAlias(alias)
.setSubject(new X500Principal("CN="+ subject))
.setSerialNumber(BigInteger.valueOf(new Random().nextInt(Integer.MAX_VALUE)))
.setStartDate(start.getTime())
.setEndDate(end.getTime())
.setKeySize(2048)
.build();
generator.initialize(spec);
}
return generator.generateKeyPair();
} catch (Exception e) {
logger.warn("Failed to create private key in store", e);
return null;
}
The keystore itself intialized using the following code:
KeyStore androidKeyStore = KeyStore.getInstance("AndroidKeyStore");
androidKeyStore.load(null);
return androidKeyStore;
And we use the following code to retrieve the key, the bug is that on some devices the keystore returns null:
try {
Key key = keyStore.getKey(alias, null);
if (key == null){
logger.warn("Key not found in key store");
return null;
}
if (key instanceof PrivateKey) {
// Get certificate of public key
Certificate cert = keyStore.getCertificate(alias);
// Get public key
PublicKey publicKey = cert.getPublicKey();
// Return a key pair
return new KeyPair(publicKey, (PrivateKey) key);
} else {
logger.warn("Key found, but not from current type. type found: " + key.getClass().getSimpleName());
}
return null;
}catch (Exception e){
logger.warn("Failed to get private key in store", e);
return null;
}
Thanks,
Omer
Just in case someone will run into the same problem:
I've found out that Azure Active Directory library for android suffer from similar issue, and from reading the code I've saw they linked to two issues that are similar to this problem and to another issue we have. Because of that I am planing to use keystore based on p12 file, stored in the app private storage.
Is it that you appear to lose your keys immediately after generation or some time later they are lost? Take a look at this question AndroidKeyStore getEntry is consistently failing after certain point which links to this great article: http://doridori.github.io/android-security-the-forgetful-keystore/
The moral of this story is if you use the AndroidKeyStore be prepared to lose your keys under certain circumstances!
I'm trying to create certificate requests according to PKCS#10.
X509v3 Key Usage should be set to: Digital Signature, Key Encipherment.
This is what I've found so far, but since I use latest bouncycastle (1.52) the X509Extension.keyUsage is deprecated.
public static PKCS10CertificationRequest generateCSRFile(KeyPair keyPair, KeyUsage keyUsage) throws IOException, OperatorCreationException {
String principal = "CN=" + Utils.getCertificateCommonName() + ", O=" + Utils.getCertificateOrganization();
AsymmetricKeyParameter privateKey = PrivateKeyFactory.createKey(keyPair.getPrivate().getEncoded());
AlgorithmIdentifier signatureAlgorithm = new DefaultSignatureAlgorithmIdentifierFinder().find("SHA1WITHRSA");
AlgorithmIdentifier digestAlgorithm = new DefaultDigestAlgorithmIdentifierFinder().find("SHA-1");
ContentSigner signer = new BcRSAContentSignerBuilder(signatureAlgorithm, digestAlgorithm).build(privateKey);
PKCS10CertificationRequestBuilder csrBuilder = new JcaPKCS10CertificationRequestBuilder(new X500Name(principal), keyPair.getPublic());
ExtensionsGenerator extensionsGenerator = new ExtensionsGenerator();
extensionsGenerator.addExtension(X509Extension.basicConstraints, true, new BasicConstraints(true));
extensionsGenerator.addExtension(X509Extension.keyUsage, true, keyUsage);
csrBuilder.addAttribute(PKCSObjectIdentifiers.pkcs_9_at_extensionRequest, extensionsGenerator.generate());
PKCS10CertificationRequest csr = csrBuilder.build(signer);
return csr;
}
Is there another way to add KeyUsage?
Basically this is what I'm trying to achieve:
Generate certificate requests with PKCS#10
Create two certificates from two keypairs (Digital Signature + Key Encipherment and Non Repudiation).
Save the private keys from the certificate requests in PKCS#12.
I'm quite new when it comes to cryptography.
Does this work for you? X509Extension seems to be replaceable with Extension. Not sure on the Android context though.
public static PKCS10CertificationRequest generateCSRFile(KeyPair keyPair, KeyUsage keyUsage) throws IOException, OperatorCreationException {
String principal = "CN=" + Utils.getCertificateCommonName() + ", O=" + Utils.getCertificateOrganization();
AsymmetricKeyParameter privateKey = PrivateKeyFactory.createKey(keyPair.getPrivate().getEncoded());
AlgorithmIdentifier signatureAlgorithm = new DefaultSignatureAlgorithmIdentifierFinder().find("SHA1WITHRSA");
AlgorithmIdentifier digestAlgorithm = new DefaultDigestAlgorithmIdentifierFinder().find("SHA-1");
ContentSigner signer = new BcRSAContentSignerBuilder(signatureAlgorithm, digestAlgorithm).build(privateKey);
PKCS10CertificationRequestBuilder csrBuilder = new JcaPKCS10CertificationRequestBuilder(new X500Name(principal), keyPair.getPublic());
ExtensionsGenerator extensionsGenerator = new ExtensionsGenerator();
extensionsGenerator.addExtension(Extension.basicConstraints, true, new BasicConstraints(true));
extensionsGenerator.addExtension(Extension.keyUsage, true, keyUsage);
csrBuilder.addAttribute(PKCSObjectIdentifiers.pkcs_9_at_extensionRequest, extensionsGenerator.generate());
PKCS10CertificationRequest csr = csrBuilder.build(signer);
return csr;
}