在 Windows 中创建新的 GPG Key,你需要安装一个称为 gnupg 小工具。

下载的地址为:https://www.gnupg.org/download/

针对 Windows ,你可以下载 Gpg4win 这个版本。

 

 

 
Windows 上创建一个新的 GPG key
 

 

双击运行安装

下载到本地后,可以双击下载的程序进行安装。

在安装的时候,可能会询问你权限的问题。

 
Windows 上创建一个新的 GPG key
 

选择语言版本

在这里选择默认的英文版本就可以了。

 
Windows 上创建一个新的 GPG key
 

下一步继续

单击下一步来继续安装过程。

 
Windows 上创建一个新的 GPG key
 

安装组件

选择默认的安装组件,然后下一步进行安装。

 
Windows 上创建一个新的 GPG key
 

安装路径

使用默认的安装路径就可以了。

 
Windows 上创建一个新的 GPG key
 

安装进程

在这里需要等一下,等待安装完成。

 
Windows 上创建一个新的 GPG key
 

安装完成

单击安装完成按钮来完成安装。

 
Windows 上创建一个新的 GPG key
 

然后你可以看到运行的 Kleopatra,我们是需要使用这个来创建 PGP Key 的。

 

 

 
Windows 上创建一个新的 GPG key
 

 

https://www.ossez.com/t/windows-gpg-key/745

安装Gpg4win,下载链接:https://download.csdn.net/download/zyhlwzy/11099228

1、生成密钥对

C:\WINDOWS\system32>gpg --gen-key
gpg (GnuPG) 2.0.30; Copyright (C) 2015 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、数字签名算法也是RSA)
RSA keys may be between 1024 and 4096 bits long.
What keysize do you want? (2048) 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: Ron   #输入姓名
Name must be at least 5 characters long  #姓名至少为5个字符
Real name: ron.zheng   #输入姓名
Email address: ron.zheng@tfschange.com  #输入邮箱地址
Comment: tfs #输入备注
You selected this USER-ID:
    "ron.zheng (tfs) <ron.zheng@tfschange.com>"

Change (N)ame, (C)omment, (E)mail or (O)kay/(Q)uit? o  #选择OK
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 0452FE75 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/0452FE75 2019-03-28
      Key fingerprint = 65B4 846F 7E63 A32B 34E3  A9FB C99D B8B9 0452 FE75
uid       [ultimate] ron.zheng (tfs) <ron.zheng@tfschange.com>
sub   2048R/488F27D2 2019-03-28

2、查看刚才生成的公钥

C:\WINDOWS\system32>gpg --export --armor 488F27D2
-----BEGIN PGP PUBLIC KEY BLOCK-----
Version: GnuPG v2

