Exemple #1
0
// addKeyUsages adds approrpiate key usages to the template given the creation
// information
func addKeyUsages(creationInfo *creationBundle, certTemplate *x509.Certificate) {
	certTemplate.KeyUsage = x509.KeyUsage(x509.KeyUsageDigitalSignature | x509.KeyUsageKeyEncipherment | x509.KeyUsageKeyAgreement)

	if creationInfo.Usage&serverUsage != 0 {
		certTemplate.ExtKeyUsage = append(certTemplate.ExtKeyUsage, x509.ExtKeyUsageServerAuth)
	}
	if creationInfo.Usage&clientUsage != 0 {
		certTemplate.ExtKeyUsage = append(certTemplate.ExtKeyUsage, x509.ExtKeyUsageClientAuth)
	}
	if creationInfo.Usage&codeSigningUsage != 0 {
		certTemplate.ExtKeyUsage = append(certTemplate.ExtKeyUsage, x509.ExtKeyUsageCodeSigning)
	}
	if creationInfo.Usage&emailProtectionUsage != 0 {
		certTemplate.ExtKeyUsage = append(certTemplate.ExtKeyUsage, x509.ExtKeyUsageEmailProtection)
	}

	if creationInfo.IsCA {
		// Go performs validation not according to spec but according to the Windows
		// Crypto API, so we add all usages to CA certs
		certTemplate.KeyUsage = x509.KeyUsage(certTemplate.KeyUsage | x509.KeyUsageCertSign | x509.KeyUsageCRLSign)
		certTemplate.ExtKeyUsage = []x509.ExtKeyUsage{
			x509.ExtKeyUsageAny,
			x509.ExtKeyUsageServerAuth,
			x509.ExtKeyUsageClientAuth,
			x509.ExtKeyUsageCodeSigning,
			x509.ExtKeyUsageEmailProtection,
			x509.ExtKeyUsageTimeStamping,
			x509.ExtKeyUsageOCSPSigning,
		}
	}
}
Exemple #2
0
// keyUsage decodes/prints key usage from a certificate.
func keyUsage(sKu simpleKeyUsage) []string {
	ku := x509.KeyUsage(sKu)
	out := []string{}
	for _, key := range keyUsages {
		if ku&key > 0 {
			out = append(out, keyUsageStrings[key])
		}
	}
	return out
}
Exemple #3
0
// addKeyUsages adds approrpiate key usages to the template given the creation
// information
func addKeyUsages(creationInfo *creationBundle, certTemplate *x509.Certificate) {
	if creationInfo.IsCA {
		certTemplate.KeyUsage = x509.KeyUsage(x509.KeyUsageCertSign | x509.KeyUsageCRLSign)
		return
	}

	if creationInfo.Usage&serverUsage != 0 {
		certTemplate.ExtKeyUsage = append(certTemplate.ExtKeyUsage, x509.ExtKeyUsageServerAuth)
	}
	if creationInfo.Usage&clientUsage != 0 {
		certTemplate.ExtKeyUsage = append(certTemplate.ExtKeyUsage, x509.ExtKeyUsageClientAuth)
	}
	if creationInfo.Usage&codeSigningUsage != 0 {
		certTemplate.ExtKeyUsage = append(certTemplate.ExtKeyUsage, x509.ExtKeyUsageCodeSigning)
	}
	if creationInfo.Usage&emailProtectionUsage != 0 {
		certTemplate.ExtKeyUsage = append(certTemplate.ExtKeyUsage, x509.ExtKeyUsageEmailProtection)
	}
}
Exemple #4
0
func TestUpgradeCACertificate(t *testing.T) {
	// generated at
	// https://eu-west-1.console.aws.amazon.com/apigateway/home?region=eu-west-1#/client-certificates
	const awsApiGWCert = `
-----BEGIN CERTIFICATE-----
MIIC6DCCAdCgAwIBAgIIZAgycYqDRqQwDQYJKoZIhvcNAQELBQAwNDELMAkGA1UE
BhMCVVMxEDAOBgNVBAcTB1NlYXR0bGUxEzARBgNVBAMTCkFwaUdhdGV3YXkwHhcN
MTYwNzEyMTkzMTMwWhcNMTcwNzEyMTkzMTMwWjA0MQswCQYDVQQGEwJVUzEQMA4G
A1UEBxMHU2VhdHRsZTETMBEGA1UEAxMKQXBpR2F0ZXdheTCCASIwDQYJKoZIhvcN
AQEBBQADggEPADCCAQoCggEBAM/a0LKQd/obIwcKu09EjlHP4b7QqmK/JnJfd1Eq
m6We85FGu+26s7+Bpw1xiyK2jFuzQ4JFyXVkWLJH8e3Mp7P91MvJ1x6UCRk+Fz6Q
Lauw5SBVmDO5CauB4NcICTYEeTT3c0m8t6sDpHf+DHZC87gq9rhBggKXfNO3ntWw
Kq2uGscvnOz2/n2XIucFf2U7GI/cOapGXvIyrB5e/swSCyNkgOJ2HekzWjprxSs5
zu9JSOIzejgm8+/nnPOO9ycVrjN3qazUEXfF1QdvZeNCZ9GL6ZICAYo9xnnNLJnW
6p5d0Fw6U+V/nlNpgCB5djTwXaY51ScoW/i3ukHBZe9QIEcCAwEAATANBgkqhkiG
9w0BAQsFAAOCAQEAzwUJlSv/9XVoeCbot+3mdviZI5B7VnEKGl2Oam1fQzGZkkzB
kqBgtRrHux3BRxPRqS4jM4akdplFhejHExVatOxfS+DEXzFefi+aMb7qApB1YjV/
5FIIQdZaVOlw2KIRXCy04nxrKJmJ1T5RCkYC80dYpNfmDb5REUtp8jU78/Schsx7
0nCsrWkBSO1QtR4NnBlHbEM+imh3aCQz23SUK5Q/NTe4r2pu0zUl5b2YNgefvWle
7fe6T137rmhji9K+tYNznLGk0XmiguQPM2qJLxqeVQsA32wUbbSIFWH+KsXRPfpU
n/iFVG4Y6zyXQY2RzTt+ZB2VPR72X4wqS9fBeQ==
-----END CERTIFICATE-----`

	p, rest := pem.Decode([]byte(awsApiGWCert))
	if len(rest) > 0 {
		t.Fatal("want only one cert")
	}
	cert, err := x509.ParseCertificate(p.Bytes)
	if err != nil {
		t.Fatal(err)
	}

	// check that cert does not have the flags set
	if got, want := cert.BasicConstraintsValid, false; got != want {
		t.Fatalf("got %v want %v", got, want)
	}
	if got, want := cert.IsCA, false; got != want {
		t.Fatalf("got %v want %v", got, want)
	}
	if got, want := cert.KeyUsage, x509.KeyUsage(0); got != want {
		t.Fatalf("got %v want %v", got, want)
	}

	// run upgrade with not-matching CN expecting no change
	upgradeCACertificate(cert, "no match")
	if got, want := cert.BasicConstraintsValid, false; got != want {
		t.Fatalf("got %v want %v", got, want)
	}
	if got, want := cert.IsCA, false; got != want {
		t.Fatalf("got %v want %v", got, want)
	}
	if got, want := cert.KeyUsage, x509.KeyUsage(0); got != want {
		t.Fatalf("got %v want %v", got, want)
	}

	// run upgrade with matching CN
	upgradeCACertificate(cert, "ApiGateway")
	if got, want := cert.BasicConstraintsValid, true; got != want {
		t.Fatalf("got %v want %v", got, want)
	}
	if got, want := cert.IsCA, true; got != want {
		t.Fatalf("got %v want %v", got, want)
	}
	if got, want := cert.KeyUsage, x509.KeyUsageCertSign; got != want {
		t.Fatalf("got %v want %v", got, want)
	}
}
Exemple #5
0
// Performs the heavy lifting of creating a certificate. Returns
// a fully-filled-in ParsedCertBundle.
func createCertificate(creationInfo *certCreationBundle) (*certutil.ParsedCertBundle, error) {
	var clientPrivKey crypto.Signer
	var err error
	result := &certutil.ParsedCertBundle{}

	var serialNumber *big.Int
	serialNumber, err = rand.Int(rand.Reader, (&big.Int{}).Exp(big.NewInt(2), big.NewInt(159), nil))
	if err != nil {
		return nil, certutil.InternalError{Err: fmt.Sprintf("Error getting random serial number")}
	}

	switch creationInfo.KeyType {
	case "rsa":
		result.PrivateKeyType = certutil.RSAPrivateKey
		clientPrivKey, err = rsa.GenerateKey(rand.Reader, creationInfo.KeyBits)
		if err != nil {
			return nil, certutil.InternalError{Err: fmt.Sprintf("Error generating RSA private key")}
		}
		result.PrivateKey = clientPrivKey
		result.PrivateKeyBytes = x509.MarshalPKCS1PrivateKey(clientPrivKey.(*rsa.PrivateKey))
	case "ec":
		result.PrivateKeyType = certutil.ECPrivateKey
		var curve elliptic.Curve
		switch creationInfo.KeyBits {
		case 224:
			curve = elliptic.P224()
		case 256:
			curve = elliptic.P256()
		case 384:
			curve = elliptic.P384()
		case 521:
			curve = elliptic.P521()
		default:
			return nil, certutil.UserError{Err: fmt.Sprintf("Unsupported bit length for EC key: %d", creationInfo.KeyBits)}
		}
		clientPrivKey, err = ecdsa.GenerateKey(curve, rand.Reader)
		if err != nil {
			return nil, certutil.InternalError{Err: fmt.Sprintf("Error generating EC private key")}
		}
		result.PrivateKey = clientPrivKey
		result.PrivateKeyBytes, err = x509.MarshalECPrivateKey(clientPrivKey.(*ecdsa.PrivateKey))
		if err != nil {
			return nil, certutil.InternalError{Err: fmt.Sprintf("Error marshalling EC private key")}
		}
	default:
		return nil, certutil.UserError{Err: fmt.Sprintf("Unknown key type: %s", creationInfo.KeyType)}
	}

	subjKeyID, err := certutil.GetSubjKeyID(result.PrivateKey)
	if err != nil {
		return nil, certutil.InternalError{Err: fmt.Sprintf("Error getting subject key ID: %s", err)}
	}

	subject := pkix.Name{
		Country:            creationInfo.CACert.Subject.Country,
		Organization:       creationInfo.CACert.Subject.Organization,
		OrganizationalUnit: creationInfo.CACert.Subject.OrganizationalUnit,
		Locality:           creationInfo.CACert.Subject.Locality,
		Province:           creationInfo.CACert.Subject.Province,
		StreetAddress:      creationInfo.CACert.Subject.StreetAddress,
		PostalCode:         creationInfo.CACert.Subject.PostalCode,
		SerialNumber:       serialNumber.String(),
		CommonName:         creationInfo.CommonNames[0],
	}

	certTemplate := &x509.Certificate{
		SignatureAlgorithm:    x509.SHA256WithRSA,
		SerialNumber:          serialNumber,
		Subject:               subject,
		NotBefore:             time.Now(),
		NotAfter:              time.Now().Add(creationInfo.Lease),
		KeyUsage:              x509.KeyUsage(x509.KeyUsageDigitalSignature | x509.KeyUsageKeyEncipherment | x509.KeyUsageKeyAgreement),
		BasicConstraintsValid: true,
		IsCA:                        false,
		SubjectKeyId:                subjKeyID,
		DNSNames:                    creationInfo.CommonNames,
		IPAddresses:                 creationInfo.IPSANs,
		PermittedDNSDomainsCritical: false,
		PermittedDNSDomains:         nil,
		CRLDistributionPoints:       creationInfo.CACert.CRLDistributionPoints,
	}

	if creationInfo.Usage&serverUsage != 0 {
		certTemplate.ExtKeyUsage = append(certTemplate.ExtKeyUsage, x509.ExtKeyUsageServerAuth)
	}
	if creationInfo.Usage&clientUsage != 0 {
		certTemplate.ExtKeyUsage = append(certTemplate.ExtKeyUsage, x509.ExtKeyUsageClientAuth)
	}
	if creationInfo.Usage&codeSigningUsage != 0 {
		certTemplate.ExtKeyUsage = append(certTemplate.ExtKeyUsage, x509.ExtKeyUsageCodeSigning)
	}

	cert, err := x509.CreateCertificate(rand.Reader, certTemplate, creationInfo.CACert, clientPrivKey.Public(), creationInfo.SigningBundle.PrivateKey)
	if err != nil {
		return nil, certutil.InternalError{Err: fmt.Sprintf("Unable to create certificate: %s", err)}
	}

	result.CertificateBytes = cert
	result.Certificate, err = x509.ParseCertificate(cert)
	if err != nil {
		return nil, certutil.InternalError{Err: fmt.Sprintf("Unable to parse created certificate: %s", err)}
	}

	result.IssuingCABytes = creationInfo.SigningBundle.CertificateBytes
	result.IssuingCA = creationInfo.SigningBundle.Certificate

	return result, nil
}
Exemple #6
0
// generateCreationBundle is a shared function that reads parameters supplied
// from the various endpoints and generates a creationBundle with the
// parameters that can be used to issue or sign
func generateCreationBundle(b *backend,
	role *roleEntry,
	signingBundle *caInfoBundle,
	csr *x509.CertificateRequest,
	req *logical.Request,
	data *framework.FieldData) (*creationBundle, error) {
	var err error
	var ok bool

	// Get the common name
	var cn string
	{
		if csr != nil {
			if role.UseCSRCommonName {
				cn = csr.Subject.CommonName
			}
		}
		if cn == "" {
			cn = data.Get("common_name").(string)
			if cn == "" {
				return nil, errutil.UserError{Err: `the common_name field is required, or must be provided in a CSR with "use_csr_common_name" set to true`}
			}
		}
	}

	// Read in alternate names -- DNS and email addresses
	dnsNames := []string{}
	emailAddresses := []string{}
	{
		if !data.Get("exclude_cn_from_sans").(bool) {
			if strings.Contains(cn, "@") {
				// Note: emails are not disallowed if the role's email protection
				// flag is false, because they may well be included for
				// informational purposes; it is up to the verifying party to
				// ensure that email addresses in a subject alternate name can be
				// used for the purpose for which they are presented
				emailAddresses = append(emailAddresses, cn)
			} else {
				dnsNames = append(dnsNames, cn)
			}
		}
		cnAltInt, ok := data.GetOk("alt_names")
		if ok {
			cnAlt := cnAltInt.(string)
			if len(cnAlt) != 0 {
				for _, v := range strings.Split(cnAlt, ",") {
					if strings.Contains(v, "@") {
						emailAddresses = append(emailAddresses, v)
					} else {
						dnsNames = append(dnsNames, v)
					}
				}
			}
		}

		// Check for bad email and/or DNS names
		badName, err := validateNames(req, dnsNames, role)
		if len(badName) != 0 {
			return nil, errutil.UserError{Err: fmt.Sprintf(
				"name %s not allowed by this role", badName)}
		} else if err != nil {
			return nil, errutil.InternalError{Err: fmt.Sprintf(
				"error validating name %s: %s", badName, err)}
		}

		badName, err = validateNames(req, emailAddresses, role)
		if len(badName) != 0 {
			return nil, errutil.UserError{Err: fmt.Sprintf(
				"email %s not allowed by this role", badName)}
		} else if err != nil {
			return nil, errutil.InternalError{Err: fmt.Sprintf(
				"error validating name %s: %s", badName, err)}
		}
	}

	// Get and verify any IP SANs
	ipAddresses := []net.IP{}
	var ipAltInt interface{}
	{
		ipAltInt, ok = data.GetOk("ip_sans")
		if ok {
			ipAlt := ipAltInt.(string)
			if len(ipAlt) != 0 {
				if !role.AllowIPSANs {
					return nil, errutil.UserError{Err: fmt.Sprintf(
						"IP Subject Alternative Names are not allowed in this role, but was provided %s", ipAlt)}
				}
				for _, v := range strings.Split(ipAlt, ",") {
					parsedIP := net.ParseIP(v)
					if parsedIP == nil {
						return nil, errutil.UserError{Err: fmt.Sprintf(
							"the value '%s' is not a valid IP address", v)}
					}
					ipAddresses = append(ipAddresses, parsedIP)
				}
			}
		}
	}

	// Get the TTL and very it against the max allowed
	var ttlField string
	var ttl time.Duration
	var maxTTL time.Duration
	var ttlFieldInt interface{}
	{
		ttlFieldInt, ok = data.GetOk("ttl")
		if !ok {
			ttlField = role.TTL
		} else {
			ttlField = ttlFieldInt.(string)
		}

		if len(ttlField) == 0 {
			ttl = b.System().DefaultLeaseTTL()
		} else {
			ttl, err = time.ParseDuration(ttlField)
			if err != nil {
				return nil, errutil.UserError{Err: fmt.Sprintf(
					"invalid requested ttl: %s", err)}
			}
		}

		if len(role.MaxTTL) == 0 {
			maxTTL = b.System().MaxLeaseTTL()
		} else {
			maxTTL, err = time.ParseDuration(role.MaxTTL)
			if err != nil {
				return nil, errutil.UserError{Err: fmt.Sprintf(
					"invalid ttl: %s", err)}
			}
		}

		if ttl > maxTTL {
			// Don't error if they were using system defaults, only error if
			// they specifically chose a bad TTL
			if len(ttlField) == 0 {
				ttl = maxTTL
			} else {
				return nil, errutil.UserError{Err: fmt.Sprintf(
					"ttl is larger than maximum allowed (%d)", maxTTL/time.Second)}
			}
		}

		// If it's not self-signed, verify that the issued certificate won't be
		// valid past the lifetime of the CA certificate
		if signingBundle != nil &&
			time.Now().Add(ttl).After(signingBundle.Certificate.NotAfter) {
			return nil, errutil.UserError{Err: fmt.Sprintf(
				"cannot satisfy request, as TTL is beyond the expiration of the CA certificate")}
		}
	}

	// Build up usages
	var extUsage certExtKeyUsage
	{
		if role.ServerFlag {
			extUsage = extUsage | serverExtKeyUsage
		}
		if role.ClientFlag {
			extUsage = extUsage | clientExtKeyUsage
		}
		if role.CodeSigningFlag {
			extUsage = extUsage | codeSigningExtKeyUsage
		}
		if role.EmailProtectionFlag {
			extUsage = extUsage | emailProtectionExtKeyUsage
		}
	}

	creationBundle := &creationBundle{
		CommonName:     cn,
		DNSNames:       dnsNames,
		EmailAddresses: emailAddresses,
		IPAddresses:    ipAddresses,
		KeyType:        role.KeyType,
		KeyBits:        role.KeyBits,
		SigningBundle:  signingBundle,
		TTL:            ttl,
		KeyUsage:       x509.KeyUsage(parseKeyUsages(role.KeyUsage)),
		ExtKeyUsage:    extUsage,
	}

	// Don't deal with URLs or max path length if it's self-signed, as these
	// normally come from the signing bundle
	if signingBundle == nil {
		return creationBundle, nil
	}

	// This will have been read in from the getURLs function
	creationBundle.URLs = signingBundle.URLs

	// If the max path length in the role is not nil, it was specified at
	// generation time with the max_path_length parameter; otherwise derive it
	// from the signing certificate
	if role.MaxPathLength != nil {
		creationBundle.MaxPathLength = *role.MaxPathLength
	} else {
		switch {
		case signingBundle.Certificate.MaxPathLen < 0:
			creationBundle.MaxPathLength = -1
		case signingBundle.Certificate.MaxPathLen == 0 &&
			signingBundle.Certificate.MaxPathLenZero:
			// The signing function will ensure that we do not issue a CA cert
			creationBundle.MaxPathLength = 0
		default:
			// If this takes it to zero, we handle this case later if
			// necessary
			creationBundle.MaxPathLength = signingBundle.Certificate.MaxPathLen - 1
		}
	}

	return creationBundle, nil
}
// Performs the heavy lifting of generating a certificate from a CSR.
// Returns a ParsedCertBundle sans private keys.
func signCertificate(creationInfo *creationBundle,
	csr *x509.CertificateRequest) (*certutil.ParsedCertBundle, error) {
	switch {
	case creationInfo == nil:
		return nil, certutil.UserError{Err: "nil creation info given to signCertificate"}
	case creationInfo.SigningBundle == nil:
		return nil, certutil.UserError{Err: "nil signing bundle given to signCertificate"}
	case csr == nil:
		return nil, certutil.UserError{Err: "nil csr given to signCertificate"}
	}

	err := csr.CheckSignature()
	if err != nil {
		return nil, certutil.UserError{Err: "request signature invalid"}
	}

	result := &certutil.ParsedCertBundle{}

	serialNumber, err := certutil.GenerateSerialNumber()
	if err != nil {
		return nil, err
	}

	marshaledKey, err := x509.MarshalPKIXPublicKey(csr.PublicKey)
	if err != nil {
		return nil, certutil.InternalError{Err: fmt.Sprintf("error marshalling public key: %s", err)}
	}
	subjKeyID := sha1.Sum(marshaledKey)

	subject := pkix.Name{
		CommonName: creationInfo.CommonName,
	}

	certTemplate := &x509.Certificate{
		SerialNumber: serialNumber,
		Subject:      subject,
		NotBefore:    time.Now(),
		NotAfter:     time.Now().Add(creationInfo.TTL),
		SubjectKeyId: subjKeyID[:],
	}

	switch creationInfo.SigningBundle.PrivateKeyType {
	case certutil.RSAPrivateKey:
		certTemplate.SignatureAlgorithm = x509.SHA256WithRSA
	case certutil.ECPrivateKey:
		certTemplate.SignatureAlgorithm = x509.ECDSAWithSHA256
	}

	if creationInfo.UseCSRValues {
		certTemplate.Subject = csr.Subject

		certTemplate.DNSNames = csr.DNSNames
		certTemplate.EmailAddresses = csr.EmailAddresses
		certTemplate.IPAddresses = csr.IPAddresses

		certTemplate.ExtraExtensions = csr.Extensions
		// Do not sign a CA certificate if they didn't go through the sign-intermediate
		// endpoint
		if !creationInfo.IsCA && oidInExtensions(oidExtensionBasicConstraints, certTemplate.ExtraExtensions) {
			return nil, certutil.UserError{Err: "will not sign a CSR asking for CA rights through this endpoint"}
		}

	} else {
		certTemplate.DNSNames = creationInfo.DNSNames
		certTemplate.EmailAddresses = creationInfo.EmailAddresses
		certTemplate.IPAddresses = creationInfo.IPAddresses

		certTemplate.KeyUsage = x509.KeyUsage(x509.KeyUsageDigitalSignature | x509.KeyUsageKeyEncipherment | x509.KeyUsageKeyAgreement)

		if creationInfo.Usage&serverUsage != 0 {
			certTemplate.ExtKeyUsage = append(certTemplate.ExtKeyUsage, x509.ExtKeyUsageServerAuth)
		}
		if creationInfo.Usage&clientUsage != 0 {
			certTemplate.ExtKeyUsage = append(certTemplate.ExtKeyUsage, x509.ExtKeyUsageClientAuth)
		}
		if creationInfo.Usage&codeSigningUsage != 0 {
			certTemplate.ExtKeyUsage = append(certTemplate.ExtKeyUsage, x509.ExtKeyUsageCodeSigning)
		}
		if creationInfo.Usage&emailProtectionUsage != 0 {
			certTemplate.ExtKeyUsage = append(certTemplate.ExtKeyUsage, x509.ExtKeyUsageEmailProtection)
		}

		if creationInfo.IsCA {
			certTemplate.KeyUsage = x509.KeyUsage(certTemplate.KeyUsage | x509.KeyUsageCertSign | x509.KeyUsageCRLSign)
			certTemplate.ExtKeyUsage = append(certTemplate.ExtKeyUsage, x509.ExtKeyUsageOCSPSigning)
		}
	}

	var certBytes []byte
	caCert := creationInfo.SigningBundle.Certificate

	certTemplate.IssuingCertificateURL = creationInfo.URLs.IssuingCertificates
	certTemplate.CRLDistributionPoints = creationInfo.URLs.CRLDistributionPoints
	certTemplate.OCSPServer = creationInfo.SigningBundle.URLs.OCSPServers

	if creationInfo.IsCA {
		certTemplate.BasicConstraintsValid = true
		certTemplate.IsCA = true

		if creationInfo.SigningBundle.Certificate.MaxPathLen == 0 &&
			creationInfo.SigningBundle.Certificate.MaxPathLenZero {
			return nil, certutil.UserError{Err: "signing certificate has a max path length of zero, and cannot issue further CA certificates"}
		}

		certTemplate.MaxPathLen = creationInfo.MaxPathLength
		if certTemplate.MaxPathLen == 0 {
			certTemplate.MaxPathLenZero = true
		}
	}

	certBytes, err = x509.CreateCertificate(rand.Reader, certTemplate, caCert, csr.PublicKey, creationInfo.SigningBundle.PrivateKey)

	if err != nil {
		return nil, certutil.InternalError{Err: fmt.Sprintf("unable to create certificate: %s", err)}
	}

	result.CertificateBytes = certBytes
	result.Certificate, err = x509.ParseCertificate(certBytes)
	if err != nil {
		return nil, certutil.InternalError{Err: fmt.Sprintf("unable to parse created certificate: %s", err)}
	}

	result.IssuingCABytes = creationInfo.SigningBundle.CertificateBytes
	result.IssuingCA = creationInfo.SigningBundle.Certificate

	return result, nil
}
// Performs the heavy lifting of creating a certificate. Returns
// a fully-filled-in ParsedCertBundle.
func createCertificate(creationInfo *creationBundle) (*certutil.ParsedCertBundle, error) {
	var err error
	result := &certutil.ParsedCertBundle{}

	serialNumber, err := certutil.GenerateSerialNumber()
	if err != nil {
		return nil, err
	}

	if err := certutil.GeneratePrivateKey(creationInfo.KeyType,
		creationInfo.KeyBits,
		result); err != nil {
		return nil, err
	}

	subjKeyID, err := certutil.GetSubjKeyID(result.PrivateKey)
	if err != nil {
		return nil, certutil.InternalError{Err: fmt.Sprintf("error getting subject key ID: %s", err)}
	}

	subject := pkix.Name{
		CommonName: creationInfo.CommonName,
	}

	certTemplate := &x509.Certificate{
		SerialNumber:   serialNumber,
		Subject:        subject,
		NotBefore:      time.Now(),
		NotAfter:       time.Now().Add(creationInfo.TTL),
		KeyUsage:       x509.KeyUsage(x509.KeyUsageDigitalSignature | x509.KeyUsageKeyEncipherment | x509.KeyUsageKeyAgreement),
		IsCA:           false,
		SubjectKeyId:   subjKeyID,
		DNSNames:       creationInfo.DNSNames,
		EmailAddresses: creationInfo.EmailAddresses,
		IPAddresses:    creationInfo.IPAddresses,
	}

	if creationInfo.Usage&serverUsage != 0 {
		certTemplate.ExtKeyUsage = append(certTemplate.ExtKeyUsage, x509.ExtKeyUsageServerAuth)
	}
	if creationInfo.Usage&clientUsage != 0 {
		certTemplate.ExtKeyUsage = append(certTemplate.ExtKeyUsage, x509.ExtKeyUsageClientAuth)
	}
	if creationInfo.Usage&codeSigningUsage != 0 {
		certTemplate.ExtKeyUsage = append(certTemplate.ExtKeyUsage, x509.ExtKeyUsageCodeSigning)
	}
	if creationInfo.Usage&emailProtectionUsage != 0 {
		certTemplate.ExtKeyUsage = append(certTemplate.ExtKeyUsage, x509.ExtKeyUsageEmailProtection)
	}

	certTemplate.IssuingCertificateURL = creationInfo.URLs.IssuingCertificates
	certTemplate.CRLDistributionPoints = creationInfo.URLs.CRLDistributionPoints
	certTemplate.OCSPServer = creationInfo.URLs.OCSPServers

	var certBytes []byte
	if creationInfo.SigningBundle != nil {
		switch creationInfo.SigningBundle.PrivateKeyType {
		case certutil.RSAPrivateKey:
			certTemplate.SignatureAlgorithm = x509.SHA256WithRSA
		case certutil.ECPrivateKey:
			certTemplate.SignatureAlgorithm = x509.ECDSAWithSHA256
		}

		caCert := creationInfo.SigningBundle.Certificate

		certBytes, err = x509.CreateCertificate(rand.Reader, certTemplate, caCert, result.PrivateKey.Public(), creationInfo.SigningBundle.PrivateKey)
	} else {
		// Creating a self-signed root
		if creationInfo.MaxPathLength == 0 {
			certTemplate.MaxPathLen = 0
			certTemplate.MaxPathLenZero = true
		} else {
			certTemplate.MaxPathLen = creationInfo.MaxPathLength
		}

		switch creationInfo.KeyType {
		case "rsa":
			certTemplate.SignatureAlgorithm = x509.SHA256WithRSA
		case "ec":
			certTemplate.SignatureAlgorithm = x509.ECDSAWithSHA256
		}

		certTemplate.BasicConstraintsValid = true
		certTemplate.IsCA = true
		certTemplate.KeyUsage = x509.KeyUsage(certTemplate.KeyUsage | x509.KeyUsageCertSign | x509.KeyUsageCRLSign)
		certTemplate.ExtKeyUsage = append(certTemplate.ExtKeyUsage, x509.ExtKeyUsageOCSPSigning)
		certBytes, err = x509.CreateCertificate(rand.Reader, certTemplate, certTemplate, result.PrivateKey.Public(), result.PrivateKey)
	}

	if err != nil {
		return nil, certutil.InternalError{Err: fmt.Sprintf("unable to create certificate: %s", err)}
	}

	result.CertificateBytes = certBytes
	result.Certificate, err = x509.ParseCertificate(certBytes)
	if err != nil {
		return nil, certutil.InternalError{Err: fmt.Sprintf("unable to parse created certificate: %s", err)}
	}

	if creationInfo.SigningBundle != nil {
		result.IssuingCABytes = creationInfo.SigningBundle.CertificateBytes
		result.IssuingCA = creationInfo.SigningBundle.Certificate
	} else {
		result.IssuingCABytes = result.CertificateBytes
		result.IssuingCA = result.Certificate
	}

	return result, nil
}
Exemple #9
0
// Base code to actually create and store a new certificate
// Return the id of the new certificate(or -1) and an error (or nil)
func (c App) createCertificate(id int, certificate models.FullCertificate) (*models.Certificate, error) {

	// Keep track of whether this is a self signed certificate
	var sign_cert *models.Certificate

	// Get the CA certificate that is signing this new cert
	var project *models.Project
	if id >= 0 {
		project = c.getProject(id)
		if project == nil {
			return nil, errors.New("Unable to retreive project")
		}
		cas := c.getProjectCAs(project)

		ca := c.Params.Values["certificate.SignedBy"][0]
		ca_val, _ := strconv.Atoi(ca)
		for _, c := range cas {
			if c.Id == ca_val {
				sign_cert = c
			}
		}
	}

	serialNumber := 1
	caCount := &models.CACount{}
	var CRLLocation []string
	if sign_cert != nil {
		// Another Cert is CA
		obj, err := c.Txn.Get(models.CACount{}, sign_cert.Id)
		if err != nil {
			return nil, errors.New("Error determining Serial Number")
		}
		caCount = obj.(*models.CACount)
		serialNumber = caCount.SerialNumber + 1
		caCount.SerialNumber = serialNumber
		_, err = c.Txn.Update(caCount)
		if err != nil {
			return nil, newError("Error saving ca count", err)
		}
		// Use the CRL of the cert that is signing it
		CRLLocation := []string{c.makeCRLLocation(caCount.Id)}
		if certificate.IsCA {
			// This certificate should now have it's own
			CRLLocation = append(CRLLocation, c.makeCRLLocation(caCount.Id))

		}
	} else {
		crlCount := c.getCRLCount()
		CRLLocation = []string{c.makeCRLLocation(crlCount + 1)}

	}

	// Set up ways Key can be used
	certificate.KeyUses = c.Params.Values["certificate.KeyUses"]
	certificate.ExtKeyUses = c.Params.Values["certificate.ExtKeyUses"]

	//Set up primary key uses
	keyUsage := 0
	for _, use := range certificate.KeyUses {
		v, _ := strconv.Atoi(use)
		keyUsage = keyUsage | v
	}
	// CAs need to be able to sign other certificates and Certificate Revocation Lists
	if certificate.IsCA {
		keyUsage = keyUsage | int(x509.KeyUsageCertSign) | int(x509.KeyUsageCRLSign)
	}

	// Set up extra key uses for certificate
	extKeyUsage := make([]x509.ExtKeyUsage, 0)
	for _, use := range certificate.ExtKeyUses {
		v, _ := strconv.Atoi(use)
		extKeyUsage = append(extKeyUsage, x509.ExtKeyUsage(v))
	}

	endTime, _ := time.Parse("2006-01-02", certificate.Expires)

	encrypted := false

	template := x509.Certificate{
		SerialNumber: new(big.Int).SetInt64(int64(serialNumber)),
		Subject: pkix.Name{
			Organization:       []string{certificate.Organization},
			OrganizationalUnit: []string{certificate.OrganizationUnit},
			Country:            []string{certificate.Country},
			Province:           []string{certificate.State},
			Locality:           []string{certificate.City},
			CommonName:         certificate.CommonName,
		},
		NotBefore:             time.Now(),
		NotAfter:              endTime,
		IsCA:                  certificate.IsCA,
		KeyUsage:              x509.KeyUsage(keyUsage),
		ExtKeyUsage:           extKeyUsage,
		BasicConstraintsValid: true,
		CRLDistributionPoints: CRLLocation,
	}

	enc := strings.Split(certificate.PrivateKeyType, " ")
	var privCert []byte
	var pubCert []byte
	var keyType models.KeyType
	var signKey interface{}
	var parent *x509.Certificate

	// Create RSA Cert
	if strings.HasPrefix(enc[0], "RSA") {
		keyType = models.RSA
		size, err := strconv.Atoi(enc[1])
		if err != nil {
			return nil, newError("Failed to generate private key", err)
		}
		priv, err := rsa.GenerateKey(rand.Reader, size)

		if err != nil {
			return nil, newError("Failed to generate private key", err)
		}
		if sign_cert == nil {
			signKey = priv
			parent = &template
		} else {

			// Getting signed by CA, get CA private key bytes
			var ca_bytes []byte
			ca_block, _ := pem.Decode(sign_cert.PrivateKey)
			if ca_block == nil {
				return nil, newError("Unable to decode CA privatekey", nil)
			}
			ca_bytes = ca_block.Bytes
			if x509.IsEncryptedPEMBlock(ca_block) {
				if len(certificate.CAEncryptionKey) == 0 {
					return nil, errors.New("Unable to unlock CA key")
				} else {
					// CA is encrypted so decrypt it
					ca_bytes, err = x509.DecryptPEMBlock(ca_block, []byte(certificate.CAEncryptionKey))
					if err != nil {
						return nil, errors.New("Unable to decrypt CA key")
					}
				}
			}

			// Obtain the key for the CA for signing
			if sign_cert.KeyType == models.RSA {
				signKey, err = x509.ParsePKCS1PrivateKey(ca_bytes)
				if err != nil {
					return nil, newError("Error parsing certificate", err)
				}

			} else {
				signKey, err = x509.ParseECPrivateKey(ca_bytes)
				if err != nil {
					return nil, newError("Error parsing certificate", err)
				}
			}

			// We need the public certificate of the CA as well
			block, _ := pem.Decode(sign_cert.PEM)
			if block == nil {
				return nil, newError("Unable to decode CA cert", nil)
			}
			parent, err = x509.ParseCertificate(block.Bytes)
			if err != nil {
				return nil, newError("Failed to generate private key:", err)
			}

		}
		pub := priv.PublicKey
		marshalledKey := x509.MarshalPKCS1PrivateKey(priv)
		derBytes, err := x509.CreateCertificate(rand.Reader, &template, parent, &pub, signKey)
		if err != nil {
			return nil, newError("Failed to create certificate \n", err)
		}
		// Convert certificate to PEM to be stored in DB
		pubCert = pem.EncodeToMemory(&pem.Block{Type: "CERTIFICATE", Bytes: derBytes})
		pemKeyBlock := &pem.Block{Type: "RSA PRIVATE KEY", Bytes: marshalledKey}
		// Encrypt the private key if an encryption key was provided
		if len(certificate.EncryptionKey) > 0 {
			pemKeyBlock, err = x509.EncryptPEMBlock(rand.Reader, pemKeyBlock.Type, pemKeyBlock.Bytes, []byte(certificate.EncryptionKey), x509.PEMCipherAES256)
			if err != nil {
				return nil, newError("Failed to encrypt key", err)
			}
			encrypted = true
		}
		privCert = pem.EncodeToMemory(pemKeyBlock)

	} else { // Create ECDSA Cert

		keyType = models.ECDSA
		var curve elliptic.Curve
		// Select the Elliptic curve to use
		switch enc[1] {
		case "224":
			curve = elliptic.P224()

		case "256":
			curve = elliptic.P256()

		case "384":
			curve = elliptic.P384()

		case "521":
			curve = elliptic.P521()
		}
		priv, err := ecdsa.GenerateKey(curve, rand.Reader)
		pub := priv.PublicKey
		if err != nil {
			return nil, newError("Failed to generate private key", err)
		}

		if sign_cert == nil {
			signKey = priv
			parent = &template
		} else {

			// Getting signed by CA, get CA private key bytes
			var ca_bytes []byte
			ca_block, _ := pem.Decode(sign_cert.PrivateKey)
			if ca_block == nil {
				return nil, newError("Unable to decode CA privatekey", nil)
			}
			ca_bytes = ca_block.Bytes
			if x509.IsEncryptedPEMBlock(ca_block) {
				if len(certificate.CAEncryptionKey) == 0 {
					return nil, errors.New("Unable to unlock CA key")
				} else {
					// CA is encrypted so decrypt it
					ca_bytes, err = x509.DecryptPEMBlock(ca_block, []byte(certificate.CAEncryptionKey))
					if err != nil {
						return nil, errors.New("Unable to decrypt CA key")
					}
				}
			}

			// Obtain the key for the CA for signing
			if sign_cert.KeyType == models.RSA {
				signKey, err = x509.ParsePKCS1PrivateKey(ca_bytes)
				if err != nil {
					return nil, newError("Error parsing certificate", err)
				}

			} else {
				signKey, err = x509.ParseECPrivateKey(ca_bytes)
				if err != nil {
					return nil, newError("Error parsing certificate", err)
				}
			}

			// We need the public certificate of the CA as well
			block, _ := pem.Decode(sign_cert.PEM)
			if block == nil {
				return nil, newError("Unable to decode CA cert", nil)
			}
			parent, err = x509.ParseCertificate(block.Bytes)
			if err != nil {
				return nil, newError("Error parsing certificate", err)
			}

		}

		// All the parts to make a certificate are available
		// Create an x509 certificate
		derBytes, err := x509.CreateCertificate(rand.Reader, &template, parent, &pub, signKey)
		if err != nil {
			return nil, newError("Failed to create certificate", err)
		}

		// Convert certificate to PEM to be stored in DB
		pubCert = pem.EncodeToMemory(&pem.Block{Type: "CERTIFICATE", Bytes: derBytes})

		//marshalledKey, err := MarshalECPrivateKey(priv)
		marshalledKey, err := x509.MarshalECPrivateKey(priv) // To be used for go 1.2 (added to default x509 lib)
		if err != nil {
			return nil, newError("Failed Marshal EC Private Key", err)
		}

		pemKeyBlock := &pem.Block{Type: "EC PRIVATE KEY", Bytes: marshalledKey}
		if len(certificate.EncryptionKey) > 0 {
			pemKeyBlock, err = x509.EncryptPEMBlock(rand.Reader, pemKeyBlock.Type, pemKeyBlock.Bytes, []byte(certificate.EncryptionKey), x509.PEMCipherAES256)
			if err != nil {
				return nil, newError("Failed to encrypt key", err)
			}
			encrypted = true
		}
		privCert = pem.EncodeToMemory(pemKeyBlock)
	}

	// Create Certificate model for certificate table
	cert := models.Certificate{PEM: pubCert, PrivateKey: privCert, CommonName: certificate.CommonName, CA: certificate.IsCA, Project: project, KeyType: keyType, Encrypted: encrypted, SerialNumber: serialNumber}

	// Save the certificate in the database
	err := c.Txn.Insert(&cert)
	if err != nil {
		return nil, newError("Error saving certificate", err)
	}

	if sign_cert == nil {
		// Cert is self signed
		caCount.SerialNumber = serialNumber
		caCount.Certificate = &cert

		err := c.Txn.Insert(caCount)
		if err != nil {
			return nil, newError("Error saving ca count", err)
		}
	}

	return &cert, nil
}