0


加密与安全_PGP、OpenPGP和GPG加密通信协议

文章目录

在这里插入图片描述

PGP

PGP (Pretty Good Privacy) 是一种加密通信协议,用于保护电子邮件和文件的安全性和隐私。它通过使用加密、数字签名和压缩技术来确保数据的保密性、完整性和可验证性。

GP最初由麻省理工学院的Nick embrace和Eric Hughes开发,后来由Phil Zimmermann进一步发展。它使用公钥加密和私钥解密的机制,以确保只有信息的接收者才能解密和阅读邮件内容。

PGP的主要优点是它易于使用,并能在大多数流行的电子邮件客户端中集成。然而,随着更高级的加密技术和标准(如OpenPGP和GPG)的出现,PGP已经在一定程度上被这些新标准取代。

PGP不仅仅用于电子邮件,它也可以用于加密文件和数据。

  1. 加密和解密: PGP 使用对称加密和非对称加密相结合的方式来实现加密和解密。发送方使用接收方的公钥对消息进行加密,接收方使用自己的私钥对消息进行解密。此外,PGP 还支持对数据进行数字签名,以确保数据的完整性和验证发送方的身份。
  2. 密钥管理: PGP 使用密钥对来管理加密和解密过程。每个用户都有一个公钥和一个私钥。公钥用于加密消息,私钥用于解密消息。这些密钥对可以通过密钥服务器或密钥交换方式获取。
  3. 数字签名: PGP 允许用户使用自己的私钥对消息进行数字签名。接收方可以使用发送方的公钥验证签名,以确保消息的完整性和发送方的身份。
  4. 信任模型: PGP 使用基于信任的模型来验证密钥的真实性。用户可以通过直接交换密钥、使用信任链或通过信任服务器来建立信任。
  5. 开放标准: PGP 是一种开放标准,意味着任何人都可以实现和使用该协议,而不受限于特定的厂商或供应商。

总的来说,PGP 是一种强大的加密协议,用于保护通信内容的机密性和完整性,同时提供身份验证机制。它广泛用于电子邮件和文件加密,以确保用户的数据安全和隐私。


OpenPGP和GPG(GNU Privacy Guard)是PGP(Pretty Good Privacy)的开放标准和自由软件实现。 随着时间的推移,PGP的标准和实现逐渐演进,OpenPGP和GPG就是其中的两个重要发展。


OpenPGP

OpenPGP是一个开放标准,它定义了一种用于加密和数字签名数据的协议。这个标准允许不同的加密软件相互兼容,这意味着使用不同OpenPGP实现的用户可以安全地交换加密信息。OpenPGP标准是由RFC 4880定义的,它包括了公钥和私钥的生成、交换和验证方法,以及加密和签名的算法。

GPG

GPG是OpenPGP的一个流行实现,它是GNU项目的一部分,由GNU通用公共许可证(GPL)发布。GPG是一个命令行工具,可以在多种操作系统中运行,包括Linux、macOS和Windows。GPG提供了创建和验证数字签名、加密文件和电子邮件以及安全地交换密钥等功能。

GPG的核心组件包括:

  • keyring:用于存储公钥和私钥。
  • gpg:命令行工具,用于执行加密、解密、签名和验证等操作。
  • gpgconf:用于配置GPG的命令行工具。
  • gpg-agent:一个守护进程,用于提供密钥管理、加密和服务器功能。

GPG的使用场景包括:

  • 安全地交换电子邮件和文件。
  • 验证软件的完整性和来源。
  • 保护个人隐私和商业机密。

工作原理

PGP(Pretty Good Privacy)涉及加密、数字签名和密钥管理等关键步骤

在这里插入图片描述

  1. 密钥生成: 用户生成一对公钥和私钥。公钥用于加密消息,私钥用于解密消息和生成数字签名。
  2. 加密: 发送方使用接收方的公钥来加密消息。这样,只有拥有相应私钥的接收方才能解密消息。
  3. 数字签名: 发送方使用自己的私钥对消息进行签名。接收方使用发送方的公钥验证签名,确保消息的完整性和发送方的身份。
  4. 密钥管理: 用户可以通过密钥服务器或直接交换密钥的方式来管理和共享公钥。

工作流程

  1. 密钥交换: 发送方和接收方需要交换公钥。这可以通过密钥服务器、直接交换或其他安全渠道完成。
  2. 加密消息:- 发送方选择要发送的消息,并使用接收方的公钥对消息进行加密。- 发送方可以选择使用对称加密算法来加密消息内容,然后再使用接收方的公钥来加密对称密钥,这样可以提高效率。- 发送方发送加密后的消息给接收方。
  3. 解密消息:- 接收方使用自己的私钥解密接收到的消息。- 如果消息有数字签名,接收方使用发送方的公钥验证签名。
  4. 数字签名验证:- 接收方使用发送方的公钥验证数字签名,确保消息的完整性和发送方的身份。
  5. 信任管理:- 用户可以建立信任关系,以确保使用其他用户的公钥时其真实性。- 信任关系可以通过直接交换密钥、信任链或信任服务器来建立。

总的来说,PGP的工作原理涉及加密、数字签名和密钥管理,通过这些步骤保证了消息的机密性、完整性和可验证性。


用途

在这里插入图片描述

PGP 本质上有三个主要用途:

  • 发送和接收加密电子邮件。
  • 验证向你发送消息的人的身份。
  • 加密文件。

案例说明

假设Alice和Bob是两个使用PGP加密通信的用户。他们希望通过电子邮件进行安全通信,以保护其消息的机密性和完整性。

过程

  1. 密钥生成:- Alice 和 Bob 分别生成一对公钥和私钥。
  2. 密钥交换:- Alice 将她的公钥发送给 Bob,而 Bob 也将他的公钥发送给 Alice。这可以通过安全的电子邮件或其他安全通道完成。
  3. 加密消息:- Alice 决定向 Bob 发送一封加密的电子邮件。- Alice 使用 Bob 的公钥将邮件内容进行加密。- Alice 还可以选择使用对称加密算法来加密邮件内容,然后再使用 Bob 的公钥来加密对称密钥,以提高效率。
  4. 解密消息:- Bob 收到 Alice 发送的加密邮件后,使用自己的私钥解密邮件内容。- 如果邮件有数字签名,Bob 使用 Alice 的公钥验证签名,确保邮件的完整性和 Alice 的身份。
  5. 数字签名验证:- 如果 Alice 在邮件中添加了数字签名,Bob 使用 Alice 的公钥验证签名,以确保邮件的完整性和 Alice 的身份。
  6. 信任管理:- Alice 和 Bob 可能通过直接交换公钥或使用信任服务器来建立信任关系,以确保对方公钥的真实性。

在这个案例中,Alice 和 Bob 使用PGP协议加密和解密他们之间的通信,同时还可以使用数字签名来确保消息的完整性和验证发送方的身份。通过这种方式,他们可以安全地交换信息,而不用担心被未经授权的第三方窃取或篡改。


代码实现

在Java中完全实现PGP协议需要使用第三方库,因为PGP是一个复杂的加密协议。常用的库之一是Bouncy Castle.

在这里插入图片描述

pom依赖

<?xml version="1.0" encoding="UTF-8"?><projectxmlns="http://maven.apache.org/POM/4.0.0"xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd"><modelVersion>4.0.0</modelVersion><groupId>com.artisan</groupId><artifactId>pgp-encryption</artifactId><version>1.0-SNAPSHOT</version><build><plugins><plugin><groupId>org.apache.maven.plugins</groupId><artifactId>maven-compiler-plugin</artifactId><configuration><source>8</source><target>8</target></configuration></plugin></plugins></build><dependencies><!-- https://mvnrepository.com/artifact/junit/junit --><dependency><groupId>junit</groupId><artifactId>junit</artifactId><version>4.13.2</version><scope>test</scope></dependency><!-- https://mvnrepository.com/artifact/org.bouncycastle/bcprov-jdk15on --><dependency><groupId>org.bouncycastle</groupId><artifactId>bcpg-jdk15on</artifactId><version>1.70</version><scope>compile</scope></dependency><!-- https://mvnrepository.com/artifact/org.bouncycastle/bcpkix-jdk15on --><dependency><groupId>org.bouncycastle</groupId><artifactId>bcpkix-jdk15on</artifactId><version>1.70</version><scope>compile</scope></dependency><dependency><groupId>org.projectlombok</groupId><artifactId>lombok</artifactId><version>1.18.24</version><scope>provided</scope></dependency><!-- https://mvnrepository.com/artifact/commons-io/commons-io --><dependency><groupId>commons-io</groupId><artifactId>commons-io</artifactId><version>2.11.0</version></dependency></dependencies></project>

PgpEncryptionUtil

packagecom.artisan.pgpUtils;importlombok.AllArgsConstructor;importlombok.Builder;importlombok.Getter;importorg.apache.commons.io.IOUtils;importorg.bouncycastle.bcpg.ArmoredOutputStream;importorg.bouncycastle.bcpg.CompressionAlgorithmTags;importorg.bouncycastle.bcpg.SymmetricKeyAlgorithmTags;importorg.bouncycastle.jce.provider.BouncyCastleProvider;importorg.bouncycastle.openpgp.PGPCompressedDataGenerator;importorg.bouncycastle.openpgp.PGPEncryptedDataGenerator;importorg.bouncycastle.openpgp.PGPException;importorg.bouncycastle.openpgp.operator.jcajce.JcePGPDataEncryptorBuilder;importorg.bouncycastle.openpgp.operator.jcajce.JcePublicKeyKeyEncryptionMethodGenerator;importjava.io.ByteArrayInputStream;importjava.io.ByteArrayOutputStream;importjava.io.File;importjava.io.IOException;importjava.io.InputStream;importjava.io.OutputStream;importjava.nio.charset.Charset;importjava.nio.file.Files;importjava.security.SecureRandom;importjava.security.Security;importjava.util.Objects;/**
 * @author artisan
 */@Getter@Builder@AllArgsConstructorpublicclassPgpEncryptionUtil{/**
     * 将Bouncy Castle添加到JVM中
     */static{// 将Bouncy Castle添加到JVM中if(Objects.isNull(Security.getProvider(BouncyCastleProvider.PROVIDER_NAME))){Security.addProvider(newBouncyCastleProvider());}}/**
     * 压缩算法,默认为ZIP
     */@Builder.Defaultprivateint compressionAlgorithm =CompressionAlgorithmTags.ZIP;/**
     * 对称密钥算法,默认为AES-128
     */@Builder.Defaultprivateint symmetricKeyAlgorithm =SymmetricKeyAlgorithmTags.AES_128;/**
     * 是否启用ASCII Armor,默认为true
     */@Builder.Defaultprivateboolean armor =true;/**
     * 是否启用完整性检查,默认为true
     */@Builder.Defaultprivateboolean withIntegrityCheck =true;/**
     * 缓冲区大小,默认为65536字节
     */@Builder.Defaultprivateint bufferSize =1<<16;/**
     * 加密方法,将明文数据使用公钥进行加密
     *
     * @param encryptOut
     * @param clearIn
     * @param length
     * @param publicKeyIn
     * @throws IOException
     * @throws PGPException
     */publicvoidencrypt(OutputStream encryptOut,InputStream clearIn,long length,InputStream publicKeyIn)throwsIOException,PGPException{// 创建压缩数据生成器PGPCompressedDataGenerator compressedDataGenerator =newPGPCompressedDataGenerator(compressionAlgorithm);// 创建PGP加密数据生成器PGPEncryptedDataGenerator pgpEncryptedDataGenerator =newPGPEncryptedDataGenerator(// 配置加密数据生成器newJcePGPDataEncryptorBuilder(symmetricKeyAlgorithm).setWithIntegrityPacket(withIntegrityCheck).setSecureRandom(newSecureRandom()).setProvider(BouncyCastleProvider.PROVIDER_NAME));// 添加公钥
        pgpEncryptedDataGenerator.addMethod(newJcePublicKeyKeyEncryptionMethodGenerator(CommonUtils.getPublicKey(publicKeyIn)));// 如果启用ASCII Armor,则创建ArmoredOutputStreamif(armor){
            encryptOut =newArmoredOutputStream(encryptOut);}// 打开加密输出流OutputStream cipherOutStream = pgpEncryptedDataGenerator.open(encryptOut,newbyte[bufferSize]);// 将压缩数据生成器打开,并将明文数据以字面形式复制到加密输出流中CommonUtils.copyAsLiteralData(compressedDataGenerator.open(cipherOutStream), clearIn, length, bufferSize);// 依次关闭所有输出流
        compressedDataGenerator.close();
        cipherOutStream.close();
        encryptOut.close();}/**
     * 加密方法,返回加密后的字节数组
     *
     * @param clearData
     * @param pubicKeyIn
     * @return
     * @throws PGPException
     * @throws IOException
     */publicbyte[]encrypt(byte[] clearData,InputStream pubicKeyIn)throwsPGPException,IOException{ByteArrayInputStream inputStream =newByteArrayInputStream(clearData);ByteArrayOutputStream outputStream =newByteArrayOutputStream();encrypt(outputStream, inputStream, clearData.length, pubicKeyIn);return outputStream.toByteArray();}// 加密方法,返回加密后的输入流publicInputStreamencrypt(InputStream clearIn,long length,InputStream publicKeyIn)throwsIOException,PGPException{File tempFile =File.createTempFile("pgp-","-encrypted");encrypt(Files.newOutputStream(tempFile.toPath()), clearIn, length, publicKeyIn);returnFiles.newInputStream(tempFile.toPath());}/**
     * 加密方法,将明文数据使用公钥字符串进行加密
     *
     * @param clearData
     * @param publicKeyStr
     * @return
     * @throws PGPException
     * @throws IOException
     */publicbyte[]encrypt(byte[] clearData,String publicKeyStr)throwsPGPException,IOException{returnencrypt(clearData,IOUtils.toInputStream(publicKeyStr,Charset.defaultCharset()));}/**
     * 加密方法,返回加密后的输入流
     *
     * @param clearIn
     * @param length
     * @param publicKeyStr
     * @return
     * @throws IOException
     * @throws PGPException
     */publicInputStreamencrypt(InputStream clearIn,long length,String publicKeyStr)throwsIOException,PGPException{returnencrypt(clearIn, length,IOUtils.toInputStream(publicKeyStr,Charset.defaultCharset()));}}

PgpDecryptionUtil

packagecom.artisan.pgpUtils;importorg.apache.commons.io.IOUtils;importorg.bouncycastle.jce.provider.BouncyCastleProvider;importorg.bouncycastle.openpgp.PGPEncryptedData;importorg.bouncycastle.openpgp.PGPEncryptedDataList;importorg.bouncycastle.openpgp.PGPException;importorg.bouncycastle.openpgp.PGPPrivateKey;importorg.bouncycastle.openpgp.PGPPublicKeyEncryptedData;importorg.bouncycastle.openpgp.PGPSecretKey;importorg.bouncycastle.openpgp.PGPSecretKeyRingCollection;importorg.bouncycastle.openpgp.PGPUtil;importorg.bouncycastle.openpgp.jcajce.JcaPGPObjectFactory;importorg.bouncycastle.openpgp.operator.jcajce.JcaKeyFingerprintCalculator;importorg.bouncycastle.openpgp.operator.jcajce.JcePBESecretKeyDecryptorBuilder;importjava.io.ByteArrayInputStream;importjava.io.ByteArrayOutputStream;importjava.io.IOException;importjava.io.InputStream;importjava.io.OutputStream;importjava.nio.charset.Charset;importjava.security.Security;importjava.util.Iterator;importjava.util.Objects;/**
 * @author artisan
 */publicclassPgpDecryptionUtil{static{// 将Bouncy Castle添加到JVM中if(Objects.isNull(Security.getProvider(BouncyCastleProvider.PROVIDER_NAME))){Security.addProvider(newBouncyCastleProvider());}}/**
     * 私钥密码
     */privatefinalchar[] passCode;/**
     * 密钥环集合
     */privatefinalPGPSecretKeyRingCollection pgpSecretKeyRingCollection;/**
     * @param privateKeyIn
     * @param passCode
     * @throws IOException
     * @throws PGPException
     */publicPgpDecryptionUtil(InputStream privateKeyIn,String passCode)throwsIOException,PGPException{this.passCode = passCode.toCharArray();// 将密码转换为字符数组// 从输入流中读取PGP密钥环集合this.pgpSecretKeyRingCollection =newPGPSecretKeyRingCollection(PGPUtil.getDecoderStream(privateKeyIn),newJcaKeyFingerprintCalculator());}/**
     * @param privateKeyStr
     * @param passCode
     * @throws IOException
     * @throws PGPException
     */publicPgpDecryptionUtil(String privateKeyStr,String passCode)throwsIOException,PGPException{// 将私钥字符串转换为输入流this(IOUtils.toInputStream(privateKeyStr,Charset.defaultCharset()), passCode);}/**
     * 查找指定ID的私钥
     *
     * @param keyID
     * @return
     * @throws PGPException
     */privatePGPPrivateKeyfindSecretKey(long keyID)throwsPGPException{// 从密钥环中获取指定ID的私钥PGPSecretKey pgpSecretKey = pgpSecretKeyRingCollection.getSecretKey(keyID);// 使用密码解密私钥return pgpSecretKey ==null?null: pgpSecretKey.extractPrivateKey(newJcePBESecretKeyDecryptorBuilder().setProvider(BouncyCastleProvider.PROVIDER_NAME).build(passCode));}/**
     * 解密方法,将加密输入流解密为明文输出流
     *
     * @param encryptedIn
     * @param clearOut
     * @throws PGPException
     * @throws IOException
     */publicvoiddecrypt(InputStream encryptedIn,OutputStream clearOut)throwsPGPException,IOException{// 去除ASCII Armor并返回底层的二进制加密流
        encryptedIn =PGPUtil.getDecoderStream(encryptedIn);JcaPGPObjectFactory pgpObjectFactory =newJcaPGPObjectFactory(encryptedIn);Object obj = pgpObjectFactory.nextObject();// 第一个对象可能是标记数据包PGPEncryptedDataList pgpEncryptedDataList =(obj instanceofPGPEncryptedDataList)?(PGPEncryptedDataList) obj :(PGPEncryptedDataList) pgpObjectFactory.nextObject();PGPPrivateKey pgpPrivateKey =null;PGPPublicKeyEncryptedData publicKeyEncryptedData =null;Iterator<PGPEncryptedData> encryptedDataItr = pgpEncryptedDataList.getEncryptedDataObjects();while(pgpPrivateKey ==null&& encryptedDataItr.hasNext()){
            publicKeyEncryptedData =(PGPPublicKeyEncryptedData) encryptedDataItr.next();
            pgpPrivateKey =findSecretKey(publicKeyEncryptedData.getKeyID());// 查找私钥}if(Objects.isNull(publicKeyEncryptedData)){thrownewPGPException("无法生成PGPPublicKeyEncryptedData对象");}if(pgpPrivateKey ==null){thrownewPGPException("无法提取私钥");}CommonUtils.decrypt(clearOut, pgpPrivateKey, publicKeyEncryptedData);// 调用通用解密方法}/**
     * 解密方法,将加密字节数组解密为明文字节数组
     *
     * @param encryptedBytes
     * @return
     * @throws PGPException
     * @throws IOException
     */publicbyte[]decrypt(byte[] encryptedBytes)throwsPGPException,IOException{ByteArrayInputStream encryptedIn =newByteArrayInputStream(encryptedBytes);ByteArrayOutputStream clearOut =newByteArrayOutputStream();// 调用解密方法decrypt(encryptedIn, clearOut);// 将解密后的明文字节数组返回return clearOut.toByteArray();}}

CommonUtils

packagecom.artisan.pgpUtils;importorg.apache.commons.io.IOUtils;importorg.bouncycastle.jce.provider.BouncyCastleProvider;importorg.bouncycastle.openpgp.PGPCompressedData;importorg.bouncycastle.openpgp.PGPException;importorg.bouncycastle.openpgp.PGPLiteralData;importorg.bouncycastle.openpgp.PGPLiteralDataGenerator;importorg.bouncycastle.openpgp.PGPOnePassSignatureList;importorg.bouncycastle.openpgp.PGPPrivateKey;importorg.bouncycastle.openpgp.PGPPublicKey;importorg.bouncycastle.openpgp.PGPPublicKeyEncryptedData;importorg.bouncycastle.openpgp.PGPPublicKeyRing;importorg.bouncycastle.openpgp.PGPPublicKeyRingCollection;importorg.bouncycastle.openpgp.PGPUtil;importorg.bouncycastle.openpgp.jcajce.JcaPGPObjectFactory;importorg.bouncycastle.openpgp.operator.PublicKeyDataDecryptorFactory;importorg.bouncycastle.openpgp.operator.jcajce.JcaKeyFingerprintCalculator;importorg.bouncycastle.openpgp.operator.jcajce.JcePublicKeyDataDecryptorFactoryBuilder;importjava.io.BufferedInputStream;importjava.io.IOException;importjava.io.InputStream;importjava.io.OutputStream;importjava.sql.Date;importjava.time.LocalDateTime;importjava.time.ZoneOffset;importjava.util.Arrays;importjava.util.Iterator;importjava.util.Optional;/**
 * @author artisan
 */publicclassCommonUtils{/**
     * 使用提供的私钥解密公钥加密数据,并将其写入输出流
     *
     * @param clearOut               要写入数据的输出流
     * @param pgpPrivateKey          私钥实例
     * @param publicKeyEncryptedData 公钥加密数据实例
     * @throws IOException  IO相关错误
     * @throws PGPException PGP相关错误
     */staticvoiddecrypt(OutputStream clearOut,PGPPrivateKey pgpPrivateKey,PGPPublicKeyEncryptedData publicKeyEncryptedData)throwsIOException,PGPException{PublicKeyDataDecryptorFactory decryptorFactory =newJcePublicKeyDataDecryptorFactoryBuilder().setProvider(BouncyCastleProvider.PROVIDER_NAME).build(pgpPrivateKey);InputStream decryptedCompressedIn = publicKeyEncryptedData.getDataStream(decryptorFactory);JcaPGPObjectFactory decCompObjFac =newJcaPGPObjectFactory(decryptedCompressedIn);PGPCompressedData pgpCompressedData =(PGPCompressedData) decCompObjFac.nextObject();InputStream compressedDataStream =newBufferedInputStream(pgpCompressedData.getDataStream());JcaPGPObjectFactory pgpCompObjFac =newJcaPGPObjectFactory(compressedDataStream);Object message = pgpCompObjFac.nextObject();if(message instanceofPGPLiteralData){PGPLiteralData pgpLiteralData =(PGPLiteralData) message;InputStream decDataStream = pgpLiteralData.getInputStream();IOUtils.copy(decDataStream, clearOut);
            clearOut.close();}elseif(message instanceofPGPOnePassSignatureList){thrownewPGPException("加密消息包含签名消息而不是文字数据");}else{thrownewPGPException("消息不是简单加密文件 - 类型未知");}// 执行完整性检查if(publicKeyEncryptedData.isIntegrityProtected()){if(!publicKeyEncryptedData.verify()){thrownewPGPException("消息未通过完整性检查");}}}/**
     * 将输入流中的数据复制到pgp文字数据,并写入提供的输出流
     *
     * @param outputStream 输出流,要写入其中的数据
     * @param in           要读取数据的输入流
     * @param length       要读取的数据长度
     * @param bufferSize   缓冲区大小,因为使用缓冲区加速复制
     * @throws IOException IO相关错误
     */staticvoidcopyAsLiteralData(OutputStream outputStream,InputStream in,long length,int bufferSize)throwsIOException{PGPLiteralDataGenerator lData =newPGPLiteralDataGenerator();OutputStream pOut = lData.open(outputStream,PGPLiteralData.BINARY,PGPLiteralData.CONSOLE,Date.from(LocalDateTime.now().toInstant(ZoneOffset.UTC)),newbyte[bufferSize]);byte[] buff =newbyte[bufferSize];try{int len;long totalBytesWritten =0L;while(totalBytesWritten <= length &&(len = in.read(buff))>0){
                pOut.write(buff,0, len);
                totalBytesWritten += len;}
            pOut.close();}finally{// 清除缓冲区Arrays.fill(buff,(byte)0);// 关闭输入流
            in.close();}}/**
     * 从密钥输入流获取公钥
     *
     * @param keyInputStream 密钥输入流
     * @return PGPPublicKey实例
     * @throws IOException  IO相关错误
     * @throws PGPException PGP相关错误
     */staticPGPPublicKeygetPublicKey(InputStream keyInputStream)throwsIOException,PGPException{PGPPublicKeyRingCollection pgpPublicKeyRings =newPGPPublicKeyRingCollection(PGPUtil.getDecoderStream(keyInputStream),newJcaKeyFingerprintCalculator());Iterator<PGPPublicKeyRing> keyRingIterator = pgpPublicKeyRings.getKeyRings();while(keyRingIterator.hasNext()){PGPPublicKeyRing pgpPublicKeyRing = keyRingIterator.next();Optional<PGPPublicKey> pgpPublicKey =extractPGPKeyFromRing(pgpPublicKeyRing);if(pgpPublicKey.isPresent()){return pgpPublicKey.get();}}thrownewPGPException("无效的公钥");}privatestaticOptional<PGPPublicKey>extractPGPKeyFromRing(PGPPublicKeyRing pgpPublicKeyRing){for(PGPPublicKey publicKey : pgpPublicKeyRing){if(publicKey.isEncryptionKey()){returnOptional.of(publicKey);}}returnOptional.empty();}}

PgpEncryptionTest

packagecom.artisan.pgpUtils;importorg.apache.commons.io.IOUtils;importorg.bouncycastle.bcpg.CompressionAlgorithmTags;importorg.bouncycastle.bcpg.SymmetricKeyAlgorithmTags;importorg.bouncycastle.openpgp.PGPException;importorg.junit.AfterClass;importorg.junit.Before;importorg.junit.BeforeClass;importorg.junit.Test;importorg.junit.rules.TemporaryFolder;importjava.io.File;importjava.io.IOException;importjava.io.InputStream;importjava.io.OutputStream;importjava.net.URISyntaxException;importjava.net.URL;importjava.nio.charset.Charset;importjava.nio.file.Files;importjava.util.Optional;importstaticorg.junit.Assert.assertEquals;publicclassPgpEncryptionTest{publicstaticfinalTemporaryFolder tempFolder =newTemporaryFolder();privatePgpEncryptionUtil pgpEncryptionUtil =null;privatePgpDecryptionUtil pgpDecryptionUtil =null;/**
     * 加载资源文件的辅助方法
     * @param resourcePath
     * @return
     */privatestaticURLloadResource(String resourcePath){returnOptional.ofNullable(PgpEncryptionTest.class.getResource(resourcePath)).orElseThrow(()->newIllegalArgumentException("Resource not found"));}privatestaticfinalString passkey ="dummy";privatefinalURL privateKey =loadResource("/private.pgp");privatefinalURL publicKey =loadResource("/public.pgp");privatefinalURL testFile =loadResource("/Sample_CSV_5300kb.csv");privatestaticfinalString testString ="This text needs to be PGP encrypted";/**
     * 在测试类运行之前创建临时文件夹
     * @throws IOException
     */@BeforeClasspublicstaticvoidconstruct()throwsIOException{
        tempFolder.delete();
        tempFolder.create();}/**
     * 在测试类运行之后清理临时文件夹
     */@AfterClasspublicstaticvoiddestroy(){
        tempFolder.delete();}/**
     *  初始化方法,在每个测试方法运行之前执行
     */@Beforepublicvoidinit(){// 初始化加密工具类
        pgpEncryptionUtil =PgpEncryptionUtil.builder().armor(true).compressionAlgorithm(CompressionAlgorithmTags.ZIP).symmetricKeyAlgorithm(SymmetricKeyAlgorithmTags.AES_128).withIntegrityCheck(true).build();try{// 初始化解密工具类
            pgpDecryptionUtil =newPgpDecryptionUtil(privateKey.openStream(), passkey);}catch(IOException|PGPException e){thrownewRuntimeException(e);}}/**
     *  测试字节数组加密
     * @throws IOException
     * @throws PGPException
     */@TestpublicvoidtestByteEncryption()throwsIOException,PGPException{// 加密测试字节数组byte[] encryptedBytes = pgpEncryptionUtil.encrypt(testString.getBytes(Charset.defaultCharset()),
                publicKey.openStream());// 解密生成的加密字节数组byte[] decryptedBytes = pgpDecryptionUtil.decrypt(encryptedBytes);// 将解密后的字节数组转换为字符串,并与原始测试字符串进行比较assertEquals(testString,newString(decryptedBytes,Charset.defaultCharset()));}/**
     * 测试文件加密
     * @throws IOException
     * @throws URISyntaxException
     * @throws PGPException
     */@TestpublicvoidtestFileEncryption()throwsIOException,URISyntaxException,PGPException{// 生成一个从测试文件生成的PGP加密临时文件File encryptedFile = tempFolder.newFile();File originalFile =newFile(testFile.toURI());try(OutputStream fos =Files.newOutputStream(encryptedFile.toPath())){
            pgpEncryptionUtil.encrypt(fos,Files.newInputStream(originalFile.toPath()), originalFile.length(),
                    publicKey.openStream());}// 解密生成的PGP加密临时文件,并写入另一个临时文件File decryptedFile = tempFolder.newFile();
        pgpDecryptionUtil.decrypt(Files.newInputStream(encryptedFile.toPath()),Files.newOutputStream(decryptedFile.toPath()));// 比较原始文件内容与解密后的文件内容assertEquals(IOUtils.toString(Files.newInputStream(originalFile.toPath()),Charset.defaultCharset()),IOUtils.toString(Files.newInputStream(decryptedFile.toPath()),Charset.defaultCharset()));}/**
     * 测试输入流加密
     * @throws IOException
     * @throws URISyntaxException
     * @throws PGPException
     */@TestpublicvoidtestInputStreamEncryption()throwsIOException,URISyntaxException,PGPException{// 生成一个从测试文件生成的PGP加密输入流File originalFile =newFile(testFile.toURI());InputStream encryptedIn = pgpEncryptionUtil.encrypt(Files.newInputStream(originalFile.toPath()), originalFile.length(), publicKey.openStream());// 解密生成的输入流,并写入临时文件File decryptedFile = tempFolder.newFile();
        pgpDecryptionUtil.decrypt(encryptedIn,Files.newOutputStream(decryptedFile.toPath()));// 比较原始文件内容与解密后的文件内容assertEquals(IOUtils.toString(Files.newInputStream(originalFile.toPath()),Charset.defaultCharset()),IOUtils.toString(Files.newInputStream(decryptedFile.toPath()),Charset.defaultCharset()));}/**
     * 使用新配置测试字节数组加密
     * @throws IOException
     * @throws PGPException
     */@TestpublicvoidtestByteEncryptionWithNewConf()throwsIOException,PGPException{
        pgpEncryptionUtil =PgpEncryptionUtil.builder().armor(false).compressionAlgorithm(CompressionAlgorithmTags.BZIP2).symmetricKeyAlgorithm(SymmetricKeyAlgorithmTags.BLOWFISH).withIntegrityCheck(false).build();// 加密测试字节数组byte[] encryptedBytes = pgpEncryptionUtil.encrypt(testString.getBytes(Charset.defaultCharset()),
                publicKey.openStream());// 解密生成的加密字节数组byte[] decryptedBytes = pgpDecryptionUtil.decrypt(encryptedBytes);// 将解密后的字节数组转换为字符串,并与原始测试字符串进行比较assertEquals(testString,newString(decryptedBytes,Charset.defaultCharset()));}}

在这里插入图片描述


小结

当我们在互联网上发送电子邮件或文件时,我们希望它们的内容能够保密,并且我们希望确认发送方的身份和数据的完整性。这就是PGP(Pretty Good Privacy)的作用。

想象一下,你有一把钥匙。这把钥匙有两个部分:一个是公钥,一个是私钥。

  • 公钥:就像你家门口的邮箱钥匙一样,你可以把它给任何人。任何人都可以用你的公钥锁住一份文件,但只有你才能用你的私钥打开它。
  • 私钥:就像你的家里的钥匙一样,只有你有它。你用它来打开那些别人用你的公钥锁住的文件。

当你想给某人发送私密信息时,你会使用他们的公钥来加密消息。然后,只有他们可以使用自己的私钥来解密消息。这样,即使在传输过程中,即使有人截获了消息,他们也无法阅读它,因为他们没有私钥。

此外,PGP还可以用于数字签名。就像在一封信上签名一样,数字签名证明了发送方的身份和消息的完整性。发送方使用自己的私钥对消息进行签名,然后接收方使用发送方的公钥来验证签名,确保消息没有被篡改,并且是来自于发送方的。

总而言之,PGP是一种用于保护电子邮件和文件安全的加密技术,它通过使用公钥和私钥来加密和解密消息,并通过数字签名来验证消息的来源和完整性。

在这里插入图片描述

标签: 安全 java PGP

本文转载自: https://blog.csdn.net/yangshangwei/article/details/136565584
版权归原作者 小小工匠 所有, 如有侵权,请联系我们删除。

“加密与安全_PGP、OpenPGP和GPG加密通信协议”的评论:

还没有评论