Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Access PFX File ECC PrivateKey For Symmetric Key Generation in Java

I'm writing a program (in Java using the Bouncy Castle API) that encrypts files with AES-256/GCM with a key generated by ONE EC key pair. I have the symmetric encryption portion working perfectly but now the key generation is proving to be difficult. When trying to use the ECPublicKey object in the javax.crypto.KeyAgreement.init() method, it returns this error:

Exception in thread "main" java.security.InvalidKeyException: ECDH key agreement requires ECPrivateKey for initialisation
    at org.bouncycastle.jcajce.provider.asymmetric.ec.KeyAgreementSpi.initFromKey(Unknown Source)
    at org.bouncycastle.jcajce.provider.asymmetric.ec.KeyAgreementSpi.engineInit(Unknown Source)
    at javax.crypto.KeyAgreement.init(KeyAgreement.java:461)
    at javax.crypto.KeyAgreement.init(KeyAgreement.java:435)
    at Encrpytion.generateKey(Encrpytion.java:188)  ---aKA.init(key);---
    at Encrpytion.main(Encrpytion.java:40) ---byte[] key = generateKey();---

The source code for the method is as follows:

public static byte[] generateKey() {
    KeyStore ks = KeyStore.getInstance("PKCS12");
    FileInputStream fis = new FileInputStream("file.pfx");
    Scanner input = new Scanner(System.in);
    char[] chars = {};
    System.out.println("Enter the password for your .pfx: ");
    chars = input.nextLine().toCharArray();
    input.close();
    Enumeration aliasEnum = null;
    ks.load(fis, chars);
    aliasEnum = ks.aliases();
    Key key = null;
    Certificate cert = null;
    while (aliasEnum.hasMoreElements()){
        String param = (String)aliasEnum.nextElement();
        if (ks.isKeyEntry(param))  {
            String keyName = param;
            key = ks.getKey(keyName,chars);
        }
        cert = ks.getCertificateChain(param)[0];
    }
    KeyPair kp = new KeyPair(cert.getPublicKey(),(ECPrivateKey) key);
    KeyAgreement aKA = null;
    aKA = KeyAgreement.getInstance("ECDH", "BC");
    aKA.init(key);
    aKA.doPhase(kp.getPublic(), true);
    return aKA.generateSecret();
}

How can I access the PrivateKey of the certificate?

**UPDATE: **updated sourcecode

** EDIT ** The following commands and instructions can be used to create some test files to demonstrate the problem.

#Make root key
openssl ecparam -name secp521r1 -genkey -out root.key
#password protect key
openssl ec -in root.key -out root.key -aes256
####change req x509_attributes to rootCA
#selfsign root
openssl req -new -x509 -key root.key -out root.crt -days 1825 -config openssl.cfg
###comment out req x509_attributes
#make new key for clientCA
openssl ecparam -name secp521r1 -genkey -out client.key
#make clientCA csr
openssl req -new -sha384 -key client.key -out client.csr -config openssl.cfg
#sign clientCA
openssl ca -out client.crt -name root -in client.csr -config openssl.cfg
#make client key
openssl ecparam -name secp521r1 -genkey -out client.key
#make server csr
openssl req -new -sha384 -key client.key -out client.csr -config openssl.cfg
#sign server cert
openssl ca -out client.crt -name client -in client.csr -config openssl.cfg
#MAKE CHAIN - copy base64 encoded root and intermidiate client ca into same "chain.cer" #export user cert 
openssl pkcs12 -export -chain -CAfile chain.crt -in client.crt -inkey client.key -out client.pfx -aes256
like image 450
Jim Avatar asked Oct 22 '22 22:10

Jim


1 Answers

For any given alias only one of key and cert will be non-null. In this case evidently cert was non-null and key was null. If there is an EC private key in your keystore you will need to work a little harder to find it. You can determine what kind of entry is at the alias by testing with isCertificateEntry and isKeyEntry.

EDIT 1

The error message is unfortunately confusing because it is actually coming from the bouncycastle library. The class referred to in ECDH key agreement requires ECPrivateKey is not java.security.interfaces.ECPrivateKey but rather org.bouncycastle.jce.interfaces.ECPrivateKey

The following short adaptation of the OPs sample code illustrates one way to overcome this.

import org.bouncycastle.jce.provider.JCEECPrivateKey;
import org.bouncycastle.jce.provider.JCEECPublicKey;
// ...
// . <original example code goes here>
// .
JCEECPrivateKey ecPrivKey = new JCEECPrivateKey((ECPrivateKey) key);
JCEECPublicKey ecPubKey = new JCEECPublicKey((ECPublicKey) cert.getPublicKey());
KeyPair kp = new KeyPair(ecPubKey, ecPrivKey);
KeyAgreement aKA = null;
aKA = KeyAgreement.getInstance("ECDH", "BC");
aKA.init(ecPrivKey);
aKA.doPhase(kp.getPublic(), true);
return aKA.generateSecret();
// ...
like image 124
President James K. Polk Avatar answered Oct 29 '22 18:10

President James K. Polk