加密

最近在作的項目,須要帳戶密碼登陸,採用 AES 加密算法,從認識到了解,以及後續的擴展學習。html

首先,明確幾個加解密的重要概念java

密鑰react

在明文轉換爲密文或將密文轉換爲明文的算法中輸入的參數。密鑰分爲對稱密鑰(私用密鑰)與非對稱密鑰(公共密鑰)。git

密鑰加密算法

發送和接收數據的雙方,使用相同的或對稱的密鑰對明文進行加密解密運算的加密方法。c#

加密向量數組

一般採用密鑰長度的隨機文本塊對純文本進行異或運算,而後再對其進行加密,產生一個加密文本塊。而後,將前面產生的密文塊做爲一個初始化向量對下一個純文本塊進行異或運算。安全

對稱加密算法中,若是隻有一個密鑰來加密數據的話,明文中的相同文字就會也會被加密成相同的密文,這樣密文和明文就有徹底相同的結構,容易破解。若是給一個初始化向量,第一個明文使用初始化向量混合並加密,第二個明文用第一個明文的加密後的密文與第二個明文混合加密,這樣加密出來的密文的結構則徹底與明文不一樣,更加安全可靠。網絡

綜上,加密之因此安全,並不是不知道加解密算法,而是加密的密鑰和向量是絕對的隱藏,加密向量能夠增長加密算法的強度(區塊加密)。架構

AES

那麼,什麼是 AES 加密呢?

Advanced Encryption Standard(高級加密標準),在密碼學中又叫 Rijndael 加密法,是美國聯邦政府採用的一種區塊加密標準。

  • 替代 DES
  • 對稱密鑰加密(迭代對稱分組密碼體制)
  • 代換-置換網絡(基於排列和置換運算),非Feistel架構(DES)
  • AES 加密數據塊分組長度必須爲128比特,密鑰長度能夠是128比特、192比特、256比特中的任意一個

Rijndael 密碼的設計力求知足如下3條標準:

  抵抗全部已知的攻擊
  在多個平臺上速度快,編碼緊湊
  設計簡單

當前的大多數分組密碼,其輪函數是Feistel結構。Rijndael 輪函數是由3個不一樣的可逆均勻變換組成。

具體信息參見:AES是個什麼鬼?

本文後續代碼基礎:AES加密,密鑰位數不足轉換成byte[]後填充(byte)0,加密向量16位,加密模式CBC,填充模式PKCS5Padding,字符集是UTF-8,輸出是HEX格式

