在Java中解析Armored ECC公钥/私钥(从gpg cli生成)

9
我正在尝试将一把加密的ECC gpg密钥转换为相应的Java类ECPrivateKey/ECPublicKey。
我使用以下命令生成密钥对:gpg --expert --full-generate-key 然后选择(9)ECC和ECC(或(10)ECC(仅签名))
然后选择(3)NIST P-256
结果如下:
-----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-----

我该如何将这个加密文本格式转换为有效的java.security.interfaces.ECPrivateKey和java.security.interfaces.ECPublicKey类?

我的最终目标是以以下方式进行签名:

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
    }
2个回答

7
如果您只传入“私钥块”,那么这将提取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);
}

为了回答一个关于如何获得“privateKey”的评论问题,如果整个:
-----BEGIN PGP PRIVATE KEY BLOCK-----
...
-----END PGP PRIVATE KEY BLOCK-----

如果文件中的内容是文本形式,那么只需要将整个文件读入到一个byte[]数组中:
InputStream fIn = new BufferedInputStream(new FileInputStream(...));
byte[] privateKey = org.bouncycastle.util.io.Streams.readAll(fIn);

你能更具体地说明如何从私钥块获取到 byte[] privateKey 参数吗? - Tomer Mayost
我得到了 java.io.IOException: 遇到未知的PGP公钥算法 在以下代码中:PGPSecretKeyRing pgpSecRing = (PGPSecretKeyRing)pgpFact.nextObject()当以以下方式提供私钥块给您的方法时: privateKeyBlock.split('\n').join().bytes - Tomer Mayost
你使用的BC版本是什么?我认为PGP EC密钥只在1.50左右开始支持。 - Peter Dettman
谢谢。我正在使用“org.bouncycastle:bcpg-jdk16:1.46”,将尝试更新。 - Tomer Mayost
更新了BC到bcprov-jdk15on:1.57(最新版) 我遇到了org.bouncycastle.openpgp.PGPException: exception constructing public key的问题 Caused by: java.security.spec.InvalidParameterSpecException: Not a supported curve: java.security.spec.ECGenParameterSpec@4dc27487 在 return (ECPrivateKey)new JcaPGPKeyConverter().getPrivateKey(pgpPriv); - Tomer Mayost

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;
    }

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接