X.509數字證書的結構與解析
1、什么叫數字簽名
數字簽名:
將報文按雙方約定的HASH算法計算得到一個固定位數的報文摘要。在數學上保證:只要改動報文中任何一位,重新計算出的報文摘要值就會與原先的值不相符。這樣就保證了報文的不可更改性。
將該報文摘要值用發送者的私人密鑰加密,然后連同原報文一起發送給接收者,而產生的報文即稱數字簽名
2、什么叫數字證書
數字證書:
數字證書就是互聯網通訊中標志通訊各方身份信息的一系列數據,提供了一種在Internet上驗證您身份的方式,其作用類似於司機的駕駛執照或日常生活中的身份證。它是由一個由權威機構—–CA機構,又稱為證書授權,(Certificate Authority)中心發行的,人們可以在網上用它來識別對方的身份。數字證書是一個經證書授權中心數字簽名的包含公開密鑰擁有者信息以及公開密鑰的文件。最簡單的證書包含一個公開密鑰、名稱以及證書授權中心的數字簽名。
3、PKI
- PKI 是一組服務和策略,提供了一個將公鑰和用戶身份唯一綁
定的機制,以及如何實施並維護這個綁定相關信息的框架; - PKI 是一個通過使用公開密鑰技術和數字證書來確保系統信息
安全,並負責驗證數字證書持有者身份的體系。
體系詳解
(1) 鮑勃有兩把鑰匙,一把是公鑰,另一把是私鑰。
(2) 鮑勃把公鑰送給他的朋友們—-帕蒂、道格、蘇珊—-每人一把。
(3) 蘇珊要給鮑勃寫一封保密的信。她寫完后用鮑勃的公鑰加密,就可以達到保密的效果
(4) 鮑勃收信后,用私鑰解密,就看到了信件內容。這里要強調的是,只要鮑勃的私鑰不泄露,這封信就是安全的,即使落在別人手里,也無法解密。
(5) 鮑勃給蘇珊回信,決定采用 “數字簽名”。他寫完后先用Hash函數,生成信件的摘要(digest)。
(6) 然后,鮑勃使用私鑰,對這個摘要加密,生成”數字簽名”(signature)。
(7) 鮑勃將這個簽名,附在信件下面,一起發給蘇珊。
(8) 蘇珊收信后,取下數字簽名,用鮑勃的公鑰解密,得到信件的摘要。由此證明,這封信確實是鮑勃發出的。
(9) 蘇珊再對信件本身使用Hash函數,將得到的結果,與上一步得到的摘要進行對比。如果兩者一致,就證明這封信未被修改過。
(10) 復雜的情況出現了。道格想欺騙蘇珊,他偷偷使用了蘇珊的電腦,用自己的公鑰換走了鮑勃的公鑰。此時,蘇珊實際擁有的是道格的公鑰,但是還以為這是鮑勃的公鑰。因此,道格就可以冒充鮑勃,用自己的私鑰做成“數字簽名”,寫信給蘇珊,讓蘇珊用假的鮑勃公鑰進行解密。
(11) 后來,蘇珊感覺不對勁,發現自己無法確定公鑰是否真的屬於鮑勃。她想到了一個辦法,要求鮑勃去找”證書中心”(certificate authority,簡稱CA),為公鑰做認證。證書中心用自己的私鑰,對鮑勃的公鑰和一些相關信息一起加密,生成“數字證書”(Digital Certificate)。
(12) 鮑勃拿到數字證書以后,就可以放心了。以后再給蘇珊寫信,只要在簽名的同時,再附上數字證書就行了。
(13) 蘇珊收信后,用CA的公鑰解開數字證書,就可以拿到鮑勃真實的公鑰了,然后就能證明”數字簽名”是否真的是鮑勃簽的。
4、X.509
X.509 是密碼學里公鑰證書的格式標准。
X.509 標准規定了證書可以包含什么信息,並說明了記錄信息的方法(數據格式)。
X.509 證書里含有公鑰、身份信息(比如網絡主機名,組織的名稱或個體名稱等)和簽名信息(可以是證書簽發機構CA的簽名,也可以是自簽名)。
對於一份經由可信的證書簽發機構簽名或者可以通過其它方式驗證的證書,證書的擁有者就可以用證書及相應的私鑰來創建安全的通信,對文檔進行數字簽名。
另外除了證書本身功能,X.509還附帶了證書吊銷列表和用於從最終對證書進行簽名的證書簽發機構直到最終可信點為止的證書合法性驗證算法。
X.509是ITU-T標准化部門基於他們之前的ASN.1定義的一套證書標准。
X.509證書的結構
1、X.509證書基本部分
1.1. 版本號(Version)
——標識證書的版本(版本1、版本2或是版本3)。
1.2. 序列號(Serial Number)
——標識證書的唯一整數,由證書頒發者分配的本證書的唯一標識符。
1.3. 簽名(Signature)
——用於簽證書的算法標識,由對象標識符加上相關的參數組成,用於說明本證書所用的數字簽名算法。例如,SHA-1和RSA的對象標識符就用來說明該數字簽名是利用RSA對SHA-1雜湊加密。
1.4. 頒發者(Issuer:)
——證書頒發者的可識別名(DN)。
1.5. 有效期(Validity)
——證書有效期的時間段。本字段由”Not Before”和”Not After”兩項組成,它們分別由UTC時間或一般的時間表示(在RFC2459中有詳細的時間表示規則)。
1.6. 主體(Subject)
——證書擁有者的可識別名,這個字段必須是非空的,除非你在證書擴展中有別名。
1.7. 主體公鑰信息(Subject Public Key Info)
——主體的公鑰(以及算法標識符)。
1.8. 頒發者唯一標識符(Issuer Unique Identifier)
——標識符—證書頒發者的唯一標識符,僅在版本2和版本3中有要求,屬於可選項。
1.9. 主體唯一標識符(Subject Unique Identifier)
——證書擁有者的唯一標識符,僅在版本2和版本3中有要求,屬於可選項。
2、X.509證書擴展部分(Extensions)
可選的標准和專用的擴展(僅在版本2和版本3中使用),擴展部分的元素都有這樣的結構:
Extension ::= SEQUENCE {
extnID OBJECT IDENTIFIER,
critical BOOLEAN DEFAULT FALSE,
extnValue OCTET STRING }
- 1
- 2
- 3
- 4
extnID:表示一個擴展元素的OID
critical:表示這個擴展元素是否極重要
extnValue:表示這個擴展元素的值,字符串類型。
擴展部分包括:
2.1. 發行者密鑰標識符(Autority Key Identifier)
——證書所含密鑰的唯一標識符,用來區分同一證書擁有者的多對密鑰。
2.2. 密鑰使用(Key Usage)
——一個比特串,指明(限定)證書的公鑰可以完成的功能或服務,如:證書簽名、數據加密等。
如果某一證書將 KeyUsage 擴展標記為“極重要”,而且設置為“keyCertSign”,則在 SSL 通信期間該證書出現時將被拒絕,因為該證書擴展表示相關私鑰應只用於簽寫證書,而不應該用於 SSL。
2.3. CRL分布點(CRL Distribution Points)
——指明CRL的分布地點。
2.4. 私鑰的使用期
——指明證書中與公鑰相聯系的私鑰的使用期限,它也有Not Before和Not After組成。若此項不存在時,公私鑰的使用期是一樣的。
2.5. 證書策略(Certificate Policies)
——由對象標識符和限定符組成,這些對象標識符說明證書的頒發和使用策略有關。
2.6. 策略映射
——表明兩個CA域之間的一個或多個策略對象標識符的等價關系,僅在CA證書里存在。
2.7. 主體別名
——指出證書擁有者的別名,如電子郵件地址、IP地址等,別名是和DN綁定在一起的。
2.8. 頒發者別名
——指出證書頒發者的別名,如電子郵件地址、IP地址等,但頒發者的DN必須出現在證書的頒發者字段。
2.9. 主體目錄屬性
——指出證書擁有者的一系列屬性。可以使用這一項來傳遞訪問控制信息。
X.509 證書實例
https://www.github.com 的證書信息如下:
調用Openssl庫的API對githubcom.pem證書文件解析結果如下:
X.509 C語言解析源碼
#include <fstream> #include <openssl/pem.h> #include <openssl/x509.h> #include <openssl/x509v3.h> #include <iostream> #include <sstream> using namespace std; //----------------------------------------------------------------------*/ void parseCert(X509* x509) { cout <<"--------------------" << endl; BIO *bio_out = BIO_new_fp(stdout, BIO_NOCLOSE); //PEM_write_bio_X509(bio_out, x509);//STD OUT the PEM X509_print(bio_out, x509);//STD OUT the details //X509_print_ex(bio_out, x509, XN_FLAG_COMPAT, X509_FLAG_COMPAT);//STD OUT the details BIO_free(bio_out); } //----------------------------------------------------------------------*/ int main(int argc, char **argv) { OpenSSL_add_all_algorithms(); std::ifstream t; int length; t.open("./githubcom.pem"); // open input file t.seekg(0, std::ios::end); // go to the end length = t.tellg(); // report location (this is the length) t.seekg(0, std::ios::beg); // go back to the beginning char* buffer = new char[length]; // allocate memory for a buffer of appropriate dimension t.read(buffer, length); // read the whole file into the buffer t.close(); // close file handle BIO *bio_mem = BIO_new(BIO_s_mem()); BIO_puts(bio_mem, buffer); X509 * x509 = PEM_read_bio_X509(bio_mem, NULL, NULL, NULL); parseCert(x509); BIO_free(bio_mem); X509_free(x509); } //----------------------------------------------------------------------