| // 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 x509 |
| |
| import ( |
| "bytes" |
| "errors" |
| "fmt" |
| "net" |
| "runtime" |
| "strings" |
| "time" |
| "unicode/utf8" |
| ) |
| |
| type InvalidReason int |
| |
| const ( |
| // NotAuthorizedToSign results when a certificate is signed by another |
| // which isn't marked as a CA certificate. |
| NotAuthorizedToSign InvalidReason = iota |
| // Expired results when a certificate has expired, based on the time |
| // given in the VerifyOptions. |
| Expired |
| // CANotAuthorizedForThisName results when an intermediate or root |
| // certificate has a name constraint which doesn't include the name |
| // being checked. |
| CANotAuthorizedForThisName |
| // TooManyIntermediates results when a path length constraint is |
| // violated. |
| TooManyIntermediates |
| // IncompatibleUsage results when the certificate's key usage indicates |
| // that it may only be used for a different purpose. |
| IncompatibleUsage |
| // NameMismatch results when the subject name of a parent certificate |
| // does not match the issuer name in the child. |
| NameMismatch |
| ) |
| |
| // CertificateInvalidError results when an odd error occurs. Users of this |
| // library probably want to handle all these errors uniformly. |
| type CertificateInvalidError struct { |
| Cert *Certificate |
| Reason InvalidReason |
| } |
| |
| func (e CertificateInvalidError) Error() string { |
| switch e.Reason { |
| case NotAuthorizedToSign: |
| return "x509: certificate is not authorized to sign other certificates" |
| case Expired: |
| return "x509: certificate has expired or is not yet valid" |
| case CANotAuthorizedForThisName: |
| return "x509: a root or intermediate certificate is not authorized to sign in this domain" |
| case TooManyIntermediates: |
| return "x509: too many intermediates for path length constraint" |
| case IncompatibleUsage: |
| return "x509: certificate specifies an incompatible key usage" |
| case NameMismatch: |
| return "x509: issuer name does not match subject from issuing certificate" |
| } |
| return "x509: unknown error" |
| } |
| |
| // HostnameError results when the set of authorized names doesn't match the |
| // requested name. |
| type HostnameError struct { |
| Certificate *Certificate |
| Host string |
| } |
| |
| func (h HostnameError) Error() string { |
| c := h.Certificate |
| |
| var valid string |
| if ip := net.ParseIP(h.Host); ip != nil { |
| // Trying to validate an IP |
| if len(c.IPAddresses) == 0 { |
| return "x509: cannot validate certificate for " + h.Host + " because it doesn't contain any IP SANs" |
| } |
| for _, san := range c.IPAddresses { |
| if len(valid) > 0 { |
| valid += ", " |
| } |
| valid += san.String() |
| } |
| } else { |
| if c.hasSANExtension() { |
| valid = strings.Join(c.DNSNames, ", ") |
| } else { |
| valid = c.Subject.CommonName |
| } |
| } |
| |
| if len(valid) == 0 { |
| return "x509: certificate is not valid for any names, but wanted to match " + h.Host |
| } |
| return "x509: certificate is valid for " + valid + ", not " + h.Host |
| } |
| |
| // UnknownAuthorityError results when the certificate issuer is unknown |
| type UnknownAuthorityError struct { |
| Cert *Certificate |
| // hintErr contains an error that may be helpful in determining why an |
| // authority wasn't found. |
| hintErr error |
| // hintCert contains a possible authority certificate that was rejected |
| // because of the error in hintErr. |
| hintCert *Certificate |
| } |
| |
| func (e UnknownAuthorityError) Error() string { |
| s := "x509: certificate signed by unknown authority" |
| if e.hintErr != nil { |
| certName := e.hintCert.Subject.CommonName |
| if len(certName) == 0 { |
| if len(e.hintCert.Subject.Organization) > 0 { |
| certName = e.hintCert.Subject.Organization[0] |
| } else { |
| certName = "serial:" + e.hintCert.SerialNumber.String() |
| } |
| } |
| s += fmt.Sprintf(" (possibly because of %q while trying to verify candidate authority certificate %q)", e.hintErr, certName) |
| } |
| return s |
| } |
| |
| // SystemRootsError results when we fail to load the system root certificates. |
| type SystemRootsError struct { |
| Err error |
| } |
| |
| func (se SystemRootsError) Error() string { |
| msg := "x509: failed to load system roots and no roots provided" |
| if se.Err != nil { |
| return msg + "; " + se.Err.Error() |
| } |
| return msg |
| } |
| |
| // errNotParsed is returned when a certificate without ASN.1 contents is |
| // verified. Platform-specific verification needs the ASN.1 contents. |
| var errNotParsed = errors.New("x509: missing ASN.1 contents; use ParseCertificate") |
| |
| // VerifyOptions contains parameters for Certificate.Verify. It's a structure |
| // because other PKIX verification APIs have ended up needing many options. |
| type VerifyOptions struct { |
| DNSName string |
| Intermediates *CertPool |
| Roots *CertPool // if nil, the system roots are used |
| CurrentTime time.Time // if zero, the current time is used |
| // KeyUsage specifies which Extended Key Usage values are acceptable. |
| // An empty list means ExtKeyUsageServerAuth. Key usage is considered a |
| // constraint down the chain which mirrors Windows CryptoAPI behavior, |
| // but not the spec. To accept any key usage, include ExtKeyUsageAny. |
| KeyUsages []ExtKeyUsage |
| } |
| |
| const ( |
| leafCertificate = iota |
| intermediateCertificate |
| rootCertificate |
| ) |
| |
| func matchNameConstraint(domain, constraint string) bool { |
| // The meaning of zero length constraints is not specified, but this |
| // code follows NSS and accepts them as matching everything. |
| if len(constraint) == 0 { |
| return true |
| } |
| |
| if len(domain) < len(constraint) { |
| return false |
| } |
| |
| prefixLen := len(domain) - len(constraint) |
| if !strings.EqualFold(domain[prefixLen:], constraint) { |
| return false |
| } |
| |
| if prefixLen == 0 { |
| return true |
| } |
| |
| isSubdomain := domain[prefixLen-1] == '.' |
| constraintHasLeadingDot := constraint[0] == '.' |
| return isSubdomain != constraintHasLeadingDot |
| } |
| |
| // isValid performs validity checks on the c. |
| func (c *Certificate) isValid(certType int, currentChain []*Certificate, opts *VerifyOptions) error { |
| if len(c.UnhandledCriticalExtensions) > 0 { |
| return UnhandledCriticalExtension{} |
| } |
| |
| if len(currentChain) > 0 { |
| child := currentChain[len(currentChain)-1] |
| if !bytes.Equal(child.RawIssuer, c.RawSubject) { |
| return CertificateInvalidError{c, NameMismatch} |
| } |
| } |
| |
| now := opts.CurrentTime |
| if now.IsZero() { |
| now = time.Now() |
| } |
| if now.Before(c.NotBefore) || now.After(c.NotAfter) { |
| return CertificateInvalidError{c, Expired} |
| } |
| |
| if len(c.PermittedDNSDomains) > 0 { |
| ok := false |
| for _, constraint := range c.PermittedDNSDomains { |
| ok = matchNameConstraint(opts.DNSName, constraint) |
| if ok { |
| break |
| } |
| } |
| |
| if !ok { |
| return CertificateInvalidError{c, CANotAuthorizedForThisName} |
| } |
| } |
| |
| for _, constraint := range c.ExcludedDNSDomains { |
| if matchNameConstraint(opts.DNSName, constraint) { |
| return CertificateInvalidError{c, CANotAuthorizedForThisName} |
| } |
| } |
| |
| // KeyUsage status flags are ignored. From Engineering Security, Peter |
| // Gutmann: A European government CA marked its signing certificates as |
| // being valid for encryption only, but no-one noticed. Another |
| // European CA marked its signature keys as not being valid for |
| // signatures. A different CA marked its own trusted root certificate |
| // as being invalid for certificate signing. Another national CA |
| // distributed a certificate to be used to encrypt data for the |
| // country’s tax authority that was marked as only being usable for |
| // digital signatures but not for encryption. Yet another CA reversed |
| // the order of the bit flags in the keyUsage due to confusion over |
| // encoding endianness, essentially setting a random keyUsage in |
| // certificates that it issued. Another CA created a self-invalidating |
| // certificate by adding a certificate policy statement stipulating |
| // that the certificate had to be used strictly as specified in the |
| // keyUsage, and a keyUsage containing a flag indicating that the RSA |
| // encryption key could only be used for Diffie-Hellman key agreement. |
| |
| if certType == intermediateCertificate && (!c.BasicConstraintsValid || !c.IsCA) { |
| return CertificateInvalidError{c, NotAuthorizedToSign} |
| } |
| |
| if c.BasicConstraintsValid && c.MaxPathLen >= 0 { |
| numIntermediates := len(currentChain) - 1 |
| if numIntermediates > c.MaxPathLen { |
| return CertificateInvalidError{c, TooManyIntermediates} |
| } |
| } |
| |
| return nil |
| } |
| |
| // Verify attempts to verify c by building one or more chains from c to a |
| // certificate in opts.Roots, using certificates in opts.Intermediates if |
| // needed. If successful, it returns one or more chains where the first |
| // element of the chain is c and the last element is from opts.Roots. |
| // |
| // If opts.Roots is nil and system roots are unavailable the returned error |
| // will be of type SystemRootsError. |
| // |
| // WARNING: this doesn't do any revocation checking. |
| func (c *Certificate) Verify(opts VerifyOptions) (chains [][]*Certificate, err error) { |
| // Platform-specific verification needs the ASN.1 contents so |
| // this makes the behavior consistent across platforms. |
| if len(c.Raw) == 0 { |
| return nil, errNotParsed |
| } |
| if opts.Intermediates != nil { |
| for _, intermediate := range opts.Intermediates.certs { |
| if len(intermediate.Raw) == 0 { |
| return nil, errNotParsed |
| } |
| } |
| } |
| |
| // Use Windows's own verification and chain building. |
| if opts.Roots == nil && runtime.GOOS == "windows" { |
| return c.systemVerify(&opts) |
| } |
| |
| if opts.Roots == nil { |
| opts.Roots = systemRootsPool() |
| if opts.Roots == nil { |
| return nil, SystemRootsError{systemRootsErr} |
| } |
| } |
| |
| err = c.isValid(leafCertificate, nil, &opts) |
| if err != nil { |
| return |
| } |
| |
| if len(opts.DNSName) > 0 { |
| err = c.VerifyHostname(opts.DNSName) |
| if err != nil { |
| return |
| } |
| } |
| |
| var candidateChains [][]*Certificate |
| if opts.Roots.contains(c) { |
| candidateChains = append(candidateChains, []*Certificate{c}) |
| } else { |
| if candidateChains, err = c.buildChains(make(map[int][][]*Certificate), []*Certificate{c}, &opts); err != nil { |
| return nil, err |
| } |
| } |
| |
| keyUsages := opts.KeyUsages |
| if len(keyUsages) == 0 { |
| keyUsages = []ExtKeyUsage{ExtKeyUsageServerAuth} |
| } |
| |
| // If any key usage is acceptable then we're done. |
| for _, usage := range keyUsages { |
| if usage == ExtKeyUsageAny { |
| chains = candidateChains |
| return |
| } |
| } |
| |
| for _, candidate := range candidateChains { |
| if checkChainForKeyUsage(candidate, keyUsages) { |
| chains = append(chains, candidate) |
| } |
| } |
| |
| if len(chains) == 0 { |
| err = CertificateInvalidError{c, IncompatibleUsage} |
| } |
| |
| return |
| } |
| |
| func appendToFreshChain(chain []*Certificate, cert *Certificate) []*Certificate { |
| n := make([]*Certificate, len(chain)+1) |
| copy(n, chain) |
| n[len(chain)] = cert |
| return n |
| } |
| |
| func (c *Certificate) buildChains(cache map[int][][]*Certificate, currentChain []*Certificate, opts *VerifyOptions) (chains [][]*Certificate, err error) { |
| possibleRoots, failedRoot, rootErr := opts.Roots.findVerifiedParents(c) |
| nextRoot: |
| for _, rootNum := range possibleRoots { |
| root := opts.Roots.certs[rootNum] |
| |
| for _, cert := range currentChain { |
| if cert.Equal(root) { |
| continue nextRoot |
| } |
| } |
| |
| err = root.isValid(rootCertificate, currentChain, opts) |
| if err != nil { |
| continue |
| } |
| chains = append(chains, appendToFreshChain(currentChain, root)) |
| } |
| |
| possibleIntermediates, failedIntermediate, intermediateErr := opts.Intermediates.findVerifiedParents(c) |
| nextIntermediate: |
| for _, intermediateNum := range possibleIntermediates { |
| intermediate := opts.Intermediates.certs[intermediateNum] |
| for _, cert := range currentChain { |
| if cert.Equal(intermediate) { |
| continue nextIntermediate |
| } |
| } |
| err = intermediate.isValid(intermediateCertificate, currentChain, opts) |
| if err != nil { |
| continue |
| } |
| var childChains [][]*Certificate |
| childChains, ok := cache[intermediateNum] |
| if !ok { |
| childChains, err = intermediate.buildChains(cache, appendToFreshChain(currentChain, intermediate), opts) |
| cache[intermediateNum] = childChains |
| } |
| chains = append(chains, childChains...) |
| } |
| |
| if len(chains) > 0 { |
| err = nil |
| } |
| |
| if len(chains) == 0 && err == nil { |
| hintErr := rootErr |
| hintCert := failedRoot |
| if hintErr == nil { |
| hintErr = intermediateErr |
| hintCert = failedIntermediate |
| } |
| err = UnknownAuthorityError{c, hintErr, hintCert} |
| } |
| |
| return |
| } |
| |
| func matchHostnames(pattern, host string) bool { |
| host = strings.TrimSuffix(host, ".") |
| pattern = strings.TrimSuffix(pattern, ".") |
| |
| if len(pattern) == 0 || len(host) == 0 { |
| return false |
| } |
| |
| patternParts := strings.Split(pattern, ".") |
| hostParts := strings.Split(host, ".") |
| |
| if len(patternParts) != len(hostParts) { |
| return false |
| } |
| |
| for i, patternPart := range patternParts { |
| if i == 0 && patternPart == "*" { |
| continue |
| } |
| if patternPart != hostParts[i] { |
| return false |
| } |
| } |
| |
| return true |
| } |
| |
| // toLowerCaseASCII returns a lower-case version of in. See RFC 6125 6.4.1. We use |
| // an explicitly ASCII function to avoid any sharp corners resulting from |
| // performing Unicode operations on DNS labels. |
| func toLowerCaseASCII(in string) string { |
| // If the string is already lower-case then there's nothing to do. |
| isAlreadyLowerCase := true |
| for _, c := range in { |
| if c == utf8.RuneError { |
| // If we get a UTF-8 error then there might be |
| // upper-case ASCII bytes in the invalid sequence. |
| isAlreadyLowerCase = false |
| break |
| } |
| if 'A' <= c && c <= 'Z' { |
| isAlreadyLowerCase = false |
| break |
| } |
| } |
| |
| if isAlreadyLowerCase { |
| return in |
| } |
| |
| out := []byte(in) |
| for i, c := range out { |
| if 'A' <= c && c <= 'Z' { |
| out[i] += 'a' - 'A' |
| } |
| } |
| return string(out) |
| } |
| |
| // VerifyHostname returns nil if c is a valid certificate for the named host. |
| // Otherwise it returns an error describing the mismatch. |
| func (c *Certificate) VerifyHostname(h string) error { |
| // IP addresses may be written in [ ]. |
| candidateIP := h |
| if len(h) >= 3 && h[0] == '[' && h[len(h)-1] == ']' { |
| candidateIP = h[1 : len(h)-1] |
| } |
| if ip := net.ParseIP(candidateIP); ip != nil { |
| // We only match IP addresses against IP SANs. |
| // https://tools.ietf.org/html/rfc6125#appendix-B.2 |
| for _, candidate := range c.IPAddresses { |
| if ip.Equal(candidate) { |
| return nil |
| } |
| } |
| return HostnameError{c, candidateIP} |
| } |
| |
| lowered := toLowerCaseASCII(h) |
| |
| if c.hasSANExtension() { |
| for _, match := range c.DNSNames { |
| if matchHostnames(toLowerCaseASCII(match), lowered) { |
| return nil |
| } |
| } |
| // If Subject Alt Name is given, we ignore the common name. |
| } else if matchHostnames(toLowerCaseASCII(c.Subject.CommonName), lowered) { |
| return nil |
| } |
| |
| return HostnameError{c, h} |
| } |
| |
| func checkChainForKeyUsage(chain []*Certificate, keyUsages []ExtKeyUsage) bool { |
| usages := make([]ExtKeyUsage, len(keyUsages)) |
| copy(usages, keyUsages) |
| |
| if len(chain) == 0 { |
| return false |
| } |
| |
| usagesRemaining := len(usages) |
| |
| // We walk down the list and cross out any usages that aren't supported |
| // by each certificate. If we cross out all the usages, then the chain |
| // is unacceptable. |
| |
| NextCert: |
| for i := len(chain) - 1; i >= 0; i-- { |
| cert := chain[i] |
| if len(cert.ExtKeyUsage) == 0 && len(cert.UnknownExtKeyUsage) == 0 { |
| // The certificate doesn't have any extended key usage specified. |
| continue |
| } |
| |
| for _, usage := range cert.ExtKeyUsage { |
| if usage == ExtKeyUsageAny { |
| // The certificate is explicitly good for any usage. |
| continue NextCert |
| } |
| } |
| |
| const invalidUsage ExtKeyUsage = -1 |
| |
| NextRequestedUsage: |
| for i, requestedUsage := range usages { |
| if requestedUsage == invalidUsage { |
| continue |
| } |
| |
| for _, usage := range cert.ExtKeyUsage { |
| if requestedUsage == usage { |
| continue NextRequestedUsage |
| } else if requestedUsage == ExtKeyUsageServerAuth && |
| (usage == ExtKeyUsageNetscapeServerGatedCrypto || |
| usage == ExtKeyUsageMicrosoftServerGatedCrypto) { |
| // In order to support COMODO |
| // certificate chains, we have to |
| // accept Netscape or Microsoft SGC |
| // usages as equal to ServerAuth. |
| continue NextRequestedUsage |
| } |
| } |
| |
| usages[i] = invalidUsage |
| usagesRemaining-- |
| if usagesRemaining == 0 { |
| return false |
| } |
| } |
| } |
| |
| return true |
| } |