From a07ce38c2426038c7aa98c74e949c4ae27044f76 Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?=E9=AB=98=E5=9D=9A=E6=9E=9C?= <753610399@qq.com>
Date: Thu, 9 Jun 2022 01:29:08 +0800
Subject: [PATCH] =?UTF-8?q?=E5=8F=91=E5=B8=831.5=E7=89=88=E6=9C=AC?=
MIME-Version: 1.0
Content-Type: text/plain; charset=UTF-8
Content-Transfer-Encoding: 8bit
---
README.md | 188 ++++++++++++++++++++++++++++++++++++++------------
RSA_PEM.java | 27 ++++----
RSA_Util.java | 184 ++++++++++++++++++++++++++++++++++++++++++++++++
Test.java | 163 ++++++++++++++++++++++---------------------
4 files changed, 427 insertions(+), 135 deletions(-)
create mode 100644 RSA_Util.java
diff --git a/README.md b/README.md
index 38e016f..91768ca 100644
--- a/README.md
+++ b/README.md
@@ -1,23 +1,67 @@
+**【[源GitHub仓库](https://github.com/xiangyuecn/RSA-java)】 | 【[Gitee镜像库](https://gitee.com/xiangyuecn/RSA-java)】如果本文档图片没有显示,请手动切换到Gitee镜像库阅读文档。**
+
# :open_book:RSA-java的帮助文档
本项目核心功能:支持`Java`环境下`PEM`(`PKCS#1`、`PKCS#8`)格式RSA密钥对导入、导出。
-底层实现采用PEM文件二进制层面上进行字节码解析,简单轻巧0依赖;附带实现了一个含有RSA加密解密操作测试控制台程序(双击`Start.bat`即可运行,前提装了JDK)。
+你可以只copy `RSA_PEM.java` 文件到你的项目中使用(建好package目录或者修改一下package),只需这一个文件你就拥有了通过PEM格式密钥简单快速创建`RSA Cipher`的能力。也可以clone整个项目代码双击`Start.bat`即可直接测试。
+
+底层实现采用PEM文件二进制层面上进行字节码解析,简单轻巧0依赖;附带实现了一个RSA封装操作类(`RSA_Util.java`),和一个测试控制台程序(`Test.java`,双击`Start.bat`即可运行,前提装了JDK)。
-你可以只copy `RSA_PEM.java` 文件到你的项目中使用(建好package目录或者修改一下package),只需这一个文件你就拥有了通过PEM格式密钥简单快速创建`RSA Cipher`的能力。clone整个项目代码双击`Start.bat`即可观摩效果,经目测看起来没什么卵用的文件都svn:ignore掉了(svn滑稽。
+**如需功能定制,网站、App、小程序开发等需求,请加本文档下面的QQ群,联系群主(即作者),谢谢~**
【C#版】:[RSA-csharp](https://github.com/xiangyuecn/RSA-csharp),本Java项目从C#版移植。
+[](?)
+
## 特性
- 通过`XML格式`密钥对创建RSAPublicKey、RSAPrivateKey、Cipher
- 通过`PEM格式`密钥对创建RSAPublicKey、RSAPrivateKey、Cipher
+- 通过指定密钥位数创建RSA(生成公钥、私钥)
+- RSA加密、解密
+- RSA签名、验证
- 导出`XML格式`公钥、私钥
- 导出`PEM格式`公钥、私钥
- `PEM格式`秘钥对和`XML格式`秘钥对互转
+[](?)
+
+## 如何加密、解密、签名、校验
+得到了RSA_PEM后,加密解密就异常简单了,没那么多啰嗦难懂的代码:
+``` java
+//先解析pem,公钥私钥都行,如果是xml就用 RSA_PEM.FromXML("....")
+RSA_PEM pem=RSA_PEM.FromPEM("-----BEGIN XXX KEY-----..此处意思意思..-----END XXX KEY-----");
+
+//直接创建RSA操作类
+RSA_Util rsa=new RSA_Util(pem);
+//RSA_Util rsa=new RSA_Util(2048); //也可以直接生成新密钥,rsa.ToPEM()得到pem对象
+
+//加密
+String enTxt=rsa.Encode("测试123");
+
+//解密
+String deTxt=rsa.Decode(enTxt);
+
+//签名
+String sign=rsa.Sign("SHA1", "测试123");
+
+//校验签名
+boolean isVerify=rsa.Verify("SHA1", sign, "测试123");
+
+//导出pem文本
+String pemTxt=rsa.ToPEM(false).ToPEM_PKCS8(false);
+
+System.out.println(pemTxt+"\n"+enTxt+"\n"+deTxt+"\n"+sign+"\n"+isVerify);
+//****更多的实例,请阅读 Test.java****
+//****更多功能方法,请阅读下面的详细文档****
+```
+
+
+
+[](?)
## 【QQ群】交流与支持
@@ -27,6 +71,18 @@
+[](?)
+
+[](?)
+
+[](?)
+
+[](?)
+
+[](?)
+
+[](?)
+
# :open_book:文档
## 【RSA_PEM.java】
@@ -38,9 +94,9 @@
### 静态方法
-**static RSA_PEM FromPEM(String pem)**:用PEM格式密钥对创建RSA,支持PKCS#1、PKCS#8格式的PEM,出错将会抛出异常。pem格式如:`-----BEGIN XXX KEY-----....-----END XXX KEY-----`。
+`RSA_PEM` **FromPEM(String pem)**:用PEM格式密钥对创建RSA,支持PKCS#1、PKCS#8格式的PEM,出错将会抛出异常。pem格式如:`-----BEGIN XXX KEY-----....-----END XXX KEY-----`。
-**static RSA_PEM FromXML(String xml)**:将XML格式密钥转成PEM,支持公钥xml、私钥xml,出错将会抛出异常。xml格式如:`....`。
+`RSA_PEM` **FromXML(String xml)**:将XML格式密钥转成PEM,支持公钥xml、私钥xml,出错将会抛出异常。xml格式如:`....`。
### 构造方法
@@ -54,60 +110,93 @@
### 实例属性
-byte[]:**Key_Modulus**(模数n,公钥、私钥都有)、**Key_Exponent**(公钥指数e,公钥、私钥都有)、**Key_D**(私钥指数d,只有私钥的时候才有);有这3个足够用来加密解密。
+`byte[]`:**Key_Modulus**(模数n,公钥、私钥都有)、**Key_Exponent**(公钥指数e,公钥、私钥都有)、**Key_D**(私钥指数d,只有私钥的时候才有);有这3个足够用来加密解密。
-byte[]:**Val_P**(prime1)、**Val_Q**(prime2)、**Val_DP**(exponent1)、**Val_DQ**(exponent2)、**Val_InverseQ**(coefficient); (PEM中的私钥才有的更多的数值;可通过n、e、d反推出这些值(只是反推出有效值,和原始的值大概率不同))。
+`byte[]`:**Val_P**(prime1)、**Val_Q**(prime2)、**Val_DP**(exponent1)、**Val_DQ**(exponent2)、**Val_InverseQ**(coefficient); (PEM中的私钥才有的更多的数值;可通过n、e、d反推出这些值(只是反推出有效值,和原始的值大概率不同))。
-int:**keySize()**(密钥位数)
+`int` **keySize()**:密钥位数。
-boolean:**hasPrivate()**(是否包含私钥)
+`boolean` **hasPrivate()**:是否包含私钥。
### 实例方法
-**RSAPublicKey getRSAPublicKey()**:得到公钥Java对象。
+`RSAPublicKey` **getRSAPublicKey()**:得到公钥Java对象。
-**RSAPrivateKey getRSAPrivateKey()**:得到私钥Java对象,如果此PEM不含私钥会直接报错。
+`RSAPrivateKey` **getRSAPrivateKey()**:得到私钥Java对象,如果此PEM不含私钥会直接报错。
-**String ToPEM(boolean convertToPublic, boolean privateUsePKCS8, boolean publicUsePKCS8)**:将RSA中的密钥对转换成PEM格式。convertToPublic:等于true时含私钥的RSA将只返回公钥,仅含公钥的RSA不受影响 。**privateUsePKCS8**:私钥的返回格式,等于true时返回PKCS#8格式(`-----BEGIN PRIVATE KEY-----`),否则返回PKCS#1格式(`-----BEGIN RSA PRIVATE KEY-----`),返回公钥时此参数无效;两种格式使用都比较常见。**publicUsePKCS8**:公钥的返回格式,等于true时返回PKCS#8格式(`-----BEGIN PUBLIC KEY-----`),否则返回PKCS#1格式(`-----BEGIN RSA PUBLIC KEY-----`),返回私钥时此参数无效;一般用的多的是true PKCS#8格式公钥,PKCS#1格式公钥似乎比较少见。
+`String` **ToPEM(boolean convertToPublic, boolean privateUsePKCS8, boolean publicUsePKCS8)**:将RSA中的密钥对转换成PEM格式。convertToPublic:等于true时含私钥的RSA将只返回公钥,仅含公钥的RSA不受影响 。**privateUsePKCS8**:私钥的返回格式,等于true时返回PKCS#8格式(`-----BEGIN PRIVATE KEY-----`),否则返回PKCS#1格式(`-----BEGIN RSA PRIVATE KEY-----`),返回公钥时此参数无效;两种格式使用都比较常见。**publicUsePKCS8**:公钥的返回格式,等于true时返回PKCS#8格式(`-----BEGIN PUBLIC KEY-----`),否则返回PKCS#1格式(`-----BEGIN RSA PUBLIC KEY-----`),返回私钥时此参数无效;一般用的多的是true PKCS#8格式公钥,PKCS#1格式公钥似乎比较少见。
-**String ToPEM_PKCS1(boolean convertToPublic)**:ToPEM方法的简化写法,不管公钥还是私钥都返回PKCS#1格式;似乎导出PKCS#1公钥用的比较少,PKCS#8的公钥用的多些,私钥#1#8都差不多。
+`String` **ToPEM_PKCS1(boolean convertToPublic)**:ToPEM方法的简化写法,不管公钥还是私钥都返回PKCS#1格式;似乎导出PKCS#1公钥用的比较少,PKCS#8的公钥用的多些,私钥#1#8都差不多。
-**String ToPEM_PKCS8(boolean convertToPublic)**:ToPEM方法的简化写法,不管公钥还是私钥都返回PKCS#8格式。
+`String` **ToPEM_PKCS8(boolean convertToPublic)**:ToPEM方法的简化写法,不管公钥还是私钥都返回PKCS#8格式。
-**String ToXML(boolean convertToPublic)**:将RSA中的密钥对转换成XML格式,如果convertToPublic含私钥的RSA将只返回公钥,仅含公钥的RSA不受影响。
+`String` **ToXML(boolean convertToPublic)**:将RSA中的密钥对转换成XML格式,如果convertToPublic含私钥的RSA将只返回公钥,仅含公钥的RSA不受影响。
-## 如何加密、解密、签名、校验
-得到了RSA_PEM后,加密解密就异常简单了,没那么多啰嗦难懂的代码。
-``` java
-RAS_PEM pem=RSA_PEM.FromPEM("-----BEGIN XXX KEY-----..此处意思意思..-----END XXX KEY-----");
-
-//通过公钥构造加密Cipher
-Cipher enc = Cipher.getInstance("RSA");
-enc.init(Cipher.ENCRYPT_MODE, pem.getRSAPublicKey());
-byte[] en = enc.doFinal("测试123".getBytes("utf-8"));
-
-//通过私钥构造解密Cipher
-Cipher dec = Cipher.getInstance("RSA");
-dec.init(Cipher.DECRYPT_MODE, pem.getRSAPrivateKey());
-byte[] de = dec.doFinal(en);
-String deTxt=new String(de,"utf-8");//测试123
-
-//通过私钥构造签名对象
-Signature signature=Signature.getInstance("SHA1WithRSA");
-signature.initSign(pem.getRSAPrivateKey());
-signature.update("测试123".getBytes("utf-8"));
-byte[] signBytes=signature.sign();
-
-//通过公钥构造签名校验对象
-Signature signVerify=Signature.getInstance("SHA1WithRSA");
-signVerify.initVerify(pem.getRSAPublicKey());
-signVerify.update("测试123".getBytes("utf-8"));
-boolean isVerify=signVerify.verify(signBytes);
-```
-更多的实例,请阅读`Test.java`。
+
+## 【RSA_Util.java】
+这个文件依赖`RSA_PEM.java`,封装了加密、解密、签名、验证、秘钥导入导出操作。
+
+### 构造方法
+
+**RSA_Util(int keySize)**:用指定密钥大小创建一个新的RSA,会生成新密钥,出错抛异常。
+
+**RSA_Util(String pemOrXML)**:通过`PEM格式`或`XML格式`密钥,创建一个RSA,pem或xml内可以只包含一个公钥或私钥,或都包含,出错抛异常。`XML格式`如:`...`。pem支持`PKCS#1`、`PKCS#8`格式,格式如:`-----BEGIN XXX KEY-----....-----END XXX KEY-----`。
+
+**RSA_Util(RSA_PEM pem)**:通过一个pem对象创建RSA,pem为公钥或私钥,出错抛异常。
+
+
+### 实例属性
+
+`RSAPublicKey` **publicKey**:RSA公钥。
+
+`RSAPrivateKey` **privateKey**:RSA私钥,仅有公钥时为null。
+
+`int` **keySize()**:密钥位数。
+
+`boolean` **hasPrivate()**:是否包含私钥。
+
+
+### 实例方法
+
+`String` **ToXML(boolean convertToPublic)**:导出`XML格式`秘钥对,如果convertToPublic含私钥的RSA将只返回公钥,仅含公钥的RSA不受影响。
+
+`RSA_PEM` **ToPEM(boolean convertToPublic)**:导出RSA_PEM对象(然后可以通过RSA_PEM.ToPEM方法导出PEM文本),如果convertToPublic含私钥的RSA将只返回公钥,仅含公钥的RSA不受影响。
+
+`String` **Encode(String str)**:加密操作,支持任意长度数据,出错抛异常。
+
+`byte[]` **Encode(byte[] data)**:加密数据,支持任意长度数据,出错抛异常。
+
+`String` **Decode(String str)**:解密字符串(utf-8),出错抛异常。
+
+`byte[]` **Decode(byte[] data)**:解密数据,出错抛异常。
+
+`String` **Sign(String hash, String str)**:对str进行签名,并指定hash算法(如:SHA256 大写)。
+
+`byte[]` **Sign(String hash, byte[] data)**:对data进行签名,并指定hash算法(如:SHA256 大写)。
+
+`boolean` **Verify(String hash, String sign, String str)**:验证字符串str的签名是否是sign,并指定hash算法(如:SHA256 大写)。
+
+`boolean` **Verify(String hash, byte[] sign, byte[] data)**:验证data的签名是否是sign,并指定hash算法(如:SHA256 大写)。
+
+
+
+
+
+
+[](?)
+
+[](?)
+
+[](?)
+
+[](?)
+
+[](?)
+
+[](?)
# :open_book:图例
@@ -122,6 +211,13 @@ RSA工具(非开源):
+
+[](?)
+
+[](?)
+
+[](?)
+
# :open_book:知识库
请移步到[RSA-csharp](https://github.com/xiangyuecn/RSA-csharp)阅读知识库部分,知识库内包含了详细的PEM格式解析,和部分ASN.1语法;然后逐字节分解PEM字节码教程。
@@ -131,6 +227,12 @@ RSA工具(非开源):
本库的代码整理未使用IDE,RSA_PEM.java copy过来的,Test.java直接用的文本编辑器编写,*.java文件全部丢到根目录,没有创建包名目录,源码直接根目录裸奔,简单粗暴;这样的项目结构肉眼看去也算是简洁,也方便copy文件使用。
+[](?)
+
+[](?)
+
+[](?)
+
# :star:捐赠
如果这个库有帮助到您,请 Star 一下。
diff --git a/RSA_PEM.java b/RSA_PEM.java
index ff1eea9..cc9112f 100644
--- a/RSA_PEM.java
+++ b/RSA_PEM.java
@@ -28,15 +28,15 @@ public class RSA_PEM {
public byte[] Key_D;
//以下参数只有私钥才有 https://docs.microsoft.com/zh-cn/dotnet/api/system.security.cryptography.rsaparameters?redirectedfrom=MSDN&view=netframework-4.8
- /**prime1**/
+ /**prime1,只有私钥的时候才有**/
public byte[] Val_P;
- /**prime2**/
+ /**prime2,只有私钥的时候才有**/
public byte[] Val_Q;
- /**exponent1**/
+ /**exponent1,只有私钥的时候才有**/
public byte[] Val_DP;
- /**exponent2**/
+ /**exponent2,只有私钥的时候才有**/
public byte[] Val_DQ;
- /**coefficient**/
+ /**coefficient,只有私钥的时候才有**/
public byte[] Val_InverseQ;
private RSA_PEM() {}
@@ -59,14 +59,17 @@ public RSA_PEM(RSAPublicKey publicKey, RSAPrivateKey privateKeyOrNull) {
public RSA_PEM(byte[] modulus, byte[] exponent, byte[] d, byte[] p, byte[] q, byte[] dp, byte[] dq, byte[] inverseQ) {
Key_Modulus=modulus;
Key_Exponent=exponent;
- Key_D=BigL(d, modulus.length);
- int keyLen = modulus.length / 2;
- Val_P=BigL(p, keyLen);
- Val_Q=BigL(q, keyLen);
- Val_DP=BigL(dp, keyLen);
- Val_DQ=BigL(dq, keyLen);
- Val_InverseQ=BigL(inverseQ, keyLen);
+ if(d!=null){
+ Key_D=BigL(d, modulus.length);
+
+ int keyLen = modulus.length / 2;
+ Val_P=BigL(p, keyLen);
+ Val_Q=BigL(q, keyLen);
+ Val_DP=BigL(dp, keyLen);
+ Val_DQ=BigL(dq, keyLen);
+ Val_InverseQ=BigL(inverseQ, keyLen);
+ }
}
/***
* 通过公钥指数和私钥指数构造一个PEM,会反推计算出P、Q但和原始生成密钥的P、Q极小可能相同
diff --git a/RSA_Util.java b/RSA_Util.java
new file mode 100644
index 0000000..5b0f04c
--- /dev/null
+++ b/RSA_Util.java
@@ -0,0 +1,184 @@
+package com.github.xiangyuecn.rsajava;
+
+import java.io.ByteArrayOutputStream;
+import java.security.KeyPair;
+import java.security.KeyPairGenerator;
+import java.security.SecureRandom;
+import java.security.Signature;
+import java.security.interfaces.RSAPrivateKey;
+import java.security.interfaces.RSAPublicKey;
+import java.util.Base64;
+
+import javax.crypto.Cipher;
+
+
+/**
+ * RSA操作封装
+ *
+ * GitHub:https://github.com/xiangyuecn/RSA-java
+ */
+public class RSA_Util {
+ /**
+ * 导出XML格式密钥对,如果convertToPublic含私钥的RSA将只返回公钥,仅含公钥的RSA不受影响
+ */
+ public String ToXML(boolean convertToPublic) {
+ return ToPEM(convertToPublic).ToXML(convertToPublic);
+ }
+ /**
+ * 将密钥对导出成PEM对象,如果convertToPublic含私钥的RSA将只返回公钥,仅含公钥的RSA不受影响
+ */
+ public RSA_PEM ToPEM(boolean convertToPublic) {
+ return new RSA_PEM(publicKey, convertToPublic?null:privateKey);
+ }
+
+
+
+
+ /**
+ * 加密字符串(utf-8),出错抛异常
+ */
+ public String Encode(String str) throws Exception {
+ return Base64.getEncoder().encodeToString(Encode(str.getBytes("utf-8")));
+ }
+ /**
+ * 加密数据,出错抛异常
+ */
+ public byte[] Encode(byte[] data) throws Exception {
+ try(ByteArrayOutputStream stream=new ByteArrayOutputStream()){
+ Cipher enc = Cipher.getInstance("RSA");
+ enc.init(Cipher.ENCRYPT_MODE, publicKey);
+ int blockLen = keySize / 8 - 11;
+ int start=0;
+ while(startdata.length) {
+ len=data.length-start;
+ }
+
+ byte[] en = enc.doFinal(data, start, len);
+ stream.write(en);
+ start+=len;
+ }
+
+ return stream.toByteArray();
+ }
+ }
+ /**
+ * 解密字符串(utf-8),出错抛异常
+ */
+ public String Decode(String str) throws Exception {
+ if (str==null || str.length()==0) {
+ return "";
+ }
+ byte[] byts = Base64.getDecoder().decode(str);
+ byte[] val = Decode(byts);
+ return new String(val, "utf-8");
+ }
+ /**
+ * 解密数据,出错抛异常
+ */
+ public byte[] Decode(byte[] data) throws Exception {
+ try(ByteArrayOutputStream stream=new ByteArrayOutputStream()){
+ Cipher dec = Cipher.getInstance("RSA");
+ dec.init(Cipher.DECRYPT_MODE, privateKey);
+ int blockLen = keySize / 8;
+ int start=0;
+ while(startdata.length) {
+ len=data.length-start;
+ }
+
+ byte[] de = dec.doFinal(data, start, len);
+ stream.write(de);
+ start+=len;
+ }
+
+ return stream.toByteArray();
+ }
+ }
+ /**
+ * 对str进行签名,并指定hash算法(如:SHA256 大写),出错抛异常
+ */
+ public String Sign(String hash, String str) throws Exception {
+ return Base64.getEncoder().encodeToString(Sign(hash, str.getBytes("utf-8")));
+ }
+ /**
+ * 对data进行签名,并指定hash算法(如:SHA256 大写),出错抛异常
+ */
+ public byte[] Sign(String hash, byte[] data) throws Exception {
+ Signature signature=Signature.getInstance(hash+"WithRSA");
+ signature.initSign(privateKey);
+ signature.update(data);
+ return signature.sign();
+ }
+ /**
+ * 验证字符串str的签名是否是sign,并指定hash算法(如:SHA256 大写),出错抛异常
+ */
+ public boolean Verify(String hash, String sign, String str) throws Exception {
+ byte[] byts = Base64.getDecoder().decode(sign);
+ return Verify(hash, byts, str.getBytes("utf-8"));
+ }
+ /**
+ * 验证data的签名是否是sign,并指定hash算法(如:SHA256 大写)
+ */
+ public boolean Verify(String hash, byte[] sign, byte[] data) throws Exception {
+ Signature signVerify=Signature.getInstance(hash+"WithRSA");
+ signVerify.initVerify(publicKey);
+ signVerify.update(data);
+ return signVerify.verify(sign);
+ }
+
+
+
+
+ private int keySize;
+ /**秘钥位数**/
+ public int keySize(){
+ return keySize;
+ }
+ /**是否包含私钥**/
+ public boolean hasPrivate(){
+ return privateKey!=null;
+ }
+
+
+ /** 公钥 **/
+ public RSAPublicKey publicKey;
+ /** 私钥 **/
+ public RSAPrivateKey privateKey;
+
+ /**
+ * 用指定密钥大小创建一个新的RSA,会生成新密钥,出错抛异常
+ */
+ public RSA_Util(int keySize) throws Exception {
+ KeyPairGenerator keygen = KeyPairGenerator.getInstance("RSA");
+ keygen.initialize(keySize,new SecureRandom());
+ KeyPair keyPair = keygen.generateKeyPair();
+ publicKey=(RSAPublicKey)keyPair.getPublic();
+ privateKey=(RSAPrivateKey)keyPair.getPrivate();
+ this.keySize=keySize;
+ }
+ /**
+ * 通过指定的pem文件密钥或xml字符串密钥,创建一个RSA,pem或xml内可以只包含一个公钥或私钥,或都包含,出错抛异常
+ */
+ public RSA_Util(String pemOrXML) throws Exception {
+ RSA_PEM pem;
+ if (pemOrXML.trim().startsWith("<")) {
+ pem = RSA_PEM.FromXML(pemOrXML);
+ } else {
+ pem = RSA_PEM.FromPEM(pemOrXML);
+ }
+ publicKey=pem.getRSAPublicKey();
+ privateKey=pem.getRSAPrivateKey();
+ keySize=pem.keySize();
+ }
+ /**
+ * 通过一个pem对象创建RSA,pem为公钥或私钥,出错抛异常
+ */
+ public RSA_Util(RSA_PEM pem) throws Exception {
+ publicKey=pem.getRSAPublicKey();
+ privateKey=pem.getRSAPrivateKey();
+ keySize=pem.keySize();
+ }
+}
diff --git a/Test.java b/Test.java
index e6256d1..230c0b3 100644
--- a/Test.java
+++ b/Test.java
@@ -1,112 +1,109 @@
package com.github.xiangyuecn.rsajava;
-import javax.crypto.Cipher;
-import java.security.SecureRandom;
-import java.security.Signature;
-import java.security.KeyPair;
-import java.security.KeyPairGenerator;
-import java.util.Base64;
-
/**
* RSA_PEM测试控制台主程序
*
* GitHub:https://github.com/xiangyuecn/RSA-java
*/
public class Test {
- static void RSATest() throws Exception{
- KeyPairGenerator keygen = KeyPairGenerator.getInstance("RSA");
- keygen.initialize(512,new SecureRandom());
- KeyPair keyPair = keygen.generateKeyPair();
-
- String pemRawTxt=""
- +"-----BEGIN PRIVATE KEY-----"
- +Base64.getEncoder().encodeToString(keyPair.getPrivate().getEncoded())
- +"-----END PRIVATE KEY-----";
- //使用PEM PKCS#8文件的文本构造出pem对象
- RSA_PEM pem=RSA_PEM.FromPEM(pemRawTxt);
-
- boolean isEqRaw=pem.ToPEM_PKCS8(false).replaceAll("\\r|\\n","").equals(pemRawTxt);
- //生成PKCS#1和XML
- System.out.println("【" + pem.keySize() + "私钥(XML)】:");
- System.out.println(pem.ToXML(false));
+ static void RSATest(boolean fast) throws Exception{
+ //新生成一个RSA密钥,也可以通过已有的pem、xml文本密钥来创建RSA
+ RSA_Util rsa = new RSA_Util(512);
+ // RSA_Util rsa = new RSA_Util("pem或xml文本密钥");
+ // RSA_Util rsa = new RSA_Util(RSA_PEM.FromPEM("pem文本密钥"));
+ // RSA_Util rsa = new RSA_Util(RSA_PEM.FromXML("xml文本密钥"));
+
+ //得到pem对象
+ RSA_PEM pem=rsa.ToPEM(false);
+ //提取密钥pem字符串
+ String pem_pkcs1 = pem.ToPEM_PKCS1(false);
+ String pem_pkcs8 = pem.ToPEM_PKCS8(false);
+ //提取密钥xml字符串
+ String xml = rsa.ToXML(false);
+
+ System.out.println("【" + rsa.keySize() + "私钥(XML)】:");
+ System.out.println(xml);
System.out.println();
- System.out.println("【" + pem.keySize() + "私钥(PKCS#1)】:是否和KeyPair生成的相同"+(isEqRaw));
- System.out.println(pem.ToPEM_PKCS1(false));
+ System.out.println("【" + rsa.keySize() + "私钥(PKCS#1)】:");
+ System.out.println(pem_pkcs1);
System.out.println();
- System.out.println("【" + pem.keySize() + "公钥(PKCS#8)】:");
+ System.out.println("【" + rsa.keySize() + "公钥(PKCS#8)】:");
System.out.println(pem.ToPEM_PKCS8(true));
System.out.println();
String str = "abc内容123";
- //加密内容
- Cipher enc = Cipher.getInstance("RSA");
- enc.init(Cipher.ENCRYPT_MODE, pem.getRSAPublicKey());
- byte[] en = enc.doFinal(str.getBytes("utf-8"));
+ String en=rsa.Encode(str);
System.out.println("【加密】:");
- System.out.println(Base64.getEncoder().encodeToString(en));
+ System.out.println(en);
- //解密内容
- Cipher dec = Cipher.getInstance("RSA");
- dec.init(Cipher.DECRYPT_MODE, pem.getRSAPrivateKey());
- byte[] de = dec.doFinal(en);
System.out.println("【解密】:");
- System.out.println(new String(de,"utf-8"));
-
-
- //私钥签名
- Signature signature=Signature.getInstance("SHA1WithRSA");
- signature.initSign(pem.getRSAPrivateKey());
- signature.update(str.getBytes("utf-8"));
- byte[] sign=signature.sign();
- System.out.println("【SHA1签名】:");
- System.out.println("签名:"+Base64.getEncoder().encodeToString(sign));
-
- //公钥校验
- Signature signVerify=Signature.getInstance("SHA1WithRSA");
- signVerify.initVerify(pem.getRSAPublicKey());
- signVerify.update(str.getBytes("utf-8"));
- boolean verify=signVerify.verify(sign);
- System.out.println("校验:"+verify);
+ String de=rsa.Decode(en);
+ AssertMsg(de, de.equals(str));
+
+ if (!fast) {
+ String str2 = str; for (int i = 0; i < 15; i++) str2 += str2;
+ System.out.println("【长文本加密解密】:");
+ AssertMsg(str2.length() + "个字 OK", rsa.Decode(rsa.Encode(str2)).equals(str2));
+ }
+
+
+ System.out.println("【签名SHA1】:");
+ String sign = rsa.Sign("SHA1", str);
+ System.out.println(sign);
+ AssertMsg("校验 OK", rsa.Verify("SHA1", sign, str));
System.out.println();
- //使用PEM PKCS#1构造pem对象
- RSA_PEM pem2=RSA_PEM.FromPEM(pem.ToPEM_PKCS1(false));
+ //用pem文本创建RSA
+ RSA_Util rsa2=new RSA_Util(RSA_PEM.FromPEM(pem_pkcs8));
+ RSA_PEM pem2=rsa2.ToPEM(false);
System.out.println("【用PEM新创建的RSA是否和上面的一致】:");
- System.out.println("XML:" + (pem2.ToXML(false) .equals( pem.ToXML(false) )));
- System.out.println("PKCS1:" + (pem2.ToPEM_PKCS1(false) .equals( pem.ToPEM_PKCS1(false) )));
- System.out.println("PKCS8:" + (pem2.ToPEM_PKCS8(false) .equals( pem.ToPEM_PKCS8(false) )));
+ Assert("XML:", pem2.ToXML(false) .equals( pem.ToXML(false) ));
+ Assert("PKCS1:", pem2.ToPEM_PKCS1(false) .equals( pem.ToPEM_PKCS1(false) ));
+ Assert("PKCS8:", pem2.ToPEM_PKCS8(false) .equals( pem.ToPEM_PKCS8(false) ));
- //使用XML构造pem对象
- RSA_PEM pem3=RSA_PEM.FromXML(pem.ToXML(false));
+ //用xml文本创建RSA
+ RSA_Util rsa3=new RSA_Util(RSA_PEM.FromXML(xml));
+ RSA_PEM pem3=rsa3.ToPEM(false);
System.out.println("【用XML新创建的RSA是否和上面的一致】:");
- System.out.println("XML:" + (pem3.ToXML(false) .equals( pem.ToXML(false) )));
- System.out.println("PKCS1:" + (pem3.ToPEM_PKCS1(false) .equals( pem.ToPEM_PKCS1(false) )));
- System.out.println("PKCS8:" + (pem3.ToPEM_PKCS8(false) .equals( pem.ToPEM_PKCS8(false) )));
+ Assert("XML:", pem3.ToXML(false) .equals( pem.ToXML(false) ));
+ Assert("PKCS1:", pem3.ToPEM_PKCS1(false) .equals( pem.ToPEM_PKCS1(false) ));
+ Assert("PKCS8:", pem3.ToPEM_PKCS8(false) .equals( pem.ToPEM_PKCS8(false) ));
- //--------RSA_PEM验证---------
+ //--------RSA_PEM私钥验证---------
//使用PEM全量参数构造pem对象
RSA_PEM pemX = new RSA_PEM(pem.Key_Modulus, pem.Key_Exponent, pem.Key_D
, pem.Val_P, pem.Val_Q, pem.Val_DP, pem.Val_DQ, pem.Val_InverseQ);
System.out.println("【RSA_PEM是否和原始RSA一致】:");
- System.out.println(pem.keySize() + "位");
- System.out.println("XML:" + (pemX.ToXML(false) .equals( pem.ToXML(false) )));
- System.out.println("PKCS1:" + (pemX.ToPEM_PKCS1(false) .equals( pem.ToPEM_PKCS1(false) )));
- System.out.println("PKCS8:" + (pemX.ToPEM_PKCS8(false) .equals( pem.ToPEM_PKCS8(false) )));
+ System.out.println(pemX.keySize() + "位");
+ Assert("XML:", pemX.ToXML(false) .equals( pem.ToXML(false) ));
+ Assert("PKCS1:", pemX.ToPEM_PKCS1(false) .equals( pem.ToPEM_PKCS1(false) ));
+ Assert("PKCS8:", pemX.ToPEM_PKCS8(false) .equals( pem.ToPEM_PKCS8(false) ));
System.out.println("仅公钥:");
- System.out.println("XML:" + (pemX.ToXML(true) .equals( pem.ToXML(true) )));
- System.out.println("PKCS1:" + (pemX.ToPEM_PKCS1(true) .equals( pem.ToPEM_PKCS1(true) )));
- System.out.println("PKCS8:" + (pemX.ToPEM_PKCS8(true) .equals( pem.ToPEM_PKCS8(true) )));
+ Assert("XML:", pemX.ToXML(true) .equals( pem.ToXML(true) ));
+ Assert("PKCS1:", pemX.ToPEM_PKCS1(true) .equals( pem.ToPEM_PKCS1(true) ));
+ Assert("PKCS8:", pemX.ToPEM_PKCS8(true) .equals( pem.ToPEM_PKCS8(true) ));
+
+ //--------RSA_PEM公钥验证---------
+ RSA_PEM pemY = new RSA_PEM(pem.Key_Modulus, pem.Key_Exponent, null);
+ System.out.println("【RSA_PEM仅公钥是否和原始RSA一致】:");
+ System.out.println(pemY.keySize() + "位");
+ Assert("XML:", pemY.ToXML(false) .equals( pem.ToXML(true) ));
+ Assert("PKCS1:", pemY.ToPEM_PKCS1(false) .equals( pem.ToPEM_PKCS1(true) ));
+ Assert("PKCS8:", pemY.ToPEM_PKCS8(false) .equals( pem.ToPEM_PKCS8(true) ));
- //使用n、e、d构造pem对象
- RSA_PEM pem4 = new RSA_PEM(pem.Key_Modulus, pem.Key_Exponent, pem.Key_D);
- Cipher dec4 = Cipher.getInstance("RSA");
- dec4.init(Cipher.DECRYPT_MODE, pem4.getRSAPrivateKey());
- System.out.println("【用n、e、d构造解密】");
- System.out.println(new String(dec4.doFinal(en),"utf-8"));
+
+ if (!fast) {
+ //使用n、e、d构造pem对象
+ RSA_PEM pem4 = new RSA_PEM(pem.Key_Modulus, pem.Key_Exponent, pem.Key_D);
+ RSA_Util rsa4=new RSA_Util(pem4);
+ System.out.println("【用n、e、d构造解密】");
+ de=rsa4.Decode(en);
+ AssertMsg(de, de.equals(str));
+ }
@@ -121,17 +118,23 @@ static void RSATest() throws Exception{
-
+ static void Assert(String msg, boolean check) throws Exception {
+ AssertMsg(msg + check, check);
+ }
+ static void AssertMsg(String msg, boolean check) throws Exception {
+ if (!check) throw new Exception(msg);
+ System.out.println(msg);
+ }
public static void main(String[] argv) throws Exception{
System.out.println("---------------------------------------------------------");
System.out.println("◆◆◆◆◆◆◆◆◆◆◆◆ RSA测试 ◆◆◆◆◆◆◆◆◆◆◆◆");
System.out.println("---------------------------------------------------------");
- //for(int i=0;i<1000;i++){System.out.println("第"+i+"次>>>>>"); RSATest(); }
- RSATest();
+ //for(int i=0;i<1000;i++){System.out.println("第"+i+"次>>>>>"); RSATest(true); }
+ RSATest(false);
System.out.println("-------------------------------------------------------------");
System.out.println("◆◆◆◆◆◆◆◆◆◆◆◆ 测试结束 ◆◆◆◆◆◆◆◆◆◆◆◆");
}
-}
\ No newline at end of file
+}