go與證書crl實踐
一些說明
在證書中,CRL(證書註銷列表)是一個很重要的東西,證書一旦發出,那麼就無法收回。證書的有效性的判斷就會有點麻煩。一種方法就是通過證書的有效期,但是這種方法存在問題,萬一用戶的私鑰丟失,用戶向CA提交證書的吊銷請求。CA對該用戶使用的這張證書進行了吊銷。這張證書就應該是失效的。對於這種情況有兩種方式進行檢測:
CRL
OCSP
CRL方法存在一些缺點:其一,CRL不可能實時更新。其二,當註銷證書的數量很大及用戶基礎很大時,每次CRL分發會大量消耗網絡帶寬和服務器處理能力。
今天,先研究CRL方式在go中的實現。
CRL的位置
證書的CRL內容是存放在證書的擴展中。證書在go語言中使用的結構是x509.Certificate
這個結構體。延展部分在Extensions
字段中。該字段是一個結構體數組Extensions []pkix.Extension
。該結構體內容如下:
type Extension struct {
Id asn1.ObjectIdentifier
Critical bool `asn1:"optional"`
Value []byte
}
我結合chrome中證書內容的部分進行說明:
其中 Id對應的是該延展在RFC規範中的id(即上圖的2.5.29.31),Value對應的是內容上圖的http://pki.google.com/GIAG2.crl
, Critical即對應的是關鍵
。
獲取並解析CRL
通過extensions
中的Value
字段可以獲取到CRL文件的路徑。那麼就能夠使用net/http
包下的方法去獲取到該crl文件的內容。
for _, extension := range cert.Extensions {
if extension.Id.Equal(crlasn1) {
crlURL = string(extension.Value)
log.Infof("crlURL:%v", crlURL) //提取http
//提取http部分內容
spl := strings.Split(crlURL, "http://")
crlURL = "http://" + spl[1]
log.Infof("crlURL:%v", crlURL) //提取http
break
}
}
resp, err := http.Get(crlURL)
if err != nil {
log.Infof("http 獲取失敗:%v", err)
}
defer resp.Body.Close()
data, err = ioutil.ReadAll(resp.Body)
if err != nil {
log.Infof("獲取body失敗:%v", err)
return
}
在獲取的Value
中會出現一段`0&0$?”? ?這一段內容,通過抓包發現:
在獲取到Id後還有很長一段數據才能獲取到crl的地址,因此我直接用strings
進行切分。
CRL解析
CRL的解析可以通過x509
包下的func ParseCRL(crlBytes []byte) (*pkix.CertificateList, error)
方法。(該方法無法解析DER編碼的CRL文件。如果要解析DER編碼的CRL文件需要使用ParseDERCRL()
)。通過該方法解析後的結構體內容如下:
type CertificateList struct {
TBSCertList TBSCertificateList
SignatureAlgorithm AlgorithmIdentifier
SignatureValue asn1.BitString
}
該結構滿足RFC 5280中的定義:
CertificateList ::= SEQUENCE {
tbsCertList TBSCertList,
signatureAlgorithm AlgorithmIdentifier,
signatureValue BIT STRING }
TBSCertList ::= SEQUENCE {
version Version OPTIONAL,
-- if present, MUST be v2
signature AlgorithmIdentifier,
issuer Name,
thisUpdate Time,
nextUpdate Time OPTIONAL,
revokedCertificates SEQUENCE OF SEQUENCE {
userCertificate CertificateSerialNumber,
revocationDate Time,
crlEntryExtensions Extensions OPTIONAL
-- if present, version MUST be v2
} OPTIONAL,
crlExtensions [0] EXPLICIT Extensions OPTIONAL
-- if present, version MUST be v2
}
TBSCertList中存儲的是吊銷情況。
CRL的結構體定義:
CRL的內容包括CRL的版本號、計算本CRL的數字簽名所用的算法的標識符、頒發CRL的CA的可識別名、CRL的發佈/更新時間、被吊銷證書的列表以及擴展項
在go中所對應的結構體:
type TBSCertificateList struct {
Raw asn1.RawContent
Version int `asn1:"optional,default:0"`
Signature AlgorithmIdentifier
Issuer RDNSequence
ThisUpdate time.Time
NextUpdate time.Time `asn1:"optional"`
RevokedCertificates []RevokedCertificate `asn1:"optional"`
Extensions []Extension `asn1:"tag:0,optional,explicit"`
}
Raw是原始數據,其他的和RFC中定義一樣。查詢是否調用需要看RevokedCertificates
字段,RevokedCertificate
的結構體內容如下:
type RevokedCertificate struct {
SerialNumber *big.Int
RevocationTime time.Time
Extensions []Extension `asn1:"optional"`
}
其中SerialNumber
代表的是被吊銷證書的序列號,RevocationTime
代表吊銷的時間。
通過這裏的SerialNumber
和證書中的SerialNumber
進行比較,如果相同則該證書已經被吊銷。