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

文章目录

  • PGP
  • OpenPGP
  • GPG
  • 工作原理
  • 工作流程
  • 用途
  • 案例说明
    • 过程
  • 代码实现
    • pom依赖
    • PgpEncryptionUtil
    • PgpDecryptionUtil
    • CommonUtils
    • PgpEncryptionTest
  • 小结

在这里插入图片描述


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"?>
<project xmlns="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

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

PgpDecryptionUtil

 package com.artisan.pgpUtils;import org.apache.commons.io.IOUtils;
import org.bouncycastle.jce.provider.BouncyCastleProvider;
import org.bouncycastle.openpgp.PGPEncryptedData;
import org.bouncycastle.openpgp.PGPEncryptedDataList;
import org.bouncycastle.openpgp.PGPException;
import org.bouncycastle.openpgp.PGPPrivateKey;
import org.bouncycastle.openpgp.PGPPublicKeyEncryptedData;
import org.bouncycastle.openpgp.PGPSecretKey;
import org.bouncycastle.openpgp.PGPSecretKeyRingCollection;
import org.bouncycastle.openpgp.PGPUtil;
import org.bouncycastle.openpgp.jcajce.JcaPGPObjectFactory;
import org.bouncycastle.openpgp.operator.jcajce.JcaKeyFingerprintCalculator;
import org.bouncycastle.openpgp.operator.jcajce.JcePBESecretKeyDecryptorBuilder;import java.io.ByteArrayInputStream;
import java.io.ByteArrayOutputStream;
import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStream;
import java.nio.charset.Charset;
import java.security.Security;
import java.util.Iterator;
import java.util.Objects;/*** @author artisan*/
public class PgpDecryptionUtil {static {// 将Bouncy Castle添加到JVM中if (Objects.isNull(Security.getProvider(BouncyCastleProvider.PROVIDER_NAME))) {Security.addProvider(new BouncyCastleProvider());}}/*** 私钥密码*/private final char[] passCode;/*** 密钥环集合*/private final PGPSecretKeyRingCollection pgpSecretKeyRingCollection;/*** @param privateKeyIn* @param passCode* @throws IOException* @throws PGPException*/public PgpDecryptionUtil(InputStream privateKeyIn, String passCode) throws IOException, PGPException {this.passCode = passCode.toCharArray(); // 将密码转换为字符数组// 从输入流中读取PGP密钥环集合this.pgpSecretKeyRingCollection = new PGPSecretKeyRingCollection(PGPUtil.getDecoderStream(privateKeyIn), new JcaKeyFingerprintCalculator());}/*** @param privateKeyStr* @param passCode* @throws IOException* @throws PGPException*/public PgpDecryptionUtil(String privateKeyStr, String passCode) throws IOException, PGPException {// 将私钥字符串转换为输入流this(IOUtils.toInputStream(privateKeyStr, Charset.defaultCharset()), passCode);}/*** 查找指定ID的私钥** @param keyID* @return* @throws PGPException*/private PGPPrivateKey findSecretKey(long keyID) throws PGPException {// 从密钥环中获取指定ID的私钥PGPSecretKey pgpSecretKey = pgpSecretKeyRingCollection.getSecretKey(keyID);// 使用密码解密私钥return pgpSecretKey == null ? null : pgpSecretKey.extractPrivateKey(new JcePBESecretKeyDecryptorBuilder().setProvider(BouncyCastleProvider.PROVIDER_NAME).build(passCode));}/*** 解密方法,将加密输入流解密为明文输出流** @param encryptedIn* @param clearOut* @throws PGPException* @throws IOException*/public void decrypt(InputStream encryptedIn, OutputStream clearOut)throws PGPException, IOException {// 去除ASCII Armor并返回底层的二进制加密流encryptedIn = PGPUtil.getDecoderStream(encryptedIn);JcaPGPObjectFactory pgpObjectFactory = new JcaPGPObjectFactory(encryptedIn);Object obj = pgpObjectFactory.nextObject();// 第一个对象可能是标记数据包PGPEncryptedDataList pgpEncryptedDataList = (obj instanceof PGPEncryptedDataList)? (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)) {throw new PGPException("无法生成PGPPublicKeyEncryptedData对象");}if (pgpPrivateKey == null) {throw new PGPException("无法提取私钥");}CommonUtils.decrypt(clearOut, pgpPrivateKey, publicKeyEncryptedData); // 调用通用解密方法}/*** 解密方法,将加密字节数组解密为明文字节数组** @param encryptedBytes* @return* @throws PGPException* @throws IOException*/public byte[] decrypt(byte[] encryptedBytes) throws PGPException, IOException {ByteArrayInputStream encryptedIn = new ByteArrayInputStream(encryptedBytes);ByteArrayOutputStream clearOut = new ByteArrayOutputStream();// 调用解密方法decrypt(encryptedIn, clearOut);// 将解密后的明文字节数组返回return clearOut.toByteArray();}
}

CommonUtils

package com.artisan.pgpUtils;import org.apache.commons.io.IOUtils;
import org.bouncycastle.jce.provider.BouncyCastleProvider;
import org.bouncycastle.openpgp.PGPCompressedData;
import org.bouncycastle.openpgp.PGPException;
import org.bouncycastle.openpgp.PGPLiteralData;
import org.bouncycastle.openpgp.PGPLiteralDataGenerator;
import org.bouncycastle.openpgp.PGPOnePassSignatureList;
import org.bouncycastle.openpgp.PGPPrivateKey;
import org.bouncycastle.openpgp.PGPPublicKey;
import org.bouncycastle.openpgp.PGPPublicKeyEncryptedData;
import org.bouncycastle.openpgp.PGPPublicKeyRing;
import org.bouncycastle.openpgp.PGPPublicKeyRingCollection;
import org.bouncycastle.openpgp.PGPUtil;
import org.bouncycastle.openpgp.jcajce.JcaPGPObjectFactory;
import org.bouncycastle.openpgp.operator.PublicKeyDataDecryptorFactory;
import org.bouncycastle.openpgp.operator.jcajce.JcaKeyFingerprintCalculator;
import org.bouncycastle.openpgp.operator.jcajce.JcePublicKeyDataDecryptorFactoryBuilder;import java.io.BufferedInputStream;
import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStream;
import java.sql.Date;
import java.time.LocalDateTime;
import java.time.ZoneOffset;
import java.util.Arrays;
import java.util.Iterator;
import java.util.Optional;/*** @author artisan*/
public class CommonUtils {/*** 使用提供的私钥解密公钥加密数据,并将其写入输出流** @param clearOut               要写入数据的输出流* @param pgpPrivateKey          私钥实例* @param publicKeyEncryptedData 公钥加密数据实例* @throws IOException  IO相关错误* @throws PGPException PGP相关错误*/static void decrypt(OutputStream clearOut, PGPPrivateKey pgpPrivateKey, PGPPublicKeyEncryptedData publicKeyEncryptedData) throws IOException, PGPException {PublicKeyDataDecryptorFactory decryptorFactory = new JcePublicKeyDataDecryptorFactoryBuilder().setProvider(BouncyCastleProvider.PROVIDER_NAME).build(pgpPrivateKey);InputStream decryptedCompressedIn = publicKeyEncryptedData.getDataStream(decryptorFactory);JcaPGPObjectFactory decCompObjFac = new JcaPGPObjectFactory(decryptedCompressedIn);PGPCompressedData pgpCompressedData = (PGPCompressedData) decCompObjFac.nextObject();InputStream compressedDataStream = new BufferedInputStream(pgpCompressedData.getDataStream());JcaPGPObjectFactory pgpCompObjFac = new JcaPGPObjectFactory(compressedDataStream);Object message = pgpCompObjFac.nextObject();if (message instanceof PGPLiteralData) {PGPLiteralData pgpLiteralData = (PGPLiteralData) message;InputStream decDataStream = pgpLiteralData.getInputStream();IOUtils.copy(decDataStream, clearOut);clearOut.close();} else if (message instanceof PGPOnePassSignatureList) {throw new PGPException("加密消息包含签名消息而不是文字数据");} else {throw new PGPException("消息不是简单加密文件 - 类型未知");}// 执行完整性检查if (publicKeyEncryptedData.isIntegrityProtected()) {if (!publicKeyEncryptedData.verify()) {throw new PGPException("消息未通过完整性检查");}}}/*** 将输入流中的数据复制到pgp文字数据,并写入提供的输出流** @param outputStream 输出流,要写入其中的数据* @param in           要读取数据的输入流* @param length       要读取的数据长度* @param bufferSize   缓冲区大小,因为使用缓冲区加速复制* @throws IOException IO相关错误*/static void copyAsLiteralData(OutputStream outputStream, InputStream in, long length, int bufferSize) throws IOException {PGPLiteralDataGenerator lData = new PGPLiteralDataGenerator();OutputStream pOut = lData.open(outputStream, PGPLiteralData.BINARY, PGPLiteralData.CONSOLE,Date.from(LocalDateTime.now().toInstant(ZoneOffset.UTC)), new byte[bufferSize]);byte[] buff = new byte[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相关错误*/static PGPPublicKey getPublicKey(InputStream keyInputStream) throws IOException, PGPException {PGPPublicKeyRingCollection pgpPublicKeyRings = new PGPPublicKeyRingCollection(PGPUtil.getDecoderStream(keyInputStream), new JcaKeyFingerprintCalculator());Iterator<PGPPublicKeyRing> keyRingIterator = pgpPublicKeyRings.getKeyRings();while (keyRingIterator.hasNext()) {PGPPublicKeyRing pgpPublicKeyRing = keyRingIterator.next();Optional<PGPPublicKey> pgpPublicKey = extractPGPKeyFromRing(pgpPublicKeyRing);if (pgpPublicKey.isPresent()) {return pgpPublicKey.get();}}throw new PGPException("无效的公钥");}private static Optional<PGPPublicKey> extractPGPKeyFromRing(PGPPublicKeyRing pgpPublicKeyRing) {for (PGPPublicKey publicKey : pgpPublicKeyRing) {if (publicKey.isEncryptionKey()) {return Optional.of(publicKey);}}return Optional.empty();}}

PgpEncryptionTest

package com.artisan.pgpUtils;import org.apache.commons.io.IOUtils;
import org.bouncycastle.bcpg.CompressionAlgorithmTags;
import org.bouncycastle.bcpg.SymmetricKeyAlgorithmTags;
import org.bouncycastle.openpgp.PGPException;
import org.junit.AfterClass;
import org.junit.Before;
import org.junit.BeforeClass;
import org.junit.Test;
import org.junit.rules.TemporaryFolder;import java.io.File;
import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStream;
import java.net.URISyntaxException;
import java.net.URL;
import java.nio.charset.Charset;
import java.nio.file.Files;
import java.util.Optional;import static org.junit.Assert.assertEquals;public class PgpEncryptionTest {public static final TemporaryFolder tempFolder = new TemporaryFolder();private PgpEncryptionUtil pgpEncryptionUtil = null;private PgpDecryptionUtil pgpDecryptionUtil = null;/*** 加载资源文件的辅助方法* @param resourcePath* @return*/private static URL loadResource(String resourcePath) {return Optional.ofNullable(PgpEncryptionTest.class.getResource(resourcePath)).orElseThrow(() -> new IllegalArgumentException("Resource not found"));}private static final String passkey = "dummy";private final URL privateKey = loadResource("/private.pgp");private final URL publicKey = loadResource("/public.pgp");private final URL testFile = loadResource("/Sample_CSV_5300kb.csv");private static final String testString = "This text needs to be PGP encrypted";/*** 在测试类运行之前创建临时文件夹* @throws IOException*/@BeforeClasspublic static void construct() throws IOException {tempFolder.delete();tempFolder.create();}/*** 在测试类运行之后清理临时文件夹*/@AfterClasspublic static void destroy() {tempFolder.delete();}/***  初始化方法,在每个测试方法运行之前执行*/@Beforepublic void init() {// 初始化加密工具类pgpEncryptionUtil = PgpEncryptionUtil.builder().armor(true).compressionAlgorithm(CompressionAlgorithmTags.ZIP).symmetricKeyAlgorithm(SymmetricKeyAlgorithmTags.AES_128).withIntegrityCheck(true).build();try {// 初始化解密工具类pgpDecryptionUtil = new PgpDecryptionUtil(privateKey.openStream(), passkey);} catch (IOException | PGPException e) {throw new RuntimeException(e);}}/***  测试字节数组加密* @throws IOException* @throws PGPException*/@Testpublic void testByteEncryption() throws IOException, PGPException {// 加密测试字节数组byte[] encryptedBytes = pgpEncryptionUtil.encrypt(testString.getBytes(Charset.defaultCharset()),publicKey.openStream());// 解密生成的加密字节数组byte[] decryptedBytes = pgpDecryptionUtil.decrypt(encryptedBytes);// 将解密后的字节数组转换为字符串,并与原始测试字符串进行比较assertEquals(testString, new String(decryptedBytes, Charset.defaultCharset()));}/*** 测试文件加密* @throws IOException* @throws URISyntaxException* @throws PGPException*/@Testpublic void testFileEncryption() throws IOException, URISyntaxException, PGPException {// 生成一个从测试文件生成的PGP加密临时文件File encryptedFile = tempFolder.newFile();File originalFile = new File(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*/@Testpublic void testInputStreamEncryption() throws IOException, URISyntaxException, PGPException {// 生成一个从测试文件生成的PGP加密输入流File originalFile = new File(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*/@Testpublic void testByteEncryptionWithNewConf() throws IOException, 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, new String(decryptedBytes, Charset.defaultCharset()));}
}

在这里插入图片描述


小结

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

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

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

  • 私钥:就像你的家里的钥匙一样,只有你有它。你用它来打开那些别人用你的公钥锁住的文件。

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

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

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

在这里插入图片描述

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.mzph.cn/news/738428.shtml

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

uni-app开发特点和开发流程

uni-app是一个基于Vue.js框架的跨平台应用开发框架&#xff0c;通过一套代码可以同时运行在多个平台上&#xff0c;包括iOS、Android、H5等。它采用了基于流布局的页面渲染机制&#xff0c;可以自动适配不同平台的屏幕尺寸和分辨率。uniapp官网&#xff1a;https://uniapp.dclo…

qt 汉字输出 中文输出 显示乱码 qDebug() 乱码 解决

要正确显示汉字&#xff0c;必须要先了解计算机文字编码相关知识&#xff0c;参考&#xff1a;unicode ucs2 utf16 utf8 ansi GBK GB2312 互转 及 渲染_ucs2编码转换-CSDN博客 1、汉字输出到 应用程序输出面板 qt 自定义的输出类qDebug() 、QDebug对象、QMessageLogger默认输…

用眼精星票证识别系统,轻松将户口本转成结构化Excel

眼精星票证识别系统是一款革命性的软件&#xff0c;它以高度的精准性和效率为特点&#xff0c;为用户提供了一个简单便捷的工具&#xff0c;将纸质户口本转化为结构化数据。这一创新技术不仅改变了传统数据录入的方式&#xff0c;还大大提高了工作效率&#xff0c;为用户节省了…

Buildroot 之二 详解构建架构、流程、external tree、示例

构建系统 Buildroot 中的构建系统使用的是从 Linux Kernel(4.17-rc2) 中移植的 Kconfig(配置) + Makefile & Kbuild(编译)这套构建系统,移植后的源码位于 support/kconfig/ 目录下。Buildroot 本身是一个构建系统,与直接编译源码不同,因此,它对这套系统进行了比较…

web服务器集群试题

服务器IP地址规划&#xff1a;client&#xff1a;12.0.0.12/24&#xff0c;网关服务器&#xff1a;ens36:12.0.0.1/24、ens33&#xff1a;192.168.11.1/24&#xff1b;Web1&#xff1a;192.168.11.10/24&#xff0c;Web2&#xff1a;192.168.11.11/24&#xff0c;Nginx&#xf…

鸿蒙(HarmonyOS)项目方舟框架(ArkUI)之Stack容器组件

鸿蒙&#xff08;HarmonyOS&#xff09;项目方舟框架&#xff08;ArkUI&#xff09;之Stack容器组件 一、操作环境 操作系统: Windows 10 专业版、IDE:DevEco Studio 3.1、SDK:HarmonyOS 3.1 二、Stack容器组件 堆叠容器&#xff0c;子组件按照顺序依次入栈&#xff0c;后一…

Yarn:Node.js依赖管理的现代选择

&#x1f90d; 前端开发工程师、技术日更博主、已过CET6 &#x1f368; 阿珊和她的猫_CSDN博客专家、23年度博客之星前端领域TOP1 &#x1f560; 牛客高级专题作者、打造专栏《前端面试必备》 、《2024面试高频手撕题》 &#x1f35a; 蓝桥云课签约作者、上架课程《Vue.js 和 E…

HTML5七天学会基础动画网页10(2)

制作立方体 学完前面的基础内容&#xff0c;制作立方体是个不错的练习方法&#xff0c;先看成品 再分析一下&#xff0c;六个面让每个面旋转平移就可以实现一个立方体&#xff0c;来看代码: <title> 制作立方体</title> <style> *{ margin: 0; padding: 0; …

Kotlin:枚举类

点击查看枚举类中文文档 点击查看枚举类英文文档 枚举类的最基本的用法是实现类型安全的枚举&#xff1a; enum class Direction {NORTH, SOUTH, WEST, EAST }每个枚举常量都是一个对象。枚举常量用逗号分隔。 初始化 因为每一个枚举都是枚举类的实例&#xff0c;所以他们可…

【HTML】div设为输入框,巧妙添加placeholder效果

效果图 文本撑满时 初始化时 码 contenteditable"true" 设为可编辑状态 <divcontenteditable"true"placeholder"评个小论吧&#xff01;"class"textarea"></div><style>/* 设置最小高度&#xff0c;超过滚动显示 …

Python 导入Excel三维坐标数据 生成三维曲面地形图(面) 3、线条平滑曲面但有条纹

环境和包: 环境 python:python-3.12.0-amd64包: matplotlib 3.8.2 pandas 2.1.4 openpyxl 3.1.2 scipy 1.12.0 代码: import pandas as pd import matplotlib.pyplot as plt from mpl_toolkits.mplot3d import Axes3D from scipy.interpolate import griddata im…

操作系统:进程地址空间

目录 1.程序地址空间 1.1.程序地址空间的介绍 1.2.程序地址空间的本质 2.进程地址空间 3.Linux下的地址空间 1.程序地址空间 1.1.程序地址空间的介绍 我们在学习C/C时&#xff0c;对于各组分的地址分配在程序地址空间的不同模块 如图我们能够验证各组分的对应的地址排布位…

xss.haozi.me靶场“0x00-0x0A”通关教程

君衍. 一、靶场介绍二、第一关 0x00 不做限制三、第二关 0x01 文本闭合标签绕过四、第三关 0x02 双引号闭合绕过五、第四关 0x03 过滤括号六、第五关 0x04 编码绕过七、第六关 0x05 注释闭合绕过八、第七关 0x06 换行绕过九、第八关 0x07 删除标签十、第九关 0x08 多加空格绕过…

负数的四舍五入

负数的四舍五入 标签:基础 System.out.println(Math.round(-0.2)); System.out.println(Math.round(-0.5)); System.out.println(Math.round(-0.6));0 0 -1理解:四舍五入,找一个离目标小数近的整数,-0.2和0近,-0.6和-1近,中间的往右靠

力扣每日一题 将标题首字母大写 模拟 String API

Problem: 2129. 将标题首字母大写 文章目录 思路复杂度Code 思路 &#x1f468;‍&#x1f3eb; 灵神题解 复杂度 ⏰ 时间复杂度: O ( n ) O(n) O(n) &#x1f30e; 空间复杂度: O ( n ) O(n) O(n) Code class Solution {public String capitalizeTitle(String title)…

重定向、管道与环境变量

前言&#xff1a;测试开发的学习已经开启第三周的学习了。 奋斗至此&#xff0c;内容已稍有难度。 发现每一点都有其深挖的地方&#xff0c;但读书在某些方面来讲要不求甚解&#xff0c;如果只在一点深挖&#xff0c;那可能进度很难提的上来。 继续加油&#xff01;&#xf…

es 分词器详解

基本概念 分词器官方称之为文本分析器&#xff0c;顾名思义&#xff0c;是对文本进行分析处理的一种手段&#xff0c;基本处理逻辑为按照预先制定的分词规则&#xff0c;把原始文档分割成若干更小粒度的词项&#xff0c;粒度大小取决于分词器规则。 分词器发生的时期 1、分词…

实现支持多选的QComboBox

Qt提供的QComboBox只支持下拉列表内容的单选&#xff0c;但通过QComboBox提供的setModel、setView、setLineEdit三个方法&#xff0c;可以对QComboBox进行改造&#xff0c;使其实现下拉列表选项的多选。 QComboBox可以看作两个组件的组合&#xff1a;一个QLineEdit和一个QList…

ctfshow web入门 php特性总结

1.web89 intval函数的利用&#xff0c;intval函数获取变量的整数值&#xff0c;失败时返回0&#xff0c;空的数组返回&#xff0c;非空数组返回1 num[]1 intval ( mixed $var [, int $base 10 ] ) : int Note: 如果 base 是 0&#xff0c;通过检测 var 的格式来决定使用的进…

【工具】Git的介绍与安装

目录 前言 1W&#xff1a;什么是Git&#xff1f; 2W&#xff1a;为什么使用Git&#xff1f; 3W&#xff1a;如何使用Git&#xff1f; Git的安装步骤 测试 3.1 桌面空白部分鼠标右击 3.2 选择 Open Git Bash here 3.3 输入 git -v 命令查看版本 Git区域分布 Git的工作…