RSA加密算法實現以及C#與java互通加解密


一.RSA算法簡介

關於RSA加密算法可以參考:http://zh.wikipedia.org/wiki/RSA%E5%8A%A0%E5%AF%86%E6%BC%94%E7%AE%97%E6%B3%95

大體是先生成兩個大素數p和q,再生成e,e和(p-1)*(q-1)互素。

取p和q的乘積:n=p*q 為公共模數。

再生成正整數d,滿足d*e-1可以被(p-1)*(q-1)整除。

這樣d就為私鑰,(e,n)為公鑰,形成rsa的公私鑰對。

其中n的二進制位稱為該密鑰長度,密鑰越長越難破解,也就越安全。

二.填充算法

由於密鑰長度有限,一次性加密的數據長度也有限,因此必須對明文進行分塊加密,再合並加密結果。

以1024位密鑰為例,n為1024位,即128個字節,則明文需要分塊成每塊長128個字節,不足128位的使用特定格式的數據填充。

所以分塊的算法稱為填充算法,有不同的標准,如NoPPadding、OAEPPadding、PKCS1Padding等。

本文實現的是PKCS1Padding填充算法,規范文檔是這個:http://man.chinaunix.net/develop/rfc/RFC2313.txt

該算法規定的格式如下:

00 || BT || PS || 00 || D

其中BT可選擇01和02,01表示私鑰加密,02表示公鑰加密。如果BT是01則PS需要使用oxFF填充,為02的話則需要使用隨機非0填充。D表示分割的明文塊。

PS的長度至少為8個字節,因此D至多只能為128-8-2=117個字節長。

三.C#實現

之所以要使用C#實現是因為官方版本只實現了公鑰加密且在明文中間增加了一些數字再進行的加密,使用私鑰解密出來后需要去除增加的數字。

而要實現私鑰加密則只能自己實現了,這里使用到了一個大整數類:BigInteger。我是從這里下載的:http://www.codeproject.com/Articles/2728/C-BigInteger-Class

C#里生成公私鑰對可以使用如下代碼:

RSACryptoServiceProvider key = new RSACryptoServiceProvider();
RSAParameters param = key.ExportParameters(true);
Console.WriteLine(Convert.ToBase64String(param.Modulus));
Console.WriteLine(Convert.ToBase64String(param.Exponent));
Console.WriteLine(Convert.ToBase64String(param.D));

其中Modulus+Exponent為公鑰,D為私鑰,C#里是以base64格式保存的。

填充算法實現如下(blockLen為模的字節數,這里為128):

//填充
private byte[] add_PKCS1_padding(byte[] oText, int blockLen)
{
byte[] result = new byte[blockLen];
result[0] = 0x00;
result[1] = 0x01;

int padLen = blockLen - 3 - oText.Length;
for (int i = 0; i < padLen; i++)
{
result[i + 2] = 0xff;
}

result[padLen + 2] = 0x00;

int j = 0;
for (int i = padLen + 3; i < blockLen; i++)
{
result[i] = oText[j++];
}

return result;
}

私鑰加密方法如下:

//私鑰加密
private byte[] priEncrypt(byte[] block, RSACryptoServiceProvider key)
{
RSAParameters param = key.ExportParameters(true);
BigInteger d = new BigInteger(param.D);
BigInteger n = new BigInteger(param.Modulus);
BigInteger biText = new BigInteger(block);
BigInteger biEnText = biText.modPow(d, n);
return biEnText.getBytes();
}

則整個私鑰加密方法可以如下進行:

//私鑰加密
public byte[] encryptByPriKey(String src, RSACryptoServiceProvider key){
//獲得明文字節數組
byte[] oText = System.Text.Encoding.Default.GetBytes(src);
//填充
oText = add_PKCS1_padding(oText, 128);
//加密
byte[] result = priEncrypt(oText, key);
return result;
}

加密結果為字節數組,可以轉化為base64編碼的字符串,但是建議轉化為十六進制字符串,便於用於web傳遞。

對於大於128字節的明文,需要分成多段進行加密,使用本文的填充算法實現是大於117字節的明文就需要分段。

為了方便的web環境下的數據交換,可以對明文進行urlencode后再進行加密,這樣解密后使用urldecode可以得到明文。

實現如下:

//url編碼
String urlEncode = System.Uri.EscapeDataString(src);

//以117個字符為長度分割字符串進行加密
int index = 0;
int len = urlEncode.Length;
String toEncrypt = "";
while (index < len)
{
String temp = "";
if (index + 117 < len)
{
temp = urlEncode.Substring(index, index + 117);
}
else
{
temp = urlEncode.Substring(index);
}

//加密
byte[] encrypted = encryptByPriKey(temp, r);

//轉化為16進制字符串
String enc = bytesToHexStr(encrypted);
toEncrypt += enc;

index += 117;
}
Console.WriteLine(toEncrypt);

對節字數組轉化為十六進制字符串可以如下實現:

char[] bcdLookup = { '0', '1', '2', '3', '4', '5',
'6', '7', '8', '9', 'a', 'b', 'c', 'd', 'e', 'f' };

//字節轉化為十六進制字符串
public String bytesToHexStr(byte[] bcd) {
String s ="";

for (int i = 0; i < bcd.Length; i++) {
s +=(bcdLookup[(((byte)bcd[i]) >> 4) & 0x0f]);
s+=(bcdLookup[(byte)bcd[i] & 0x0f]);
}

return s;
}

至此為止,我們便實現了C#的私鑰加密算法實現。

C#的簽名實現使用官方實現即可與java互通,應該是沒有使用填充的原因。

C#簽名和驗證實現如下:

//明文轉化為字節數組
byte[] srcBytes = System.Text.Encoding.Default.GetBytes(src);

//簽名
byte[] signed = key.SignData(srcBytes, "sha1");

//轉化為16進制字符串
String sign = nr.bytesToHexStr(signed);
Console.WriteLine(sign);

//驗證
bool verify = r.VerifyData(srcBytes, "sha1", signed);
Console.WriteLine(verify);

使用此方法簽名的數據可以使用java驗證。由於是簽名和驗證,不需要還原數據,所以可以直接對明文加密,不需要使用urlencode。

 

四.結語

java所實現的算法有完整的一套框架,包括了各種填充算法,可以輕松實現公鑰加密和解密,簽名和驗證等,本文就不再示例。

 

附:公鑰解密實現

//公鑰解密
public String decryptByPubKey(String enc, RSACryptoServiceProvider key)
{
String result = "";
int blockLen = 256;
int i = 0;
while (i < enc.Length)
{
String temp = enc.Substring(i, blockLen);

byte[] oText = hexToBytes(temp);

//解密
byte[] dec = pubDecrypt(oText, key);

//去除填充
dec = remove_PKCS1_padding(dec);

result += System.Text.Encoding.Default.GetString(dec);

i += blockLen;
}
return result;
}

//公鑰解密
private byte[] pubDecrypt(byte[] block, RSACryptoServiceProvider key)
{
RSAParameters param = key.ExportParameters(true);
BigInteger e = new BigInteger(param.Exponent);
BigInteger n = new BigInteger(param.Modulus);
BigInteger biText = new BigInteger(block);
BigInteger biEnText = biText.modPow(e, n);
return biEnText.getBytes();
}

//去除填充
private byte[] remove_PKCS1_padding(byte[] oText)
{
int i = 2;
byte b = (byte)(oText[i] & 0xff);
while (b != 0)
{
i++;
b = (byte)(oText[i] & 0xff);
}
byte[] result = new byte[oText.Length - i];
int j = 0;
while (i < oText.Length)
{
result[j++] = oText[i++];
}
return result;
}

//十六進制字符串轉化成字節數組
public byte[] hexToBytes(String s)
{
byte[] bytes = new byte[s.Length / 2];

for (int i = 0; i < bytes.Length; i++)
{
bytes[i] = (byte)Convert.ToInt32(s.Substring(2 * i, 2), 16);
}

return bytes;
}




免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM