// upgradeCACertificate upgrades a certificate to a self-signing CA certificate if the CN matches. // Issue #108: Allow generated AWS API Gateway certs to be used for client cert authentication func upgradeCACertificate(cert *x509.Certificate, caUpgradeCN string) { if caUpgradeCN != "" && caUpgradeCN == cert.Issuer.CommonName { cert.BasicConstraintsValid = true cert.IsCA = true cert.KeyUsage = x509.KeyUsageCertSign log.Printf("[INFO] cert: Upgrading cert %s to CA cert", cert.Issuer.CommonName) } }
func TestCheckCert(t *testing.T) { testKey, _ := rsa.GenerateKey(rand.Reader, 1024) checker := newChecker(nil) fc := clock.NewFake() fc.Add(time.Hour * 24 * 90) checker.clock = fc issued := checker.clock.Now().Add(-time.Hour * 24 * 45) goodExpiry := issued.Add(checkPeriod) serial := big.NewInt(1337) // Problems // Blacklsited common name // Expiry period is too long // Basic Constraints aren't set // Wrong key usage (none) rawCert := x509.Certificate{ Subject: pkix.Name{ CommonName: "example.com", }, NotAfter: goodExpiry.AddDate(0, 0, 1), // Period too long DNSNames: []string{"example-a.com"}, SerialNumber: serial, BasicConstraintsValid: false, } brokenCertDer, err := x509.CreateCertificate(rand.Reader, &rawCert, &rawCert, &testKey.PublicKey, testKey) test.AssertNotError(t, err, "Couldn't create certificate") // Problems // Digest doesn't match // Serial doesn't match // Expiry doesn't match cert := core.Certificate{ Status: core.StatusValid, DER: brokenCertDer, Issued: issued, Expires: goodExpiry.AddDate(0, 0, 2), // Expiration doesn't match } problems := checker.checkCert(cert) test.AssertEquals(t, len(problems), 7) // Fix the problems rawCert.Subject.CommonName = "example-a.com" rawCert.NotAfter = goodExpiry rawCert.BasicConstraintsValid = true rawCert.ExtKeyUsage = []x509.ExtKeyUsage{x509.ExtKeyUsageServerAuth, x509.ExtKeyUsageClientAuth} goodCertDer, err := x509.CreateCertificate(rand.Reader, &rawCert, &rawCert, &testKey.PublicKey, testKey) test.AssertNotError(t, err, "Couldn't create certificate") parsed, err := x509.ParseCertificate(goodCertDer) test.AssertNotError(t, err, "Couldn't parse created certificate") cert.Serial = core.SerialToString(serial) cert.Digest = core.Fingerprint256(goodCertDer) cert.DER = goodCertDer cert.Expires = parsed.NotAfter problems = checker.checkCert(cert) test.AssertEquals(t, len(problems), 0) }
// FillTemplate is a utility function that tries to load as much of // the certificate template as possible from the profiles and current // template. It fills in the key uses, expiration, revocation URLs // and SKI. func FillTemplate(template *x509.Certificate, defaultProfile, profile *config.SigningProfile) error { ski, err := ComputeSKI(template) var ( eku []x509.ExtKeyUsage ku x509.KeyUsage backdate time.Duration expiry time.Duration notBefore time.Time notAfter time.Time crlURL, ocspURL string ) // The third value returned from Usages is a list of unknown key usages. // This should be used when validating the profile at load, and isn't used // here. ku, eku, _ = profile.Usages() if profile.IssuerURL == nil { profile.IssuerURL = defaultProfile.IssuerURL } if ku == 0 && len(eku) == 0 { return cferr.New(cferr.PolicyError, cferr.NoKeyUsages) } if expiry = profile.Expiry; expiry == 0 { expiry = defaultProfile.Expiry } if crlURL = profile.CRL; crlURL == "" { crlURL = defaultProfile.CRL } if ocspURL = profile.OCSP; ocspURL == "" { ocspURL = defaultProfile.OCSP } if backdate = profile.Backdate; backdate == 0 { backdate = -5 * time.Minute } else { backdate = -1 * profile.Backdate } if !profile.NotBefore.IsZero() { notBefore = profile.NotBefore.UTC() } else { notBefore = time.Now().Round(time.Minute).Add(backdate).UTC() } if !profile.NotAfter.IsZero() { notAfter = profile.NotAfter.UTC() } else { notAfter = notBefore.Add(expiry).UTC() } template.NotBefore = notBefore template.NotAfter = notAfter template.KeyUsage = ku template.ExtKeyUsage = eku template.BasicConstraintsValid = true template.IsCA = profile.CA template.SubjectKeyId = ski if ocspURL != "" { template.OCSPServer = []string{ocspURL} } if crlURL != "" { template.CRLDistributionPoints = []string{crlURL} } if len(profile.IssuerURL) != 0 { template.IssuingCertificateURL = profile.IssuerURL } if len(profile.Policies) != 0 { err = addPolicies(template, profile.Policies) if err != nil { return cferr.Wrap(cferr.PolicyError, cferr.InvalidPolicy, err) } } if profile.OCSPNoCheck { ocspNoCheckExtension := pkix.Extension{ Id: asn1.ObjectIdentifier{1, 3, 6, 1, 5, 5, 7, 48, 1, 5}, Critical: false, Value: []byte{0x05, 0x00}, } template.ExtraExtensions = append(template.ExtraExtensions, ocspNoCheckExtension) } return nil }
func (s *Signer) sign(template *x509.Certificate, profile *config.SigningProfile) (cert []byte, err error) { pub := template.PublicKey encodedpub, err := x509.MarshalPKIXPublicKey(pub) if err != nil { return } pubhash := sha1.New() pubhash.Write(encodedpub) if profile == nil { profile = s.Policy.Default } var ( eku []x509.ExtKeyUsage ku x509.KeyUsage expiry time.Duration crlURL, ocspURL string ) // The third value returned from Usages is a list of unknown key usages. // This should be used when validating the profile at load, and isn't used // here. ku, eku, _ = profile.Usages() expiry = profile.Expiry if profile.IssuerURL == nil { profile.IssuerURL = s.Policy.Default.IssuerURL } if ku == 0 && len(eku) == 0 { err = cferr.New(cferr.PolicyError, cferr.NoKeyUsages, errors.New("no key usage available")) return } if expiry == 0 { expiry = s.Policy.Default.Expiry } if crlURL = profile.CRL; crlURL == "" { crlURL = s.Policy.Default.CRL } if ocspURL = profile.OCSP; ocspURL == "" { ocspURL = s.Policy.Default.OCSP } now := time.Now() serialNumber, err := rand.Int(rand.Reader, new(big.Int).SetInt64(math.MaxInt64)) if err != nil { err = cferr.New(cferr.CertificateError, cferr.Unknown, err) } template.SerialNumber = serialNumber template.NotBefore = now.Add(-5 * time.Minute).UTC() template.NotAfter = now.Add(expiry).UTC() template.KeyUsage = ku template.ExtKeyUsage = eku template.BasicConstraintsValid = true template.IsCA = profile.CA template.SubjectKeyId = pubhash.Sum(nil) if ocspURL != "" { template.OCSPServer = []string{ocspURL} } if crlURL != "" { template.CRLDistributionPoints = []string{crlURL} } if len(profile.IssuerURL) != 0 { template.IssuingCertificateURL = profile.IssuerURL } var initRoot bool if s.CA == nil { if !template.IsCA { err = cferr.New(cferr.PolicyError, cferr.InvalidRequest, nil) return } template.DNSNames = nil s.CA = template initRoot = true template.MaxPathLen = 2 } else if template.IsCA { template.MaxPathLen = 1 template.DNSNames = nil } derBytes, err := x509.CreateCertificate(rand.Reader, template, s.CA, pub, s.Priv) if err != nil { return } if initRoot { s.CA, err = x509.ParseCertificate(derBytes) if err != nil { err = cferr.New(cferr.CertificateError, cferr.ParseFailed, err) return } } cert = pem.EncodeToMemory(&pem.Block{Type: "CERTIFICATE", Bytes: derBytes}) return }
func GenerateCertifcate(pkiroot, name string, template *x509.Certificate) error { // TODO(jclerc): check that pki has been init var crtPath string privateKeyPath := filepath.Join(pkiroot, "private", name+".key") if name == "ca" { crtPath = filepath.Join(pkiroot, name+".crt") } else { crtPath = filepath.Join(pkiroot, "issued", name+".crt") } var caCrt *x509.Certificate var caKey *rsa.PrivateKey if _, err := os.Stat(privateKeyPath); err == nil { return fmt.Errorf("a key pair for %v already exists", name) } privateKey, err := GeneratePrivateKey(privateKeyPath) if err != nil { return fmt.Errorf("generate private key: %v", err) } publicKeyBytes, err := asn1.Marshal(*privateKey.Public().(*rsa.PublicKey)) if err != nil { return fmt.Errorf("marshal public key: %v", err) } subjectKeyId := sha1.Sum(publicKeyBytes) template.SubjectKeyId = subjectKeyId[:] template.NotBefore = time.Now() template.SignatureAlgorithm = x509.SHA256WithRSA if template.IsCA { serialNumberLimit := new(big.Int).Lsh(big.NewInt(1), 128) serialNumber, err := rand.Int(rand.Reader, serialNumberLimit) if err != nil { return fmt.Errorf("failed to generate ca serial number: %s", err) } template.SerialNumber = serialNumber template.KeyUsage = x509.KeyUsageCertSign | x509.KeyUsageCRLSign template.BasicConstraintsValid = true template.Issuer = template.Subject template.AuthorityKeyId = template.SubjectKeyId caCrt = template caKey = privateKey } else { template.KeyUsage = x509.KeyUsageDigitalSignature | x509.KeyUsageKeyEncipherment serialNumber, err := NextNumber(pkiroot, "serial") if err != nil { return fmt.Errorf("get next serial: %v", err) } template.SerialNumber = serialNumber caCrt, caKey, err = GetCA(pkiroot) if err != nil { return fmt.Errorf("get ca: %v", err) } } crt, err := x509.CreateCertificate(rand.Reader, template, caCrt, privateKey.Public(), caKey) if err != nil { return fmt.Errorf("create certificate: %v", err) } crtFile, err := os.Create(crtPath) if err != nil { return fmt.Errorf("create %v: %v", crtPath, err) } defer crtFile.Close() err = pem.Encode(crtFile, &pem.Block{ Type: "CERTIFICATE", Bytes: crt, }) if err != nil { return fmt.Errorf("pem encode crt: %v", err) } // I do not think we have to write the ca.crt in the index if !template.IsCA { WriteIndex(pkiroot, name, template) if err != nil { return fmt.Errorf("write index: %v", err) } } return nil }
func TestCheckCert(t *testing.T) { saDbMap, err := sa.NewDbMap(vars.DBConnSA, 0) test.AssertNotError(t, err, "Couldn't connect to database") saCleanup := test.ResetSATestDatabase(t) defer func() { saCleanup() }() testKey, _ := rsa.GenerateKey(rand.Reader, 1024) fc := clock.NewFake() fc.Add(time.Hour * 24 * 90) checker := newChecker(saDbMap, fc, pa, expectedValidityPeriod) issued := checker.clock.Now().Add(-time.Hour * 24 * 45) goodExpiry := issued.Add(expectedValidityPeriod) serial := big.NewInt(1337) // Problems // Expiry period is too long // Basic Constraints aren't set // Wrong key usage (none) rawCert := x509.Certificate{ Subject: pkix.Name{ CommonName: "eeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeexample.com", }, NotBefore: issued, NotAfter: goodExpiry.AddDate(0, 0, 1), // Period too long DNSNames: []string{"example-a.com"}, SerialNumber: serial, BasicConstraintsValid: false, } brokenCertDer, err := x509.CreateCertificate(rand.Reader, &rawCert, &rawCert, &testKey.PublicKey, testKey) test.AssertNotError(t, err, "Couldn't create certificate") // Problems // Digest doesn't match // Serial doesn't match // Expiry doesn't match // Issued doesn't match cert := core.Certificate{ Serial: "8485f2687eba29ad455ae4e31c8679206fec", DER: brokenCertDer, Issued: issued.Add(12 * time.Hour), Expires: goodExpiry.AddDate(0, 0, 2), // Expiration doesn't match } problems := checker.checkCert(cert) problemsMap := map[string]int{ "Stored digest doesn't match certificate digest": 1, "Stored serial doesn't match certificate serial": 1, "Stored expiration doesn't match certificate NotAfter": 1, "Certificate doesn't have basic constraints set": 1, "Certificate has a validity period longer than 2160h0m0s": 1, "Stored issuance date is outside of 6 hour window of certificate NotBefore": 1, "Certificate has incorrect key usage extensions": 1, "Certificate has common name >64 characters long (65)": 1, } for _, p := range problems { _, ok := problemsMap[p] if !ok { t.Errorf("Found unexpected problem '%s'.", p) } delete(problemsMap, p) } for k := range problemsMap { t.Errorf("Expected problem but didn't find it: '%s'.", k) } test.AssertEquals(t, len(problems), 8) // Same settings as above, but the stored serial number in the DB is invalid. cert.Serial = "not valid" problems = checker.checkCert(cert) foundInvalidSerialProblem := false for _, p := range problems { if p == "Stored serial is invalid" { foundInvalidSerialProblem = true } } test.Assert(t, foundInvalidSerialProblem, "Invalid certificate serial number in DB did not trigger problem.") // Fix the problems rawCert.Subject.CommonName = "example-a.com" rawCert.NotAfter = goodExpiry rawCert.BasicConstraintsValid = true rawCert.ExtKeyUsage = []x509.ExtKeyUsage{x509.ExtKeyUsageServerAuth, x509.ExtKeyUsageClientAuth} goodCertDer, err := x509.CreateCertificate(rand.Reader, &rawCert, &rawCert, &testKey.PublicKey, testKey) test.AssertNotError(t, err, "Couldn't create certificate") parsed, err := x509.ParseCertificate(goodCertDer) test.AssertNotError(t, err, "Couldn't parse created certificate") cert.Serial = core.SerialToString(serial) cert.Digest = core.Fingerprint256(goodCertDer) cert.DER = goodCertDer cert.Expires = parsed.NotAfter cert.Issued = parsed.NotBefore problems = checker.checkCert(cert) test.AssertEquals(t, len(problems), 0) }
func TestCheckCert(t *testing.T) { saDbMap, err := sa.NewDbMap(saDbConnStr) test.AssertNotError(t, err, "Couldn't connect to database") saCleanup := test.ResetTestDatabase(t, saDbMap.Db) paDbMap, err := sa.NewDbMap(paDbConnStr) test.AssertNotError(t, err, "Couldn't connect to policy database") paCleanup := test.ResetTestDatabase(t, paDbMap.Db) defer func() { saCleanup() paCleanup() }() testKey, _ := rsa.GenerateKey(rand.Reader, 1024) fc := clock.NewFake() fc.Add(time.Hour * 24 * 90) checker := newChecker(saDbMap, paDbMap, fc, false) issued := checker.clock.Now().Add(-time.Hour * 24 * 45) goodExpiry := issued.Add(checkPeriod) serial := big.NewInt(1337) // Problems // Expiry period is too long // Basic Constraints aren't set // Wrong key usage (none) rawCert := x509.Certificate{ Subject: pkix.Name{ CommonName: "example.com", }, NotBefore: issued, NotAfter: goodExpiry.AddDate(0, 0, 1), // Period too long DNSNames: []string{"example-a.com"}, SerialNumber: serial, BasicConstraintsValid: false, } brokenCertDer, err := x509.CreateCertificate(rand.Reader, &rawCert, &rawCert, &testKey.PublicKey, testKey) test.AssertNotError(t, err, "Couldn't create certificate") // Problems // Digest doesn't match // Serial doesn't match // Expiry doesn't match // Issued doesn't match cert := core.Certificate{ DER: brokenCertDer, Issued: issued.Add(12 * time.Hour), Expires: goodExpiry.AddDate(0, 0, 2), // Expiration doesn't match } problems := checker.checkCert(cert) fmt.Println(strings.Join(problems, "\n")) test.AssertEquals(t, len(problems), 7) // Fix the problems rawCert.Subject.CommonName = "example-a.com" rawCert.NotAfter = goodExpiry rawCert.BasicConstraintsValid = true rawCert.ExtKeyUsage = []x509.ExtKeyUsage{x509.ExtKeyUsageServerAuth, x509.ExtKeyUsageClientAuth} goodCertDer, err := x509.CreateCertificate(rand.Reader, &rawCert, &rawCert, &testKey.PublicKey, testKey) test.AssertNotError(t, err, "Couldn't create certificate") parsed, err := x509.ParseCertificate(goodCertDer) test.AssertNotError(t, err, "Couldn't parse created certificate") cert.Serial = core.SerialToString(serial) cert.Digest = core.Fingerprint256(goodCertDer) cert.DER = goodCertDer cert.Expires = parsed.NotAfter cert.Issued = parsed.NotBefore problems = checker.checkCert(cert) test.AssertEquals(t, len(problems), 0) }