Skip to content

Commit

Permalink
Release Update v1.6, 重新发布,调整部分逻辑,修复错误
Browse files Browse the repository at this point in the history
  • Loading branch information
xiangyuecn committed Sep 12, 2023
1 parent 34b1efc commit 1e4ae87
Show file tree
Hide file tree
Showing 4 changed files with 115 additions and 37 deletions.
50 changes: 33 additions & 17 deletions Program.cs
Original file line number Diff line number Diff line change
Expand Up @@ -114,13 +114,18 @@ static void RSATest(bool fast) {

//对调交换公钥私钥
ST("【Unsafe|对调公钥私钥,私钥加密公钥解密】", "[ Unsafe | Swap the public key and private key, private key encryption and public key decryption ]");
rsa4 = rsa.SwapKey_Exponent_D__Unsafe();
var rsaPri = rsa.SwapKey_Exponent_D__Unsafe();
var rsaPub = new RSA_Util(rsa.ToPEM(true)).SwapKey_Exponent_D__Unsafe();
if (!RSA_Util.IsUseBouncyCastle) {
rsaPub = rsaPri;
ST(".NET自带的RSA不支持仅含公钥的密钥进行解密和签名,使用NoPadding填充方式或IsUseBouncyCastle时无此问题", "The RSA that comes with .NET does not support decryption and signing with keys containing only public keys. This problem does not occur when using NoPadding or IsUseBouncyCastle.");
}
try {
var en4 = rsa4.Encrypt("PKCS1", str);
var sign4 = rsa4.Sign("SHA1", str);
de = rsa4.Decrypt("PKCS1", en4);
var enPri = rsaPri.Encrypt("PKCS1", str);
var signPub = rsaPub.Sign("SHA1", str);
de = rsaPub.Decrypt("PKCS1", enPri);
AssertMsg(de, de == str);
AssertMsg(T("校验 OK", "Verify OK"), rsa4.Verify("SHA1", sign4, str));
AssertMsg(T("校验 OK", "Verify OK"), rsaPri.Verify("SHA1", signPub, str));
} catch (Exception e) {
if (!RSA_Util.IS_CoreOr46 && !RSA_Util.IsUseBouncyCastle) {
S(T("不支持在RSACryptoServiceProvider中使用:", "Not supported in RSACryptoServiceProvider: ") + e.Message);
Expand All @@ -129,7 +134,7 @@ static void RSATest(bool fast) {
}
}

rsa4 = rsa4.SwapKey_Exponent_D__Unsafe();
rsa4 = rsaPri.SwapKey_Exponent_D__Unsafe();
de = rsa4.Decrypt("PKCS1", en);
AssertMsg(de, de == str);
AssertMsg(T("校验 OK", "Verify OK"), rsa4.Verify("SHA1", sign, str));
Expand All @@ -141,7 +146,7 @@ static void RSATest(bool fast) {
ST("【测试一遍所有的加密、解密填充方式】 按回车键继续测试...", "[ Test all the encryption and decryption padding mode ] Press Enter to continue testing...");
ReadIn();
RSA_Util rsa5 = new RSA_Util(2048);
testPaddings(false, rsa5, true);
testPaddings(false, rsa5, new RSA_Util(rsa5.ToPEM(true)), true);
}
}
static Type Type_RuntimeInformation(Type[] outOSPlatform) {
Expand Down Expand Up @@ -404,10 +409,20 @@ static void testProvider(bool checkOpenSSL) {

S(HR);
ST("测试一遍所有的加密、解密填充方式:", "Test all the encryption and decryption padding mode:");
testPaddings(checkOpenSSL, rsa, true);
testPaddings(checkOpenSSL, rsa, new RSA_Util(rsa.ToPEM(true)), true);

S(HR);
ST("Unsafe|是否要对调公钥私钥(私钥加密公钥解密)重新测试一遍?(Y/N) N", "Unsafe | Do you want to swap the public and private keys (private key encryption and public key decryption) and test again? (Y/N) N");
Console.Write("> ");
string yn = ReadIn().Trim().ToUpper();
if (yn == "Y") {
var rsaPri = rsa.SwapKey_Exponent_D__Unsafe();
var rsaPub = new RSA_Util(rsa.ToPEM(true)).SwapKey_Exponent_D__Unsafe();
testPaddings(checkOpenSSL, rsaPub, rsaPri, true);
}
}
/// <summary>测试一遍所有的加密、解密填充方式</summary>
static int testPaddings(bool checkOpenSSL, RSA_Util rsa, bool log) {
static int testPaddings(bool checkOpenSSL, RSA_Util rsaPri, RSA_Util rsaPub, bool log) {
int errCount = 0;
var errMsgs = new List<string>();
var txt = "1234567890";
Expand All @@ -419,7 +434,7 @@ static int testPaddings(bool checkOpenSSL, RSA_Util rsa, bool log) {

if (checkOpenSSL) {
try {
runOpenSSL(rsa, txtData);
runOpenSSL(rsaPri.HasPrivate ? rsaPri : rsaPub, txtData);
} catch (Exception e) {
S(T("运行OpenSSL失败:", "Failed to run OpenSSL: ") + e.Message);
return errCount;
Expand All @@ -431,8 +446,8 @@ static int testPaddings(bool checkOpenSSL, RSA_Util rsa, bool log) {
var errMsg = "";
try {
{
byte[] enc = rsa.Encrypt(type, txtData);
byte[] dec = rsa.Decrypt(type, enc);
byte[] enc = rsaPub.Encrypt(type, txtData);
byte[] dec = rsaPri.Decrypt(type, enc);
bool isOk = true;
if (dec.Length != txtData.Length) {
isOk = false;
Expand All @@ -456,7 +471,7 @@ static int testPaddings(bool checkOpenSSL, RSA_Util rsa, bool log) {
errMsg = "+OpenSSL: " + T("OpenSSL加密出错", "OpenSSL encryption error");
throw e;
}
byte[] dec = rsa.Decrypt(type, enc);
byte[] dec = rsaPri.Decrypt(type, enc);
bool isOk = true;
if (dec.Length != txtData.Length) {
isOk = false;
Expand Down Expand Up @@ -493,8 +508,8 @@ static int testPaddings(bool checkOpenSSL, RSA_Util rsa, bool log) {
var errMsg = "";
try {
{
byte[] sign = rsa.Sign(type, txtData);
var isOk = rsa.Verify(type, sign, txtData);
byte[] sign = rsaPri.Sign(type, txtData);
var isOk = rsaPub.Verify(type, sign, txtData);
if (!isOk) {
errMsg = T("未通过校验", "Failed verification");
throw new Exception(errMsg);
Expand All @@ -508,7 +523,7 @@ static int testPaddings(bool checkOpenSSL, RSA_Util rsa, bool log) {
errMsg = "+OpenSSL: " + T("OpenSSL签名出错", "OpenSSL signature error");
throw e;
}
var isOk = rsa.Verify(type, sign, txtData);
var isOk = rsaPub.Verify(type, sign, txtData);
if (!isOk) {
errMsg = "+OpenSSL: " + T("未通过校验", "Failed verification");
throw new Exception(errMsg);
Expand Down Expand Up @@ -550,12 +565,13 @@ static void threadRun() {
int Count = 0;
int ErrCount = 0;
RSA_Util rsa = new RSA_Util(2048);
RSA_Util rsaPub = new RSA_Util(rsa.ToPEM(true));
S(T("正在测试中,线程数:", "Under test, number of threads: ") + ThreadCount + T(",按回车键结束测试...", ", press enter to end the test..."));

for (int i = 0; i < ThreadCount; i++) {
new Thread(() => {
while (!Abort) {
int err = testPaddings(false, rsa, false);
int err = testPaddings(false, rsa, rsaPub, false);
if (err > 0) {
Interlocked.Add(ref ErrCount, err);
}
Expand Down
10 changes: 6 additions & 4 deletions README-English.md
Original file line number Diff line number Diff line change
Expand Up @@ -75,8 +75,10 @@ var isVerify=rsa.Verify("PKCS1+SHA1", sign, "test123");
var pemTxt=rsa.ToPEM().ToPEM_PKCS8();

//Unconventional (unsafe, not recommended): private key encryption, public key decryption, public key signature, private key verification
RSA_Util rsa2=rsa.SwapKey_Exponent_D__Unsafe();
//... rsa2.Encrypt rsa2.Decrypt rsa2.Sign rsa2.Verify
RSA_Util rsaS_Private=rsa.SwapKey_Exponent_D__Unsafe();
RSA_Util rsaS_Public=new RSA_Util(rsa.ToPEM(true)).SwapKey_Exponent_D__Unsafe();
//... rsaS_Private.Encrypt rsaS_Public.Decrypt
//... rsaS_Public.Sign rsaS_Private.Verify
Console.WriteLine(pemTxt+"\n"+enTxt+"\n"+deTxt+"\n"+sign+"\n"+isVerify);
Console.ReadLine();
Expand Down Expand Up @@ -121,7 +123,7 @@ Welcome to join QQ group: 421882406, pure lowercase password: `xiangyuecn`
Padding|Algorithm|Frame|Core|BC
:-|:-|:-:|:-:|:-:
NO|RSA/ECB/NoPadding|×|×|√
NO|RSA/ECB/NoPadding|√|√|√
PKCS1 |RSA/ECB/PKCS1Padding|√|√|√
OAEP+SHA1 |RSA/ECB/OAEPwithSHA-1andMGF1Padding|√|√|√
OAEP+SHA256|RSA/ECB/OAEPwithSHA-256andMGF1Padding|4.6+|√|√
Expand Down Expand Up @@ -291,7 +293,7 @@ The `RSA_Util.cs` file depends on `RSA_PEM.cs`, which encapsulates encryption, d

`RSA_PEM` **ToPEM(bool convertToPublic = false)**: Export RSA_PEM object (then you can export PEM text by RSA_PEM.ToPEM method), if convertToPublic RSA containing private key will only return public key, RSA containing only public key will not be affected.

`RSA_Util` **SwapKey_Exponent_D__Unsafe()**: [Unsafe and not recommended] Swap the public key exponent (Key_Exponent) and the private key exponent (Key_D): use the public key as the private key (new.Key_D=this.Key_Exponent) and the private key as the public key (new. Key_Exponent=this.Key_D), returns a new RSA object; for example, used for: private key encryption, public key decryption, this is an unconventional usage. The current object must contain a private key, otherwise an exception will be thrown if it cannot be swapped. Note: It is very insecure to use the public key as a private key, because the public key exponent of most generated keys is 0x10001 (AQAB), which is too easy to guess and cannot be used as a real private key. The swapped key does not support use in RSACryptoServiceProvider (.NET Framework 4.5 and below): `!IS_CoreOr46 && !IsUseBouncyCastle`.
`RSA_Util` **SwapKey_Exponent_D__Unsafe()**: [Unsafe and not recommended] Swap the public key exponent (Key_Exponent) and the private key exponent (Key_D): use the public key as the private key (new.Key_D=this.Key_Exponent) and the private key as the public key (new.Key_Exponent=this.Key_D), returns a new RSA object; for example, used for: private key encryption, public key decryption, this is an unconventional usage. If the current key only contains the public key, the swap will not occur, and the returned new RSA will allow decryption and signing operations with the public key; However, the RSA that comes with .NET does not support decryption and signing with keys containing only the public key, and the exponent must be swapped (If it is .NET Framework 4.5 and below, public and private keys are not supported), there is no such problem when using NoPadding or IsUseBouncyCastle. Note: It is very unsafe to use a public key as a private key, because the public key exponent of most generated keys is 0x10001 (AQAB), which is too easy to guess and cannot be used as a true private key. In some private key encryption implementations, such as Java's own RSA, when using non-NoPadding padding, encryption with private key objects may use EMSA-PKCS1-v1_5 padding (using the private key exponent to construct a public key object does not have this problem ), so when interoperating between different programs, you may need to use the corresponding padding algorithm to first fill the data, and then use NoPadding padding to encrypt (decryption also uses NoPadding padding to decrypt, and then remove the padding data).

`string` **Encrypt(string padding, string str)**: Encrypt arbitrary length string (utf-8) returns base64, and an exception is thrown if an error occurs. This method is thread safe. padding specifies the encryption padding, such as: PKCS1, OAEP+SHA256 uppercase, refer to the encryption padding table above, and the default is PKCS1 when using a null value.

Expand Down
10 changes: 6 additions & 4 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -76,8 +76,10 @@ var isVerify=rsa.Verify("PKCS1+SHA1", sign, "测试123");
var pemTxt=rsa.ToPEM().ToPEM_PKCS8();

//非常规的(不安全、不建议使用):私钥加密、公钥解密,公钥签名、私钥验证
RSA_Util rsa2=rsa.SwapKey_Exponent_D__Unsafe();
//... rsa2.Encrypt rsa2.Decrypt rsa2.Sign rsa2.Verify
RSA_Util rsaS_Private=rsa.SwapKey_Exponent_D__Unsafe();
RSA_Util rsaS_Public=new RSA_Util(rsa.ToPEM(true)).SwapKey_Exponent_D__Unsafe();
//... rsaS_Private.Encrypt rsaS_Public.Decrypt
//... rsaS_Public.Sign rsaS_Private.Verify
Console.WriteLine(pemTxt+"\n"+enTxt+"\n"+deTxt+"\n"+sign+"\n"+isVerify);
Console.ReadLine();
Expand Down Expand Up @@ -122,7 +124,7 @@ Console.ReadLine();
加密填充方式|Algorithm|Frame|Core|BC
:-|:-|:-:|:-:|:-:
NO|RSA/ECB/NoPadding|×|×|√
NO|RSA/ECB/NoPadding|√|√|√
PKCS1 |RSA/ECB/PKCS1Padding|√|√|√
OAEP+SHA1 |RSA/ECB/OAEPwithSHA-1andMGF1Padding|√|√|√
OAEP+SHA256|RSA/ECB/OAEPwithSHA-256andMGF1Padding|4.6+|√|√
Expand Down Expand Up @@ -292,7 +294,7 @@ PSS+MD5 |MD5withRSA/PSS|4.6+|√|√

`RSA_PEM` **ToPEM(bool convertToPublic = false)**:导出RSA_PEM对象(然后可以通过RSA_PEM.ToPEM方法导出PEM文本),如果convertToPublic含私钥的RSA将只返回公钥,仅含公钥的RSA不受影响。

`RSA_Util` **SwapKey_Exponent_D__Unsafe()**:【不安全、不建议使用】对调交换公钥指数(Key_Exponent)和私钥指数(Key_D):把公钥当私钥使用(new.Key_D=this.Key_Exponent)、私钥当公钥使用(new.Key_Exponent=this.Key_D),返回一个新RSA对象;比如用于:私钥加密、公钥解密,这是非常规的用法。当前对象必须含私钥,否则无法交换会直接抛异常。注意:把公钥当私钥使用是非常不安全的,因为绝大部分生成的密钥的公钥指数为 0x10001(AQAB),太容易被猜测到,无法作为真正意义上的私钥。交换后的密钥不支持在RSACryptoServiceProvider(.NET Framework 4.5及以下版本)中使用:`!IS_CoreOr46 && !IsUseBouncyCastle`
`RSA_Util` **SwapKey_Exponent_D__Unsafe()**:【不安全、不建议使用】对调交换公钥指数(Key_Exponent)和私钥指数(Key_D):把公钥当私钥使用(new.Key_D=this.Key_Exponent)、私钥当公钥使用(new.Key_Exponent=this.Key_D),返回一个新RSA对象;比如用于:私钥加密、公钥解密,这是非常规的用法。当前密钥如果只包含公钥,将不会发生对调,返回的新RSA将允许用公钥进行解密和签名操作;但.NET自带的RSA不支持仅含公钥的密钥进行解密和签名,必须进行指数对调(如果是.NET Framework 4.5及以下版本,公钥私钥均不支持),使用NoPadding填充方式或IsUseBouncyCastle时无此问题。注意:把公钥当私钥使用是非常不安全的,因为绝大部分生成的密钥的公钥指数为 0x10001(AQAB),太容易被猜测到,无法作为真正意义上的私钥。部分私钥加密实现中,比如Java自带的RSA,使用非NoPadding填充方式时,用私钥对象进行加密可能会采用EMSA-PKCS1-v1_5填充方式(用私钥指数构造成公钥对象无此问题),因此在不同程序之间互通时,可能需要自行使用对应填充算法先对数据进行填充,然后再用NoPadding填充方式进行加密(解密也按NoPadding填充进行解密,然后去除填充数据)

`string` **Encrypt(string padding, string str)**:加密任意长度字符串(utf-8)返回base64,出错抛异常。本方法线程安全。padding指定填充方式,如:PKCS1、OAEP+SHA256大写,参考上面的加密填充方式表格,使用空值时默认为PKCS1。

Expand Down
Loading

0 comments on commit 1e4ae87

Please sign in to comment.