How to retrieve a PrivateKey from Azure Key Vault - java

I'm building a xml document signature API backed by Azure Key Vault (AKV).
I have an asymmetric certificate imported into AKV, which is stored as [Key, Secret and Certificate].
I've managed to sign the document, but I think that I'm not getting the right key.
The Java XML Digital Signature API need a key pair (private/public) to get some info.
I've modified a provider that I found here, and now the signature process is called from AKV instead of the java implementation.
The thing is, when I get a Key from AKV, only the public key is coming.
The private key is stored as a Secret, and I run into trouble when I try to convert the value into an instance of PrivateKey.
How can I convert the SecretBundle value into an instance of java.security.PrivateKey ?
Thanks in advance.

Below is how I had converted the Secret into a certificate file. You might be able to convert that into Java.
$kvSecret = Get-AzureKeyVaultSecret -VaultName 'VaultFromCode' -Name 'TestCertificate'
$kvSecretBytes = [System.Convert]::FromBase64String($kvSecret.SecretValueText)
$certCollection = New-Object System.Security.Cryptography.X509Certificates.X509Certificate2Collection
$certCollection.Import($kvSecretBytes,$null,[System.Security.Cryptography.X509Certificates.X509KeyStorageFlags]::Exportable)
$protectedCertificateBytes = $certCollection.Export([System.Security.Cryptography.X509Certificates.X509ContentType]::Pkcs12, 'test')
$pfxPath = 'C:\cert\test.pfx'
[System.IO.File]::WriteAllBytes($pfxPath, $protectedCertificateBytes)
You can find more details in my post - Manage Certificates in Azure Key Vault. You can also find some details on Exportable and Non-Exportable certificates in Key Vault and how those can be used to sign a PDF file.
Hope that helps

Related

Calling .getEncoded() on SecretKey returns null

