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 +}