[C#.NET] 使用 X509 數位電子簽章 加解密
上篇提到了使用X509的的憑證使用方式,請參考 [C#.NET] X509 數位電子簽章
這次便來進入主題加解密,要使用憑證來加解密,憑證必須要帶有私密金鑰,否則會無法解密,
PS.對於資安這塊實在完全不瞭解,憑證信任等等相關設定,我也不懂,問 方丈 也只是得到了些天書答案,完全不知道它講的天書是哪個章節,我只會拿憑證來加解密而已,若有憑證問題請找咱們家的 方丈
X509 是用RSA演算法來處理加解密的,所以邏輯完全跟之前的帖子一樣,請參考:
[C#.NET] 字串及檔案,利用 RSA 演算法加解密
[C#.NET] RSA的長度限制
重點是這句:var rsaCrypto = (RSACryptoServiceProvider)this.Certificate.PublicKey.Key;
還記得RSA的限制吧?所以一樣採用分段加密,加密邏輯如下:
{
if (OriginalData == null) throw new ArgumentNullException("OriginalData");
if (OriginalData.Length <= 0) throw new ArgumentOutOfRangeException("OriginalData");
if (this.Certificate == null)
{
//使用預設憑證
var defaultCert = RsaAndX509.Properties.Resources.artag_certnew;
this.Certificate = new X509Certificate2(defaultCert, "pass@w0rd1");
}
var rsaCrypto = (RSACryptoServiceProvider)this.Certificate.PublicKey.Key;
int bufferSize = (rsaCrypto.KeySize / 8) - 11;
byte[] buffer = new byte[bufferSize];
//分段加密
using (MemoryStream input = new MemoryStream(OriginalData))
using (MemoryStream ouput = new MemoryStream())
{
while (true)
{
int readLine = input.Read(buffer, 0, bufferSize);
if (readLine <= 0)
{
break;
}
byte[] temp = new byte[readLine];
Array.Copy(buffer, 0, temp, 0, readLine);
byte[] encrypt = rsaCrypto.Encrypt(temp, false);
ouput.Write(encrypt, 0, encrypt.Length);
}
return ouput.ToArray();
}
}
解密,重點是這句:var rsaCrypto = (RSACryptoServiceProvider)this.Certificate.PrivateKey;
{
if (EncryptDada == null) throw new ArgumentNullException("EncryptDada");
if (EncryptDada.Length <= 0) throw new ArgumentOutOfRangeException("EncryptDada");
if (this.Certificate == null)
{
//使用預設憑證
var defaultCert = RsaAndX509.Properties.Resources.artag_certnew;
this.Certificate = new X509Certificate2(defaultCert, "pass@w0rd1");
}
var rsaCrypto = (RSACryptoServiceProvider)this.Certificate.PrivateKey;
int keySize = rsaCrypto.KeySize / 8;
byte[] buffer = new byte[keySize];
using (MemoryStream input = new MemoryStream(EncryptDada))
using (MemoryStream output = new MemoryStream())
{
while (true)
{
int readLine = input.Read(buffer, 0, keySize);
if (readLine <= 0)
{
break;
}
byte[] temp = new byte[readLine];
Array.Copy(buffer, 0, temp, 0, readLine);
byte[] decrypt = rsaCrypto.Decrypt(temp, false);
output.Write(decrypt, 0, decrypt.Length);
}
return output.ToArray();
}
}
完整範例如下:
{
public class RsaCryptService
{
private Encoding _encoding = Encoding.UTF8;
public Encoding Encoding
{
get { return _encoding; }
set { _encoding = value; }
}
private X509ContentType _x509ContentType = X509ContentType.Cert;
public X509ContentType X509ContentType
{
get { return _x509ContentType; }
set { _x509ContentType = value; }
}
private StoreName _storeName = StoreName.My;
public StoreName StoreName
{
get { return _storeName; }
set { _storeName = value; }
}
private StoreLocation _locationr = StoreLocation.CurrentUser;
public StoreLocation Location
{
get { return _locationr; }
set { _locationr = value; }
}
private X509Certificate2 _certificate;
public X509Certificate2 Certificate
{
get { return _certificate; }
private set { _certificate = value; }
}
public X509Certificate2 CreateCertificate(string CertFile, string Password)
{
if (CertFile == null) throw new ArgumentNullException("CertFile");
if (Password == null) throw new ArgumentNullException("Password");
if (string.IsNullOrEmpty(Password))
{
this.Certificate = new X509Certificate2(CertFile);
}
else
{
this.Certificate = new X509Certificate2(CertFile, Password);
}
return this.Certificate;
}
public X509Certificate2 ImportCertificate(string CertFile, string Password)
{
if (CertFile == null) throw new ArgumentNullException("CertFile");
if (Password == null) throw new ArgumentNullException("Password");
var cert = this.CreateCertificate(CertFile, Password);
if (cert == null) return null;
X509Store store = new X509Store(this.StoreName, this.Location);
store.Open(OpenFlags.ReadWrite);
store.Add(cert);
store.Close();
return cert;
}
public bool ExportCertificate(string CertSubjectName, string ExportFile)
{
if (CertSubjectName == null) throw new ArgumentNullException("CertSubjectName");
if (ExportFile == null) throw new ArgumentNullException("ExportFile");
return ExportCertificate(CertSubjectName, null, ExportFile);
}
public bool ExportCertificate(string CertSubjectName, string Password, string ExportFile)
{
if (CertSubjectName == null) throw new ArgumentNullException("CertSubjectName");
if (ExportFile == null) throw new ArgumentNullException("ExportFile");
X509Store store = new X509Store(this.StoreName, this.Location);
store.Open(OpenFlags.ReadOnly);
FileStream fileStream = null;
try
{
fileStream = new FileStream(ExportFile, FileMode.Create, FileAccess.Write);
foreach (X509Certificate2 cert in store.Certificates)
{
if (cert.Subject == CertSubjectName)
{
byte[] CertByte;
if (string.IsNullOrEmpty(Password))
{
CertByte = cert.Export(X509ContentType);
}
else
{
CertByte = cert.Export(X509ContentType, Password);
}
fileStream.Write(CertByte, 0, CertByte.Length);
return true;
}
}
}
finally
{
if (fileStream != null) fileStream.Dispose();
store.Close();
}
return false;
}
public void VerifyCertificate(X509Certificate2 Cert)
{
if (Cert == null) throw new ArgumentNullException("Ccert");
X509Chain chain = new X509Chain();
chain.ChainPolicy.RevocationMode = X509RevocationMode.Online;
//RevocationStatusUnknown
chain.Build(Cert);
if (chain.ChainStatus.Length > 0)
{
StringBuilder builder = new StringBuilder();
builder.AppendLine("憑證檢查錯誤:/r/n");
foreach (X509ChainStatus status in chain.ChainStatus)
{
builder.Append(string.Format("{0}={1}/r/n", status.Status, status.StatusInformation));
}
throw new ApplicationException(builder.ToString());
}
if (Cert.NotAfter <= DateTime.Now)
{
throw new ApplicationException(string.Format("憑証過期"));
}
}
public byte[] Encryptor(byte[] OriginalData)
{
if (OriginalData == null) throw new ArgumentNullException("OriginalData");
if (OriginalData.Length <= 0) throw new ArgumentOutOfRangeException("OriginalData");
if (this.Certificate == null)
{
//使用資源檔裡的預設憑證
var defaultCert = RsaAndX509.Properties.Resources.artag_certnew;
this.Certificate = new X509Certificate2(defaultCert, "pass@w0rd1");
}
var rsaCrypto = (RSACryptoServiceProvider)this.Certificate.PublicKey.Key;
int bufferSize = (rsaCrypto.KeySize / 8) - 11;
byte[] buffer = new byte[bufferSize];
//分段加密
using (MemoryStream input = new MemoryStream(OriginalData))
using (MemoryStream ouput = new MemoryStream())
{
while (true)
{
int readLine = input.Read(buffer, 0, bufferSize);
if (readLine <= 0)
{
break;
}
byte[] temp = new byte[readLine];
Array.Copy(buffer, 0, temp, 0, readLine);
byte[] encrypt = rsaCrypto.Encrypt(temp, false);
ouput.Write(encrypt, 0, encrypt.Length);
}
return ouput.ToArray();
}
}
public string EncryptString(string OriginalString)
{
if (OriginalString == null) throw new ArgumentNullException("OriginalString");
var originalData = this.Encoding.GetBytes(OriginalString);
var encryptData = this.Encryptor(originalData);
var base64 = Convert.ToBase64String(encryptData);
return base64;
}
public void EncryptFile(string OriginalFile, string EncrytpFile)
{
using (FileStream originalStream = new FileStream(OriginalFile, FileMode.Open, FileAccess.Read))
using (FileStream encrytpStream = new FileStream(EncrytpFile, FileMode.Create, FileAccess.Write))
{
//加密
var dataByteArray = new byte[originalStream.Length];
originalStream.Read(dataByteArray, 0, dataByteArray.Length);
var encryptData = this.Encryptor(dataByteArray);
//寫檔
encrytpStream.Write(encryptData, 0, encryptData.Length);
}
}
public byte[] Decryptor(byte[] EncryptDada)
{
if (EncryptDada == null) throw new ArgumentNullException("EncryptDada");
if (EncryptDada.Length <= 0) throw new ArgumentOutOfRangeException("EncryptDada");
if (this.Certificate == null)
{
//使用資源檔裡的預設憑證
var defaultCert = RsaAndX509.Properties.Resources.artag_certnew;
this.Certificate = new X509Certificate2(defaultCert, "pass@w0rd1");
}
var rsaCrypto = (RSACryptoServiceProvider)this.Certificate.PrivateKey;
int keySize = rsaCrypto.KeySize / 8;
byte[] buffer = new byte[keySize];
using (MemoryStream input = new MemoryStream(EncryptDada))
using (MemoryStream output = new MemoryStream())
{
while (true)
{
int readLine = input.Read(buffer, 0, keySize);
if (readLine <= 0)
{
break;
}
byte[] temp = new byte[readLine];
Array.Copy(buffer, 0, temp, 0, readLine);
byte[] decrypt = rsaCrypto.Decrypt(temp, false);
output.Write(decrypt, 0, decrypt.Length);
}
return output.ToArray();
}
}
public string DecryptString(string EncryptString)
{
if (EncryptString == null) throw new ArgumentNullException("EncryptString");
var encryptData = Convert.FromBase64String(EncryptString);
var decryptData = this.Decryptor(encryptData);
var decryptString = this.Encoding.GetString(decryptData);
return decryptString;
}
public void DecryptFile(string EncrytpFile, string DecrytpFile)
{
if (EncrytpFile == null) throw new ArgumentNullException("EncrytpFile");
if (DecrytpFile == null) throw new ArgumentNullException("DecrytpFile");
using (FileStream encrytpStream = new FileStream(EncrytpFile, FileMode.Open, FileAccess.Read))
using (FileStream decrytpStream = new FileStream(DecrytpFile, FileMode.Create, FileAccess.Write))
{
//解密
var dataByteArray = new byte[encrytpStream.Length];
encrytpStream.Read(dataByteArray, 0, dataByteArray.Length);
var decryptData = this.Decryptor(dataByteArray);
//寫檔
decrytpStream.Write(decryptData, 0, decryptData.Length);
}
}
}
}
字串加密單元測試:
public void EncryptStringTest()
{
RsaCryptService target = new RsaCryptService();
string OriginalString =
@"
你同事剛開完會走出來,你想知道結果如何?你的朋友願意幫你忙嗎?你的部屬是否對你做的新安排感到滿意?《看穿人心的問話術》教你透過五大技巧探知對方的真實感受,不論他們的口風有多緊!
技巧一:他願意幫忙嗎?請用「得寸進尺」法
研究顯示,在你想請某人幫你一個忙之前,先對他提出一個簡單容易達成的小小要求,在他幫忙之後,隨即給予感謝或表彰。事後你再提出原本要請他做的事情,或是尋求他更多的幫助,他就比較有可能會全盤接受。
佛德門和佛瑞賽(Freedman and Fraser)曾經提到一項實驗,他們將受訪住戶隨機分成兩組,他們直接向第一組住戶提出請求,要在他們在前院樹立一個大型的「小心駕駛」告示牌,結果只有百分之十七的人同意。至於第二組住戶,則先被要求在窗戶上貼一個三吋大小,上面寫著「當個安全駕駛人」的標示,幾乎所有的住戶都同意接受。幾週後,測試者再向第二組住戶提出請求,要他們跟第一組住戶一樣,在前院設置大型標示,最後竟有高達百分之七十六的住戶同意此項要求。
技巧二:他贊成還是反對?改問他心情好不好
想知道他的真實想法,可以問他不相干的問題。如果他的話中似乎隱含著好兆頭的跡象,那麼就表示他對此事看法樂觀。然而,若他說這件事似乎是負面結果的前兆,那麼就表示他的看法頗為悲觀。
例如,你的同事約翰最近在為一個新的秘密計畫爭取支援,剛剛開完相關會議走出來。他不能透露任何會議細節,以避免別人從他的神情中,窺探公司對其計畫的支持程度,他一直盡可能地維持面無表情的狀態。
";
string expected = string.Empty;
string actual;
actual = target.EncryptString(OriginalString);
Assert.AreNotEqual(expected, actual);
}
字串解密單元測試:
public void DecryptStringTest()
{
RsaCryptService target = new RsaCryptService();
string EncryptString = "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";
string expected =
@"
你同事剛開完會走出來,你想知道結果如何?你的朋友願意幫你忙嗎?你的部屬是否對你做的新安排感到滿意?《看穿人心的問話術》教你透過五大技巧探知對方的真實感受,不論他們的口風有多緊!
技巧一:他願意幫忙嗎?請用「得寸進尺」法
研究顯示,在你想請某人幫你一個忙之前,先對他提出一個簡單容易達成的小小要求,在他幫忙之後,隨即給予感謝或表彰。事後你再提出原本要請他做的事情,或是尋求他更多的幫助,他就比較有可能會全盤接受。
佛德門和佛瑞賽(Freedman and Fraser)曾經提到一項實驗,他們將受訪住戶隨機分成兩組,他們直接向第一組住戶提出請求,要在他們在前院樹立一個大型的「小心駕駛」告示牌,結果只有百分之十七的人同意。至於第二組住戶,則先被要求在窗戶上貼一個三吋大小,上面寫著「當個安全駕駛人」的標示,幾乎所有的住戶都同意接受。幾週後,測試者再向第二組住戶提出請求,要他們跟第一組住戶一樣,在前院設置大型標示,最後竟有高達百分之七十六的住戶同意此項要求。
技巧二:他贊成還是反對?改問他心情好不好
想知道他的真實想法,可以問他不相干的問題。如果他的話中似乎隱含著好兆頭的跡象,那麼就表示他對此事看法樂觀。然而,若他說這件事似乎是負面結果的前兆,那麼就表示他的看法頗為悲觀。
例如,你的同事約翰最近在為一個新的秘密計畫爭取支援,剛剛開完相關會議走出來。他不能透露任何會議細節,以避免別人從他的神情中,窺探公司對其計畫的支持程度,他一直盡可能地維持面無表情的狀態。
";
string actual;
actual = target.DecryptString(EncryptString);
Assert.AreEqual(expected, actual);
}
測試結果如下:
若有謬誤,煩請告知,新手發帖請多包涵
Microsoft MVP Award 2010~2017 C# 第四季
Microsoft MVP Award 2018~2022 .NET