I use the following code to generate an AES key:
KeyGenParameterSpec.Builder builder = new KeyGenParameterSpec.Builder("db_enc_key", KeyProperties.PURPOSE_ENCRYPT | KeyProperties.PURPOSE_DECRYPT);
KeyGenParameterSpec keySpec = builder
.setKeySize(256)
.setBlockModes("CBC")
.setEncryptionPaddings("PKCS7Padding")
.setRandomizedEncryptionRequired(true)
.setUserAuthenticationRequired(true)
.setUserAuthenticationValidityDurationSeconds(5 * 60)
.build();
KeyGenerator keyGen = KeyGenerator.getInstance("AES", "AndroidKeyStore");
keyGen.init(keySpec);
SecretKey sk = keyGen.generateKey();
but everytime I try to get the byte[] version of the key via sk.getEncoded(), the method returns null. The documentation says that it should return the encoded key, or null if the key does not support encoding, but I don't think that the key doesn't support encoding.
I need the byte[] because I want to encrypt a realm database (for which I need to combine 2 AES-256 keys as byte-arrays) [https://realm.io/docs/java/latest/#encryption]
The official documentation uses SecureRandom, but also states that this is a silly way of doing this and that the key is never stored. Therefore, I wanted to use the KeyStore to securely store the two separate AES-256 keys.
P.S.: The code is only a test code and not the final product, so any comment on coding style is useless. I'm currently just trying to get a working version going.
edit: So I tried the following code, which successfully generates an AES key (though only 16 bytes of length):
SecretKey sk1 = KeyGenerator.getInstance("AES").generateKey();
When I use the getEncoded() method on it, I'll even get the byte array, so naturally I went on and saved it to the KeyStore with the following code:
KeyStore.SecretKeyEntry entry = new KeyStore.SecretKeyEntry(sk1);
KeyStore.ProtectionParameter pp = new KeyProtection.Builder(KeyProperties.PURPOSE_DECRYPT | KeyProperties.PURPOSE_ENCRYPT).build();
keyStore.setEntry("db_enc_key_test", entry, pp);
Which also works. So I tried to read the key from the keystore via KeyStore.Entry entry2 = keyStore.getEntry("db_enc_key_test", null); which worked as well. But when I call entry2.getEncoded() the method returns null again. Is this a keystore problem?
edit2: So I just found out, that symmetric keys generated in (and apparently saved to) the keystore are unexportable in Android M, which seems to be intended, which puts me in a bit of a problem, as I need the key itself to encrypt the realm database.
Some realm-developer here to recommend a best-practice?
The fact that you cannot retrieve the encoded key is by design as the Keystore should be the only one knowing it. However you can use a double layered key:
Generate a random key and store it in the Keystore.
Generate the "real" key used by Realm and encrypt it using the key from the Keystore.
Now you have some completely random text that can be stored in e.g SharedPreferences or in a file on disk.
Whenever people wants to open the Realm, read the encrypted key on disk, decrypt it using the Keystore and now you can use it to open the Realm.
This repo here uses the same technique to save User data in a secure way: https://github.com/realm/realm-android-user-store
This is probably the class you are after: https://github.com/realm/realm-android-user-store/blob/master/app/src/main/java/io/realm/android/CipherClient.java It also handle fallback through the various Android versions (the Keystore has quite a few quirks).

Create a .jks file from a Certificate and private Key

I wanted to create a .jks file using a Certificate and a private Key from an etoken programmatically.
I could access the etoken using the password but could not read the Certificate or the private key.
In an earlier question I had asked whether the private Key could be extracted from the etoken for which I was told it was not possible.
So, my question is whether I could get the reference of the private key using PKCS11..
Thank you.
You can get the reference (object handle) of the PrivateKey using PKCS11. You can even get the public components of the PrivateKey. But you cannot extract the sensitive data that comprises the key.

BouncyCastle: Extract public key informationfrom Certificate Signing Request

I am using Bouncy Castle library in Java for reading CSR. I need to extract the public key information from CSR. I can see that openssl is able to extract required information from CSR.
I can't find any way to do this in BouncyCastle. I have been able to read PKCS10CertificationRequest object from the CSR. I have seen examples using SubjectPublicKeyInfo for extracting public key. But the code relies on the fact that algorithm of public key is already known. I can do a "instanceof" operation for various algorithm parameters and match but I think there would be something better. I want to derive the algorithm from CSR itself. I tried to find this information but couldn't find anything related to this.
Thanks for help.
Solution is to create a new wrapper around the PKCS10CertificateRequest like this:
JcaPKCS10CertificationRequest jcaCertRequest =
new JcaPKCS10CertificationRequest(pkcs10CertRequest.getEncoded()).setProvider("BC");
This class has the getPublicKey() method.
PublicKey publicKey = jcaCertRequest.getPublicKey();

Parsing certificates with bouncycastle

I'm a certificate noob. I've been trying to import certificates for the past couple of hours and the more I dig into security stuff, the more it feels impossible to understand.
Here is what I'm trying to achieve in java:
the user can upload any kind of formatted certificate with an optional passphrase
I need to convert the certificate to a non-binary PEM format
Extract the private and public keys to store in a database, throw error if missing one of the two
So far I've been able to parse some certificates using java security's x509Certificate but I can't get the private key. I've also tried bouncy castle but no success there either.
Thanks for your help
An X509Certificate only contains a public key.
Private keys are usually encoded using PKCS#8. Try KeyFactory with a PKCS8EncodedKeySpec.
Combined public key certificates with private keys are usually encoded using PKCS#12 (.pfx, .p12). Try a KeyStore of "PKCS12" type (with Bouncy Castle as provider).

PKI verification across Java and Python

I am trying to implement a PKI verification scheme, where a message string is signed with a private key on server, the signature is stored on the client along with the message string. The client then verifies the signature using a public key.
The restrictions of my environment are, the server is Google App Engine and the client is a Java program. I have played with Java-only and Python-only solutions of PKI verification and got them to work, however when doing one operation in Python and another in Java is posing problem, mainly due to Key file format restrictions and my limited understanding of cryptography terminology.
One of the biggest limitations is crypto support in GAE. The only library supported is PyCrypto and this library can't read public/private keys stored in PEM, DER or X509 formats. As far as I could find, only M2Crypto supports reading from these files, but it can't be used inside GAE because it's a wrapper around openssl, so not a pure python solution. Even if I could find a way to translate the public/private keys from PEM/DER/X509 to the format that PyCrypto understands, that will work for me. But I couldn't find any way to do it. Any ideas there?
I found one possible solution in the form of tlslite. tlslite could read a private key from PEM file and create a signature. Here is the code.
from tlslite.utils.cryptomath import bytesToBase64
from tlslite.utils.keyfactory import parsePEMKey
s = open('private.pem').read()
key = parsePEMKey(s)
doc = 'Sample text'
bytes = array('B')
bytes.fromstring(doc)
print bytesToBase64(key.sign(bytes))
The corresponding Java code I used to verify the signature is.
String signAlgo = "SHA1WithRSAEncryption";
// read public key from public.der
byte[] encodedKey = new byte[294]; // shortcut hardcoding
getAssets().open("public.der").read(encodedKey);
// create public key object
X509EncodedKeySpec publicKeySpec = new X509EncodedKeySpec(encodedKey);
KeyFactory kf = KeyFactory.getInstance("RSA");
PublicKey pk = kf.generatePublic(publicKeySpec);
// read signature (created by python code above)
byte[] encodedSig = new byte[345];
getAssets().open("signature.txt").read(encodedSig);
byte[] decodedSig = Base64.decodeBase64(encodedSig);
// Do verification
Signature verifyalg = Signature.getInstance(signAlgo);
verifyalg.initVerify(pk);
verifyalg.update(message.getBytes());
Log.d(TAG, "Verif : "+verifyalg.verify(decodedSig));
The verification fails.
I suspected if the tlslite is using different algorithm for signature creation than what the java code expects.
So I tried to find that out.
On python side
print key.getSigningAlgorithm()
gave me
pkcs1-sha1
on Java side, I tried to find all supported algorithms with this code:
Set<String> algos = java.security.Security.getAlgorithms("Signature");
for(String algo : algos) {
Log.d(TAG, algo);
}
That gave me
MD4WithRSAEncryption
RSASSA-PSS
SHA1withDSA
SHA1withRSA/ISO9796-2
1.2.840.113549.1.1.10
SHA512withRSA/PSS
MD5withRSA/ISO9796-2
DSA
SHA512WithRSAEncryption
SHA224withRSA/PSS
NONEWITHDSA
SHA256withRSA/PSS
SHA224WithRSAEncryption
SHA256WithRSAEncryption
SHA1withRSA/PSS
SHA1WithRSAEncryption
SHA384withRSA/PSS
SHA384WithRSAEncryption
MD5WithRSAEncryption
I tried all the SHA1 values on the Java side. But none helped to verify the signature generated by tlslite with pkcs1-sha1 algo. Any idea about this mapping?
These are different operations. In Python, you need to use hashAndSign. The default happens to be SHA1 hash.
Keyczar should work fine on App Engine, and is available in both Java and Python flavours.

Categories

Resources