C#与JavaRsa加密与解密互通

合集下载
  1. 1、下载文档前请自行甄别文档内容的完整性,平台不提供额外的编辑、内容补充、找答案等附加服务。
  2. 2、"仅部分预览"的文档,不可在线预览部分如存在完整性等问题,可反馈申请退款(可完整预览的文档不适用该条件!)。
  3. 3、如文档侵犯您的权益,请联系客服反馈,我们会尽快为您处理(人工客服工作时间:9:00-18:30)。

C#与JavaRsa加密与解密互通
Rsa 加密标准的制定已经过去了⼗多年了. 这两天在看rsa 加密的⽂章,基本上都是在说 .net 与 java 之间的 rsa加密是不能互通的.因为项⽬有⽤到,所以花了点时间对rsa加密做了⼀
点点了解,发现,不管是java 还是 C# 都对 rsa 的标准加密进⾏了实现, 是对于标准是实现,不能互通就讲不过去了. 今天特意写了⼀段java 代码试了⼀下,发现是完全可以的.
密钥的描述: C#(.net) 中有三种⽅式导出密钥,⼀种是blob,⼀种是 xml 另⼀种是参数,其中xml 的⽅式是把参数进⾏了 xml序列化.blob 的形式我没看懂是什么意思,只知道⽂档上说
是给微软的什么api⽤的,下⾯给出其参数的形式.
RSAParameters 字段Contains对应的 PKCS #1 字段
d,私钥指数privateExponent
d mod (p - 1) exponent1
d mod (q - 1) exponent2
e,公钥指数publicExponent
(InverseQ)(q) = 1 mod p coefficient
n modulus
p prime1
q prime2
RSA 算法
若要⽣成密钥对,可以从创建名为 p 和 q 的两个⼤的质数开始; 这两个数相乘,结果称为 n; 因为 p 和 q 都是质数,所以 n 的全部因数为 1、p、q 和 n;
如果仅考虑⼩于 n 的数,则与 n 为互质数(即与 n 没有公因数)的数的个数等于 (p - 1)(q - 1);
现在,选择⼀个数 e,它与计算的值为互质数; 则公钥表⽰为 {e, n};
若要创建私钥,则必须计算 d,它是满⾜ (d)(e) mod n = 1 的⼀个数; 根据 Euclidean 算法,私钥为 {d, n};
纯⽂本 m 到密码⽂本 c 的加密定义为 c = (m ^ e) mod n; 解密则定义为 m = (c ^ d) mod n;
总之,我们可以从 .net 的rsa 中拿到合适的密钥描述就是了,⾄于 java的,我想也是可以做到的,⽽且通常密钥是应该放在密钥容器的.
接下来我们⽤.net ⽣成⼀个 rsa 的密钥,以⼗六进制的⽅式输出的(new ⼀个RSACryptoServiceProvider,把密钥导出就可以了)
D: 2FE7479CF4CFEE63218C44D763C3E552DC5FBC94A31F944B88AE8E58F0ED16874B8BED35307B143F413761B2ECFFC95F48DF0D0A29FC155C0B968EFE9FFF36E7
DP: 6777B761BC29637622FC63682243BB2E05CCFC6FF710ADE1DCE6B0C843B17C4F
DQ: 68771CCDA40F0DA0B504C438BB03F7DF30F77364094D475E70270D148260D247
Exponent: 010001
InverseQ: 5665AB47697008CC2CECB544B582B9C50628281C400846C1E736629B03FE5C85
Modulus: B3F276C8EDF515FD3248CCF4163480B9F77443A666522D66B89411EC6DFE11DEA917A97C977750EE777DACBD4D2C11BC363FDC110E5CCA0A1361D51AFA4A7ADD
P: ECC60A01B1BDCBA1C5422D8A0A34FC0E46727DB4ED5089E54C356F052E0AB573
Q: C28F233948483D0CD0E3FA7B5D2955F2B15E831B38876FB0E7180D873EDF7A6F
为了⽅便写.net 代码,这⾥贴⼀下blob的密钥
0702000000A40000525341320002000001000100DD7A4AFA1AD561130ACA5C0E11DC3F36BC112C4DBDAC7D77EE5077977CA917A9DE11FE6DEC1194B8662D5266A64374F7B9803416F4CC4832FD15F5EDC876F2B373B50A2E056F354CE58因为是私钥,所以同时可以加密和解密.下⾯上 C# 代码,⽐较简单
using System;
using System.Security.Cryptography;
class Program
{
public static void Main(string[] args)
{
byte[] plainText = new byte[]{0,1,2,3,4,5};
byte[] cipherText;
byte[] key = String2Bytes("0702000000A40000525341328001000001000100D3D10816051881319774576B67B1D24F3AA303471A4402AB625208EC1CB04D508AF2098227C5EE185890ECB83E6971C12BDCF4F8AB0FD729167C815D34 using(var rsa = new RSACryptoServiceProvider())
{
rsa.ImportCspBlob(key);
Console.WriteLine("OAEP:");
Console.WriteLine(Bytes2String(rsa.Encrypt(plainText, true)));
Console.WriteLine("PCSK1-v1_5:");
Console.WriteLine(Bytes2String(rsa.Encrypt(plainText, false)));
}
Console.Write("Press any key to continue . . . ");
Console.ReadKey(true);
}
const string pattern = @"[^0-9a-fA-F]+";
static byte[] String2Bytes(string str)
{
str = System.Text.RegularExpressions.Regex.Replace(str, pattern, "");
if (str == string.Empty)
return null;
byte[] data = new byte[str.Length / 2];
for (int i = 0; i < data.Length; ++i)
data[i] = byte.Parse(str.Substring(2 * i, 2), System.Globalization.NumberStyles.HexNumber);
return data;
}
static string Bytes2String(byte[] data)
{
System.Text.StringBuilder builder = new System.Text.StringBuilder();
foreach (var element in data) {
builder.AppendFormat("{0:X2}",element);
}
return builder.ToString();
}
}
运⾏后输出(rsa加密,密⽂每次都不⼀定⼀样):
OAEP:
87F04B0F28B81D23E63DA71C8278E0B7E357F40583BDDCAB493D44A58080EB178EC8E0DB0DCD4BE5427FDB8190229B8DF2511BDA1082607C92BD03B0615D5AD3 PCSK1-v1_5:
358AB4D336D0C35DAE3895E8A125F4F5AD0FB58117A4100FAF15DE95FF8615F01FFB1A59C9B579792B7C14E93E54A3E7E236D464DDB93D8DF9D96F63F46BACD7现在我们有密⽂了,⾄于.net 的解密 .net 加密后的密⽂,我没兴趣去看,反正铁定可以的就是了.
下⾯我们写java 的解密部分
package rsatest;
import java.math.BigInteger;
import java.security.KeyFactory;
import java.security.PrivateKey;
import java.security.PublicKey;
import java.security.spec.RSAPrivateKeySpec;
import java.security.spec.RSAPublicKeySpec;
import javax.crypto.Cipher;
public class RsaTest {
public static void main(String[] args) throws Exception {
//前⾯补了个0,符号位为0,表⽰⾮负
BigInteger n = new BigInteger("B3F276C8EDF515FD3248CCF4163480B9F77443A666522D66B89411EC6DFE11DEA917A97C977750EE777DACBD4D2C11BC363FDC110E5CCA0A1361D51AFA4A7ADD", 16); BigInteger e = new BigInteger("010001", 16);
BigInteger d = new BigInteger("2FE7479CF4CFEE63218C44D763C3E552DC5FBC94A31F944B88AE8E58F0ED16874B8BED35307B143F413761B2ECFFC95F48DF0D0A29FC155C0B968EFE9FFF36E7", 16);
RSAPublicKeySpec keySpec = new RSAPublicKeySpec(n, e);
PublicKey publicKey = KeyFactory.getInstance("RSA").generatePublic(keySpec);
RSAPrivateKeySpec prvKeySpec = new RSAPrivateKeySpec(n, d);
PrivateKey privateKey = KeyFactory.getInstance("RSA").generatePrivate(prvKeySpec);
//现在key 准备好了,把前⾯的密⽂放这⾥解密
byte[] oaepCiphertext = Hex2Bytes("87F04B0F28B81D23E63DA71C8278E0B7E357F40583BDDCAB493D44A58080EB178EC8E0DB0DCD4BE5427FDB8190229B8DF2511BDA1082607C92BD03B0615D5AD3");
//解密OAEP 加密的数据
Cipher cipher = Cipher.getInstance("RSA/ECB/OAEPPADDING");
cipher.init(Cipher.DECRYPT_MODE, privateKey);
byte[] plaintext = cipher.doFinal(oaepCiphertext);
System.out.println(Bytes2Hex(plaintext));
//解密PKCS1-v1_5 加密的数据
byte[] ciphertext = Hex2Bytes("358AB4D336D0C35DAE3895E8A125F4F5AD0FB58117A4100FAF15DE95FF8615F01FFB1A59C9B579792B7C14E93E54A3E7E236D464DDB93D8DF9D96F63F46BACD7");
cipher = Cipher.getInstance("RSA");
cipher.init(Cipher.DECRYPT_MODE, privateKey);
plaintext = cipher.doFinal(ciphertext);
System.out.println(Bytes2Hex(plaintext));
}
public static byte[] Hex2Bytes(String hexStr) {
if (hexStr.length() % 2 != 0) {
hexStr = "0" + hexStr;
}
byte[] bytes = new byte[hexStr.length() / 2];
for (int i = 0; i < bytes.length; ++i) {
bytes[i] = (byte) Integer.parseUnsignedInt(hexStr.substring(i * 2, i * 2 + 2), 16);
}
return bytes;
}
public static String Bytes2Hex(byte[] bytes) {
StringBuilder builder = new StringBuilder();
for (byte b : bytes) {
builder.append(String.format("%02X", b));
}
return builder.toString();
}
}
程序运⾏后输出:
000102030405
000102030405
跟我们预期的是⼀样的.
我们再⽤java 对明⽂加密,代码⽚段
// plaintext {0,1,2,3,4,5}
cipher = Cipher.getInstance("RSA/ECB/OAEPPADDING");
cipher.init(Cipher.ENCRYPT_MODE, publicKey);
oaepCiphertext = cipher.doFinal(plaintext);
System.out.println("OAEP:");
System.out.println(Bytes2Hex(oaepCiphertext));
cipher = Cipher.getInstance("RSA");
cipher.init(Cipher.ENCRYPT_MODE, publicKey);
ciphertext = cipher.doFinal(plaintext);
System.out.println("PCSK1-v1_5:");
System.out.println(Bytes2Hex(ciphertext));
对应输出(rsa加密,密⽂每次都不⼀定⼀样):
OAEP:
3144C4CB06C7F49D31E65D09C840069F7CCF602487908CCEAB33D473B949199E1795530B69E1FA20EB59E392B2B934024D46E979DEA1682BDFA61D6FDD980F9C PCSK1-v1_5:
699A694BEB75616879C6B8D311CC10D987EA109D494EE6C9380CD2C02A124613F130C440CB1CA6D3405E50B62CF96A79EB43C3370253E5D8C1A9132CFE01D686接下来⽤C# 对数据解密,代码⽚段
//还是⽤之前那个rsa对象
//OAEP
cipherText = String2Bytes("3144C4CB06C7F49D31E65D09C840069F7CCF602487908CCEAB33D473B949199E1795530B69E1FA20EB59E392B2B934024D46E979DEA1682BDFA61D6FDD980F9C"); Console.WriteLine(Bytes2String(rsa.Decrypt(cipherText, true)));
//PCSK1-v1_5
cipherText = String2Bytes("699A694BEB75616879C6B8D311CC10D987EA109D494EE6C9380CD2C02A124613F130C440CB1CA6D3405E50B62CF96A79EB43C3370253E5D8C1A9132CFE01D686"); Console.WriteLine(Bytes2String(rsa.Decrypt(cipherText, false)));
对应的输出:
000102030405
000102030405
解密也成功了.
或许该头疼的问题是,已知 {e,n} 或者 {d,n} 怎么⽣成.net 使⽤的密钥.。

相关文档
最新文档