mirror of
https://github.com/golang/go
synced 2024-11-19 14:54:43 -07:00
crypto/x509/crl: add package
crl parses CRLs and exposes their details. In the future, Verify should be able to use this for revocation checking. R=bradfitz CC=golang-dev https://golang.org/cl/4485045
This commit is contained in:
parent
d6b2925923
commit
55d43f0ce8
@ -59,6 +59,7 @@ DIRS=\
|
||||
crypto/tls\
|
||||
crypto/twofish\
|
||||
crypto/x509\
|
||||
crypto/x509/crl\
|
||||
crypto/xtea\
|
||||
debug/dwarf\
|
||||
debug/macho\
|
||||
|
11
src/pkg/crypto/x509/crl/Makefile
Normal file
11
src/pkg/crypto/x509/crl/Makefile
Normal file
@ -0,0 +1,11 @@
|
||||
# Copyright 2011 The Go Authors. All rights reserved.
|
||||
# Use of this source code is governed by a BSD-style
|
||||
# license that can be found in the LICENSE file.
|
||||
|
||||
include ../../../../Make.inc
|
||||
|
||||
TARG=crypto/x509/crl
|
||||
GOFILES=\
|
||||
crl.go\
|
||||
|
||||
include ../../../../Make.pkg
|
96
src/pkg/crypto/x509/crl/crl.go
Normal file
96
src/pkg/crypto/x509/crl/crl.go
Normal file
@ -0,0 +1,96 @@
|
||||
// Copyright 2011 The Go Authors. All rights reserved.
|
||||
// Use of this source code is governed by a BSD-style
|
||||
// license that can be found in the LICENSE file.
|
||||
|
||||
// Package crl exposes low-level details of PKIX Certificate Revocation Lists
|
||||
// as specified in RFC 5280, section 5.
|
||||
package crl
|
||||
|
||||
import (
|
||||
"asn1"
|
||||
"bytes"
|
||||
"encoding/pem"
|
||||
"os"
|
||||
"time"
|
||||
)
|
||||
|
||||
// CertificateList represents the ASN.1 structure of the same name. See RFC
|
||||
// 5280, section 5.1. Use crypto/x509/Certificate.CheckCRLSignature to verify
|
||||
// the signature.
|
||||
type CertificateList struct {
|
||||
TBSCertList TBSCertificateList
|
||||
SignatureAlgorithm AlgorithmIdentifier
|
||||
SignatureValue asn1.BitString
|
||||
}
|
||||
|
||||
// HasExpired returns true iff currentTimeSeconds is past the expiry time of
|
||||
// certList.
|
||||
func (certList *CertificateList) HasExpired(currentTimeSeconds int64) bool {
|
||||
return certList.TBSCertList.NextUpdate.Seconds() <= currentTimeSeconds
|
||||
}
|
||||
|
||||
// TBSCertificateList represents the ASN.1 structure of the same name. See RFC
|
||||
// 5280, section 5.1.
|
||||
type TBSCertificateList struct {
|
||||
Raw asn1.RawContent
|
||||
Version int "optional,default:2"
|
||||
Signature AlgorithmIdentifier
|
||||
Issuer asn1.RawValue
|
||||
ThisUpdate *time.Time
|
||||
NextUpdate *time.Time
|
||||
RevokedCertificates []RevokedCertificate "optional"
|
||||
Extensions []Extension "tag:0,optional,explicit"
|
||||
}
|
||||
|
||||
// AlgorithmIdentifier represents the ASN.1 structure of the same name. See RFC
|
||||
// 5280, section 4.1.1.2.
|
||||
type AlgorithmIdentifier struct {
|
||||
Algo asn1.ObjectIdentifier
|
||||
Params asn1.RawValue "optional"
|
||||
}
|
||||
|
||||
// AlgorithmIdentifier represents the ASN.1 structure of the same name. See RFC
|
||||
// 5280, section 5.1.
|
||||
type RevokedCertificate struct {
|
||||
SerialNumber asn1.RawValue
|
||||
RevocationTime *time.Time
|
||||
Extensions []Extension "optional"
|
||||
}
|
||||
|
||||
// AlgorithmIdentifier represents the ASN.1 structure of the same name. See RFC
|
||||
// 5280, section 4.2.
|
||||
type Extension struct {
|
||||
Id asn1.ObjectIdentifier
|
||||
IsCritial bool "optional"
|
||||
Value []byte
|
||||
}
|
||||
|
||||
// pemCRLPrefix is the magic string that indicates that we have a PEM encoded
|
||||
// CRL.
|
||||
var pemCRLPrefix = []byte("-----BEGIN X509 CRL")
|
||||
// pemType is the type of a PEM encoded CRL.
|
||||
var pemType = "X509 CRL"
|
||||
|
||||
// Parse parses a CRL from the given bytes. It's often the case that PEM
|
||||
// encoded CRLs will appear where they should be DER encoded, so this function
|
||||
// will transparently handle PEM encoding as long as there isn't any leading
|
||||
// garbage.
|
||||
func Parse(crlBytes []byte) (certList *CertificateList, err os.Error) {
|
||||
if bytes.HasPrefix(crlBytes, pemCRLPrefix) {
|
||||
block, _ := pem.Decode(crlBytes)
|
||||
if block != nil && block.Type == pemType {
|
||||
crlBytes = block.Bytes
|
||||
}
|
||||
}
|
||||
return ParseDER(crlBytes)
|
||||
}
|
||||
|
||||
// ParseDER parses a DER encoded CRL from the given bytes.
|
||||
func ParseDER(derBytes []byte) (certList *CertificateList, err os.Error) {
|
||||
certList = new(CertificateList)
|
||||
_, err = asn1.Unmarshal(derBytes, certList)
|
||||
if err != nil {
|
||||
certList = nil
|
||||
}
|
||||
return
|
||||
}
|
63
src/pkg/crypto/x509/crl/crl_test.go
Normal file
63
src/pkg/crypto/x509/crl/crl_test.go
Normal file
@ -0,0 +1,63 @@
|
||||
// Copyright 2011 The Go Authors. All rights reserved.
|
||||
// Use of this source code is governed by a BSD-style
|
||||
// license that can be found in the LICENSE file.
|
||||
|
||||
package crl
|
||||
|
||||
import (
|
||||
"encoding/base64"
|
||||
"testing"
|
||||
)
|
||||
|
||||
func fromBase64(in string) []byte {
|
||||
out := make([]byte, base64.StdEncoding.DecodedLen(len(in)))
|
||||
_, err := base64.StdEncoding.Decode(out, []byte(in))
|
||||
if err != nil {
|
||||
panic("failed to base64 decode")
|
||||
}
|
||||
return out
|
||||
}
|
||||
|
||||
func TestParseDER(t *testing.T) {
|
||||
derBytes := fromBase64(derCRLBase64)
|
||||
certList, err := ParseDER(derBytes)
|
||||
if err != nil {
|
||||
t.Errorf("error parsing: %s", err)
|
||||
return
|
||||
}
|
||||
numCerts := len(certList.TBSCertList.RevokedCertificates)
|
||||
expected := 88
|
||||
if numCerts != expected {
|
||||
t.Errorf("bad number of revoked certificates. got: %d want: %d", numCerts, expected)
|
||||
}
|
||||
|
||||
if certList.HasExpired(1302517272) {
|
||||
t.Errorf("CRL has expired (but shouldn't have)")
|
||||
}
|
||||
|
||||
// Can't check the signature here without a package cycle.
|
||||
}
|
||||
|
||||
func TestParsePEM(t *testing.T) {
|
||||
pemBytes := fromBase64(pemCRLBase64)
|
||||
certList, err := Parse(pemBytes)
|
||||
if err != nil {
|
||||
t.Errorf("error parsing: %s", err)
|
||||
return
|
||||
}
|
||||
numCerts := len(certList.TBSCertList.RevokedCertificates)
|
||||
expected := 2
|
||||
if numCerts != expected {
|
||||
t.Errorf("bad number of revoked certificates. got: %d want: %d", numCerts, expected)
|
||||
}
|
||||
|
||||
if certList.HasExpired(1302517272) {
|
||||
t.Errorf("CRL has expired (but shouldn't have)")
|
||||
}
|
||||
|
||||
// Can't check the signature here without a package cycle.
|
||||
}
|
||||
|
||||
const derCRLBase64 = "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"
|
||||
|
||||
const pemCRLBase64 = "LS0tLS1CRUdJTiBYNTA5IENSTC0tLS0tDQpNSUlCOWpDQ0FWOENBUUV3RFFZSktvWklodmNOQVFFRkJRQXdiREVhTUJnR0ExVUVDaE1SVWxOQklGTmxZM1Z5DQphWFI1SUVsdVl5NHhIakFjQmdOVkJBTVRGVkpUUVNCUWRXSnNhV01nVW05dmRDQkRRU0IyTVRFdU1Dd0dDU3FHDQpTSWIzRFFFSkFSWWZjbk5oYTJWdmJuSnZiM1J6YVdkdVFISnpZWE5sWTNWeWFYUjVMbU52YlJjTk1URXdNakl6DQpNVGt5T0RNd1doY05NVEV3T0RJeU1Ua3lPRE13V2pDQmpEQktBaEVBckRxb2g5RkhKSFhUN09QZ3V1bjQrQmNODQpNRGt4TVRBeU1UUXlOekE1V2pBbU1Bb0dBMVVkRlFRRENnRUpNQmdHQTFVZEdBUVJHQTh5TURBNU1URXdNakUwDQpNalExTlZvd1BnSVJBTEd6blowOTVQQjVhQU9MUGc1N2ZNTVhEVEF5TVRBeU16RTBOVEF4TkZvd0dqQVlCZ05WDQpIUmdFRVJnUE1qQXdNakV3TWpNeE5EVXdNVFJhb0RBd0xqQWZCZ05WSFNNRUdEQVdnQlQxVERGNlVRTS9MTmVMDQpsNWx2cUhHUXEzZzltekFMQmdOVkhSUUVCQUlDQUlRd0RRWUpLb1pJaHZjTkFRRUZCUUFEZ1lFQUZVNUFzNk16DQpxNVBSc2lmYW9iUVBHaDFhSkx5QytNczVBZ2MwYld5QTNHQWR4dXI1U3BQWmVSV0NCamlQL01FSEJXSkNsQkhQDQpHUmNxNXlJZDNFakRrYUV5eFJhK2k2N0x6dmhJNmMyOUVlNks5cFNZd2ppLzdSVWhtbW5Qclh0VHhsTDBsckxyDQptUVFKNnhoRFJhNUczUUE0Q21VZHNITnZicnpnbUNZcHZWRT0NCi0tLS0tRU5EIFg1MDkgQ1JMLS0tLS0NCg0K"
|
@ -13,7 +13,7 @@ import (
|
||||
"crypto"
|
||||
"crypto/rsa"
|
||||
"crypto/sha1"
|
||||
"hash"
|
||||
"crypto/x509/crl"
|
||||
"io"
|
||||
"os"
|
||||
"time"
|
||||
@ -485,26 +485,47 @@ func (c *Certificate) CheckSignatureFrom(parent *Certificate) (err os.Error) {
|
||||
|
||||
// TODO(agl): don't ignore the path length constraint.
|
||||
|
||||
var h hash.Hash
|
||||
return parent.CheckSignature(c.SignatureAlgorithm, c.RawTBSCertificate, c.Signature)
|
||||
}
|
||||
|
||||
// CheckSignature verifies that signature is a valid signature over signed from
|
||||
// c's public key.
|
||||
func (c *Certificate) CheckSignature(algo SignatureAlgorithm, signed, signature []byte) (err os.Error) {
|
||||
var hashType crypto.Hash
|
||||
|
||||
switch c.SignatureAlgorithm {
|
||||
switch algo {
|
||||
case SHA1WithRSA:
|
||||
h = sha1.New()
|
||||
hashType = crypto.SHA1
|
||||
case SHA256WithRSA:
|
||||
hashType = crypto.SHA256
|
||||
case SHA384WithRSA:
|
||||
hashType = crypto.SHA384
|
||||
case SHA512WithRSA:
|
||||
hashType = crypto.SHA512
|
||||
default:
|
||||
return UnsupportedAlgorithmError{}
|
||||
}
|
||||
|
||||
pub, ok := parent.PublicKey.(*rsa.PublicKey)
|
||||
h := hashType.New()
|
||||
if h == nil {
|
||||
return UnsupportedAlgorithmError{}
|
||||
}
|
||||
|
||||
pub, ok := c.PublicKey.(*rsa.PublicKey)
|
||||
if !ok {
|
||||
return UnsupportedAlgorithmError{}
|
||||
}
|
||||
|
||||
h.Write(c.RawTBSCertificate)
|
||||
h.Write(signed)
|
||||
digest := h.Sum()
|
||||
|
||||
return rsa.VerifyPKCS1v15(pub, hashType, digest, c.Signature)
|
||||
return rsa.VerifyPKCS1v15(pub, hashType, digest, signature)
|
||||
}
|
||||
|
||||
// CheckCRLSignature checks that the signature in crl is from c.
|
||||
func (c *Certificate) CheckCRLSignature(crl *crl.CertificateList) (err os.Error) {
|
||||
algo := getSignatureAlgorithmFromOID(crl.SignatureAlgorithm.Algo)
|
||||
return c.CheckSignature(algo, crl.TBSCertList.Raw, crl.SignatureValue.RightAlign())
|
||||
}
|
||||
|
||||
type UnhandledCriticalExtension struct{}
|
||||
|
Loading…
Reference in New Issue
Block a user