天天看點

數字證書結構描述+解析的C程式設計和實作1 X.509 證書結構描述2 源代碼3 編譯運作結果

文章目錄

  • 1 X.509 證書結構描述
    • 1.1 整體結構
    • 1.2 證書内容
    • 1.3 編碼
  • 2 源代碼
  • 3 編譯運作結果

1 X.509 證書結構描述

1.1 整體結構

證書内容、簽名算法、簽名結果。

tbsCertificate TBSCertificate
signatureAlgorithm AlgorithmIdentifier
signatureValue BIT STRING

1.2 證書内容

成員 ASN.1文法(類型)
版本号Version Version
序列号Serial Number CertificateSerialNumber
簽名算法signature AlgorithmIdentifier
頒布者issuer Name
有效期validity Validity
主體subject Name
主體公鑰資訊subjectPublicKeyInfo SubjectPublicKeyInfo
頒發者唯一辨別符issuerUniqueID IMPLICIT UniqueIdentifier OPTIONAL
主體唯一辨別符subjectUniqueID IMPLICIT UniqueIdentifier OPTIONAL
拓展項extensions EXPLICIT Extensions OPTIONAL

其中:

1.2.1 版本号

版本(version)為整數格式。到目前為止,證書格式的版本隻有v1、v2、v3,分别用整數0、1、2表示。

ASN.1描述如下:

Version::=INTEGER {v1(0),v2(1),v3(2)}           

複制

1.2.2 序列号

整數格式。

ASN.1描述如下:

CertificateSerialNumber::=INTEGER           

複制

證書序列号用來在某一個CA内唯一地辨別一張證書。由此,“頒布者”和“證書序列号”配合起來就能唯一地辨別一張數字證書。

1.2.3 簽名算法

CA簽發證書時所使用的數字簽名算法,與signatureAlgorithm的值必須一緻。

1.2.4 頒布者和主體

簽發證書的CA實體和證書持有者實體。ASN.1描述如下:

Name::=CHOICE{
	RDNSequence
}

RDNSequence::=SEQUENCE OF RelativeDistinguishedName

RelativeDistinguishedName::=SET OF AttributeTypeAndValue

AttributeTypeAndValue::=SEQUENCE{
​    type    AttributeType,
​    value   AttributeValue
}

AttributeType::=OBJECT IDENTIFIER

AttributeValue::=ANY DEFINED BY AttributeType           

複制

RDN(Relative Distinguished Name)用“屬性類型=屬性值”的形式表示。常用的屬性類型名稱以及簡寫如下:

屬性類型名稱 含義 簡寫
Common Name 通用名稱 CN
Organizational Unit name 機構單元名稱 OU
Organization name 機構名 O
Locality 地理位置 L
State or province name 州/省名 S
Country 國名 C

1.2.5 有效期

證書的有效使用期,包含起、止兩個時間值。

ASN.1描述:

Validity::=SEQUENCE{
​    notBefore       Time,
​    notAfter        Time
}

Time::=CHOICE{
​    utcTime         UTCTime,
​    generalTime     GeneralizedTime
}           

複制

2049年以前的,采用UTCTime。

1.2.6 主體公鑰資訊

證書所綁定的加密算法和公鑰。

SubjectPublicKeyInfo::=SEQUENCE{
​    algorithm           AlgorithmIdentifier,
​    subjectPublicKey    BIT STRING
}           

複制

1.2.7 頒布者唯一辨別符和主體唯一辨別符

issuerUniqueID和subjectUniqueID隻能在版本2或者3中出現;extensions隻能在版本3中出現。

ASN.1描述:

UniqueIdentifier::=BIT STRING           

複制

1.2.8 拓展項

隻能在版本3中出現。

1.3 編碼

X.509證書的結構用ASN1(Abstract Syntax Notation One)描述資料結構,并使用ASN1文法進行編碼。

ASN1采用一個個的資料塊來描述整個資料結構,每個資料塊都有四個部分組成。

1.3.1 資料塊資料類型辨別(一個位元組)

資料類型包括簡單類型和結構類型。

1.3.1.1 bit8-bit7

用來标示 TAG (bit5-bit1)類型,共有四種,分别是universal(00)、application(01)、context-specific(10)和private(11)。

1.3.1.2 bit6

表示是否為結構類型。1為結構類型,0為簡單類型。

1.3.1.3 bit5-bit1

類型的TAG值。根據bit8-bit7的不同值有不同的含義。

