From: Adam Langley Date: Thu, 19 Nov 2009 00:08:24 +0000 (-0800) Subject: crypto/x509: add certificate support. X-Git-Tag: weekly.2009-12-07~207 X-Git-Url: http://www.git.cypherpunks.su/?a=commitdiff_plain;h=cc56fc38602897d76bbea550e0ec99ddacf7db26;p=gostls13.git crypto/x509: add certificate support. R=rsc CC=golang-dev https://golang.org/cl/156054 --- diff --git a/src/pkg/crypto/x509/x509.go b/src/pkg/crypto/x509/x509.go index c1488e41e0..ee0f3858d1 100644 --- a/src/pkg/crypto/x509/x509.go +++ b/src/pkg/crypto/x509/x509.go @@ -2,16 +2,19 @@ // Use of this source code is governed by a BSD-style // license that can be found in the LICENSE file. -// NOTE: PACKAGE UNDER CONSTRUCTION. -// // This package parses X.509-encoded keys and certificates. package x509 import ( "asn1"; "big"; + "container/vector"; "crypto/rsa"; + "crypto/sha1"; + "hash"; "os"; + "strings"; + "time"; ) // pkcs1PrivateKey is a structure which mirrors the PKCS#1 ASN.1 for an RSA private key. @@ -32,7 +35,11 @@ func rawValueIsInteger(raw *asn1.RawValue) bool { // ParsePKCS1PrivateKey returns an RSA private key from its ASN.1 PKCS#1 DER encoded form. func ParsePKCS1PrivateKey(der []byte) (key *rsa.PrivateKey, err os.Error) { var priv pkcs1PrivateKey; - _, err = asn1.Unmarshal(&priv, der); + rest, err := asn1.Unmarshal(&priv, der); + if len(rest) > 0 { + err = asn1.SyntaxError{"trailing data"}; + return; + } if err != nil { return } @@ -61,3 +68,524 @@ func ParsePKCS1PrivateKey(der []byte) (key *rsa.PrivateKey, err os.Error) { } return; } + +// These structures reflect the ASN.1 structure of X.509 certificates.: + +type certificate struct { + TBSCertificate tbsCertificate; + SignatureAlgorithm algorithmIdentifier; + SignatureValue asn1.BitString; +} + +type tbsCertificate struct { + Raw asn1.RawContents; + Version int "optional,explicit,default:1,tag:0"; + SerialNumber asn1.RawValue; + SignatureAlgorithm algorithmIdentifier; + Issuer rdnSequence; + Validity validity; + Subject rdnSequence; + PublicKey publicKeyInfo; + UniqueId asn1.BitString "optional,explicit,tag:1"; + SubjectUniqueId asn1.BitString "optional,explicit,tag:2"; + Extensions []extension "optional,explicit,tag:3"; +} + +type algorithmIdentifier struct { + Algorithm asn1.ObjectIdentifier; +} + +type rdnSequence []relativeDistinguishedName + +type relativeDistinguishedName []attributeTypeAndValue + +type attributeTypeAndValue struct { + Type asn1.ObjectIdentifier; + Value interface{}; +} + +type validity struct { + NotBefore, NotAfter *time.Time; +} + +type publicKeyInfo struct { + Algorithm algorithmIdentifier; + PublicKey asn1.BitString; +} + +type extension struct { + Id asn1.ObjectIdentifier; + Critical bool "optional"; + Value []byte; +} + +// RFC 5280, 4.2.1.1 +type authKeyId struct { + Id []byte "optional,tag:0"; +} + +type SignatureAlgorithm int + +const ( + UnknownSignatureAlgorithm SignatureAlgorithm = iota; + MD2WithRSA; + MD5WithRSA; + SHA1WithRSA; + SHA256WithRSA; + SHA384WithRSA; + SHA512WithRSA; +) + +type PublicKeyAlgorithm int + +const ( + UnknownPublicKeyAlgorithm PublicKeyAlgorithm = iota; + RSA; +) + +// Name represents an X.509 distinguished name. This only includes the common +// elements of a DN. Additional elements in the name are ignored. +type Name struct { + Country, Organization, OrganizationalUnit string; + CommonName, SerialNumber, Locality string; + Province, StreetAddress, PostalCode string; +} + +func (n *Name) fillFromRDNSequence(rdns *rdnSequence) { + for _, rdn := range *rdns { + if len(rdn) == 0 { + continue + } + atv := rdn[0]; + value, ok := atv.Value.(string); + if !ok { + continue + } + + t := atv.Type; + if len(t) == 4 && t[0] == 2 && t[1] == 5 && t[2] == 4 { + switch t[3] { + case 3: + n.CommonName = value + case 5: + n.SerialNumber = value + case 6: + n.Country = value + case 7: + n.Locality = value + case 8: + n.Province = value + case 9: + n.StreetAddress = value + case 10: + n.Organization = value + case 11: + n.OrganizationalUnit = value + case 17: + n.PostalCode = value + } + } + } +} + +func getSignatureAlgorithmFromOID(oid []int) SignatureAlgorithm { + if len(oid) == 7 && oid[0] == 1 && oid[1] == 2 && oid[2] == 840 && + oid[3] == 113549 && oid[4] == 1 && oid[5] == 1 { + switch oid[6] { + case 2: + return MD2WithRSA + case 4: + return MD5WithRSA + case 5: + return SHA1WithRSA + case 11: + return SHA256WithRSA + case 12: + return SHA384WithRSA + case 13: + return SHA512WithRSA + } + } + + return UnknownSignatureAlgorithm; +} + +func getPublicKeyAlgorithmFromOID(oid []int) PublicKeyAlgorithm { + if len(oid) == 7 && oid[0] == 1 && oid[1] == 2 && oid[2] == 840 && + oid[3] == 113549 && oid[4] == 1 && oid[5] == 1 { + switch oid[6] { + case 1: + return RSA + } + } + + return UnknownPublicKeyAlgorithm; +} + +// KeyUsage represents the set of actions that are valid for a given key. It's +// a bitmap of the KeyUsage* constants. +type KeyUsage int + +const ( + KeyUsageDigitalSignature KeyUsage = 1 << iota; + KeyUsageContentCommitment; + KeyUsageKeyEncipherment; + KeyUsageDataEncipherment; + KeyUsageKeyAgreement; + KeyUsageCertSign; + KeyUsageCRLSign; + KeyUsageEncipherOnly; + KeyUsageDecipherOnly; +) + +// A Certificate represents an X.509 certificate. +type Certificate struct { + Raw []byte; // Raw ASN.1 DER contents. + Signature []byte; + SignatureAlgorithm SignatureAlgorithm; + + PublicKeyAlgorithm PublicKeyAlgorithm; + PublicKey interface{}; + + Version int; + SerialNumber []byte; + Issuer Name; + Subject Name; + NotBefore, NotAfter *time.Time; // Validity bounds. + KeyUsage KeyUsage; + + BasicConstraintsValid bool; // if true then the next two fields are valid. + IsCA bool; + MaxPathLen int; + + SubjectKeyId []byte; + AuthorityKeyId []byte; + + // Subject Alternate Name values + DNSNames []string; + EmailAddresses []string; +} + +// UnsupportedAlgorithmError results from attempting to perform an operation +// that involves algorithms that are not currently implemented. +type UnsupportedAlgorithmError struct{} + +func (UnsupportedAlgorithmError) String() string { + return "cannot verify signature: algorithm unimplemented" +} + +// ConstraintViolationError results when a requested usage is not permitted by +// a certificate. For example: checking a signature when the public key isn't a +// certificate signing key. +type ConstraintViolationError struct{} + +func (ConstraintViolationError) String() string { + return "invalid signature: parent certificate cannot sign this kind of certificate" +} + +// CheckSignatureFrom verifies that the signature on c is a valid signature +// from parent. +func (c *Certificate) CheckSignatureFrom(parent *Certificate) (err os.Error) { + // RFC 5280, 4.2.1.9: + // "If the basic constraints extension is not present in a version 3 + // certificate, or the extension is present but the cA boolean is not + // asserted, then the certified public key MUST NOT be used to verify + // certificate signatures." + if parent.Version == 3 && !parent.BasicConstraintsValid || + parent.BasicConstraintsValid && !parent.IsCA { + return ConstraintViolationError{} + } + + if parent.KeyUsage != 0 && parent.KeyUsage&KeyUsageCertSign == 0 { + return ConstraintViolationError{} + } + + if parent.PublicKeyAlgorithm == UnknownPublicKeyAlgorithm { + return UnsupportedAlgorithmError{} + } + + // TODO(agl): don't ignore the path length constraint. + + var h hash.Hash; + var hashType rsa.PKCS1v15Hash; + + switch c.SignatureAlgorithm { + case SHA1WithRSA: + h = sha1.New(); + hashType = rsa.HashSHA1; + default: + return UnsupportedAlgorithmError{} + } + + pub, ok := parent.PublicKey.(*rsa.PublicKey); + if !ok { + return UnsupportedAlgorithmError{} + } + + h.Write(c.Raw); + digest := h.Sum(); + + return rsa.VerifyPKCS1v15(pub, hashType, digest, c.Signature); +} + +func matchHostnames(pattern, host string) bool { + if len(pattern) == 0 || len(host) == 0 { + return false + } + + patternParts := strings.Split(pattern, ".", 0); + hostParts := strings.Split(host, ".", 0); + + if len(patternParts) != len(hostParts) { + return false + } + + for i, patternPart := range patternParts { + if patternPart == "*" { + continue + } + if patternPart != hostParts[i] { + return false + } + } + + return true; +} + +// IsValidForHost returns true iff c is a valid certificate for the given host. +func (c *Certificate) IsValidForHost(h string) bool { + if len(c.DNSNames) > 0 { + for _, match := range c.DNSNames { + if matchHostnames(match, h) { + return true + } + } + // If Subject Alt Name is given, we ignore the common name. + return false; + } + + return matchHostnames(c.Subject.CommonName, h); +} + +type UnhandledCriticalExtension struct{} + +func (h UnhandledCriticalExtension) String() string { + return "unhandled critical extension" +} + +type basicConstraints struct { + IsCA bool "optional"; + MaxPathLen int "optional"; +} + +type rsaPublicKey struct { + N asn1.RawValue; + E int; +} + +func parsePublicKey(algo PublicKeyAlgorithm, asn1Data []byte) (interface{}, os.Error) { + switch algo { + case RSA: + p := new(rsaPublicKey); + _, err := asn1.Unmarshal(p, asn1Data); + if err != nil { + return nil, err + } + + if !rawValueIsInteger(&p.N) { + return nil, asn1.StructuralError{"tags don't match"} + } + + pub := &rsa.PublicKey{ + E: p.E, + N: new(big.Int).SetBytes(p.N.Bytes), + }; + return pub, nil; + default: + return nil, nil + } + + panic("unreachable"); +} + +func appendString(in []string, v string) (out []string) { + if cap(in)-len(in) < 1 { + out = make([]string, len(in)+1, len(in)*2+1); + for i, v := range in { + out[i] = v + } + } else { + out = in[0 : len(in)+1] + } + out[len(in)] = v; + return out; +} + +func parseCertificate(in *certificate) (*Certificate, os.Error) { + out := new(Certificate); + out.Raw = in.TBSCertificate.Raw; + + out.Signature = in.SignatureValue.RightAlign(); + out.SignatureAlgorithm = + getSignatureAlgorithmFromOID(in.TBSCertificate.SignatureAlgorithm.Algorithm); + + out.PublicKeyAlgorithm = + getPublicKeyAlgorithmFromOID(in.TBSCertificate.PublicKey.Algorithm.Algorithm); + var err os.Error; + out.PublicKey, err = parsePublicKey(out.PublicKeyAlgorithm, in.TBSCertificate.PublicKey.PublicKey.RightAlign()); + if err != nil { + return nil, err + } + + out.Version = in.TBSCertificate.Version; + out.SerialNumber = in.TBSCertificate.SerialNumber.Bytes; + out.Issuer.fillFromRDNSequence(&in.TBSCertificate.Issuer); + out.Subject.fillFromRDNSequence(&in.TBSCertificate.Subject); + out.NotBefore = in.TBSCertificate.Validity.NotBefore; + out.NotAfter = in.TBSCertificate.Validity.NotAfter; + + for _, e := range in.TBSCertificate.Extensions { + if len(e.Id) == 4 && e.Id[0] == 2 && e.Id[1] == 5 && e.Id[2] == 29 { + switch e.Id[3] { + case 15: + // RFC 5280, 4.2.1.3 + var usageBits asn1.BitString; + _, err := asn1.Unmarshal(&usageBits, e.Value); + + if err == nil { + var usage int; + for i := 0; i < 9; i++ { + if usageBits.At(i) != 0 { + usage |= 1 << uint(i) + } + } + out.KeyUsage = KeyUsage(usage); + continue; + } + case 19: + // RFC 5280, 4.2.1.9 + var constriants basicConstraints; + _, err := asn1.Unmarshal(&constriants, e.Value); + + if err == nil { + out.BasicConstraintsValid = true; + out.IsCA = constriants.IsCA; + out.MaxPathLen = constriants.MaxPathLen; + continue; + } + case 17: + // RFC 5280, 4.2.1.6 + + // SubjectAltName ::= GeneralNames + // + // GeneralNames ::= SEQUENCE SIZE (1..MAX) OF GeneralName + // + // GeneralName ::= CHOICE { + // otherName [0] OtherName, + // rfc822Name [1] IA5String, + // dNSName [2] IA5String, + // x400Address [3] ORAddress, + // directoryName [4] Name, + // ediPartyName [5] EDIPartyName, + // uniformResourceIdentifier [6] IA5String, + // iPAddress [7] OCTET STRING, + // registeredID [8] OBJECT IDENTIFIER } + var seq asn1.RawValue; + _, err := asn1.Unmarshal(&seq, e.Value); + if err != nil { + return nil, err + } + if !seq.IsCompound || seq.Tag != 16 || seq.Class != 0 { + return nil, asn1.StructuralError{"bad SAN sequence"} + } + + parsedName := false; + + rest := seq.Bytes; + for len(rest) > 0 { + var v asn1.RawValue; + rest, err = asn1.Unmarshal(&v, rest); + if err != nil { + return nil, err + } + switch v.Tag { + case 1: + out.EmailAddresses = appendString(out.EmailAddresses, string(v.Bytes)); + parsedName = true; + case 2: + out.DNSNames = appendString(out.DNSNames, string(v.Bytes)); + parsedName = true; + } + } + + if parsedName { + continue + } + // If we didn't parse any of the names then we + // fall through to the critical check below. + + case 35: + // RFC 5280, 4.2.1.1 + var a authKeyId; + _, err = asn1.Unmarshal(&a, e.Value); + if err != nil { + return nil, err + } + out.AuthorityKeyId = a.Id; + continue; + + case 14: + // RFC 5280, 4.2.1.2 + out.SubjectKeyId = e.Value; + continue; + } + } + + if e.Critical { + return out, UnhandledCriticalExtension{} + } + } + + return out, nil; +} + +// ParseCertificate parses a single certificate from the given ASN.1 DER data. +func ParseCertificate(asn1Data []byte) (*Certificate, os.Error) { + var cert certificate; + rest, err := asn1.Unmarshal(&cert, asn1Data); + if err != nil { + return nil, err + } + if len(rest) > 0 { + return nil, asn1.SyntaxError{"trailing data"} + } + + return parseCertificate(&cert); +} + +// ParseCertificates parses one or more certificates from the given ASN.1 DER +// data. The certificates must be concatenated with no intermediate padding. +func ParseCertificates(asn1Data []byte) ([]*Certificate, os.Error) { + v := vector.New(0); + + for len(asn1Data) > 0 { + cert := new(certificate); + var err os.Error; + asn1Data, err = asn1.Unmarshal(cert, asn1Data); + if err != nil { + return nil, err + } + v.Push(cert); + } + + ret := make([]*Certificate, v.Len()); + for i := 0; i < v.Len(); i++ { + cert, err := parseCertificate(v.At(i).(*certificate)); + if err != nil { + return nil, err + } + ret[i] = cert; + } + + return ret, nil; +} diff --git a/src/pkg/crypto/x509/x509_test.go b/src/pkg/crypto/x509/x509_test.go index 2c94d35ee7..17244d6ec1 100644 --- a/src/pkg/crypto/x509/x509_test.go +++ b/src/pkg/crypto/x509/x509_test.go @@ -7,6 +7,7 @@ package x509 import ( "big"; "crypto/rsa"; + "encoding/hex"; "encoding/pem"; "reflect"; "strings"; @@ -50,3 +51,94 @@ var rsaPrivateKey = &rsa.PrivateKey{ P: bigFromString("98920366548084643601728869055592650835572950932266967461790948584315647051443"), Q: bigFromString("94560208308847015747498523884063394671606671904944666360068158221458669711639"), } + +type matchHostnamesTest struct { + pattern, host string; + ok bool; +} + +var matchHostnamesTests = []matchHostnamesTest{ + matchHostnamesTest{"a.b.c", "a.b.c", true}, + matchHostnamesTest{"a.b.c", "b.b.c", false}, + matchHostnamesTest{"", "b.b.c", false}, + matchHostnamesTest{"a.b.c", "", false}, + matchHostnamesTest{"example.com", "example.com", true}, + matchHostnamesTest{"example.com", "www.example.com", false}, + matchHostnamesTest{"*.example.com", "www.example.com", true}, + matchHostnamesTest{"*.example.com", "xyz.www.example.com", false}, + matchHostnamesTest{"*.*.example.com", "xyz.www.example.com", true}, + matchHostnamesTest{"*.www.*.com", "xyz.www.example.com", true}, +} + +func TestMatchHostnames(t *testing.T) { + for i, test := range matchHostnamesTests { + r := matchHostnames(test.pattern, test.host); + if r != test.ok { + t.Errorf("#%d mismatch got: %t want: %t", i, r, test.ok) + } + } +} + +func TestCertificateParse(t *testing.T) { + s, _ := hex.DecodeString(certBytes); + certs, err := ParseCertificates(s); + if err != nil { + t.Error(err) + } + if len(certs) != 2 { + t.Errorf("Wrong number of certs: got %d want 2", len(certs)); + return; + } + + err = certs[0].CheckSignatureFrom(certs[1]); + if err != nil { + t.Error(err) + } + + if !certs[0].IsValidForHost("mail.google.com") { + t.Errorf("cert not valid for host") + } +} + +var certBytes = "308203223082028ba00302010202106edf0d9499fd4533dd1297fc42a93be1300d06092a864886" + "f70d0101050500304c310b3009060355040613025a4131253023060355040a131c546861777465" + "20436f6e73756c74696e67202850747929204c74642e311630140603550403130d546861777465" + "20534743204341301e170d3039303332353136343932395a170d3130303332353136343932395a" + "3069310b3009060355040613025553311330110603550408130a43616c69666f726e6961311630" + "140603550407130d4d6f756e7461696e205669657731133011060355040a130a476f6f676c6520" + "496e63311830160603550403130f6d61696c2e676f6f676c652e636f6d30819f300d06092a8648" + "86f70d010101050003818d0030818902818100c5d6f892fccaf5614b064149e80a2c9581a218ef" + "41ec35bd7a58125ae76f9ea54ddc893abbeb029f6b73616bf0ffd868791fba7af9c4aebf3706ba" + "3eeaeed27435b4ddcfb157c05f351d66aa87fee0de072d66d773affbd36ab78bef090e0cc861a9" + "03ac90dd98b51c9c41566c017f0beec3bff391051ffba0f5cc6850ad2a590203010001a381e730" + "81e430280603551d250421301f06082b0601050507030106082b06010505070302060960864801" + "86f842040130360603551d1f042f302d302ba029a0278625687474703a2f2f63726c2e74686177" + "74652e636f6d2f54686177746553474343412e63726c307206082b060105050701010466306430" + "2206082b060105050730018616687474703a2f2f6f6373702e7468617774652e636f6d303e0608" + "2b060105050730028632687474703a2f2f7777772e7468617774652e636f6d2f7265706f736974" + "6f72792f5468617774655f5347435f43412e637274300c0603551d130101ff04023000300d0609" + "2a864886f70d01010505000381810062f1f3050ebc105e497c7aedf87e24d2f4a986bb3b837bd1" + "9b91ebcad98b065992f6bd2b49b7d6d3cb2e427a99d606c7b1d46352527fac39e6a8b6726de5bf" + "70212a52cba07634a5e332011bd1868e78eb5e3c93cf03072276786f207494feaa0ed9d53b2110" + "a76571f90209cdae884385c882587030ee15f33d761e2e45a6bc308203233082028ca003020102" + "020430000002300d06092a864886f70d0101050500305f310b3009060355040613025553311730" + "15060355040a130e566572695369676e2c20496e632e31373035060355040b132e436c61737320" + "33205075626c6963205072696d6172792043657274696669636174696f6e20417574686f726974" + "79301e170d3034303531333030303030305a170d3134303531323233353935395a304c310b3009" + "060355040613025a4131253023060355040a131c54686177746520436f6e73756c74696e672028" + "50747929204c74642e311630140603550403130d5468617774652053474320434130819f300d06" + "092a864886f70d010101050003818d0030818902818100d4d367d08d157faecd31fe7d1d91a13f" + "0b713cacccc864fb63fc324b0794bd6f80ba2fe10493c033fc093323e90b742b71c403c6d2cde2" + "2ff50963cdff48a500bfe0e7f388b72d32de9836e60aad007bc4644a3b847503f270927d0e62f5" + "21ab693684317590f8bfc76c881b06957cc9e5a8de75a12c7a68dfd5ca1c875860190203010001" + "a381fe3081fb30120603551d130101ff040830060101ff020100300b0603551d0f040403020106" + "301106096086480186f842010104040302010630280603551d110421301fa41d301b3119301706" + "035504031310507269766174654c6162656c332d313530310603551d1f042a30283026a024a022" + "8620687474703a2f2f63726c2e766572697369676e2e636f6d2f706361332e63726c303206082b" + "0601050507010104263024302206082b060105050730018616687474703a2f2f6f6373702e7468" + "617774652e636f6d30340603551d25042d302b06082b0601050507030106082b06010505070302" + "06096086480186f8420401060a6086480186f845010801300d06092a864886f70d010105050003" + "81810055ac63eadea1ddd2905f9f0bce76be13518f93d9052bc81b774bad6950a1eededcfddb07" + "e9e83994dcab72792f06bfab8170c4a8edea5334edef1e53d906c7562bd15cf4d18a8eb42bb137" + "9048084225c53e8acb7feb6f04d16dc574a2f7a27c7b603c77cd0ece48027f012fb69b37e02a2a" + "36dcd585d6ace53f546f961e05af"