Parse Armored ECC public/private keys (generated from gpg cli) in java
Asked Answered
P

2

9

I'm trying to convert an armored ECC gpg key to corresponding java class ECPrivateKey/ECPublicKey.

To generate the key pair I'm using: gpg --expert --full-generate-key

Then selecting (9) ECC and ECC (or (10) ECC (sign only))

Then selecting (3) NIST P-256

Resulting in:

-----BEGIN PGP PUBLIC KEY BLOCK-----

mFIEWUdzwhMIKoZIzj0DAQcCAwQkAvZC1PIJ8ke1myyKhNny9vN78TIYo2MuAOY+
F38L9S3+Za9cKV/iIHOqfapbMoqdSmSnqDkevwQSr5MF2UOXtCJzaWduZWNjIChF
Q0Mgc2lnbiBvbmx5KSA8c3NAc3MuY28+iJAEExMIADgWIQRiC+kefVkjnjKovKy5
XANFl5+n1gUCWUdzwgIbAwULCQgHAgYVCAkKCwIEFgIDAQIeAQIXgAAKCRC5XANF
l5+n1mzGAQDsgutymxDTTXPKFfpFFVp4fxacx1MSqxP71gNJYjguXwD8CEXD20Vm
aU1WMi2jU7JC6oJn94Y4vWHwTLOU1zmQ19o=
=swfS
-----END PGP PUBLIC KEY BLOCK-----

-----BEGIN PGP PRIVATE KEY BLOCK-----

lHcEWUdzwhMIKoZIzj0DAQcCAwQkAvZC1PIJ8ke1myyKhNny9vN78TIYo2MuAOY+
F38L9S3+Za9cKV/iIHOqfapbMoqdSmSnqDkevwQSr5MF2UOXAAD9FhS2HZoWOyIi
l9nj+WPa9S1o50jM5bNIRALzcyS8SgoP97Qic2lnbmVjYyAoRUNDIHNpZ24gb25s
eSkgPHNzQHNzLmNvPoiQBBMTCAA4FiEEYgvpHn1ZI54yqLysuVwDRZefp9YFAllH
c8ICGwMFCwkIBwIGFQgJCgsCBBYCAwECHgECF4AACgkQuVwDRZefp9ZsxgEA7ILr
cpsQ001zyhX6RRVaeH8WnMdTEqsT+9YDSWI4Ll8A/AhFw9tFZmlNVjIto1OyQuqC
Z/eGOL1h8EyzlNc5kNfa
=qHBB
-----END PGP PRIVATE KEY BLOCK-----

How do I get from this armored text format to a valid java.security.interfaces.ECPrivateKey and java.security.interfaces.ECPublicKey java classes?

My final goal is to sign in the following way:

String createSignatureFromJson(String jsonPayload, byte[] privateKey) {
        Payload payload = new Payload(jsonPayload)
        def key = privateKeyParse(privateKey)

        JWSSigner signer = new ECDSASigner((ECPrivateKey)key)
        JWSHeader header = new JWSHeader.Builder(JWSAlgorithm.ES256).build()

        JWSObject jwsObject = new JWSObject(header, payload)
        jwsObject.sign(signer)
        jwsObject.signature
    }
Phung answered 19/6, 2017 at 15:48 Comment(0)
P
1
public static ECPrivateKey privateKeyParse(byte[] privateKey) throws Exception {

        InputStream pgpIn = PGPUtil.getDecoderStream(new ByteArrayInputStream(privateKey));

        PGPObjectFactory pgpFact = new PGPObjectFactory(pgpIn, new JcaKeyFingerprintCalculator());
        PGPSecretKeyRing pgpSecRing = (PGPSecretKeyRing) pgpFact.nextObject();
        PGPSecretKey pgpSec = pgpSecRing.getSecretKey();

        PGPPrivateKey pgpPriv = pgpSec.extractPrivateKey(null);

        JcaPGPKeyConverter converter = new JcaPGPKeyConverter();
        // this is the part i was missing from Peter Dettman's answer. pass BC provider to the converter
        converter.setProvider(new BouncyCastleProvider());
        PrivateKey key = converter.getPrivateKey(pgpPriv);
        return (ECPrivateKey) key;
    }
Phung answered 28/6, 2017 at 6:49 Comment(0)
S
7

If you just pass in the 'private key block', this will extract the ECPrivateKey:

private static ECPrivateKey privateKeyParse(byte[] privateKey) throws Exception
{
    InputStream pgpIn = PGPUtil.getDecoderStream(new ByteArrayInputStream(privateKey));

    PGPObjectFactory pgpFact = new PGPObjectFactory(pgpIn, new JcaKeyFingerprintCalculator());
    PGPSecretKeyRing pgpSecRing = (PGPSecretKeyRing)pgpFact.nextObject();
    PGPSecretKey pgpSec = pgpSecRing.getSecretKey();
    PGPPrivateKey pgpPriv = pgpSec.extractPrivateKey(null);

    return (ECPrivateKey)new JcaPGPKeyConverter().getPrivateKey(pgpPriv);
}

To answer a comment question as to how to how to get 'privateKey', if the entire:

-----BEGIN PGP PRIVATE KEY BLOCK-----
...
-----END PGP PRIVATE KEY BLOCK-----

is in a file, then just read the whole file into a byte[]:

InputStream fIn = new BufferedInputStream(new FileInputStream(...));
byte[] privateKey = org.bouncycastle.util.io.Streams.readAll(fIn);
Surfboarding answered 20/6, 2017 at 7:17 Comment(5)
can you be more specific about how to get from the private key block to the byte[] privateKey parameter?Phung
I'm getting java.io.IOException: unknown PGP public key algorithm encountered on: PGPSecretKeyRing pgpSecRing = (PGPSecretKeyRing)pgpFact.nextObject() when supplying the private key block to your method in the following way: privateKeyBlock.split('\n').join().bytesPhung
What version of BC are you using? I think PGP EC keys were only supported from around 1.50 .Surfboarding
Thanks. I'm using 'org.bouncycastle:bcpg-jdk16:1.46', will try to update.Phung
updated BC to bcprov-jdk15on:1.57 (latest) I'm getting org.bouncycastle.openpgp.PGPException: exception constructing public key Caused by: java.security.spec.InvalidParameterSpecException: Not a supported curve: java.security.spec.ECGenParameterSpec@4dc27487 on return (ECPrivateKey)new JcaPGPKeyConverter().getPrivateKey(pgpPriv);Phung
P
1
public static ECPrivateKey privateKeyParse(byte[] privateKey) throws Exception {

        InputStream pgpIn = PGPUtil.getDecoderStream(new ByteArrayInputStream(privateKey));

        PGPObjectFactory pgpFact = new PGPObjectFactory(pgpIn, new JcaKeyFingerprintCalculator());
        PGPSecretKeyRing pgpSecRing = (PGPSecretKeyRing) pgpFact.nextObject();
        PGPSecretKey pgpSec = pgpSecRing.getSecretKey();

        PGPPrivateKey pgpPriv = pgpSec.extractPrivateKey(null);

        JcaPGPKeyConverter converter = new JcaPGPKeyConverter();
        // this is the part i was missing from Peter Dettman's answer. pass BC provider to the converter
        converter.setProvider(new BouncyCastleProvider());
        PrivateKey key = converter.getPrivateKey(pgpPriv);
        return (ECPrivateKey) key;
    }
Phung answered 28/6, 2017 at 6:49 Comment(0)

© 2022 - 2024 — McMap. All rights reserved.