go與證書crl實踐

go與證書crl實踐

一些說明

在證書中,CRL(證書註銷列表)是一個很重要的東西,證書一旦發出,那麼就無法收回。證書的有效性的判斷就會有點麻煩。一種方法就是通過證書的有效期,但是這種方法存在問題,萬一用戶的私鑰丟失,用戶向CA提交證書的吊銷請求。CA對該用戶使用的這張證書進行了吊銷。這張證書就應該是失效的。對於這種情況有兩種方式進行檢測:

  1. CRL

  2. 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進行比較,如果相同則該證書已經被吊銷。

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章