整個位元組表示資料類型。常見的有:

BOOLEAN:01

INTEGER:02

BIT STRING:03

OCTET STRING:04

NULL:05

OBJECT IDENTIFIER:06

PrintableString:13

UTCTime:17

GeneralizedTime:18

序列構造類型SEQUENCE與SEQUENCE OF:0x30

集合構造類型SET和SET OF:0x31

1.3.2 資料塊長度(1-128個位元組)

長度字段,有兩種編碼格式。

若長度值小于等于127,則用一個位元組表示,bit8 = 0,bit7-bit1存放長度值;

若長度值大于127,則用多個位元組表示,可以有2到127個位元組。第一個位元組的第8位為1,其它低7位給出後面該域使用的位元組數量,從該域第二個位元組開始給出資料的長度,高位優先。

還有一種特殊情況,這個位元組為0x80,表示資料塊長度不定,由資料塊結束辨別結束資料塊。

1.3.3 資料塊的值

存放資料塊的值,具體編碼随資料塊類型不同而不同。

1.3.4 資料塊結束辨別(可選)

結束标示字段,兩個位元組(0x0000),隻有在長度值為不定時(資料塊長度字段為0x80)才會出現。

2 源代碼

# include <iostream>
# include <string.h>
# include <stdlib.h>
# include <map>

using namespace std;

typedef unsigned char byte;

struct Algorithm{
	string algorithm;
	string parameters;
};

struct SubjectPublicKeyInfo{
	Algorithm algorithm;
	string SubjectPublicKey;
};

struct TBSCertificate{
	char version;
	string serialNumber;
	Algorithm signature;
	string issuer[8];
	string validity[2];
	string subject[8];
	SubjectPublicKeyInfo subjectPublicKeyInfo;
	string issuerUniqueID;
	string subjectUniqueID;
};

struct Certificate{
	TBSCertificate tbsCertificate;
	Algorithm signatureAlgorithm;
	string Signature;
};

Certificate certificate;
char value[1000];
map<string, string> sa; // OID對應的算法
map<string, string> issuerInfo; // OID對應的屬性名
int end = 1;
FILE *fp;
int count = 0;

void OID();
void show(int);
int block();


int main(){
	// 打開檔案
	string file = "../測試證書/ca.cer";
	fp = fopen(file.c_str(), "rb");
	if (fp == NULL){
		cout << "Can't open the file!";
		exit(0);
	}
	cout << "Certificate" << endl 
			<< "\tTBS Certificate" << endl;
	OID();
	block();
	fclose(fp);
}

void OID(){
	sa.insert(pair<string, string>("1.2.840.113549.1.1.1", "RSA"));
	sa.insert(pair<string, string>("1.2.840.113549.1.1.2", "md2RSA"));
	sa.insert(pair<string, string>("1.2.840.113549.1.1.3", "md4RSA"));
	sa.insert(pair<string, string>("1.2.840.113549.1.1.4", "md5RSA"));
	sa.insert(pair<string, string>("1.2.840.113549.1.1.5", "sha1RSA"));
	sa.insert(pair<string, string>("1.2.840.10040.4.1", "DSA"));
	sa.insert(pair<string, string>("1.2.840.10040.4.3", "sha1DSA"));
	issuerInfo.insert(pair<string, string>("2.5.4.3", "Common Name"));
	issuerInfo.insert(pair<string, string>("2.5.4.6", "Country"));
	issuerInfo.insert(pair<string, string>("2.5.4.7", "Locality"));
	issuerInfo.insert(pair<string, string>("2.5.4.8", "Sate or province name"));
	issuerInfo.insert(pair<string, string>("2.5.4.10", "Organization name"));
	issuerInfo.insert(pair<string, string>("2.5.4.11", "Organizational Unit name"));
}

void show(int count){
	switch(count){
		case 4:
			cout << "\tVersion: ";
			if(strcmp(value, "00")) cout << "v1" << endl; 
			else if(strcmp(value, "01")) cout << "v2" << endl; 
			else if(strcmp(value, "02")) cout << "v3" << endl; 
			break;
		case 5:
			cout << "\tSerial Number: " << value << endl;
			break;
		case 7:{
			string s = value;
			cout << "\tSignature: " << sa.at(s) << endl;
			break;
		}
		case 8:
			cout << "\t\tParameters: " << value << endl;
			break;
		case 9:
			cout << "\tIssuer: " << endl;
			break;
		case 12:
		case 16:
		case 20:
		case 24:
		case 28:
		case 32:
				
		case 40:
		case 44:
		case 48:
		case 52:
		case 56:
		case 60:
		{
			string s = value;
			if(issuerInfo.find(s) != issuerInfo.end()) cout << "\t\t" << issuerInfo.at(s) << ": ";
			break;
		}
		case 13:
		case 17:
		case 21:
		case 25:
		case 29:
		case 33:
			
		case 41:
		case 45:
		case 49:
		case 53:
		case 57:
		case 61:
			cout << value << endl;
			break;		
		case 34:
			cout << "\tValidity: " << endl;
			break;
		case 35:
			cout << "\t\tNot Before: " << value << endl;
			break;
		case 36:
			cout << "\t\tNot After: " << value << endl;
			break;
		case 37:
			cout << "\tSubject: " << endl;
			break;
		case 62:
			cout << "\tSubject Public Key Info: " << endl;
			break;
		case 64:{
			string s = value;
			cout << "\t\tAlgorithm: " << sa.at(s) << endl;
			break;
		}
		case 65:{
			cout << "\t\t\tParameters: " << value << endl;
			break;
		}
		case 66:{
			cout << "\t\tSubject Public Key: " << value << endl;
			break;
		}
		case 69:{
			string s = value;
			cout << endl << "Signature Algorithm: " << sa.at(s) << endl;
			break;
		}
		case 70:
			cout << "\tParameters: " << value << endl;
			break;
		case 71:
			cout << "Signature: " << value << endl;
			end = 0;
			break;
	}
}

int block(){
	if(end == 0) return 1000;
	count++;
	bool flag = true;
	byte type = fgetc(fp); // type
	byte len0 = fgetc(fp); // length
	int len = len0;
	
	if(len > 0x80){  // 計算長度
		int lenOfLen = len - 0x80;
		byte b;
		len = 0;
		for(int i = 0; i < lenOfLen; i++){
			b = fgetc(fp);
			len *= 256;
			len += b;
		}
	}
	if(type < 0xa0){
		switch(type){
			case 1:{ // BOOLEAN
				byte temp = fgetc(fp);
				if(temp == 0) strcpy(value, "FALSE");
				else strcpy(value, "TRUE");
				break;
			}
			case 2: // INTEGER
			case 3: // BIT STRING
			case 4: // OCTET STRING
				strcpy(value,"");
				for(int i = 0; i < len; i++){
					byte b = fgetc(fp);
					int d = b;
					char temp[5];
					sprintf(temp, "%02x", d);
					strcat(value, temp);
				}
				break;
			case 5: // NULL
				strcpy(value, "NULL");
				break;
			case 6:{ // OBJECT IDENTIFIER
				strcpy(value, "");
				byte t = fgetc(fp);
				int i = t / 40;
				char temp[5];
				sprintf(temp, "%d", i);
				strcat(value, temp);
				strcat(value, ".");
				i = t % 40;
				sprintf(temp, "%d", i);
				strcat(value, temp);
				for(int i = 1; i < len;){
					strcat(value, ".");
					int next = 0; //下一個整數
					while(true){
						t = fgetc(fp);
						i++;
						bool over = true;
						if(t & 0x80){ // 最高位為1
							over = false;
							t &= 0x7f; // 将最高位還原為0
						}
						next *= 128;
						next += t;
						if(over) break;
					}
					sprintf(temp, "%d", next);
					strcat(value, temp);
				}
			}
				break;
			case 0x13: // PrintableString
			case 0x17: // UTCTime
			case 0x18: // GeneralizedTime
				fread(value, 1, len, fp);
				value[len] = '\0';
				break;
			case 0x30:
			case 0x31:
				flag = false;
				if(count == 9 || count == 34 || count == 37 || count == 62){
					show(count);
				}
				for(int i = 0; i < len; i += block())
					;
				break;
			default:
				cout << "Error!";
				exit(0);
		}
	}
	else{
		flag = false;
		if(type == 0xa0) block();
		else if(count == 67) fseek(fp, len, SEEK_CUR);
	}
	if(flag) show(count);
	return len;
}           

複制

3 編譯運作結果

數字證書結構描述+解析的C程式設計和實作1 X.509 證書結構描述2 源代碼3 編譯運作結果

與證書對比:

數字證書結構描述+解析的C程式設計和實作1 X.509 證書結構描述2 源代碼3 編譯運作結果