// This function sends a DomainServiceRequest of the type GET_CRL to the domain service, // and deserializes the response into a pkix.CertificateList containing the revoked certificates. func RequestCrl(network, addr string) (*pkix.CertificateList, error) { reqType := DomainServiceRequest_GET_CRL request := &DomainServiceRequest{ Type: &reqType} conn, err := net.Dial(network, addr) if err != nil { return nil, err } ms := util.NewMessageStream(conn) _, err = ms.WriteMessage(request) if err != nil { return nil, err } log.Printf("Sent crl request to Domain Service using network %s at address %s.", network, addr) var response DomainServiceResponse err = ms.ReadMessage(&response) if err != nil { return nil, err } log.Println("Got response from Domain Service.") if errStr := response.GetErrorMessage(); errStr != "" { return nil, errors.New(errStr) } parsedCrl, err := x509.ParseCRL(response.GetCrl()) return parsedCrl, err }
func FuzzCRL(data []byte) int { list, err := x509.ParseCRL(data) if err != nil { if list != nil { panic("list is not nil on error") } return 0 } return 1 }
// fetchCRL fetches and parses a CRL. func fetchCRL(url string) (*pkix.CertificateList, error) { resp, err := http.Get(url) if err != nil { return nil, err } else if resp.StatusCode >= 300 { return nil, errors.New("failed to retrieve CRL") } body, err := ioutil.ReadAll(resp.Body) if err != nil { return nil, err } resp.Body.Close() return x509.ParseCRL(body) }
func (b *backend) pathCRLWrite( req *logical.Request, d *framework.FieldData) (*logical.Response, error) { name := strings.ToLower(d.Get("name").(string)) if name == "" { return logical.ErrorResponse(`"name" parameter cannot be empty`), nil } crl := d.Get("crl").(string) certList, err := x509.ParseCRL([]byte(crl)) if err != nil { return logical.ErrorResponse(fmt.Sprintf("failed to parse CRL: %v", err)), nil } if certList == nil { return logical.ErrorResponse("parsed CRL is nil"), nil } b.crlUpdateMutex.Lock() defer b.crlUpdateMutex.Unlock() crlInfo := CRLInfo{ Serials: map[string]RevokedSerialInfo{}, } for _, revokedCert := range certList.TBSCertList.RevokedCertificates { crlInfo.Serials[revokedCert.SerialNumber.String()] = RevokedSerialInfo{} } entry, err := logical.StorageEntryJSON("crls/"+name, crlInfo) if err != nil { return nil, err } if err = req.Storage.Put(entry); err != nil { return nil, err } b.crls[name] = crlInfo return nil, nil }
// Generates steps to test out CA configuration -- certificates + CRL expiry, // and ensure that the certificates are readable after storing them func generateCATestingSteps(t *testing.T, caCert, caKey, otherCaCert string, intdata, reqdata map[string]interface{}) []logicaltest.TestStep { ret := []logicaltest.TestStep{ logicaltest.TestStep{ Operation: logical.WriteOperation, Path: "config/ca", Data: map[string]interface{}{ "pem_bundle": caKey + caCert, }, }, logicaltest.TestStep{ Operation: logical.WriteOperation, Path: "config/crl", Data: map[string]interface{}{ "expiry": "16h", }, }, // Ensure we can fetch it back via unauthenticated means, in various formats logicaltest.TestStep{ Operation: logical.ReadOperation, Path: "cert/ca", Unauthenticated: true, Check: func(resp *logical.Response) error { if resp.Data["certificate"].(string) != caCert { return fmt.Errorf("CA certificate:\n%s\ndoes not match original:\n%s\n", resp.Data["certificate"].(string), caCert) } return nil }, }, logicaltest.TestStep{ Operation: logical.ReadOperation, Path: "ca/pem", Unauthenticated: true, Check: func(resp *logical.Response) error { rawBytes := resp.Data["http_raw_body"].([]byte) if string(rawBytes) != caCert { return fmt.Errorf("CA certificate:\n%s\ndoes not match original:\n%s\n", string(rawBytes), caCert) } if resp.Data["http_content_type"].(string) != "application/pkix-cert" { return fmt.Errorf("Expected application/pkix-cert as content-type, but got %s", resp.Data["http_content_type"].(string)) } return nil }, }, logicaltest.TestStep{ Operation: logical.ReadOperation, Path: "ca", Unauthenticated: true, Check: func(resp *logical.Response) error { rawBytes := resp.Data["http_raw_body"].([]byte) pemBytes := pem.EncodeToMemory(&pem.Block{ Type: "CERTIFICATE", Bytes: rawBytes, }) if string(pemBytes) != caCert { return fmt.Errorf("CA certificate:\n%s\ndoes not match original:\n%s\n", string(pemBytes), caCert) } if resp.Data["http_content_type"].(string) != "application/pkix-cert" { return fmt.Errorf("Expected application/pkix-cert as content-type, but got %s", resp.Data["http_content_type"].(string)) } return nil }, }, logicaltest.TestStep{ Operation: logical.ReadOperation, Path: "config/crl", Check: func(resp *logical.Response) error { if resp.Data["expiry"].(string) != "16h" { return fmt.Errorf("CRL lifetimes do not match (got %s)", resp.Data["expiry"].(string)) } return nil }, }, // Ensure that both parts of the PEM bundle are required // Here, just the cert logicaltest.TestStep{ Operation: logical.WriteOperation, Path: "config/ca", Data: map[string]interface{}{ "pem_bundle": caCert, }, ErrorOk: true, }, // Here, just the key logicaltest.TestStep{ Operation: logical.WriteOperation, Path: "config/ca", Data: map[string]interface{}{ "pem_bundle": caKey, }, ErrorOk: true, }, // Ensure we can fetch it back via unauthenticated means, in various formats logicaltest.TestStep{ Operation: logical.ReadOperation, Path: "cert/ca", Unauthenticated: true, Check: func(resp *logical.Response) error { if resp.Data["certificate"].(string) != caCert { return fmt.Errorf("CA certificate:\n%s\ndoes not match original:\n%s\n", resp.Data["certificate"].(string), caCert) } return nil }, }, logicaltest.TestStep{ Operation: logical.ReadOperation, Path: "ca/pem", Unauthenticated: true, Check: func(resp *logical.Response) error { rawBytes := resp.Data["http_raw_body"].([]byte) if string(rawBytes) != caCert { return fmt.Errorf("CA certificate:\n%s\ndoes not match original:\n%s\n", string(rawBytes), caCert) } if resp.Data["http_content_type"].(string) != "application/pkix-cert" { return fmt.Errorf("Expected application/pkix-cert as content-type, but got %s", resp.Data["http_content_type"].(string)) } return nil }, }, logicaltest.TestStep{ Operation: logical.ReadOperation, Path: "ca", Unauthenticated: true, Check: func(resp *logical.Response) error { rawBytes := resp.Data["http_raw_body"].([]byte) pemBytes := pem.EncodeToMemory(&pem.Block{ Type: "CERTIFICATE", Bytes: rawBytes, }) if string(pemBytes) != caCert { return fmt.Errorf("CA certificate:\n%s\ndoes not match original:\n%s\n", string(pemBytes), caCert) } if resp.Data["http_content_type"].(string) != "application/pkix-cert" { return fmt.Errorf("Expected application/pkix-cert as content-type, but got %s", resp.Data["http_content_type"].(string)) } return nil }, }, // Test a bunch of generation stuff logicaltest.TestStep{ Operation: logical.WriteOperation, Path: "root/generate/exported", Data: map[string]interface{}{ "common_name": "Root Cert", "ttl": "180h", }, Check: func(resp *logical.Response) error { intdata["root"] = resp.Data["certificate"].(string) intdata["rootkey"] = resp.Data["private_key"].(string) reqdata["pem_bundle"] = intdata["root"].(string) + "\n" + intdata["rootkey"].(string) return nil }, }, logicaltest.TestStep{ Operation: logical.WriteOperation, Path: "intermediate/generate/exported", Data: map[string]interface{}{ "common_name": "Intermediate Cert", }, Check: func(resp *logical.Response) error { intdata["intermediatecsr"] = resp.Data["csr"].(string) intdata["intermediatekey"] = resp.Data["private_key"].(string) return nil }, }, // Re-load the root key in so we can sign it logicaltest.TestStep{ Operation: logical.WriteOperation, Path: "config/ca", Data: reqdata, Check: func(resp *logical.Response) error { delete(reqdata, "pem_bundle") delete(reqdata, "ttl") reqdata["csr"] = intdata["intermediatecsr"].(string) reqdata["common_name"] = "Intermediate Cert" reqdata["ttl"] = "90h" return nil }, }, logicaltest.TestStep{ Operation: logical.WriteOperation, Path: "root/sign-intermediate", Data: reqdata, Check: func(resp *logical.Response) error { delete(reqdata, "csr") delete(reqdata, "common_name") delete(reqdata, "ttl") intdata["intermediatecert"] = resp.Data["certificate"].(string) reqdata["serial_number"] = resp.Data["serial_number"].(string) reqdata["certificate"] = resp.Data["certificate"].(string) reqdata["pem_bundle"] = intdata["intermediatekey"].(string) + "\n" + resp.Data["certificate"].(string) return nil }, }, // First load in this way to populate the private key logicaltest.TestStep{ Operation: logical.WriteOperation, Path: "config/ca", Data: reqdata, Check: func(resp *logical.Response) error { delete(reqdata, "pem_bundle") return nil }, }, // Now test setting the intermediate, signed CA cert logicaltest.TestStep{ Operation: logical.WriteOperation, Path: "intermediate/set-signed", Data: reqdata, Check: func(resp *logical.Response) error { delete(reqdata, "certificate") return nil }, }, logicaltest.TestStep{ Operation: logical.WriteOperation, Path: "revoke", Data: reqdata, }, logicaltest.TestStep{ Operation: logical.ReadOperation, Path: "crl", Data: reqdata, Check: func(resp *logical.Response) error { crlBytes := resp.Data["http_raw_body"].([]byte) certList, err := x509.ParseCRL(crlBytes) if err != nil { t.Fatalf("err: %s", err) } revokedList := certList.TBSCertList.RevokedCertificates if len(revokedList) != 1 { t.Fatalf("length of revoked list not 1; %d", len(revokedList)) } revokedString := certutil.GetOctalFormatted(revokedList[0].SerialNumber.Bytes(), ":") if revokedString != reqdata["serial_number"].(string) { t.Fatalf("got serial %s, expecting %s", revokedString, reqdata["serial_number"].(string)) } delete(reqdata, "serial_number") return nil }, }, // Do it all again, with EC keys and DER format logicaltest.TestStep{ Operation: logical.WriteOperation, Path: "root/generate/exported", Data: map[string]interface{}{ "common_name": "Root Cert", "ttl": "180h", "key_type": "ec", "key_bits": 384, "format": "der", }, Check: func(resp *logical.Response) error { certBytes, _ := base64.StdEncoding.DecodeString(resp.Data["certificate"].(string)) certPem := pem.EncodeToMemory(&pem.Block{ Type: "CERTIFICATE", Bytes: certBytes, }) keyBytes, _ := base64.StdEncoding.DecodeString(resp.Data["private_key"].(string)) keyPem := pem.EncodeToMemory(&pem.Block{ Type: "EC PRIVATE KEY", Bytes: keyBytes, }) intdata["root"] = string(certPem) intdata["rootkey"] = string(keyPem) reqdata["pem_bundle"] = string(certPem) + "\n" + string(keyPem) return nil }, }, logicaltest.TestStep{ Operation: logical.WriteOperation, Path: "intermediate/generate/exported", Data: map[string]interface{}{ "format": "der", "key_type": "ec", "key_bits": 384, "common_name": "Intermediate Cert", }, Check: func(resp *logical.Response) error { csrBytes, _ := base64.StdEncoding.DecodeString(resp.Data["csr"].(string)) csrPem := pem.EncodeToMemory(&pem.Block{ Type: "CERTIFICATE REQUEST", Bytes: csrBytes, }) keyBytes, _ := base64.StdEncoding.DecodeString(resp.Data["private_key"].(string)) keyPem := pem.EncodeToMemory(&pem.Block{ Type: "EC PRIVATE KEY", Bytes: keyBytes, }) intdata["intermediatecsr"] = string(csrPem) intdata["intermediatekey"] = string(keyPem) return nil }, }, logicaltest.TestStep{ Operation: logical.WriteOperation, Path: "config/ca", Data: reqdata, Check: func(resp *logical.Response) error { delete(reqdata, "pem_bundle") delete(reqdata, "ttl") reqdata["csr"] = intdata["intermediatecsr"].(string) reqdata["common_name"] = "Intermediate Cert" reqdata["ttl"] = "90h" return nil }, }, logicaltest.TestStep{ Operation: logical.WriteOperation, Path: "root/sign-intermediate", Data: reqdata, Check: func(resp *logical.Response) error { delete(reqdata, "csr") delete(reqdata, "common_name") delete(reqdata, "ttl") intdata["intermediatecert"] = resp.Data["certificate"].(string) reqdata["serial_number"] = resp.Data["serial_number"].(string) reqdata["certificate"] = resp.Data["certificate"].(string) reqdata["pem_bundle"] = intdata["intermediatekey"].(string) + "\n" + resp.Data["certificate"].(string) return nil }, }, // First load in this way to populate the private key logicaltest.TestStep{ Operation: logical.WriteOperation, Path: "config/ca", Data: reqdata, Check: func(resp *logical.Response) error { delete(reqdata, "pem_bundle") return nil }, }, // Now test setting the intermediate, signed CA cert logicaltest.TestStep{ Operation: logical.WriteOperation, Path: "intermediate/set-signed", Data: reqdata, Check: func(resp *logical.Response) error { delete(reqdata, "certificate") return nil }, }, logicaltest.TestStep{ Operation: logical.WriteOperation, Path: "revoke", Data: reqdata, }, logicaltest.TestStep{ Operation: logical.ReadOperation, Path: "crl", Data: reqdata, Check: func(resp *logical.Response) error { crlBytes := resp.Data["http_raw_body"].([]byte) certList, err := x509.ParseCRL(crlBytes) if err != nil { t.Fatalf("err: %s", err) } revokedList := certList.TBSCertList.RevokedCertificates if len(revokedList) != 2 { t.Fatalf("length of revoked list not 2; %d", len(revokedList)) } found := false for _, revEntry := range revokedList { revokedString := certutil.GetOctalFormatted(revEntry.SerialNumber.Bytes(), ":") if revokedString == reqdata["serial_number"].(string) { found = true } } if !found { t.Fatalf("did not find %s in CRL", reqdata["serial_number"].(string)) } delete(reqdata, "serial_number") return nil }, }, } return ret }
func main() { interPEMPtr := flag.String("inter", "", "a PEM file containing intermediates") rootPEMPtr := flag.String("roots", "", "a PEM file containing roots") crlPtr := flag.String("crl", "", "a CRL file") flag.Parse() var rootPool, intermediatePool *x509.CertPool var roots, intermediates []*x509.Certificate if nil != rootPEMPtr && len(*rootPEMPtr) > 0 { rootPool, roots = loadCertPool(*rootPEMPtr) } if nil != interPEMPtr && len(*interPEMPtr) > 0 { intermediatePool, intermediates = loadCertPool(*interPEMPtr) } var crlData []byte if nil != crlPtr && len(*crlPtr) > 0 { // Get the crl from the args var err error crlData, err = ioutil.ReadFile(*crlPtr) check(err) } if len(crlData) > 0 { // Maybe it's PEM; try to parse as PEM, if that fails, just use the bytes block, _ := pem.Decode(crlData) if block != nil { crlData = block.Bytes } crl, err := x509.ParseCRL(crlData) if err != nil { panic("could not parse CRL") } // check the CRL is still current if crl.HasExpired(time.Now()) { fmt.Printf("crl has expired\n") } var signer *x509.Certificate if nil != roots { signer = findSigningCert(roots, crl) if nil == signer { fmt.Printf("Not signed by a root; trying known intermediates\n") if nil != intermediates { signer = findSigningCert(intermediates, crl) } } } if nil != signer { fmt.Printf("found signer! %v\n", signer.Subject) opts := x509.VerifyOptions{ Roots: rootPool, Intermediates: intermediatePool, } if _, err := signer.Verify(opts); err != nil { fmt.Println("Warning! Can't verify signer!") } } issuerData, err := asn1.Marshal(crl.TBSCertList.Issuer) if nil == err { issuerString := base64.StdEncoding.EncodeToString(issuerData) fmt.Printf("%v\n", issuerString) } for revoked := range crl.TBSCertList.RevokedCertificates { cert := crl.TBSCertList.RevokedCertificates[revoked] fmt.Printf(" %v\n", base64.StdEncoding.EncodeToString(cert.SerialNumber.Bytes())) } } }
func crlRevCheck(certChain []*x509.Certificate) (Warning, error) { var warn Warning var warnDetails string var passedChecks int numChecks := len(certChain) - 1 // revocation can't be checked on the root // main revocation check loop for i := 0; i < numChecks; i++ { target := certChain[i] issuer := certChain[i+1] // get CRL URLs urls := target.CRLDistributionPoints numURLs := len(urls) var triedURLs int // url check loop for cntr := 0; triedURLs == 0 && cntr < numURLs; cntr++ { url := urls[cntr] // get CRL crl, err := GetCRL(url) if err != nil { warnDetails = warnDetails + fmt.Sprintf("\n * Failed to get CRL, details: %s", err) warn = errors.New(warnDetails) break } // parse CRL revCertList, err := x509.ParseCRL(crl) if err != nil { warnDetails = warnDetails + fmt.Sprintf("\n * Failed to parse CRL, issuer: %s", GetSubjectDN(issuer)) warn = errors.New(warnDetails) break } // check CRL validity if revCertList.HasExpired(time.Now()) { warnDetails = warnDetails + fmt.Sprintf("\n * CRL expired, issuer: %s", GetSubjectDN(issuer)) warn = errors.New(warnDetails) break } // validate CRL signature err = issuer.CheckCRLSignature(revCertList) if err != nil { warnDetails = warnDetails + fmt.Sprintf("\n * CRL signature invalid, issuer: %s", GetSubjectDN(issuer)) warn = errors.New(warnDetails) break } // check if the cert has been revoked err = CheckRevoked(target.SerialNumber, revCertList) if err != nil { warn = errors.New(warnDetails) return warn, err } triedURLs++ passedChecks++ } } if warn != nil { warn = errors.New(warnDetails) return warn, nil } return nil, nil }