使用Bouncy Castle创建PGP密钥对

最后一个寒假了,由于正在走向Java猴子的路上,所以要适应一大批Java的轮子,那就从最基本的加密开始做笔记吧。

PGP是常用的非对称加密手段,也是我所喜欢加在项目里的轮子,Bouncy Castle中的PGP包可以满足所有PGP需求。

Bouncy Castle介绍

Bouncy Castle(以下简称为BC)是一个开源的Java包,其中几乎包含了程序猿们所听说过的所有加密方式,十分简洁易用。Bouncy Castle的优势不仅仅体现在其轻便性,更重要的是他横跨了所有的Java平台,即使是J2ME都在他的魔爪之下。

当然,Bouncy Castle还有对应的C#类库,并且中文资料挺多~

Bouncy Castle PGP包

PGP包的包名为org.bouncycastle.bcpg,使用Maven的话需要添加以下依赖:

<!-- Provider依赖 -->
<!-- https://mvnrepository.com/artifact/org.bouncycastle/bcprov-jdk15on -->
        <dependency>
            <groupId>org.bouncycastle</groupId>
            <artifactId>bcprov-jdk15on</artifactId>
            <version>1.56</version>
        </dependency>

<!-- OpenPGP包依赖 -->
<!-- https://mvnrepository.com/artifact/org.bouncycastle/bcpg-jdk15on -->
        <dependency>
            <groupId>org.bouncycastle</groupId>
            <artifactId>bcpg-jdk15on</artifactId>
            <version>1.56</version>
        </dependency>

BC中PGP密钥对的构成

这里不讨论PGP的原理(毕竟几年没学数论了23333)。我们知道在信息的非对称加密中,需要有公钥和私钥两把钥匙。

公钥(PublicKey)负责加密内容,私钥(PrivateKey)负责解密内容。在PGP的规则里,私钥是由密钥对的所有者保管的,他是用对称加密方法(CAST-128)被加密在密钥中(也就是SecretKey),这样确保只有知道密钥口令的人才能掌握私钥。

BC将PublicKey和加密后的PrivateKey存放在的SecretKey对象中。

BC所处理的PGP的加解密流程也不在本文的讨论范围中,后续会跟上。

Java使用BC生成密钥对

先上代码:

     
    /**
     * 私有方法,用于生成指定位宽的PGP RSA密钥对
     *
     * @param rsaWidth_ RSA密钥位宽
     * @return 未经私钥加密的PGP密钥对
     * @throws Exception IO错误,数值错误等
     */
    private static PGPKeyPair generateKeyPair(int rsaWidth_) throws Exception {
        KeyPairGenerator kpg = KeyPairGenerator.getInstance("RSA", "BC");//获取密钥对生成器实例
        kpg.initialize(rsaWidth_);//设定RSA位宽
        KeyPair kp = kpg.generateKeyPair();//生成RSA密钥对
        return new JcaPGPKeyPair(PGPPublicKey.RSA_GENERAL, kp, new Date());//返回根据日期,密钥对生成的PGP密钥对
    }

    /**
     * 获取PGP密钥<br>
     * 密钥是将密钥对的私钥部分用对称的加密方法CAST-128算法加密,再加上公钥部分
     *
     * @param identity_   密钥ID也就是key值,可以用来标记密钥属于谁
     * @param passPhrase_ 密钥的密码,用来解出私钥
     * @param rsaWidth_   RSA位宽
     * @return PGP密钥
     * @throws Exception IO错误和数值错误等
     */
    public static PGPSecretKey getSecretKey(String identity_, String passPhrase_, int rsaWidth_) throws Exception {
        char[] passPhrase = passPhrase_.toCharArray(); //将passPharse转换成字符数组
        PGPKeyPair keyPair = KeyPairGeneratorRSA.generateKeyPair(rsaWidth_); //生成RSA密钥对
        PGPDigestCalculator sha1Calc = new JcaPGPDigestCalculatorProviderBuilder().build().get(HashAlgorithmTags.SHA1); //使用SHA1作为证书的散列算法
        return new PGPSecretKey(
                PGPSignature.DEFAULT_CERTIFICATION,
                keyPair,
                identity_,
                sha1Calc,
                null,
                null,
                new JcaPGPContentSignerBuilder(keyPair.getPublicKey().getAlgorithm(), HashAlgorithmTags.SHA1),
                //密钥的加密方式
                new JcePBESecretKeyEncryptorBuilder(PGPEncryptedData.CAST5, sha1Calc).setProvider("BC").build(passPhrase)
        );
    }

以上就是BC根据用户标识和私钥密码生成PGPSecretKey的过程,其实最关键的是PGPSecretKey的构造方法,以下是这个构造方法JavaDoc的翻译:

	/**
     * 用证书等级生成的认证,将公私钥对和PGP ID密码绑定构造PGP密钥(SecretKey)
     *
     * @param certificationLevel PGP密钥的证书等级
     * @param keyPair 需要绑定的公私钥对
     * @param id 需要绑定的ID
     * @param checksumCalculator 散列值计算器,用于计算私钥密码散列
     * @param hashedPcks the hashed packets to be added to the certification.(先不管)
     * @param unhashedPcks the unhashed packets to be added to the certification.(也先不管)
     * @param certificationSignerBuilder PGP证书的生成器
     * @param keyEncryptor 如果需要加密私钥,需要在这里传入私钥加密器
     * @throws PGPException 一些PGP错误
     */
    public PGPSecretKey(
        int                         certificationLevel,
        PGPKeyPair                  keyPair,
        String                      id,
        PGPDigestCalculator         checksumCalculator,
        PGPSignatureSubpacketVector hashedPcks,
        PGPSignatureSubpacketVector unhashedPcks,
        PGPContentSignerBuilder     certificationSignerBuilder,
        PBESecretKeyEncryptor       keyEncryptor)

生成完整的PGP密钥证书只需要PGPSecretKey这个的构造方法。后续获得publicKey只需要使用getPublicKey()方法即可。

怎么从密钥对象中得到私钥呢?需要用extractPrivateKey()方法,这个方法有一个参数,根据PGP的逻辑,这个参数一定是用来通过密文也就是代码中的passParse来解密PrivateKey内容的。方法如下:

//我们已经有了secretKey对象
PGPPrivateKey privateKey = secretKey.extractPrivateKey(new JcePBESecretKeyDecryptorBuilder().setProvider("BC").build(passParse));

写完了,睡觉去~

发表评论?

0 条评论。

发表评论


注意 - 你可以用以下 HTML tags and attributes:
<a href="" title=""> <abbr title=""> <acronym title=""> <b> <blockquote cite=""> <cite> <code> <del datetime=""> <em> <i> <q cite=""> <s> <strike> <strong>