最近和联通的数据生成系统对接需要使用PGP工具,网上查了资料,调了一整天终于出来了,下面介绍下使用方法以及碰到的一些的小坑
<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>
bcprov-jdk15on.jar 常用的加解密jar包,相信大家都很熟悉了,我们通过它来实现PGP加解密及生成公私钥对
public class PgpUtil {
/**
* 私有方法,用于生成指定位宽的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密钥
* 密钥是将密钥对的私钥部分用对称的加密方法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 = PgpUtil.generateKeyPair(rsaWidth_); //生成RSA密钥对
PGPDigestCalculator sha1Calc = new JcaPGPDigestCalculatorProviderBuilder().build().get(HashAlgorithmTags.SHA1); //使用SHA1作为证书的散列算法
/**
* 用证书等级生成的认证,将公私钥对和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错误
*/
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)
);
}
@SuppressWarnings("restriction")
public static void main(String[] args) throws Exception {
Security.addProvider(new org.bouncycastle.jce.provider.BouncyCastleProvider());
String passPhrase_ = "123456789";
char[] passPhrase = passPhrase_.toCharArray(); //将passPharse转换成字符数组
PGPSecretKey secretKey = PgpUtil.getSecretKey("wathdata", passPhrase_, 2048);
// 这里打印私钥-------------重要
String privateKeyString = new BASE64Encoder().encode(secretKey.getEncoded());
System.out.println(privateKeyString);
PGPPublicKey publicKey = secretKey.getPublicKey();
//FileOutputStream fileOutputStream = new FileOutputStream("c://1.txt");
byte[] encoded = publicKey.getEncoded();
// 这里打印公钥----------------重要
String publicKeyString = new BASE64Encoder().encode(encoded);
System.out.println(publicKeyString);
}
}
执行结果
私钥--------------------
lQO+BF6MRCABCADGKYKqSOtysDV4TX1ubaOVVxJbQ2gIlm9J/S0qQcvkip2nX/zfrDnFLH7eJFk5
gVcBX9Vc9uearmGI6xRzp8rqsyhVL/Nbp6zOkCTrRvglSnfrqPa2f7hzlKU976Za9NMwJHU4X0Mp
xTV6/fut3oYFU7uW/INoNc2NB65ajL0qzMP3MKX9k2OLhdHAB/4jsw9tUmTjr+2H5s+hLdpsq4cp
CscrfZf+Q2BZ+1b6BFMrMnf6aFBpiwL18zF5RME45mdrfb7VfGQR0mn5UmY8EXNNOjORLTZ4PTOb
y/VGBvMtOyJ++ggaRncrW9CNB1590A2CVy48Q7gNdVLFRmzPMCFjABEBAAH+AwMClr2r0h2iECpg
u+OVyOCH/GDpWN1NJ4F2kW34B0JSfQBHREJ56tbX4BD6k79pxfid9nift8YKUCc8fF/pkJOVKDGp
wcM66oinAfyYj4ZyLEY5l29FksEMD94YCZMU+Kcaz/hvO8WBC2a1MhIHzVwaNwIbKC3l0ZuW7qKM
u1nMnaIeTJHZcHpNluwaHY0NGZXf8TcCwIbncu4AVWo9JU3CAaCILMyezi2Rpc/lKRAK9a4zMb5M
PkXThDSUEFuLIxZYiJVncpGK1n65oxnrZvPIU8+Ha+yo2e+413J1/yyfwBfj0MhyCFpil99iibam
tQZnwx9XNvszSsXvbK68I9PD28PDJth/uDIHABfceQfZOiSnRL6ZT8bZPkqONENt2+Ia4U+PuK8l
FqA0flsOhOFb3Qnv/h8yRD+lFwscVWxfzwe8zvcoerotr87tjc0HNKNXLXQefWO9DV4iAt7O0vKI
QWqgvheMIK3zMiqR9i2510Y+1PQIqWBjyZtxjRAo5XDCu1BJcnQEUV58l0X1YCsRNMv+UxaQPIos
UnuiqLetrE7HC+Fxkv7DWkIeVAe/E7VExfwNiVFRdhnU3Djjp0IUEr9B6/CAQ4Kr6P9gLlN4UKV6
x8Wqz+/s2zAJj3bHNN884G71NnBnffb6iLIdv256D3Abfuo4y4ZwiVFs2URUfdKJmFEf9BngP5+A
ITs1z01OwkmlEsESjMGhSsUayLlOGo1obCubUIlXUfd5UxGRN2fQKGA8UNXUKWL53FczHYozuJER
9n7Q/guex6u/nfVJyhZW7DSuNp41FYPs/4ychVHo9sa5biGJ8X4MilvRnNfcdkKplJQs/h7KF+7a
aF+OrBN0H6kZAxwpfNjN28Um386zLjfTOYen6/b3oXNRknJ8ka53U3udq+cO2gCplLQId2F0aGRh
dGGJARwEEAECAAYFAl6MRCAACgkQxq3anzI2L9BCKAgAsF58+UyC8RK8Rasuluj5yNtYvzTI9AdZ
n5r18XjZ5PitodN9G3uq9nDKMogFb9l4Tl1r70oUNKu1QJ42mlDzMZ4icppaKjYARN7wIxFm/Mik
R5WtnQ76qJPsyAN0aE0jhRGaadQ9J4rw/M7wEoQ2r/O9AfNpCpcs30VNihWvhB5uqiDup8AIKf4f
yWSlH7dAKVbsrVWIjjdLuvSBFiu/LFP7V/Vn61WAmcVLhRWioWCfU8HbS0d14VKCpalTSuEaEY7+
PgBs1ThqyM+/n4QdKQcjpd2wqeDqQUb7SAuQ3/j91XuKNLXT/+YsuLPxT0mB0K1e8uBzUd3CmK/D
lCkPCg==
公钥-------------------
mQENBF6MRCABCADGKYKqSOtysDV4TX1ubaOVVxJbQ2gIlm9J/S0qQcvkip2nX/zfrDnFLH7eJFk5
gVcBX9Vc9uearmGI6xRzp8rqsyhVL/Nbp6zOkCTrRvglSnfrqPa2f7hzlKU976Za9NMwJHU4X0Mp
xTV6/fut3oYFU7uW/INoNc2NB65ajL0qzMP3MKX9k2OLhdHAB/4jsw9tUmTjr+2H5s+hLdpsq4cp
CscrfZf+Q2BZ+1b6BFMrMnf6aFBpiwL18zF5RME45mdrfb7VfGQR0mn5UmY8EXNNOjORLTZ4PTOb
y/VGBvMtOyJ++ggaRncrW9CNB1590A2CVy48Q7gNdVLFRmzPMCFjABEBAAG0CHdhdGhkYXRhiQEc
BBABAgAGBQJejEQgAAoJEMat2p8yNi/QQigIALBefPlMgvESvEWrLpbo+cjbWL80yPQHWZ+a9fF4
2eT4raHTfRt7qvZwyjKIBW/ZeE5da+9KFDSrtUCeNppQ8zGeInKaWio2AETe8CMRZvzIpEeVrZ0O
+qiT7MgDdGhNI4URmmnUPSeK8PzO8BKENq/zvQHzaQqXLN9FTYoVr4Qebqog7qfACCn+H8lkpR+3
QClW7K1ViI43S7r0gRYrvyxT+1f1Z+tVgJnFS4UVoqFgn1PB20tHdeFSgqWpU0rhGhGO/j4AbNU4
asjPv5+EHSkHI6XdsKng6kFG+0gLkN/4/dV7ijS10//mLLiz8U9JgdCtXvLgc1Hdwpivw5QpDwo=
/**
* PGP加解密文件
* @author zhibin.wang
*
*/
public class KeyBasedFileProcessorKey
{
/**
*
* @param inputFileName 要解密的文件名
* @param key 私钥
* @param passwd 私钥解密key
* @param defaultFileName 输出解密的文件
* @throws IOException
* @throws NoSuchProviderException
*/
public static void decryptFile(
String inputFileName,
String key,
char[] passwd,
String defaultFileName)
throws IOException, NoSuchProviderException
{
InputStream in = new BufferedInputStream(new FileInputStream(inputFileName));
byte[] decode = Base64.getDecoder().decode(key);
decryptFile(in, decode, passwd, defaultFileName);
in.close();
}
/**
*
* decrypt the passed in message stream
*
*/
private static void decryptFile(
InputStream in,
byte[] keyIn,
char[] passwd,
String defaultFileName)
throws IOException, NoSuchProviderException
{
in = PGPUtil.getDecoderStream(in);
try
{
JcaPGPObjectFactory pgpF = new JcaPGPObjectFactory(in);
PGPEncryptedDataList enc;
Object o = pgpF.nextObject();
//
// the first object might be a PGP marker packet.
//
if (o instanceof PGPEncryptedDataList)
{
enc = (PGPEncryptedDataList) o;
}
else
{
enc = (PGPEncryptedDataList) pgpF.nextObject();
}
//
// find the secret key
//
Iterator it = enc.getEncryptedDataObjects();
PGPPrivateKey sKey = null;
PGPPublicKeyEncryptedData pbe = null;
PGPSecretKeyRingCollection pgpSec = new PGPSecretKeyRingCollection(
keyIn, new JcaKeyFingerprintCalculator());
while (sKey == null && it.hasNext())
{
pbe = (PGPPublicKeyEncryptedData) it.next();
sKey = PGPExampleUtil.findSecretKey(pgpSec, pbe.getKeyID(), passwd);
}
if (sKey == null)
{
throw new IllegalArgumentException("secret key for message not found.");
}
InputStream clear = pbe
.getDataStream(new JcePublicKeyDataDecryptorFactoryBuilder().setProvider("BC").build(sKey));
JcaPGPObjectFactory plainFact = new JcaPGPObjectFactory(clear);
Object message = plainFact.nextObject();
if (message instanceof PGPCompressedData)
{
PGPCompressedData cData = (PGPCompressedData) message;
JcaPGPObjectFactory pgpFact = new JcaPGPObjectFactory(cData.getDataStream());
message = pgpFact.nextObject();
}
if (message instanceof PGPLiteralData)
{
PGPLiteralData ld = (PGPLiteralData) message;
String outFileName = ld.getFileName();
if (outFileName.length() == 0)
{
outFileName = defaultFileName;
} else {
/**
*
* modify 20160520
*
* set fileName
*
* 不同的系统可能源文件的包含的路径信息不同。
*
*/
String separator = "";
if (outFileName.contains("/")) {
separator = "/";
} else if (outFileName.contains("\\")) {
separator = "\\";
}
String fileName = outFileName.substring(outFileName.lastIndexOf(File.separator) + 1);
//
String defseparator = "";
if (defaultFileName.contains("/")) {
defseparator = "/";
} else if (defaultFileName.contains("\\")) {
defseparator = "\\";
}
defaultFileName = defaultFileName.substring(0, defaultFileName.lastIndexOf(defseparator));
outFileName = defaultFileName + File.separator + fileName;
}
InputStream unc = ld.getInputStream();
OutputStream fOut = new BufferedOutputStream(new FileOutputStream(outFileName));
Streams.pipeAll(unc, fOut);
fOut.close();
}
else if (message instanceof PGPOnePassSignatureList)
{
throw new PGPException("encrypted message contains a signed message - not literal data.");
}
else
{
throw new PGPException("message is not a simple encrypted file - type unknown.");
}
if (pbe.isIntegrityProtected())
{
if (!pbe.verify())
{
System.err.println("message failed integrity check");
}
else
{
System.err.println("message integrity check passed");
}
}
else
{
System.err.println("no message integrity check");
}
}
catch (PGPException e)
{
System.err.println(e);
if (e.getUnderlyingException() != null)
{
e.getUnderlyingException().printStackTrace();
}
}
}
/**
*
* @param outputFileName 输出的加密文件名 2.pgp
* @param inputFileName 输入的要加密的文件
* @param encryKey 公钥
* @param armor true
* @param withIntegrityCheck true
* @throws IOException
* @throws NoSuchProviderException
* @throws PGPException
*/
public static void encryptFile(
String outputFileName,
String inputFileName,
String encryKey,
boolean armor,
boolean withIntegrityCheck)
throws IOException, NoSuchProviderException, PGPException
{
OutputStream out = new BufferedOutputStream(new FileOutputStream(outputFileName));
Decoder decoder = Base64.getDecoder();
byte[] decode = decoder.decode(encryKey);
PGPPublicKey encKey = PGPExampleUtil.readPublicKey(decode);
encryptFile(out, inputFileName, encKey, armor, withIntegrityCheck);
out.close();
}
private static void encryptFile(
OutputStream out,
String fileName,
PGPPublicKey encKey,
boolean armor,
boolean withIntegrityCheck)
throws IOException, NoSuchProviderException
{
if (armor)
{
out = new ArmoredOutputStream(out);
}
try
{
byte[] bytes = PGPExampleUtil.compressFile(fileName, CompressionAlgorithmTags.ZIP);
PGPEncryptedDataGenerator encGen = new PGPEncryptedDataGenerator(
new JcePGPDataEncryptorBuilder(PGPEncryptedData.CAST5).setWithIntegrityPacket(withIntegrityCheck)
.setSecureRandom(new SecureRandom()).setProvider("BC"));
encGen.addMethod(new JcePublicKeyKeyEncryptionMethodGenerator(encKey).setProvider("BC"));
OutputStream cOut = encGen.open(out, bytes.length);
cOut.write(bytes);
cOut.close();
if (armor)
{
out.close();
}
}
catch (PGPException e)
{
System.err.println(e);
if (e.getUnderlyingException() != null)
{
e.getUnderlyingException().printStackTrace();
}
}
}
public static void main(
String[] args)
throws Exception
{
String keyString = "mQENBF6MQAIBCACrMsize7pLL99P/r6SZc793tbQ6bwNlgPsMv4l+Yl0uD2AHaslg8/5BWNeIjzq" +
"0xL0W4+8oWHCOw68Xw5a4ah8zFhkXM92iTeGb6zivTBrLOGGD+8wJRLALKke6/c9YZTqAxUFbIxf" +
"0p9MuQLv2IQYDRr017N8q0bYN1tRz1sebEjvJ8noSkCEopkYIQ+vD6w2wOyN4bKHCa16qgM7S88D" +
"/QjPviYWrXkVr1AdMmzo4NGMs4yqRFxeGMhHPAf3Z0vuWJJssdyC1wcB3qnj9vtM2vxBJlhAYTdx" +
"C4WbB5eAXPZmBFWZ+Wb0CJaOxeCVpVHAJgTUjsRH7X3iZ0/a/EfbABEBAAG0CHdhdGhkYXRhiQEc" +
"BBABAgAGBQJejEACAAoJEMKE0EVeprn3fG4H/0G4Fp3GZmAYfLNgcxqLxMFLFPxSdft+6NE8mOmK" +
"DuKdsiHYcOpdBvm4YUeB9qyZGDeGHvRY3VzzDXU7QMD6/OXKZ3sDfcTcy0Cr2O1y7JNIJzC91cZ9" +
"kroP4p0tmcwSRGlo+BI7G5oDXSM/pf05+8JE55nXh8uQ0MaYy/wxyfp2EDeLj2FMFjFuW112a00K" +
"T1XzMqZ43Ubdsqe3sshiGFTA4tG3tQQh1sAjF6NLMnAo/pxzekcGB8RzkucW0M99IAmmhMksHPnH" +
"wQ3wKl8NAkJJXyXTmQtQ6/jbJ+fsdIj/FJNgpc5+pEbS+vUxkOUHXs2wTQ5y0Fag7R52Ps1MO3M=";
String privateKeyString = "lQO+BF6MQAIBCACrMsize7pLL99P/r6SZc793tbQ6bwNlgPsMv4l+Yl0uD2AHaslg8/5BWNeIjzq" +
"0xL0W4+8oWHCOw68Xw5a4ah8zFhkXM92iTeGb6zivTBrLOGGD+8wJRLALKke6/c9YZTqAxUFbIxf" +
"0p9MuQLv2IQYDRr017N8q0bYN1tRz1sebEjvJ8noSkCEopkYIQ+vD6w2wOyN4bKHCa16qgM7S88D" +
"/QjPviYWrXkVr1AdMmzo4NGMs4yqRFxeGMhHPAf3Z0vuWJJssdyC1wcB3qnj9vtM2vxBJlhAYTdx" +
"C4WbB5eAXPZmBFWZ+Wb0CJaOxeCVpVHAJgTUjsRH7X3iZ0/a/EfbABEBAAH+AwMCV9cV6pssqM1g" +
"DSvDGApI59SQilmCijhWSJvEUwksqvMjyeiHjDvLmrxMpsnzWe7vSK1xABpph5A/HkVhQK3B+P8j" +
"MkI4kTiiFFcJ6ZpbjiBN/A8xgnHYdimXXsPe2lsZVAzQOS2NvQ56nRwtW1EKa2++X66u6syu3DzO" +
"9yqtfKzn7+oyWAsLu7xO5nYJQGZZJTIKZi3B9nR6Q8gpQ3qgFVEmQzF6x4MRypOH/TasspWnxV7Y" +
"0zXl6CU7KXpIBfakfslOI1mPK1LycduWugxkLhkGngwFTH60HnB5hziAWyEmh1VJUHnoOERMBRtM" +
"rFFyPR3ZKUBGDFm8dpBAgXEvow0dXhurTS3SEUkw5vEAa/0M1YB9TxQyGsIP9PMtI+TqBc9CS+xA" +
"X+Ls6forCLWykjxPaEKDafH4F8dumrNvLYHWtBI/XAwwmTelhMSQcLCH5pF1PM4Svn9DoPchXp3A" +
"BMVfcr66BStNuz35L5IW6UHNOYQHQMKjySU76yrXwn476ri0SYKp8+x1EQZuUK+T5zI2RvMLZa/8" +
"EhWT79EoIXUjHQbo7vjTRsmoboLNq3lfd6OUcQf8tB+2+QQkMHWH4Ew0VTxpaUfc4NpjVvxoneig" +
"qwnL8xFgtznEFAtiMqLWzbnTM+Fsy0EL3y0nQvYkl+4qJ+pwBdT2588zff8tCkAPqyd2KAD5e2pG" +
"GfIaKtAjrcZ0OQOZy5GkwP7DJIptKcjCZBHmrV6wLdHCNqg8+29dxJHjSDMS+COMNEDjECoPIcWY" +
"1K6VTzZqOqYxOuqJK79nVIIL7lkrYn1Sr4j7jLi4RqtGPEiVURPsfoCqHlZ0mQskztIvmF6dokOU" +
"rlz46ad6kJ5B16zck1Ubk7MSrbsWoJN08WvNKn5nzgpyxecpmaX3Z4DcPTWl1fHdb7QId2F0aGRh" +
"dGGJARwEEAECAAYFAl6MQAIACgkQwoTQRV6mufd8bgf/QbgWncZmYBh8s2BzGovEwUsU/FJ1+37o" +
"0TyY6YoO4p2yIdhw6l0G+bhhR4H2rJkYN4Ye9FjdXPMNdTtAwPr85cpnewN9xNzLQKvY7XLsk0gn" +
"ML3Vxn2Sug/inS2ZzBJEaWj4EjsbmgNdIz+l/Tn7wkTnmdeHy5DQxpjL/DHJ+nYQN4uPYUwWMW5b" +
"XXZrTQpPVfMypnjdRt2yp7eyyGIYVMDi0be1BCHWwCMXo0sycCj+nHN6RwYHxHOS5xbQz30gCaaE" +
"ySwc+cfBDfAqXw0CQklfJdOZC1Dr+Nsn5+x0iP8Uk2Clzn6kRtL69TGQ5QdezbBNDnLQVqDtHnY+" +
"zUw7cw==";
Security.addProvider(new BouncyCastleProvider());
encryptFile("c://2.pgp", "c://2.txt", keyString, true, true); // 加密文件
decryptFile("c://2.pgp", privateKeyString, "123456789".toCharArray(), "c://3.txt");// 解密文件
}
}
大家可以直接拿来使用上面的代码,文件加解密和公私钥对生成都已经提供测试方法
并且上面的方法已经通过了PGP Desktop工具验证