// extKeyUsage decodes/prints extended key usage from a certificate. func extKeyUsage(sEku simpleExtKeyUsage) string { eku := x509.ExtKeyUsage(sEku) val, ok := extKeyUsageStrings[eku] if ok { return val } return fmt.Sprintf("unknown:%d", eku) }
// 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 }