Rsa 加密標准的制定已經過去了十多年了. 這兩天在看rsa 加密的文章,基本上都是在說 .net 與 java 之間的 rsa加密是不能互通的.因為項目有用到,所以花了點時間對rsa加密做了一點點了解,發現,不管是java 還是 C# 都對 rsa 的標准加密進行了實現, 是 對於標准是實現,不能互通就講不過去了. 今天特意寫了一段java 代碼試了一下,發現是完全可以的.
密鑰的描述: C#(.net) 中有三種方式導出密鑰,一種是blob,一種是 xml 另一種是參數,其中xml 的方式是把 參數進行了 xml序列化.blob 的形式我沒看懂是什麼意思,只知道文檔上說是給微軟的什麼api用的,下面給出其參數的形式.
RSAParameters 字段
Contains
對應的 PKCS #1 字段
D
d,私鑰指數
privateExponent
DP
d mod (p - 1)
exponent1
DQ
d mod (q - 1)
exponent2
Exponent
e,公鑰指數
publicExponent
InverseQ
(InverseQ)(q) = 1 mod p
coefficient
Modulus
n
modulus
P
p
prime1
Q
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的密鑰
0702000000A40000525341320002000001000100DD7A4AFA1AD561130ACA5C0E11DC3F36BC112C4DBDAC7D77EE5077977CA917A9DE11FE6DEC1194B8662D5266A64374F7B9803416F4CC4832FD15F5EDC876F2B373B50A2E056F354CE58950EDB47D72460EFC340A8A2D42C5A1CBBDB1010AC6EC6F7ADF3E870D18E7B06F87381B835EB1F255295D7BFAE3D00C3D484839238FC24F7CB143C8B0E6DCE1AD10F76FFCCC052EBB43226863FC22766329BC61B7776747D26082140D27705E474D096473F730DFF703BB38C404B5A00D0FA4CD1C7768855CFE039B6236E7C14608401C282806C5B982B544B5EC2CCC08706947AB6556E736FF9FFE8E960B5C15FC290A0DDF485FC9FFECB26137413F147B3035ED8B4B8716EDF0588EAE884B941FA394BC5FDC52E5C363D7448C2163EECFF49C47E72F
因為是私鑰,所以同時可以加密和解密.下面上 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("0702000000A40000525341328001000001000100D3D10816051881319774576B67B1D24F3AA303471A4402AB625208EC1CB04D508AF2098227C5EE185890ECB83E6971C12BDCF4F8AB0FD729167C815D3404C1AD1C0628E3544C89E9F9044A6869447310C72D7CDEB5E3582AB28BCC5069D60CD4AB5A1BB8C754AEB544FA65FB990ADB68F5AA37D7AC2CCDBF19058A2A4CF18FC29577D184E75EFD0ABEC1263893262F40C89AB2831B20B52B477770BA95FA02A71339911EBBF8630AD1AEECC205888440037D580B18AEF11FC5F35EB74E434E5A9302823BF795A34DADDFE0C6D55BC7997E667ADBE511BA06"); 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 使用的密鑰.
可以的
參考這個對C盤進行清理:
1.關閉系統還原:我的電腦屬性/系統還原/關閉所有磁盤上的系統還原,但是以後就不能用系統還原了!
2.關閉系統休眠:控制面板/電源/休眠/在啟動系統休眠前面的勾去掉
3.移動虛擬內存,我的電腦屬性/高級/性能/設置/高級/更改/選C盤也就是系統盤,選無分頁面,然後把虛擬內存設置到其磁盤,要剩余磁盤空間多的磁盤,比如D,E,F等盤. 設成內存的1.5~2.5倍,大小可設成一樣!
5.清理IE臨時文件夾,internet選項,刪除臨時文件和脫機文件
6.刪除系統日志和程序日志,我的電腦/控制面板/管理工具/計算機管理/事件查看器/應用程序,鼠標右鍵/清除所事件,在依次清除系統日志
7.清理系統緩存:2000系統是:C:\WINNT\system32\dllcache下的所有文件
XP系統是:C:\windows\system32\dllcache下的所有文件 清理系統緩存(打開我的電腦/工具/文件和文件夾選項/隱藏受保護的系統文件的勾去掉在把顯示全部文件勾上)。也可以直接運行sfc.exe /purgecache命令自動刪除。
8.清空回收站
9.刪除c:\windows\SoftwareDistribution\Download下的文件(系統更新時下載的文件如你裝好了更新也就沒有用了)
10.刪除c:\windows\RegisteredPackages下所有目錄
11.刪除C:\WINDOWS\Downloaded Program Files下所有的文件
12.我的電腦 文件夾選項 查看 隱藏已知受系統保護的文件勾去掉,顯示所有文件勾上確定。
13.刪除c:\windows\所有帶$8882305$的文件(系統更新後的備份文件)
zhidao.baidu.com/question/11035955.html
zhidao.baidu.com/question/12223613.html
zhidao.baidu.com/question/14874715.html
......余下全文>>
可以的
參考這個對C盤進行清理:
1.關閉系統還原:我的電腦屬性/系統還原/關閉所有磁盤上的系統還原,但是以後就不能用系統還原了!
2.關閉系統休眠:控制面板/電源/休眠/在啟動系統休眠前面的勾去掉
3.移動虛擬內存,我的電腦屬性/高級/性能/設置/高級/更改/選C盤也就是系統盤,選無分頁面,然後把虛擬內存設置到其磁盤,要剩余磁盤空間多的磁盤,比如D,E,F等盤. 設成內存的1.5~2.5倍,大小可設成一樣!
5.清理IE臨時文件夾,internet選項,刪除臨時文件和脫機文件
6.刪除系統日志和程序日志,我的電腦/控制面板/管理工具/計算機管理/事件查看器/應用程序,鼠標右鍵/清除所事件,在依次清除系統日志
7.清理系統緩存:2000系統是:C:\WINNT\system32\dllcache下的所有文件
XP系統是:C:\windows\system32\dllcache下的所有文件 清理系統緩存(打開我的電腦/工具/文件和文件夾選項/隱藏受保護的系統文件的勾去掉在把顯示全部文件勾上)。也可以直接運行sfc.exe /purgecache命令自動刪除。
8.清空回收站
9.刪除c:\windows\SoftwareDistribution\Download下的文件(系統更新時下載的文件如你裝好了更新也就沒有用了)
10.刪除c:\windows\RegisteredPackages下所有目錄
11.刪除C:\WINDOWS\Downloaded Program Files下所有的文件
12.我的電腦 文件夾選項 查看 隱藏已知受系統保護的文件勾去掉,顯示所有文件勾上確定。
13.刪除c:\windows\所有帶$8882305$的文件(系統更新後的備份文件)
zhidao.baidu.com/question/11035955.html
zhidao.baidu.com/question/12223613.html
zhidao.baidu.com/question/14874715.html
......余下全文>>