在 java 中解析 A​​rmored ECC 公钥/私钥(从 gpg cli 生成)

Posted

技术标签:

【中文标题】在 java 中解析 A​​rmored ECC 公钥/私钥(从 gpg cli 生成)【英文标题】:Parse Armored ECC public/private keys (generated from gpg cli) in java 【发布时间】:2017-11-21 22:13:59 【问题描述】:

我正在尝试将装甲 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 java 类?

我的最终目标是通过以下方式登录:

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
    

【问题讨论】:

【参考方案1】:

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

在一个文件中,然后只需将整个文件读入一个字节[]:

InputStream fIn = new BufferedInputStream(new FileInputStream(...));
byte[] privateKey = org.bouncycastle.util.io.Streams.readAll(fIn);

【讨论】:

您能否更具体地了解如何从私钥块获取到 byte[] privateKey 参数? 当通过以下方式向您的方法提供私钥块时,我收到 java.io.IOException:在:PGPSecretKeyRing pgpSecRing = (PGPSecretKeyRing)pgpFact.nextObject() 上遇到未知的 PGP 公钥算法: privateKeyBlock.split('\n').join().bytes 您使用的是哪个版本的 BC?我认为仅从 1.50 左右开始支持 PGP EC 密钥。 谢谢。我正在使用 'org.bouncycastle:bcpg-jdk16:1.46',将尝试更新。 将 BC 更新为 bcprov-jdk15on:1.57(最新)我收到 org.bouncycastle.openpgp.PGPException:构造公钥的异常 原因:java.security.spec.InvalidParameterSpecException:不支持的曲线: java.security.spec.ECGenParameterSpec@4dc27487 on return (ECPrivateKey)new JcaPGPKeyConverter().getPrivateKey(pgpPriv);【参考方案2】:
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;
    

【讨论】:

以上是关于在 java 中解析 A​​rmored ECC 公钥/私钥(从 gpg cli 生成)的主要内容,如果未能解决你的问题,请参考以下文章

解析 ECC 私钥缓冲区

java实现ecc加密:通过AES获取公钥和私钥进行ECC加密

u-boot-2016.03 在mini2440上移植之nandflash 硬件ecc

对称加密算法

Java ECC 编码的密钥太大

Kotlin ECC 加密