(转)创建X509证书,并获取证书密钥的一点研究
創(chuàng)建X509證書,并獲取證書密鑰的一點(diǎn)研究
作者:肖波
個(gè)人博客:http://blog.csdn.net/eaglet ; http://www.cnblogs.com/eaglet
2007/7 南京
?
背景
服務(wù)器SSL數(shù)字證書和客戶端單位數(shù)字證書的格式遵循 X.509 標(biāo)準(zhǔn)。 X.509 是由國際電信聯(lián)盟(ITU-T)制定的數(shù)字證書標(biāo)準(zhǔn)。為了提供公用網(wǎng)絡(luò)用戶目錄信息服務(wù), ITU 于 1988 年制定了 X.500 系列標(biāo)準(zhǔn)。其中 X.500 和 X.509 是安全認(rèn)證系統(tǒng)的核心, X.500 定義了一種區(qū)別命名規(guī)則,以命名樹來確保用戶名稱的唯一性; X.509 則為 X.500 用戶名稱提供了通信實(shí)體鑒別機(jī)制,并規(guī)定了實(shí)體鑒別過程中廣泛適用的證書語法和數(shù)據(jù)接口, X.509 稱之為證書。
?? X.509 給出的鑒別框架是一種基于公開密鑰體制的鑒別業(yè)務(wù)密鑰管理。一個(gè)用戶有兩把密鑰:一把是用戶的專用密鑰(簡稱為:私鑰),另一把是其他用戶都可得到和利用的公共密鑰(簡稱為:公鑰)。用戶可用常規(guī)加密算法(如 DES)為信息加密,然后再用接收者的公共密鑰對 DES 進(jìn)行加密并將之附于信息之上,這樣接收者可用對應(yīng)的專用密鑰打開 DES 密鎖,并對信息解密。該鑒別框架允許用戶將其公開密鑰存放在CA的目錄項(xiàng)中。一個(gè)用戶如果想與另一個(gè)用戶交換秘密信息,就可以直接從對方的目錄項(xiàng)中獲得相應(yīng)的公開密鑰,用于各種安全服務(wù)。
?? 最初的 X.509 版本公布于 1988 年,版本 3 的建議稿 1994 年公布,在 1995 年獲得批準(zhǔn)。本質(zhì)上, X.509 證書由用戶公共密鑰與用戶標(biāo)識符組成,此外還包括版本號、證書序列號、CA 標(biāo)識符、簽名算法標(biāo)識、簽發(fā)者名稱、證書有效期等。用戶可通過安全可靠的方式向 CA 提供其公共密鑰以獲得證書,這樣用戶就可公開其證書,而任何需要此用戶的公共密鑰者都能得到此證書,并通過 CA 檢驗(yàn)密鑰是否正確。這一標(biāo)準(zhǔn)的最新版本 -- X.509 版本 3 是針對包含擴(kuò)展信息的數(shù)字證書,提供一個(gè)擴(kuò)展字段,以提供更多的靈活性及特殊環(huán)境下所需的信息傳送。
?? 為了進(jìn)行身份認(rèn)證, X.509 標(biāo)準(zhǔn)及公共密鑰加密系統(tǒng)提供了一個(gè)稱作數(shù)字簽名的方案。用戶可生成一段信息及其摘要(亦稱作信息“指紋”)。用戶用專用密鑰對摘要加密以形成簽名,接收者用發(fā)送者的公共密鑰對簽名解密,并將之與收到的信息“指紋”進(jìn)行比較,以確定其真實(shí)性。
?? 目前, X.509 標(biāo)準(zhǔn)已在編排公共密鑰格式方面被廣泛接受,已用于許多網(wǎng)絡(luò)安全應(yīng)用程序,其中包括 IP 安全( Ipsec )、安全套接層( SSL )、安全電子交易( SET )、安全多媒體 INTERNET 郵件擴(kuò)展( S/MIME )等。
?
創(chuàng)建X509 證書
創(chuàng)建X509證書方法較多,在Windows 環(huán)境下大致總結(jié)了幾中辦法,
1)????? 通過CA獲取證書,
2)????? 通過微軟提供的makecert 工具得到測試證書
3)????? 編程的方法創(chuàng)建,.Net提供了 X509Certificate2 類,該類可以用于創(chuàng)建證書,但只能從RawData中創(chuàng)建,創(chuàng)建后無法修改除FriendlyName以外的任何屬性。
?
我在互聯(lián)網(wǎng)上找了很久,始終沒有找到完全通過程序創(chuàng)建自定義的證書的方法。后來想了一個(gè)折中辦法,就是用程序調(diào)用 makecert.exe 先生成一個(gè)證書,證書的一些參數(shù)如Subject,有效期,序列號等可以通過參數(shù)傳入,然后把生成的證書文件讀到Rawdata中,得到X509Certificate2 類型的證書對象。當(dāng)然這種方法確實(shí)比較笨,必須要依賴外部進(jìn)程。等后面有時(shí)間的話,我還是想按照X509 V3 標(biāo)準(zhǔn),自己創(chuàng)建RawData,然后生成證書,這樣應(yīng)該是比較靈活的做法。不知道網(wǎng)友們有沒有什么更好的方法來創(chuàng)建一個(gè)自定義的證書。
?
通過 makecert.exe 創(chuàng)建X509證書的代碼如下,供大家參考
?
static object semObj = new object();
?
/// <summary>
/// 自定義的證書信息
/// </summary>
public class T_CertInfo
{
??? public String FriendlyName;
??? public String Subject;
??? public DateTime BeginDate;
??? public DateTime EndDate;
??? public int SerialNumber;
}
?
/// <summary>
/// 生成X509證書
/// </summary>
/// <param name="makecrtPath">makecert進(jìn)程的目錄</param>
/// <param name="crtPath">證書文件臨時(shí)目錄</param>
/// <param name="certInfo">證書信息</param>
/// <returns></returns>
public static X509Certificate2 CreateCertificate(String makecrtPath, String crtPath,
??? T_CertInfo certInfo)
{
??? Debug.Assert(certInfo != null);
??? Debug.Assert(certInfo.Subject != null);
?
??? string MakeCert = makecrtPath + "makecert.exe";
??? string fileName = crtPath + "cer";
?
??? string userName = Guid.NewGuid().ToString();
?
??? StringBuilder arguments = new StringBuilder();
?
??? arguments.AppendFormat("-r -n \"{0}\" -ss my -sr currentuser -sky exchange ",
??????? certInfo.Subject);
?
??? if (certInfo.SerialNumber > 0)
??? {
??????? arguments.AppendFormat("-# {0} ", certInfo.SerialNumber);
??? }
?
??? arguments.AppendFormat("-b {0} ", certInfo.BeginDate.ToString(@"MM\/dd\/yyyy"));
??? arguments.AppendFormat("-e {0} ", certInfo.EndDate.ToString(@"MM\/dd\/yyyy"));
??? arguments.AppendFormat("\"{0}\"", fileName);
?
??? lock (semObj)
??? {
??????? Process p = Process.Start(MakeCert, arguments.ToString());
??????? p.WaitForExit();
?
??????? byte[] certBytes = ReadFile(fileName);
??????? X509Certificate2 cert = new X509Certificate2(certBytes);
??????? cert = new X509Certificate2(certBytes);
?
??????? if (certInfo.FriendlyName != null)
??????? {
??????????? cert.FriendlyName = certInfo.FriendlyName;
??????? }
?
??????? return cert;
??? }
}
?
?
internal static byte[] ReadFile(string fileName)
{
??? using (FileStream f = new FileStream(fileName, FileMode.Open, FileAccess.Read))
??? {
??????? int size = (int)f.Length;
??????? byte[] data = new byte[size];
??????? size = f.Read(data, 0, size);
??????? return data;
??? }
}
?
獲取證書私鑰
通過上述方法得到的X509證書,只能獲取其公鑰信息,由于公鑰私鑰是成對出現(xiàn)的,如果我們要在程序中使用該證書來加解密,就必須要獲取公鑰對應(yīng)的那個(gè)私鑰。一樣是在互聯(lián)網(wǎng)上沒有找到很好的解決辦法,只能自己研究。目前總結(jié)出兩種方法,給大家分享:
第一種方法:
從密鑰容器中獲取私鑰。具體方法如下:
首先在 makecert 的參數(shù)中要加入一條 -sk keyname ?指定主題的密鑰容器位置,該位置包含私鑰。如果密鑰容器不存在,系統(tǒng)將創(chuàng)建一個(gè)。
然后 在執(zhí)行完 p.WaitForExit(); 這一句后執(zhí)行下面語句獲取私鑰和私鑰參數(shù)
RSAParameters privateKey;
RSACryptoServiceProvider rsa = GetKeyFromContainer("keyname");
privateKey = rsa.ExportParameters(true);
?
public static RSACryptoServiceProvider GetKeyFromContainer(string ContainerName)
{
??? // Create the CspParameters object and set the key container
??? // name used to store the RSA key pair.
??? CspParameters cp = new CspParameters();
??? cp.KeyContainerName = ContainerName;
?
??? // Create a new instance of RSACryptoServiceProvider that accesses
??? // the key container MyKeyContainerName.
??? return new RSACryptoServiceProvider(cp);
}
?
這種方法有一個(gè)缺點(diǎn)就是程序的調(diào)用者必須要具備讀取密鑰容器的權(quán)限才行,如果是Web應(yīng)用,由于IIS來賓帳戶沒有這個(gè)權(quán)限,將無法讀取密鑰容器中的密鑰。嘗試采用模擬超級用戶登錄的方法(NetworkSecurity.ImpersonateUser),也無法解決這個(gè)問題,而且這樣做我個(gè)人覺得對網(wǎng)站的安全性方面也不是很好。后來想出了第二種方法,就是干脆重置密鑰對,用自己生成的密鑰對替換證書中的密鑰對,試了一下,還是行之有效的。
第二種方法:
重置密鑰對,方法如下:
首先要生成一個(gè)加密算法和加密位數(shù)與makecert生成的證書密鑰相同的密鑰。通過實(shí)測發(fā)現(xiàn)makecert采用交換密鑰時(shí),默認(rèn)產(chǎn)生一個(gè)1024位RSA密鑰,Exponent 為1,0,1,這和
RSACryptoServiceProvider 默認(rèn)的密鑰是相同的。所以只要用 RSACryptoServiceProvider RSA = new RSACryptoServiceProvider() 生成一個(gè)密鑰就可以了。
?
第二步就是替換,也就是將密鑰文件中公鑰參數(shù)替換為要置換的公鑰參數(shù)。
?
RSAParameters publicKey;
RSAParameters privateKey;
?
RSACryptoServiceProvider RSA = (RSACryptoServiceProvider)cert.PublicKey.Key;
publicKey = RSA.ExportParameters(false);
?
//查找公鑰參數(shù)在RawData中的位置
if (publicKey.Modulus.Length != 128 || publicKey.Exponent.Length != 3)
{
??? throw new Exception("public key module lenght != 128!");
}
?
if (publicKey.Exponent[0] != 1 ||
??? publicKey.Exponent[1] != 0 ||
??? publicKey.Exponent[2] != 1)
{
??? throw new Exception("public key Exponent != 101!");
}
?
byte[] module = publicKey.Modulus;
?
int i = 0;
int matchCnt = 0;
int modulePos = 0;
int j = 0;
?
while (i < certBytes.Length)
{
?
??? if (certBytes[i] == module[j]) //cerBytes 為 RawData,什么可以參加創(chuàng)建證書的代碼
??? {
??????? i++;
??????? j++;
?????? ?matchCnt++;
?
??????? if (matchCnt == 128)
??????? {
??????????? modulePos = i - 128;
??????????? break;
??????? }
??? }
??? else
??? {
??????? if (matchCnt == 128)
??????? {
??????????? modulePos = i - 128;
??????????? break;
??????? }
??????? else
????? ??{
??????????? matchCnt = 0;
??????????? j = 0;
??????????? i++;
??????? }
??? }
}
?
//創(chuàng)建密鑰對
RSA = new RSACryptoServiceProvider();
publicKey = RSA.ExportParameters(false);
privateKey = RSA.ExportParameters(true);
?
//將要重置的密鑰對中的公鑰參數(shù)覆蓋原參數(shù)
?
j = 0;
for (i = modulePos; i < modulePos + 128; i++)
{
??? certBytes[i] = publicKey.Modulus[j];
??? j++;
}
?
//用新參數(shù)重新創(chuàng)建證書
cert = new X509Certificate2(certBytes);
?
這樣一來privateKey就成了新證書的私鑰了。
?
這種方法的問題:
這種方法的問題是查找證書中公鑰信息,是通過匹配的方式來做的,這是一個(gè)偷懶的方法,正確的做法應(yīng)該是按照標(biāo)準(zhǔn)的定義來查找,由于暫時(shí)沒有太多時(shí)間去仔細(xì)研究標(biāo)準(zhǔn),所以就偷了一個(gè)懶,但感覺這種方法目前來說還是行之有效的,待以后改進(jìn)吧。
?
?
參考資料
http://www.ietf.org/rfc/rfc2459.txt?IETF 的X509 V3 版本標(biāo)準(zhǔn)全文
http://blog.csdn.net/chinaipcnet/archive/2007/05/23/1621989.aspx?makecert.exe使用說明
?from :http://www.cnblogs.com/eaglet/archive/2007/07/11/814600.html
轉(zhuǎn)載于:https://www.cnblogs.com/gxh973121/archive/2007/07/12/815117.html
總結(jié)
以上是生活随笔為你收集整理的(转)创建X509证书,并获取证书密钥的一点研究的全部內(nèi)容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: 西门子STEP7-200PLC的顺序控制
- 下一篇: 【Codeforces #130 Div