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