GPG、PGP加密&解密

叶富
2023-12-01

GPG加密&解密

PGP介绍

PGP是一种用于数据加密和数字签名的程序,它使得一般人也可以很容易地对数据文件、邮件进行加密;PGP拥有“信任网络(Web of Trust)”的美称。PGP主要的特点是它可以在一个非线程安全的网络环境下,使得从未谋面的人取得信任。这个软件非常好用,迅速流传开来,成了许多程序员的必备工具。但是,它是商业软件,不能自由使用。所以,自由软件基金会决定,开发一个PGP的替代品,取名为GnuPG。这就是GPG的由来。

1. Centos7安装GPG,并导出公钥

# 安装gnupg
yum install -y  gnupg 
# 验证是否安装成功
gpg --help
# 生成密钥
[root@43-c58487531-0048-1226258 ~]# gpg --gen-key
gpg (GnuPG) 2.0.22; Copyright (C) 2013 Free Software Foundation, Inc.
This is free software: you are free to change and redistribute it.
There is NO WARRANTY, to the extent permitted by law.

Please select what kind of key you want:
   (1) RSA and RSA (default)
   (2) DSA and Elgamal
   (3) DSA (sign only)
   (4) RSA (sign only)
Your selection? 1
RSA keys may be between 1024 and 4096 bits long.
What keysize do you want? (2048) 
Requested keysize is 2048 bits
Please specify how long the key should be valid.
         0 = key does not expire
      <n>  = key expires in n days
      <n>w = key expires in n weeks
      <n>m = key expires in n months
      <n>y = key expires in n years
Key is valid for? (0) 0
Key does not expire at all
Is this correct? (y/N) y

GnuPG needs to construct a user ID to identify your key.

Real name: gpg.test
Email address: gpg@test.com
Comment: 
You selected this USER-ID:
    "gpg.test <gpg@test.com>"

Change (N)ame, (C)omment, (E)mail or (O)kay/(Q)uit? O
You need a Passphrase to protect your secret key.

We need to generate a lot of random bytes. It is a good idea to perform
some other action (type on the keyboard, move the mouse, utilize the
disks) during the prime generation; this gives the random number
generator a better chance to gain enough entropy.
We need to generate a lot of random bytes. It is a good idea to perform
some other action (type on the keyboard, move the mouse, utilize the
disks) during the prime generation; this gives the random number
generator a better chance to gain enough entropy.
gpg: key 98B95556 marked as ultimately trusted
public and secret key created and signed.

gpg: checking the trustdb
gpg: 3 marginal(s) needed, 1 complete(s) needed, PGP trust model
gpg: depth: 0  valid:   2  signed:   0  trust: 0-, 0q, 0n, 0m, 0f, 2u
pub   2048R/98B95556 2021-04-28
      Key fingerprint = C0A1 5063 8F48 FEF4 AE5C  066E F775 9672 98B9 5556
uid                  gpg.test <gpg@test.com>
sub   2048R/8EDF4C6F 2021-04-28
# 查看密钥
[root@43-c58487531-0048-1226258 ~]# gpg --list-keys
/root/.gnupg/pubring.gpg
------------------------
pub   4096R/CF8E292A 2014-11-23
uid                  Pritunl <contact@pritunl.com>
sub   4096R/4E47D23D 2014-11-23

pub   2048R/98B95556 2021-04-28
uid                  gpg.test <gpg@test.com>
sub   2048R/8EDF4C6F 2021-04-28
# 导出公钥 gpg --export -a --output 公钥的名称 邮箱地址
[root@43-c58487531-0048-1226258 ~]# gpg --export -a --output gpg.test.asc gpg@test.com
# 查看公钥
[root@43-c58487531-0048-1226258 ~]# cat gpg.test.asc 
-----BEGIN PGP PUBLIC KEY BLOCK-----
Version: GnuPG v2.0.22 (GNU/Linux)

mQENBGCJKBIBCAC+M2wf9yXIfyRkFVvAV8CopCjCkL3oUFvyrS9rFD5plmk0vTk2
EN5Lwy3gf+0CYZ5HSKZfh1kbgKx45njX58ojUx+fffDs9aeOaAqnJ4h048Ffcpkq
jzVA9clTeN0YB5GBlIYl365lv6S/pIVldMiRUxXt6h2NseI+fXyKsKDbzTBDJUZ5
57bbtCxY8vSqxoqbM6GPthQFRp9nvo52dLfQFJhcBDCPQd4xSG2ciBPvPiHVJUCZ
d4qzfoSaHHTPRWAUdiKeFwVMpfYrKtl4ILSnqqxdhSIZhqMZLejH7iOOonUBtxXe
5JlNeFkcqMkYJd6Rw0eRuf0Yh9UnJw2zJjV5ABEBAAG0F2dwZy50ZXN0IDxncGdA
dGVzdC5jb20+iQE5BBMBAgAjBQJgiSgSAhsDBwsJCAcDAgEGFQgCCQoLBBYCAwEC
HgECF4AACgkQ93WWcpi5VVao5Af+Jb39nhqgBdJneDsh3XU4R7rah4S+Rz6uz/yW
Vc1Pfg+ROBXnGU16XYZeQo6CbpOtJ4qE80lqFB1SizVGyoV5gEW0B+VlsvNr9zDQ
jLnxx2g7JjL31suiKuXzp+JkMfbxMGtlgQ1nrVl47dvOUEIdSwq7kFWU7em3hK3C
3oNtBKQ1Bjuj+q0LRsTy9rGlb//lqnSGVw/p0dV8+Cq7yYRwjy+9NJkJ+bUPVic2
OSxqQmYSYv3C96k4P333a28C2pS8rrS64qevkdxlcDC9LCfzdDrMv4ABRL8Obk+R
rXDjXp81tp0/6Tx/+p5KQOFvxC8nFUMpEgg7yPna/7fJcqpknLkBDQRgiSgSAQgA
rGavDyykZ55QN3av0PADboRPEnpbRsGrw1tI5pb3x3fiSAOrkN4MPpXZ65qSbZ7a
lIlh8KMOb2TbDvlpYDa+wttJY32tnWnNeFjaeWdPkgf687D78fNw8ofdKbXrwu8r
3tfT82pus+lFA1vZ3CPbN1S/UIIOkajMjQArX/KHmhmYgXuI+f8e0tc1kgWEpJR2
7G0CUHOib5TQShhP+yIiHWxH0+yPALUihNOmQIJhMUGIyRX7wDTvcGRN9rtwFGJ6
gaQUCFVc7FtoOMm1X1oINY6JVIJC+9YO0wANp2lHBSe0M1cb66W2wZWw4MW7ZVMs
HmwrcLYCENpu+qBIi+zCbQARAQABiQEfBBgBAgAJBQJgiSgSAhsMAAoJEPd1lnKY
uVVWeT8H/2/qfoO6pI0t5An+HIhGVqQ4lxcl9wfkkbztChGY8Zvr2FX9n+wHtRk1
kdRZwbm3EoHZzjkQgNBnCK6qyVszZR3hJV2JnSCqkD5Au9dYj5A2MPqfTe1MlQdM
VJV/yIO/3YYUhTDZBu0/YCnWxUNSJwPkH6Xsn/lFeo1vrc/il5i3Nsktk6sDi3n4
/c+Tq5gAn/oa27AuREg1NZQ1nXDQe6hc1iKt/lUi27gvkoHbB0gnkqIT4UiH2R7i
A7JRFIcqMKbjWaRm1D5sSa9Zc2mvtvF2MV8/3TMSX+EL94V1UugMkPNyomMXiiOK
ym2jZ6RwcWUqlTaTRsMae5mSVLPzBB4=
=Jw99
-----END PGP PUBLIC KEY BLOCK-----

2. 编写GPG加密工具类

<dependency>
	<groupId>org.bouncycastle</groupId>
	<artifactId>bcpg-jdk15on</artifactId>
	<version>1.50</version>
</dependency>
<dependency>
	<groupId>commons-io</groupId>
	<artifactId>commons-io</artifactId>
	<version>2.4</version>
</dependency>
package com.example.util;

import java.io.*;
import java.security.SecureRandom;
import java.security.Security;
import java.util.Iterator;
import org.apache.commons.io.IOUtils;
import org.bouncycastle.bcpg.ArmoredOutputStream;
import org.bouncycastle.jce.provider.BouncyCastleProvider;
import org.bouncycastle.openpgp.*;
import org.bouncycastle.openpgp.operator.jcajce.JcePGPDataEncryptorBuilder;
import org.bouncycastle.openpgp.operator.jcajce.JcePublicKeyKeyEncryptionMethodGenerator;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.util.Assert;

/**
 * GPG 加密工具类
 *
 * @author jinliang 2021/04/28
 */
public class GpgCoderUtil {

    private static final Logger logger = LoggerFactory.getLogger(GpgCoderUtil.class);

    /**
     * GPG 加密
     *
     * @param inputFileName     待加密的文件名
     * @param publicKeyFileName 加密的公钥
     * @param armor             是否按文本输出
     * @return outputFileName 加密后的文件名
     */
    public static String encryptFile(String inputFileName, String publicKeyFileName, boolean armor) {
        logger.info("开始进行GPG加密,文件名:{}", inputFileName);
        Assert.notNull(inputFileName, "待加密的文件名不能为空");
        Assert.notNull(publicKeyFileName, "公钥不能为空");
        String outputFileName = inputFileName + ".gpg";
        //添加BC Security Provider
        Security.addProvider(new BouncyCastleProvider());
        OutputStream outputStream = null;
        InputStream publicKeyInputStream = null;
        OutputStream pgpOutputSteam = null;
        try {
            outputStream = new BufferedOutputStream(new FileOutputStream(outputFileName));
            publicKeyInputStream = new BufferedInputStream(new FileInputStream(publicKeyFileName));

            //生成加密的公钥
            PGPPublicKey pgpPublicKey = readPublicKey(publicKeyInputStream);
            //是否按文本输出
            if (armor) {
                outputStream = new ArmoredOutputStream(outputStream);
            }
            //将文件压缩,转换为字节流
            ByteArrayOutputStream byteArrayOutputStream = new ByteArrayOutputStream();
            PGPCompressedDataGenerator pgpCompressedDataGenerator = new PGPCompressedDataGenerator(PGPCompressedData.ZIP);

            PGPUtil.writeFileToLiteralData(pgpCompressedDataGenerator.open(byteArrayOutputStream),
                    PGPLiteralData.BINARY, new File(inputFileName));
            pgpCompressedDataGenerator.close();

            //构造PGP加密器
            JcePGPDataEncryptorBuilder pgpDataEncryptorBuilder = new JcePGPDataEncryptorBuilder(PGPEncryptedData.CAST5)
                    .setWithIntegrityPacket(true).setSecureRandom(new SecureRandom()).setProvider("BC");

            PGPEncryptedDataGenerator pgpEncryptedDataGenerator = new PGPEncryptedDataGenerator(pgpDataEncryptorBuilder);

            JcePublicKeyKeyEncryptionMethodGenerator jcePublicKeyKeyEncryptionMethodGenerator =
                    new JcePublicKeyKeyEncryptionMethodGenerator(pgpPublicKey)
                    .setProvider(new BouncyCastleProvider()).setSecureRandom(new SecureRandom());

            pgpEncryptedDataGenerator.addMethod(jcePublicKeyKeyEncryptionMethodGenerator);

            byte[] bytes = byteArrayOutputStream.toByteArray();
            //文件加密,并将结果写入输出流
            pgpOutputSteam = pgpEncryptedDataGenerator.open(outputStream, bytes.length);
            pgpOutputSteam.write(bytes);
        } catch (Exception e) {
            logger.error("GPG加密失败,失败原因:{}", e.getMessage());
        } finally {
            //关闭流
            IOUtils.closeQuietly(publicKeyInputStream);
            IOUtils.closeQuietly(outputStream);
            IOUtils.closeQuietly(pgpOutputSteam);
        }
        logger.info("PGP加密成功,加密后的文件名:{}", outputFileName);
        return outputFileName;
    }


    /**
     * @param in 密钥文件流
     * @return 公钥
     * @throws IOException
     * @throws PGPException
     */
    private static PGPPublicKey readPublicKey(InputStream in) throws IOException, PGPException {

        in = PGPUtil.getDecoderStream(in);
        PGPPublicKeyRingCollection pgpPub = new PGPPublicKeyRingCollection(in);

        PGPPublicKey key = null;
        Iterator<PGPPublicKeyRing> rIt = pgpPub.getKeyRings();
        //遍历集合获取加密的密钥
        while (key == null && rIt.hasNext()) {
            PGPPublicKeyRing kRing = rIt.next();
            Iterator<PGPPublicKey> kIt = kRing.getPublicKeys();
            while (key == null && kIt.hasNext()) {
                PGPPublicKey k = kIt.next();
                if (k.isEncryptionKey()) {
                    key = k;
                }
            }
        }
        if (key == null) {
            throw new IllegalArgumentException("在密钥环中找不到加密密钥.");
        }
        return key;
    }


}

3.测试GPG加密工具类

  @Test
    public void testGpg() {
        // 第一个参数为需要加密的文件,第二个参数为导出的公钥,第三个参数为是否需要文本化输出,true为文本输出,false为二进制输出
        GpgCoderUtil.encryptFile("D:\\data\\output\\output.txt","D:\\data\\public\\gpg.test.asc",false);
    }
2021-04-28 17:26:30.868  INFO 19884 --- [           main] com.example.util.GpgCoderUtil            : 开始进行GPG加密,文件名:D:\data\output\output.txt
2021-04-28 17:26:32.007  INFO 19884 --- [           main] com.example.util.GpgCoderUtil            : PGP加密成功,加密后的文件名:D:\data\output\output.txt.gpg

4. 上传加密后的文件到服务器进行验证

[root@43-c58487531-0048-1226258 ~]# ls -lh
total 68K
-rw-r--r-- 1 root root  643 Apr 28 17:28 output.txt.gpg
#解密 解密时需要输入密码(生成密钥匙时设置的)
[root@43-c58487531-0048-1226258 ~]# gpg output.txt.gpg 

You need a passphrase to unlock the secret key for
user: "gpg.test <gpg@test.com>"
2048-bit RSA key, ID 98B95556, created 2021-04-28

gpg: encrypted with 2048-bit RSA key, ID 98B95556, created 2021-04-28
      "gpg.test <gpg@test.com>"
gpg: [don't know]: invalid packet (ctb=0d)
gpg: [don't know]: invalid packet (ctb=0a)
gpg: mdc_packet with invalid encoding
gpg: decryption failed: Invalid packet
gpg: [don't know]: invalid packet (ctb=54)
#查看解密后的文件
[root@43-c58487531-0048-1226258 ~]# ls -lh
-rw-r--r-- 1 root root  28K Apr 28 17:29 output.txt
-rw-r--r-- 1 root root  643 Apr 28 17:28 output.txt.gpg

参考

GPG入门教程

Installing and Using PGP

 类似资料: