2009/08/02

PKI Research #3 - RSA 加解密

CA 部分還沒搞定,但生成憑證簽署方面都可以利用 OpenSSL 來完成。OpenSSL 是開放原始碼專案,在 Unix-like 界非常廣泛,Windows 有別人 Port 好的版本:OpenSSL Binary Distributions,可以下載來玩看看。下指令的方式上一篇已經有許多篇參考資料,不再說明。

複習一下 .key 檔跟 .crt 檔的樣子:

user.key: (PEM-encoded)
-----BEGIN RSA PRIVATE KEY-----
MIIBOQIBAAJBAOWeK9TSYVnPEkmK45TeI7NrC8MzvwabSVg1aEuTmcNkLRg/Qibv
B6ST5F056HCk0xvzm5rfO5A+q+4ncBMGMhkCAwEAAQJABGaLoICHrRjy2MX4ppm7
RWz/xLXxK0c+mJotbYVepQf1KYB6kPc4df1t4KrFFgxMyXzdJ/eiegf4j3HFT4Hs
gQIhAPiVVZNSMqp1uUx7OXB3GGW+3uslvA3+NvuAKqVguIo9AiEA7Hf6ykGeuRBN
k2EN+J+Vof4ryzFOtJGH+eyycmKPsQ0CIFJYteZ9nkcVhHKvh1GYQj7CQfpHn8pK
4k/iHz51kexJAiBwKs1kiVHv+QLDSQNmjtRcngNKBB6QWoQEkjlnNsdwNQIgbJYa
Ft4zOc8TnbXnGxXRfGVG0RZfXmxcQNv2yX2G4Vc=
-----END RSA PRIVATE KEY-----


user.crt: (PEM-encoded)
-----BEGIN CERTIFICATE-----
MIIBtDCCAV4CCQChpB3R8j5vwDANBgkqhkiG9w0BAQUFADBhMQswCQYDVQQGEwJU
VzEPMA0GA1UECBMGVGFpd2FuMQ8wDQYDVQQHEwZUYWl3YW4xHDAaBgNVBAoTE05D
SFUgTUlTIERlcGFydG1lbnQxEjAQBgNVBAMTCXNvdWx0YWtlcjAeFw0wOTA3MzEw
NDE1MDJaFw0xMDA3MzEwNDE1MDJaMGExCzAJBgNVBAYTAlRXMQ8wDQYDVQQIEwZU
YWl3YW4xDzANBgNVBAcTBlRhaXdhbjEcMBoGA1UEChMTTkNIVSBNSVMgRGVwYXJ0
bWVudDESMBAGA1UEAxMJc291bHRha2VyMFwwDQYJKoZIhvcNAQEBBQADSwAwSAJB
AOWeK9TSYVnPEkmK45TeI7NrC8MzvwabSVg1aEuTmcNkLRg/QibvB6ST5F056HCk
0xvzm5rfO5A+q+4ncBMGMhkCAwEAATANBgkqhkiG9w0BAQUFAANBAGQhB8VKtY2N
hecmukSUmJkTzFWwT15fPmTGzf0KdrccNCSK2bE/u6bTW9dkUF12MkTaHNVytmKd
u7MoPuNvCnA=
-----END CERTIFICATE-----


所以,當我有了 .key 檔 (RSA 私鑰) 跟 .crt 憑證 (含公鑰的憑證) 後,該怎麼使用?這是本篇要研究的目標。

C# 利用 X.509 憑證加密,網路上有很棒的教學:使用X.509数字证书加密解密实务(二)-- 使用RSA证书加密敏感数据,圖文並茂很容易了解。基本上 .NET Framework 的 X509Certificate2 類別非常好用,它屬於 System.Security.Cryptography.X509Certificates 命名空間。只要建構元放入 .crt 憑證檔路徑,就可以抽出公鑰,加密資料。

解密的話,X509Certificate2 支援公私鑰綁一起的 PKCS#12,但不支援 OpenSSL 直接匯出的私鑰格式 (稱為 PEM 格式),當你直接指定 .key 檔位置會丟出例外,所以解密要找其他方案。

剛好無意間找到利用 Bouncy Castle for C# API 直接讀取 PEM 格式 RSA 私鑰進行解密的教學:用openSSL生成了一对RSA密钥,在C#里面怎么使用它啊? ,回答的第二項就是我們要的。你可能覺得奇怪它說第三項也可以用啊,但實際上那是讀取 .pfx 或 .p12 的 PKCS#12 個人憑證,直接讀取 OpenSSL RSA 私鑰 PEM 格式檔是絕對不行的。

DER (Distinguished Encoding Rules):
二進位內容,是屬於 ASN.1 制定的編碼之一。

PEM (Privacy-enhanced Electronic Mail):
就是 DER 格式經過 BASE64 編碼後所能見到的內容,通常會有 ----- BEGIN XXX ----- / ----- END XXX ----- 之類的東西包夾起來。由於 PEM 格式採用了 BASE64 編碼,文字都被編成常用的英文數字符號,方便於網路上傳送及複製 (如即時通訊、電子郵件等)。OpenSSL 預設產生的檔案都是 PEM 格式。當然也可以透過 -inform / -outform 來指定 DER 等格式。

using System;
using System.IO;
using System.Security.Cryptography;
using System.Security.Cryptography.X509Certificates;
using System.Text;
using Org.BouncyCastle.Asn1;
using Org.BouncyCastle.Asn1.Pkcs;
using Org.BouncyCastle.Crypto;
using Org.BouncyCastle.Crypto.Encodings;
using Org.BouncyCastle.Crypto.Engines;
using Org.BouncyCastle.Crypto.Parameters;
using Org.BouncyCastle.OpenSsl;


namespace PKI_RSAEnDecryptDemo {
class Program {
static void Main(string[] args) {
byte[] plainTextByte = Encoding.UTF8.GetBytes("超爽的,撿到100塊咧~咧?");

byte[] encTextType = encrypt(plainTextByte);
string base64 = Convert.ToBase64String(encTextType);
Console.WriteLine(base64);

byte[] oristr = decrypt_DER(encTextType);
Console.WriteLine(Encoding.UTF8.GetString(oristr));

Console.ReadKey();
}

public static byte[] encrypt(byte[] plainTextByte) {
var crt = new X509Certificate2(@"B:\OpenSSL\1.crt"); // Read X509 Certificate file
var rsa = (RSACryptoServiceProvider) crt.PublicKey.Key; // Get public key

byte[] Cryptograph = rsa.Encrypt(plainTextByte, false); // Encrypt
return Cryptograph;
}

public static byte[] decrypt(byte[] encTextType) {
AsymmetricCipherKeyPair keyPair;
using(var reader = File.OpenText(@"B:\OpenSSL\1.key")) { // Direct read pem format RSA private key file
keyPair = (AsymmetricCipherKeyPair) new PemReader(reader).ReadObject();
}
var decryptEngine = new Pkcs1Encoding(new RsaEngine());
decryptEngine.Init(false, keyPair.Private);

return decryptEngine.ProcessBlock(encTextType, 0, encTextType.Length); // Decrypt
}

public static byte[] decrypt_DER(byte[] encText) {
try{
var seq = (Asn1Sequence) Asn1Object.FromStream(File.OpenRead(@"B:\OpenSSL\1-der.key"));
var rsa = new RsaPrivateKeyStructure(seq);
var privSpec = new RsaPrivateCrtKeyParameters(
rsa.Modulus, rsa.PublicExponent, rsa.PrivateExponent,
rsa.Prime1, rsa.Prime2, rsa.Exponent1, rsa.Exponent2,
rsa.Coefficient
);

var decryptEngine = new Pkcs1Encoding(new RsaEngine());
decryptEngine.Init(false, privSpec);
return decryptEngine.ProcessBlock(encText, 0, encText.Length); // Decrypt
}catch(Exception e){
throw new IOException("problem creating private key: " + e.ToString());
}
}
}
}


---
參考資料:
OpenSSL 常用語法整理

沒有留言:

張貼留言