其中,PKCS5Padding(Java)與 PKCS7(C#)是能夠互相加解密的,這是正好匹配的狀況。

可是,常常會存在合做雙方平臺不一致、跨語言接口對接致使數據不一致的問題,能夠採用ikvm工具將.jar包轉換爲.dll,則在.Net平臺直接引用並調用方法便可

具體方法:在 jre1.8版本下,使用 ikvm 將jar轉換爲dll,以供c#調用

/// <summary>
/// AES加密
/// </summary>
/// <param name="_pwd">明文密碼</param>
/// <param name="_key">加密密鑰</param>
/// <param name="_iv">加密向量</param>
/// <returns></returns>
public static string AESEncrypt(string _pwd, string _key, string _iv, int _keyLen, int _ivLen)
{
	byte[] toEncryptArray = UTF8Encoding.UTF8.GetBytes(_pwd);
	using (AesCryptoServiceProvider aesProvider = new AesCryptoServiceProvider())
	{
		aesProvider.Mode = System.Security.Cryptography.CipherMode.CBC;
		aesProvider.Padding = System.Security.Cryptography.PaddingMode.PKCS7;
		aesProvider.Key = GetAesKey(_key, _keyLen);
		aesProvider.IV = GetAesVector(_iv, _ivLen);
		using (MemoryStream ms = new MemoryStream())
		{
			using (CryptoStream cs = new CryptoStream(
				ms, aesProvider.CreateEncryptor(), CryptoStreamMode.Write))
			{
				cs.Write(toEncryptArray, 0, toEncryptArray.Length);
				cs.FlushFinalBlock();
				cs.Close();
			}

			string resultStr = ByteArrayToHexString(ms.ToArray()); // Convert.ToBase64String
			ms.Close();
			return resultStr;
		}
	}
}

/// <summary>
/// AES解密
/// </summary>
/// <param name="_pwd">暗文密碼</param>
/// <param name="_key">密鑰</param>
/// <param name="_iv">向量</param>
/// <returns></returns>
public static string AESDecrypt(string _pwd, string _key, string _iv, int _keyLen, int _ivLen)
{
	byte[] toDecryptArray = HexStringToByteArray(_pwd);
	using (AesCryptoServiceProvider aesProvider = new AesCryptoServiceProvider())
	{
		aesProvider.Mode = System.Security.Cryptography.CipherMode.CBC;
		aesProvider.Padding = System.Security.Cryptography.PaddingMode.PKCS7;
		aesProvider.Key = GetAesKey(_key, _keyLen);
		aesProvider.IV = GetAesVector(_iv, _ivLen);

		using (MemoryStream ms = new MemoryStream(toDecryptArray))
		{
			byte[] decryptBytes = new byte[toDecryptArray.Length];
			string resultStr = string.Empty;
			using (CryptoStream cs = new CryptoStream(
				ms, aesProvider.CreateDecryptor(), CryptoStreamMode.Read))
			{
				if (cs.Read(decryptBytes, 0, decryptBytes.Length) > 0)
				{
					resultStr = Encoding.UTF8.GetString(decryptBytes);
				}
				cs.Close();
			}

			ms.Close();
			return resultStr;
		}
	}
}

/// </summary>
/// 獲取AES密鑰
/// </summary>
/// <param name="_key">Aes密鑰字符串</param>
/// <param name="_length">默認長度16字節</param>
/// <returns>Aes密鑰</returns>
public static byte[] GetAesKey(string _key, int _length)
{
	byte[] resBytes = new byte[_length];
	byte[] keyBytes = UTF8Encoding.UTF8.GetBytes(_key);
	int lenTmp = keyBytes.Length;

	if (lenTmp <= _length)
	{
		Array.Copy(keyBytes, resBytes, lenTmp);
	}
	else
	{
		Array.Copy(keyBytes, resBytes, _length);
	}

	return resBytes;
}

/// <summary>
/// 獲取AES向量
/// </summary>
/// <param name="_iv">Aes向量字符串</param>
/// <param name="_length">默認長度16字節</param>
/// <returns>Aes向量</returns>
public static byte[] GetAesVector(string _iv, int _length)
{
	byte[] resBytes = new byte[_length];
	if (string.IsNullOrWhiteSpace(_iv))
	{
		// _iv爲空,返回全0字節數組
		return resBytes;
	}
	else
	{
		byte[] ivBytes = UTF8Encoding.UTF8.GetBytes(_iv);
		int lenTmp = ivBytes.Length;
		if (lenTmp <= _length)
		{
			Array.Copy(ivBytes, resBytes, lenTmp);
		}
		else
		{
			Array.Copy(ivBytes, resBytes, _length);
		}
		return resBytes;
	}
}

/// <summary>
/// 將一個byte數組轉換成一個格式化的16進制字符串
/// </summary>
/// <param name="data">byte數組</param>
/// <returns>格式化的16進制字符串</returns>
public static string ByteArrayToHexString(byte[] data)
{
	StringBuilder sb = new StringBuilder(data.Length * 3);
	foreach (byte b in data)
	{
		//16進制數字
		sb.Append(Convert.ToString(b, 16).PadLeft(2, '0'));
	}
	return sb.ToString().ToUpper();
}

/// <summary>
/// 將一個格式化的16進制字符串轉換成一個byte數組
/// </summary>
/// <param name="?"></param>
/// <returns></returns>
public static byte[] HexStringToByteArray(string str)
{
	str = str.Replace(" ", "");
	byte[] buffer = new byte[str.Length / 2];
	for (int i = 0; i < str.Length; i += 2)
	{
		buffer[i / 2] = (byte)Convert.ToByte(str.Substring(i, 2), 16);
	}
	return buffer;
}

測試發現一個解密的bug,會出現解密結果 "288008\0\0\0\0\0\0\0\0\0\0" 的狀況,遂推薦以下方法

using (ICryptoTransform transform = aesProvider.CreateDecryptor())
{
	byte[] plainText = transform.TransformFinalBlock(toDecryptArray, 0, toDecryptArray.Length);
	return Encoding.UTF8.GetString(plainText); 
} 

或直接在返回時進行替換處理: return resultStr.Replace("\0", ""); 

注意,上述代碼加密結果是 HEX格式。若是採用 Base64格式,需分別以下改動:

加密-AESEncrypt:Convert.ToBase64String()
解密-AESDecrypt:Convert.FromBase64String()

除上述加解密方法外,還能夠採用以下方式:RijndaelManaged

public static string AesEnc(string key, string content)
{
    byte[] keyBytes = Encoding.UTF8.GetBytes(key);
    byte[] contentBytes = Encoding.UTF8.GetBytes(content);
    RijndaelManaged rDel = new RijndaelManaged();
    rDel.Key = keyBytes;
    rDel.Mode = CipherMode.ECB;
    rDel.Padding = PaddingMode.PKCS7;
    ICryptoTransform cTransForm = rDel.CreateEncryptor();
    byte[] result = cTransForm.TransformFinalBlock(contentBytes, 0, contentBytes.Length);
    return Convert.ToBase64String(result, 0, result.Length);
}

public static string AesDecr(string key, string content)
{
    byte[] keyBytes = Encoding.UTF8.GetBytes(key);
    byte[] contentBytes = Encoding.UTF8.GetBytes(content);
    RijndaelManaged rDel = new RijndaelManaged();
    rDel.Key = keyBytes;
    rDel.Mode = CipherMode.ECB;
    rDel.Padding = PaddingMode.PKCS7;
    ICryptoTransform cTransForm = rDel.CreateDecryptor();
    byte[] result = cTransForm.TransformFinalBlock(contentBytes, 0, contentBytes.Length);
    return Encoding.UTF8.GetString(result);
}

具體參見:AES 加解密

 


學習 MD5 以前,再明確單向加密的概念。

單向加密

單向散列算法,經過散列生成固定長度,不可逆且防衝突,經常使用於消息摘要、密鑰加密等確保數據的完整性。

  • MD5:標準密鑰長度128位
  • SHA:能夠對任意長度的數據運算生成一個160位的數值

MD5

Message-Digest Algorithm 5(消息摘要算法),加密單向不可逆,即沒法根據密文推導出明文。計算機安全領域普遍使用的一種散列函數,用以提供消息的完整性保護。

特色:

  • 容易計算
  • 壓縮性:MD5長度固定,把一個任意長度的字節串變換成必定長的16進制數字串(標準密鑰長度128位)
  • 抗修改性:對原數據進行任何改動,哪怕只修改1個字節,所獲得的MD5值都有很大區別
  • 強抗碰撞:已知原數據和其MD5值,想找到一個具備相同MD5值的數據(即僞造數據)很是困難

主要用途:

  • 密碼加密
  • 一致性驗證文件的有效性(正確、重複、缺失、損壞)
  • 對一段信息生成信息摘要,惟一性 ---> 數字簽名
  • 哈希函數中計算散列值

MD5 以512位分組來處理輸入的信息,且每一分組又被劃分爲16個32位子分組,通過了一系列的處理後,算法的輸出由四個32位分組組成,將這四個32位分組級聯後將生成一個128位散列值。

  • 填充,使其位長對512求餘的結果等於448
  • 後64位二進制表示填充前的信息長度
  • 信息的位長 = N*512+448+64 = (N+1)*512
using System.Security.Cryptography;
using System.IO;
using System.Web;
using System.Web.Security;

/// <summary>
/// MD5加密
/// 信息摘要算法,不可逆
/// </summary>
/// <param name="myStr">待加密字符串</param>
public static string MD5Encrypt(string myStr)
{
	MD5 md5 = new MD5CryptoServiceProvider();
	byte[] myBytes = System.Text.Encoding.UTF8.GetBytes(myStr);
	byte[] hashBytes = md5.ComputeHash(myBytes);
        md5.Clear();//資源釋放

	StringBuilder _sb_ = new StringBuilder();
	for (int i = 0; i < hashBytes.Length; i++)
	{
		// "x2"表示加密結果爲32位,"x3"爲48位,"x4"爲64位
		// X/x 分別表示結果大小寫
		_sb_.Append(hashBytes[i].ToString("x2"));
	}
	return _sb_.ToString();
}

/// <summary>
/// MD5加密方法
/// 默認結果32位,若要結果16位則.SubString()截取便可
/// </summary>
public static string Get32MD5(string myStr)
{
	string md5Res = "";
	md5Res = FormsAuthentication.HashPasswordForStoringInConfigFile(myStr, "MD5");
	return (md5Res);
}

/// <summary>
/// MD5加密
/// </summary>
public static string MD5(string stringToHash)
{
    MD5 md5csp = new System.Security.Cryptography.MD5CryptoServiceProvider();
    byte[] emailBytes = Encoding.UTF8.GetBytes(stringToHash);
    byte[] hashedEmailBytes = md5csp.ComputeHash(emailBytes);
    md5csp.Clear();
    string md5 = BitConverter.ToString(hashedEmailBytes).Replace("-", "");
    return md5.ToLower();
}

在使用 MD5 加密中可能遇到的問題,參見:http://blog.csdn.net/zhao1468465728/article/details/47025645

  • Encoding.Default默認編碼 gb2312,需使用 utf-8
  • X2/x2 表示不省略首位爲0的十六進制數字

SHA

SHA-0和SHA-1已被攻破,SHA-3應用較少。主要使用SHA-2,多指:SHA256

SHA-1

/// <summary>
/// SHA1加密
/// </summary>
public static string SHA1(string data)
{
    SHA1 sha = new SHA1CryptoServiceProvider();
    byte[] temp1 = Encoding.UTF8.GetBytes(data);
    byte[] temp2 = sha.ComputeHash(temp1);
    sha.Clear(); ;
    string sig = BitConverter.ToString(temp2).Replace("-", "");
    return sig.ToLower();
}

SHA-256

/// <summary>
/// SHA256加密
/// </summary>
public static string SHA256Encrypt(string data)
{
    try
    {
        byte[] bytes = Encoding.UTF8.GetBytes(data);
        byte[] hash = SHA256Managed.Create().ComputeHash(bytes);

        StringBuilder builder = new StringBuilder();
        for (int i = 0; i < hash.Length; i++)
        {
            builder.Append(hash[i].ToString("X2"));
        }

        return builder.ToString();
    }
    catch (Exception ex)
    {
        return "";
    }
}

/// <summary>
/// SHA256加密
/// </summary>
public static string SHA256(string data)
{
    byte[] SHA256Data = Encoding.UTF8.GetBytes(data);
    SHA256Managed Sha256 = new SHA256Managed();
    byte[] bytes = Sha256.ComputeHash(SHA256Data);
    Sha256.Clear();

    //Replace掉「-」後長度爲64,視需求而定
    return BitConverter.ToString(bytes).Replace("-", "");
}

DES

使用一個 56 位的密鑰以及附加的 8 位奇偶校驗位,產生最大 64 位的分組大小。

  • 迭代的對稱分組密碼
  • 16 個循環,使用異或,置換,代換,移位操做四種基本運算
  • 使用 Feistel 技術

待加密文本塊分紅兩半,使用子密鑰對其中一半應用循環功能,而後將輸出與另外一半進行「異或」運算;接着交換這兩半、迭代操做,但最後一個循環不交換。

該加密方法瞭解便可。

 


學習 RSA 以前,再明確 2 個概念。前面的 MD5 是不可逆,而 可逆加密分爲對稱加密和非對稱加密

對稱加密

發送方和接收方採用相同的密鑰進行加密和解密。

  • 算法公開、計算量小、加密速度快、效率高
  • 加解密密鑰相同、安全性略低,密鑰管理開銷大

非對稱加密

加密和解密使用不一樣的密鑰的一類加密算法。一般有兩個密鑰A和B,用密鑰A加密數據獲得的密文,只有密鑰B能夠進行解密操做(即便密鑰A也沒法解密),相反,使用了密鑰B加密數據獲得的密文,只有密鑰A能夠解密。即存在一對公鑰和私鑰(徹底不一樣但徹底匹配),用公鑰加密的信息只能用對應的私鑰進行解密,反之亦然(一般加密是用公鑰,解密是用私鑰)。

  • 收發信雙方在通訊以前,收信方必須將本身早已隨機生成的公鑰送給發信方,而本身保留私鑰
  • 不對稱算法擁有兩個密鑰,特別適用於分佈式系統中的數據加密

在實際狀況中,一般採用方式:非對稱加密算法管理對稱算法的密鑰,而後用對稱加密算法加密數據,集成兩類加密算法的優勢

  • 既實現加密速度快的優勢
  • 又實現安全方便管理密鑰的優勢

前面的 DES 和 AES 是對稱加密方法,下面要介紹的 RSA 則是非對稱加密方法。除RSA外:

  • DSA(Digital Signature Algorithm):數字簽名算法,一種標準的DSS(數字簽名標準)
  • ECC(Elliptic Curves Cryptography):橢圓曲線密碼編碼學

其中,ECC計算量小、處理速度快,存儲空間佔用小,抗攻擊性強,帶寬要求低(IC卡、無線網領域)

RSA

非對稱加密,目前最有影響力的支持變長密鑰的公共密鑰算法,第一個能夠同時用於加密和數字簽名的算法。

主要用於:(1)數據加密 (2)數字簽名(只有非對稱加密算法能夠)

  • 安全性依賴於大數分解:將兩個大質數相乘十分容易,可是對其乘積進行因式分解卻極其困難
  • 公開密鑰加密體制:一種「由已知加密密鑰推導出解密密鑰在計算上是不可行的」密碼體制
  • 加密密鑰、加密算法、解密算法均公開,解密密鑰保留

缺點:大數計算速度慢,密鑰生成麻煩。

務必明確,RSA 的加密體制和簽名體制是不一樣的算法:

  • 加密公鑰加密,私鑰解密:KeyGen(密鑰生成算法),Encrypt(加密算法),Decrypt(解密算法)
  • 簽名私鑰簽名,公鑰驗證簽名:KeyGen(密鑰生成算法),Sign(簽名算法),Verify(驗證算法)

具體算法細節參見:RSA的公鑰和私鑰到底哪一個纔是用來加密和哪一個用來解密?@劉巍然-學酥 的講解。

下面是RSA加解密的簡單實現,學習之。

/// <summary>
/// RSA加密的密鑰結構
/// </summary>
public class RSAKey
{
	// 公鑰
	public string PublicKey { get; set; }
	// 私鑰
	public string PrivateKey { get; set; }
}

/// <summary>
/// 全局靜態變量,保存密鑰對
/// </summary>
private static RSAKey RSA_KEY = new RSAKey();

/// <summary>
/// 生成RSA密鑰對
/// </summary>
public static void GenerateRASKey()
{
	RSACryptoServiceProvider rsaProvider = new RSACryptoServiceProvider();
	RSA_KEY = new RSAKey()
		{
			PublicKey = rsaProvider.ToXmlString(false),
			PrivateKey = rsaProvider.ToXmlString(true)
		};
}
/// <summary>
/// 獲取加密公鑰
/// </summary>
private static string GetPublicKey()
{
	string publicKey = RSA_KEY.PublicKey;
	return publicKey;
}
/// <summary>
/// 獲取解密私鑰
/// </summary>
private static string GetPrivateKey()
{
	string privateKey = RSA_KEY.PrivateKey;
	return privateKey;
}

/// <summary>
/// RSA加密
/// </summary>
/// <param name="data">待加密數據</param>
public static string RSAEncrypt(string data)
{
	try
	{
		//獲取公鑰
		string publicKey = GetPublicKey();

		//建立RSA對象並載入公鑰
		RSACryptoServiceProvider rsaPublic = new RSACryptoServiceProvider();
		rsaPublic.FromXmlString(publicKey);

		//數據加密
		byte[] bytesPublic = rsaPublic.Encrypt(Encoding.UTF8.GetBytes(data), false);
		return Convert.ToBase64String(bytesPublic); //使用Base64將byte轉換爲string
	}
	catch (Exception ex)
	{
		return "";
	}
}

/// <summary>
/// RSA解密
/// </summary>
/// <param name="data">待解密數據</param>
/// <returns></returns>
public static string RSADecrypt(string data)
{
	try
	{
		//獲取公鑰
		string privateKey = GetPrivateKey();

		//建立RSA對象並載入私鑰
		RSACryptoServiceProvider rsaPrivate = new RSACryptoServiceProvider();
		rsaPrivate.FromXmlString(privateKey);

		//數據解密
		byte[] bytesPrivate = rsaPrivate.Decrypt(
			Convert.FromBase64String(data), false);//使用Base64將string轉換爲byte
		return Encoding.UTF8.GetString(bytesPrivate);
	}
	catch (Exception ex)
	{
		return "";
	}
}

主調用方式

// RSA
EncryptOperation.GenerateRASKey();
string 密文 = EncryptOperation.RSAEncrypt(明文);
string 明文De = EncryptOperation.RSADecrypt(密文);

也能夠將密鑰對保存在 PublicKey.xml 和 PrivateKey.xml 文件中,PublicKey.xml 中只包含公鑰,PrivateKey.xml 中包含公鑰和密鑰。密鑰的獲取經過讀取xml文件實現。

//將密鑰寫入指定路徑
File.WriteAllText(privateKeyPath, privateKey);
File.WriteAllText(publicKeyPath, publicKey);

//獲取密鑰對
string publicKey = File.ReadAllText(publicKeyPath);
string privateKey = File.ReadAllText(privateKeyPath);

在實際中,可能會遇到如下幾個比較特殊的問題:

(1)私鑰加密、公鑰解密的要求

在C#中,支持公鑰加密私鑰解密,但不能逆向使用(Java是能夠的)。

若是有該特殊需求,可經過第三方的加解密組件 BouncyCastle 來實現:

具體參見:利用第三方dll實現私鑰做RSA加密C#使用BouncyCastle來實現私鑰加密,公鑰解密的方法

(2)C# 的RSA公鑰是XML,如何與 Java 的RSA公鑰匹配

涉及公鑰和私鑰在C#和Java之間的相互轉換,共 4 個方法:

using System;
using System.Collections.Generic;
using System.Text;
using System;
using System.Xml;
using Org.BouncyCastle.Asn1.Pkcs;
using Org.BouncyCastle.Asn1.X509;
using Org.BouncyCastle.Crypto.Parameters;
using Org.BouncyCastle.Math;
using Org.BouncyCastle.Pkcs;
using Org.BouncyCastle.Security;
using Org.BouncyCastle.X509;

/// <summary>
/// RSA密鑰格式轉換
/// </summary>
public class RSAKeyConvert
{
	/// <summary>    	  
	/// RSA私鑰格式轉換,java->.net    	  
	/// </summary>    	  
	/// <param name="privateKey">java生成的RSA私鑰</param>    	  
	/// <returns></returns>   	  
	public static string RSAPrivateKeyJava2DotNet(string privateKey)	  
	{	  
	   var privateKeyParam =		
		   (RsaPrivateCrtKeyParameters)PrivateKeyFactory.CreateKey(Convert.FromBase64String(privateKey));		
	   return		
		  string.Format(		
			  "<RSAKeyValue><Modulus>{0}</Modulus><Exponent>{1}</Exponent><P>{2}</P><Q>{3}</Q><DP>{4}</DP><DQ>{5}</DQ><InverseQ>{6}</InverseQ><D>{7}</D></RSAKeyValue>",		
			  Convert.ToBase64String(privateKeyParam.Modulus.ToByteArrayUnsigned()),		
			  Convert.ToBase64String(privateKeyParam.PublicExponent.ToByteArrayUnsigned()),		
			  Convert.ToBase64String(privateKeyParam.P.ToByteArrayUnsigned()),		
			  Convert.ToBase64String(privateKeyParam.Q.ToByteArrayUnsigned()),		
			  Convert.ToBase64String(privateKeyParam.DP.ToByteArrayUnsigned()),		
			  Convert.ToBase64String(privateKeyParam.DQ.ToByteArrayUnsigned()),		
			  Convert.ToBase64String(privateKeyParam.QInv.ToByteArrayUnsigned()),		
			  Convert.ToBase64String(privateKeyParam.Exponent.ToByteArrayUnsigned()));		
	}	  
	  
	/// <summary>    	  
	/// RSA私鑰格式轉換,.net->java    	  
	/// </summary>    	  
	/// <param name="privateKey">.net生成的私鑰</param>    	  
	/// <returns></returns>   	  
	public static string RSAPrivateKeyDotNet2Java(string privateKey)	  
	{	  
	   XmlDocument doc = new XmlDocument();		
	   doc.LoadXml(privateKey);		
	   BigInteger m = new BigInteger(1, Convert.FromBase64String(doc.DocumentElement.GetElementsByTagName("Modulus")[0].InnerText));		
	   BigInteger exp = new BigInteger(1, Convert.FromBase64String(doc.DocumentElement.GetElementsByTagName("Exponent")[0].InnerText));		
	   BigInteger d = new BigInteger(1, Convert.FromBase64String(doc.DocumentElement.GetElementsByTagName("D")[0].InnerText));		
	   BigInteger p = new BigInteger(1, Convert.FromBase64String(doc.DocumentElement.GetElementsByTagName("P")[0].InnerText));		
	   BigInteger q = new BigInteger(1, Convert.FromBase64String(doc.DocumentElement.GetElementsByTagName("Q")[0].InnerText));		
	   BigInteger dp = new BigInteger(1, Convert.FromBase64String(doc.DocumentElement.GetElementsByTagName("DP")[0].InnerText));		
	   BigInteger dq = new BigInteger(1, Convert.FromBase64String(doc.DocumentElement.GetElementsByTagName("DQ")[0].InnerText));		
	   BigInteger qinv = new BigInteger(1, Convert.FromBase64String(doc.DocumentElement.GetElementsByTagName("InverseQ")[0].InnerText));		
	   RsaPrivateCrtKeyParameters privateKeyParam = new RsaPrivateCrtKeyParameters(m, exp, d, p, q, dp, dq, qinv);		
	   PrivateKeyInfo privateKeyInfo = PrivateKeyInfoFactory.CreatePrivateKeyInfo(privateKeyParam);		
	   byte[] serializedPrivateBytes = privateKeyInfo.ToAsn1Object().GetEncoded();		
	   return Convert.ToBase64String(serializedPrivateBytes);		
	}	  

	/// <summary>    	  
	/// RSA公鑰格式轉換,java->.net    	  
	/// </summary>    	  
	/// <param name="publicKey">java生成的公鑰</param>    	  
	/// <returns></returns>    	  
	public static string RSAPublicKeyJava2DotNet(string publicKey)	  
	{	  
	   RsaKeyParameters publicKeyParam = (RsaKeyParameters)PublicKeyFactory.CreateKey(Convert.FromBase64String(publicKey));		
	   return string.Format("<RSAKeyValue><Modulus>{0}</Modulus><Exponent>{1}</Exponent></RSAKeyValue>",		
		   Convert.ToBase64String(publicKeyParam.Modulus.ToByteArrayUnsigned()),		
		   Convert.ToBase64String(publicKeyParam.Exponent.ToByteArrayUnsigned()));		
	}	  
	  
	/// <summary>    	  
	/// RSA公鑰格式轉換,.net->java    	  
	/// </summary>    	  
	/// <param name="publicKey">.net生成的公鑰</param>    	  
	/// <returns></returns>   	  
	public static string RSAPublicKeyDotNet2Java(string publicKey)	  
	{	  
	   XmlDocument doc = new XmlDocument(); doc.LoadXml(publicKey);		
	   BigInteger m = new BigInteger(1, Convert.FromBase64String(doc.DocumentElement.GetElementsByTagName("Modulus")[0].InnerText));		
	   BigInteger p = new BigInteger(1, Convert.FromBase64String(doc.DocumentElement.GetElementsByTagName("Exponent")[0].InnerText));		
	   RsaKeyParameters pub = new RsaKeyParameters(false, m, p);		
	   SubjectPublicKeyInfo publicKeyInfo = SubjectPublicKeyInfoFactory.CreateSubjectPublicKeyInfo(pub);		
	   byte[] serializedPublicBytes = publicKeyInfo.ToAsn1Object().GetDerEncoded();		
	   return Convert.ToBase64String(serializedPublicBytes);		
	}	  
}

具體參見:C#的RSA密鑰XML格式與Java的其密鑰格式匹配

擴展應用參見:SHA256WithRSA

 


下面擴展學習數字簽名和數字證書、以及與上述加密算法之間的關係

數字簽名

Digital Signature,公鑰數字簽名/電子簽章,私鑰簽名、公鑰驗證簽名。

  • 信息完整性
  • 發送者身份驗證
  • 不可抵賴性

數字證書

Certificate Authority,數字證書是一個經證書受權中心數字簽名的包含公開密鑰擁有者信息以及公開密鑰的文件。

最簡單的證書包含一個公開密鑰、名稱以及證書受權中心的數字簽名。

  • 一段含有證書持有者身份信息並通過認證中心審覈簽發的電子數據
  • 方便:即時申請、即時開通、即時使用
  • 一個重要的特徵:只在特定的時間段內有效
  • 證實站點、文件有效合法安全

關係理解

  • 二者都是非對稱加密技術的應用
  • 把數字證書的hash值用私鑰加密獲得數字簽名
  • 信息 + HASH = 摘要,摘要 + 私鑰 = 數字簽名(給收方作對比用的,驗證收發內容是否一致
  • 公鑰 + 相關信息 + CA私鑰 = 數字證書(驗證發送者是否正確,是可信任的公鑰

具體參考:數字簽名和數字證書數字簽名、數字證書、SSL、https是什麼關係?-知乎

相關文章
相關標籤/搜索