mQENBFyceJkBCAC9m2UeEevNgTjUG+N+6AMkBiM5B75PpcT6WJoJUuxdzXIKYDuw
zuYfVUJ59maSyAZ3rD2hHlgCjV4EycjOrrr27Ke726ZccsKGg1FG1G1PEuHL6aDa
ONRdcLbFdUpv9EwQZrfPGQnATw4p83gxZ8HwXemH+wybenSbuXQQuWY5X7lmJ0sy
8Kzmz5SGL1VLByYCyJAuLGCs8rFitW/w/JpQTQZR4BoezOecAF0DZR24SaWrkBQu
vpCh8nHaVSRE7R0xi+sLKohDfgr3MLjDAopEMmUaImUaeRwyVThwvT2tq9/Qn4GI
2b3R+Tw3afs9eyWkhK+oWSfMv/0zuBWD0axRABEBAAG0KXJvbi56aGVuZyAodGZz
KSA8cm9uLnpoZW5nQHRmc2NoYW5nZS5jb20+iQE5BBMBCAAjBQJcnHiZAhsDBwsJ
CAcDAgEGFQgCCQoLBBYCAwECHgECF4AACgkQyZ24uQRS/nWFywgAmLvoaYfAy5zV
ZMM4zpbw3kf7HpIemlDUPVzJKTOM9cu62/O3eLb592jQCVcs9U0wLlkrkRGe4geC
n72M5hR/psWoLfKOAXDrT9C+a0vW991tYtPbUfnNdDfoQPn/T0Z/jsB26WkZ5X76
8jwwVB6Q8EFD2nFv2HzC4UZl/4qylp+dzEskRtp1lL54LECEyIQDWZCCO+iddile
JpxScQz/GEXzB46o1dDCraqL1Neyyp8MHmhTaxCspKZ9eGSWjXTN+8kmTdqN+6R3
ssAecvWEwqXrGBCTWg9NvmfcYWZBImzDplnuOqkP7ZeTUhSihM+uov5ALyokrLTT
QQFQ0Hs4fbkBDQRcnHiZAQgA4XpvgCB4+ElQo5hRPZcklgZwzdLElmfjDE4F5ist
AH+ndxoTWfagvThPgZh855k4/bdXBPohkuECwGESr6RrfajMozLvfeHyYSxtQmhD
fACuYuYjfAfL7OoDTmFYiNBwWNx8fzE3i2EjUJWuJpz10zb3O8ly2X+gFe5Rv4Yj
CS0nY38aGaiKypJ54nrP3pkIusFux4rMEYzVRpDx5jWFWtPWHnKZtw8LYPQOJjES
l0Z1j0ANwJjnxFf123HvgiaAZK3H1BWUcXlnDRR2w84ZUMGJTuguYd5BjqAIrfv/
8xkZwJgqOs21Idt+KBYkksDjVZ/xDg6ycv1ZUcJgZltQfQARAQABiQEfBBgBCAAJ
BQJcnHiZAhsMAAoJEMmduLkEUv51YsgH/3nfVb4Yq+hWEsLPb2v8JoMJZcyx/mS+
Yqba4SZD/9Eg6SM+bArBfG4p4jee92EJVKIzHOp6n0YPMcF8WWrIXoy+SHReHT7V
KyS6zpxUtQk3eQC4Yutmk5NNI87gzrK16uBwG110jkHh3IlglEcLxui2bASbsL6s
waePqRHmq69rUKh/za/toVbKWAnNK0hfEizFIvMdHPVx4/Hjo02EM+CBbzKD/LZg
RuAtKiEWnFH3ljnmXHxIxnV+IzUzKGkaPUrmOoDMLEtnw/vPpMbLov3bEyHROci0
FZhUNi+4EADxXbk+FwHTSLMHgpNPhUQsgLrnWFulLbCWGm98L0WqVXU=
=GFn+
-----END PGP PUBLIC KEY BLOCK-----
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24
  • 25
  • 26
  • 27
  • 28
  • 29
  • 30
  • 31

3、列出密钥

C:\WINDOWS\system32>gpg --list-keys
#显示公钥文件名
C:/Users/user/AppData/Roaming/gnupg/pubring.gpg
-----------------------------------------------

#显示公钥特征
pub   2048R/0452FE75 2019-03-28
#显示用户ID
uid       [ultimate] ron.zheng (tfs) <ron.zheng@tfschange.com>
#显示私钥特征
sub   2048R/488F27D2 2019-03-28

4、将公钥导入到文件

公钥文件(.gnupg/pubring.gpg)以二进制形式储存,armor 参数可以将其转换为 ASCII 码显示。

gpg --armor --output "输出文件名" --export "用户ID"

gpg --armor --output C:/data/cert/test-public-key.asc --export  ron.zheng

Windows 上创建一个新的 GPG key

打开查看公钥内容如下:

-----BEGIN PGP PUBLIC KEY BLOCK-----
Version: GnuPG v2

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
AdNIsweCk0+FRCyAuudYW6UtsJYab3wvRapVdQ==
=CruP
-----END PGP PUBLIC KEY BLOCK-----

5、导出私钥

export-secret-keys 参数可以导出私钥。

gpg --armor --output C:/data/cert/tfs-private-key.asc --export-secret-keys
  • 1

Windows 上创建一个新的 GPG key

打开可看到私钥信息如下:

-----BEGIN PGP PRIVATE KEY BLOCK-----
Version: GnuPG v2

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
=Jwdu
-----END PGP PRIVATE KEY BLOCK-----

6、删除密钥,在命令行输入如下命令

必须先删除私钥,然后才能删除公钥。

在命令行输入如下命令:

gpg --delete-secret-keys 标识名
gpg --delete-keys 标识名

7、PGP加解密帮助类(Java)

/**
* <p>Title: PGPUtils</p>  
* <p>Description: PGP加解密帮助类</p>  
* @author Ron  
* @date 2019年3月4日
 */
public class PGPUtils {
	/**
	 * 添加提供者
	 */
	static{
		Security.addProvider(new CryptixCrypto());
	    Security.addProvider(new CryptixOpenPGP() );
	}

	/**
	 * 构建 LiteralMessage 对象
	 * @param message		
	 * @return
	 * @throws MessageException
	 */
	private static LiteralMessage buildLiteralMessage(byte[] message) throws MessageException{
		LiteralMessageBuilder lmb = null;

		try {
			lmb = LiteralMessageBuilder.getInstance("OpenPGP");
		} catch (NoSuchAlgorithmException e) {
			e.printStackTrace();
		}
		lmb.init(message);
		LiteralMessage literal = (LiteralMessage)lmb.build();
		return literal;
	}

	/**
	 * 使用多个公钥对明文加密
	 * @param plain			明文
	 * @param recipientKeys	公钥集合
	 * @return				加密后的明文
	 * @throws MessageException
	 */
	public static byte[] encrypt(byte[] plain,List<KeyBundle> recipientKeys) throws MessageException{
		LiteralMessage literal = buildLiteralMessage(plain);

		EncryptedMessageBuilder emb = null;
		try {
			emb = EncryptedMessageBuilder.getInstance("OpenPGP");
		} catch (NoSuchAlgorithmException e) {
			e.printStackTrace();
		}
		emb.init(literal);

		//添加接受者
		for(KeyBundle key : recipientKeys){
			emb.addRecipient(key);
		}
		//压缩
		emb.setAttribute("compressed", "true");
		//得到加密信息
		Message msg = emb.build();
		PGPArmouredMessage pgpMsg = new PGPArmouredMessage(msg);
		return pgpMsg.getEncoded();
	}

	/**
	 * 使用单张公钥加密
	 * @param plain		明文	
	 * @param publicKey	公钥
	 * @return	返回加密后的密文
	 * @throws MessageException
	 */
	public static byte[] encrypt(byte[] plain,KeyBundle publicKey) throws MessageException{
		List<KeyBundle> list = new ArrayList<KeyBundle>();
		list.add(publicKey);
		return encrypt(plain, list);
	}
	
	/**
	 * 使用单张公钥加密
	 * @param plain		明文	
	 * @param publicKey	公钥路径
	 * @return	返回加密后的密文
	 * @throws MessageException
	 */
	public static byte[] encrypt(byte[] plain,String publicKeyPath) throws MessageException, IOException{
		InputStream priInputStream  = new FileInputStream(new File(publicKeyPath));
		KeyBundle publicKeyBundle = PGPUtils.streamToKeyBundle(priInputStream);
		return PGPUtils.encrypt(plain, publicKeyBundle);
	}

	/**
	 * 使用私钥和密码对明文签名
	 * @param plain			明文
	 * @param privateKey	私钥
	 * @param keypass		私钥密码
	 * @return				签名后的明文
	 * @throws MessageException
	 * @throws UnrecoverableKeyException
	 */
	public static byte[] sign(byte[] plain,KeyBundle privateKey,String keypass)throws MessageException,UnrecoverableKeyException{
        SignedMessageBuilder smb = null;
		try {
			smb = SignedMessageBuilder.getInstance("OpenPGP");
		} catch (NoSuchAlgorithmException e) {
			e.printStackTrace();
		}
		
        LiteralMessage literal = buildLiteralMessage(plain);
        smb.init(literal);
        smb.addSigner(privateKey, keypass.toCharArray());

        Message msg = smb.build();
        PGPArmouredMessage armoured = new PGPArmouredMessage(msg);
		return armoured.getEncoded();
	}
	
	/**
	 * 使用私钥和密码对明文签名
	 * @param plain			明文
	 * @param privateKey	私钥路径
	 * @param keypass		私钥密码
	 * @return				签名后的明文
	 * @throws MessageException
	 * @throws UnrecoverableKeyException
	 */
	public static byte[] sign(byte[] plain,String privateKeyPath,String keypass)throws MessageException,UnrecoverableKeyException, IOException{
		InputStream priInputStream  = new FileInputStream(new File(privateKeyPath));
		KeyBundle privateKeyBundle = PGPUtils.streamToKeyBundle(priInputStream);
		return PGPUtils.sign(plain, privateKeyBundle, keypass);
	}

	/**
	 * 使用私钥和密码解密加密后的数据
	 * @param encrypted		PGP加密过的数据
	 * @param privateKey	私钥
	 * @param keypass		私钥密码
	 * @return				解密后的明文
	 * @throws MessageException
	 * @throws IOException
	 * @throws UnrecoverableKeyException
	 * @throws NotEncryptedToParameterException
	 */
	public static byte[] decrypt(byte[] encrypted,KeyBundle privateKey,String keypass) throws MessageException, IOException, UnrecoverableKeyException, NotEncryptedToParameterException{

		MessageFactory mf = null;
		try {
			mf = MessageFactory.getInstance("OpenPGP");
		} catch (NoSuchAlgorithmException e) {
			e.printStackTrace();
		}

		Collection msgs = mf.generateMessages(new ByteArrayInputStream(encrypted));

		//得到集合中的EncryptedMessage对象
		Message message = (Message)msgs.iterator().next();

		if (!(message instanceof EncryptedMessage)) {
            throw new MessageException("Not a encrypted message.");
        }

		EncryptedMessage em = (EncryptedMessage)message;
		Message msg = em.decrypt(privateKey,keypass.toCharArray());
		return ((LiteralMessage)msg).getBinaryData();
	}
	
	/**
	 * 使用私钥和密码解密加密后的数据
	 * @param encrypted		PGP加密过的数据
	 * @param privateKey	私钥路径
	 * @param keypass		私钥密码
	 * @return				解密后的明文
	 * @throws MessageException
	 * @throws IOException
	 * @throws UnrecoverableKeyException
	 * @throws NotEncryptedToParameterException
	 */
	public static byte[] decrypt(byte[] encrypted,String privateKeyPath,String keypass) throws MessageException, IOException, UnrecoverableKeyException, NotEncryptedToParameterException{
		InputStream priInputStream  = new FileInputStream(new File(privateKeyPath));
		KeyBundle privateKeyBundle = PGPUtils.streamToKeyBundle(priInputStream);
		return PGPUtils.decrypt(encrypted,privateKeyBundle,keypass);
	}

	/**
	 * 解密验签
	 * @param encrypted		密文	
	 * @param privateKey	私钥
	 * @param keypass		私钥密码
	 * @param publicKey		公钥
	 * @return				返回明文
	 * @throws UnrecoverableKeyException
	 * @throws MessageException
	 * @throws IOException
	 * @throws NotEncryptedToParameterException
	 */
	public static byte[] decryptVerify(byte[] encrypted,KeyBundle privateKey,String keypass,KeyBundle publicKey) throws UnrecoverableKeyException, MessageException, IOException, NotEncryptedToParameterException{
		return PGPUtils.verify(PGPUtils.decrypt(encrypted, privateKey, keypass), publicKey);
	}
	
	/**
	 * 解密验签
	 * @param encrypted		密文	
	 * @param privateKey	私钥路径
	 * @param keypass		私钥密码
	 * @param publicKey		公钥路径
	 * @return				返回明文
	 * @throws UnrecoverableKeyException
	 * @throws MessageException
	 * @throws IOException
	 * @throws NotEncryptedToParameterException
	 */
	public static byte[] decryptVerify(byte[] encrypted,String privateKeyPath,String keypass,String publicKeyPath) throws UnrecoverableKeyException, MessageException, IOException, NotEncryptedToParameterException{
		InputStream priInputStream  = new FileInputStream(new File(privateKeyPath));
		KeyBundle privateKeyBundle = PGPUtils.streamToKeyBundle(priInputStream);
		
		InputStream pubInputStream = new FileInputStream(new File(publicKeyPath));
		KeyBundle pubKeyBundle = PGPUtils.streamToKeyBundle(pubInputStream);
		
		return PGPUtils.decryptVerify(encrypted,privateKeyBundle,keypass,pubKeyBundle);
	}

	/**
	 * 验证Message
	 * @param signed	验证的内容
	 * @param publickey	公钥
	 * @return			返回验证后的内容
	 * @throws MessageException
	 * @throws IOException
	 */
	public static byte[] verify(byte[] signed,KeyBundle publickey) throws MessageException, IOException{

		MessageFactory mf = null;
		try {
			mf = MessageFactory.getInstance("OpenPGP");
		} catch (NoSuchAlgorithmException e) {
			e.printStackTrace();
		}

        Message msg = (Message)mf.generateMessages(new ByteArrayInputStream(signed)).iterator().next();
        if (!(msg instanceof SignedMessage)) {
            throw new MessageException(" Not a signed message.");
        }

        SignedMessage sm = (SignedMessage)msg;
        if (sm.verify(publickey)) {

        } else {
        	throw new MessageException(" Signature verify fail. ");
        }

        if (!(sm.getContents() instanceof LiteralMessage)){
        	throw new MessageException(" Not a signed message.");
        }

        LiteralMessage lm = (LiteralMessage)sm.getContents();
		return lm.getBinaryData();
	}

	/**
	 * 流转换为PGP KeuBundle 对象
	 * @param inputStream 	Key
	 * @return	转换后的 KeuBundle
	 * @throws MessageException
	 * @throws IOException
	 */
	public static KeyBundle streamToKeyBundle(InputStream inputStream) throws MessageException, IOException {
		MessageFactory messageFactory = null;
		try {
			messageFactory = MessageFactory.getInstance("OpenPGP");
		} catch (NoSuchAlgorithmException e) {
			e.printStackTrace();
		}
		Collection msgs = messageFactory.generateMessages(inputStream);
		KeyBundleMessage keybm = (KeyBundleMessage)msgs.iterator().next();

		return keybm.getKeyBundle();

	}

	/**
	 * 签名加密
	 * @param plain			明文	
	 * @param privateKey	私钥	
	 * @param keypass		私钥密码
	 * @param recipientKeys	公钥
	 * @return				返回签名加密后的数据
	 * @throws UnrecoverableKeyException
	 * @throws MessageException
	 */
	public static byte [] signAndEncrypt(byte[] plain,KeyBundle privateKey,String keypass,List<KeyBundle> recipientKeys) throws UnrecoverableKeyException, MessageException{
		return PGPUtils.encrypt(PGPUtils.sign(plain, privateKey, keypass),recipientKeys);
	}

	/**
	 * 签名加密
	 * @param plain			明文	
	 * @param privateKey	私钥	
	 * @param keypass		私钥密码
	 * @param recipientKeys	公钥
	 * @return				返回签名加密后的数据
	 * @throws UnrecoverableKeyException
	 * @throws MessageException
	 */
	public static byte [] signAndEncrypt(byte[] plain,KeyBundle privateKey,String keypass,KeyBundle publicKey) throws UnrecoverableKeyException, MessageException{
		return PGPUtils.encrypt(PGPUtils.sign(plain, privateKey, keypass),publicKey);
	}
	
	/**
	* @author Ron
	* <p>Title: signAndEncrypt</p>  
	* <p>Description: </p>  
	* @param plain                 明文
	* @param privateKeyPath        私钥路径
	* @param keypass               私钥密码
	* @param publicKeyPath         公钥
	* @return                      返回签名加密后的数据
	* @throws UnrecoverableKeyException
	* @throws MessageException
	 * @throws IOException 
	 */
	public static byte [] signAndEncrypt(byte[] plain,String privateKeyPath,String keypass,String publicKeyPath) throws UnrecoverableKeyException, MessageException, IOException{
		InputStream priInputStream  = new FileInputStream(new File(privateKeyPath));
		KeyBundle privateKeyBundle = PGPUtils.streamToKeyBundle(priInputStream);
		
		InputStream pubInputStream = new FileInputStream(new File(publicKeyPath));
		KeyBundle pubKeyBundle = PGPUtils.streamToKeyBundle(pubInputStream);
		
		return PGPUtils.signAndEncrypt(plain,privateKeyBundle,keypass,pubKeyBundle);
	}
	
	public static void main(String[] args) throws MessageException, IOException, UnrecoverableKeyException, NotEncryptedToParameterException {
		String data = "{\"platId\": \"***\",\"authorizeCode\": \"EPORT0000102\",\"msgType\": \"VLD001\",\"format\": \"json\",\"signature\": \"signature\",\"userName\": \"test\",\"password\": \"***\",\"data\": {\"entryId\": \"E20170000003131789\",\"ownerCode\": \"**********\",\"ownerName\": \"广东鱼珠木材电子商务有限公司\",\"cnsnTradeCode\": \"********\",\"consignorCname\": \"广东广物木材产业股份有限公司\",\"consignorEname\": \"En*******************\",\"agentCode\": \"**********\",\"agentName\": \"广东省电子口岸管理有限公司\",\"ciqDespCtryCode\": \"NZL\",\"ciqDespCtryCodeName\": \"新西兰\",\"despPortCode\": \"991201\",\"despPortCodeName\": \"天津出口加工区\",\"cusTradeNationCode\": \"AFG\",\"cusTradeNationCodeName\": \"阿富汗\",\"districtCode\": \"11013\",\"districtCodeName\": \"中关村国家自主创新示范区(东城园)\",\"packNo\": \"100\",\"grossWt\": \"100\",\"netWt\": \"100\",\"supvModeCdde\": \"1039\",\"supvModeCddeName\": \"市场采购\",\"decMergeListVo\": [{\"gNo\": \"1\",\"contrItem\": \"1\",\"codeTs\": \"1001110001\",\"gName\": \"种用硬粒小麦\",\"gModel\": \"0|2|冰袋|100%涤纶|无牌 款号:AC04S1238\", \"declTotal\": \"60\",\"tradeCurr\": \"美元\",\"gQty\":\"10\"},{\"gNo\": \"1\",\"contrItem\": \"1\",\"codeTs\": \"1001110001\",\"gName\": \"种用硬粒小麦\",\"gModel\": \"0|2|冰袋|100%涤纶|无牌 款号:AC04S1238\", \"declTotal\": \"60\",\"tradeCurr\": \"美元\",\"gQty\":\"10\"}]}}";
		
		String privateKey = "D:/Work/WorkSpace/trunk_remote_dev/tfs-china-eport-api/src/main/resources/cert/test-private-key.txt";
		String keypass = "tfs12345678";
		String publicKey = "D:/Work/WorkSpace/trunk_remote_dev/tfs-china-eport-api/src/main/resources/cert/test-public-key.txt";
		
		byte[] dataEn = PGPUtils.signAndEncrypt(data.getBytes(),privateKey,keypass,publicKey);
		String dateByEn = new String(dataEn);
		System.out.println(dateByEn);
		byte[] dataDe = PGPUtils.decryptVerify(dataEn, privateKey,keypass,publicKey);
		String dateByDe = new String(dataDe);
		System.out.println(dateByDe);
		
		System.out.println("--------------------------------------分割线--------------------------------------");
		dataEn = PGPUtils.encrypt(data.getBytes(), publicKey);
		dateByEn = new String(dataEn);
		System.out.println(dateByEn);
		dataDe = PGPUtils.decrypt(dataEn, privateKey, keypass);
		dateByDe = new String(dataDe);
		System.out.println(dateByDe);
	}
}

测试:

public class PGPTest {
	@Test
	public void testPGP() throws MessageException, IOException, UnrecoverableKeyException, NotEncryptedToParameterException{
		String orgData = "Ron:广州天孚世佳信息科技有限公司";
		
		byte[] data = PGPUtils.encrypt(orgData.getBytes(), "公钥文件地址");
		
		String jsonData = new String(Base64.encodeBase64(data)); 
		
		//解密
		
		byte[] baseBytes = Base64.decodeBase64(jsonData.getBytes());
		
		//解密
		byte[] jsonByte = PGPUtils.decrypt(baseBytes,"私钥文件地址","私钥密钥");
		
		String plainData = new String(jsonByte);
		System.out.println(plainData);
	}
}


链接:https://www.jianshu.com/p/f39918033370

相关文章:

  • 2022-12-23
  • 2021-10-11
  • 2022-12-23
  • 2022-01-14
  • 2022-12-23
  • 2021-06-02
  • 2022-12-23
  • 2021-09-09
猜你喜欢
  • 2022-12-23
  • 2021-08-02
  • 2021-09-09
  • 2021-11-29
  • 2022-12-23
  • 2021-11-29
相关资源
相似解决方案