// Copyright 2015 Matthew Holt // // Licensed under the Apache License, Version 2.0 (the "License"); // you may not use this file except in compliance with the License. // You may obtain a copy of the License at // // http://www.apache.org/licenses/LICENSE-2.0 // // Unless required by applicable law or agreed to in writing, software // distributed under the License is distributed on an "AS IS" BASIS, // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. // See the License for the specific language governing permissions and // limitations under the License. package certmagic import ( "crypto" "crypto/ecdsa" "crypto/ed25519" "crypto/elliptic" "crypto/rand" "crypto/rsa" "crypto/sha256" "crypto/tls" "crypto/x509" "encoding/json" "encoding/pem" "fmt" "hash/fnv" "strings" "github.com/klauspost/cpuid" ) // encodePrivateKey marshals a EC or RSA private key into a PEM-encoded array of bytes. func encodePrivateKey(key crypto.PrivateKey) ([]byte, error) { var pemType string var keyBytes []byte switch key := key.(type) { case *ecdsa.PrivateKey: var err error pemType = "EC" keyBytes, err = x509.MarshalECPrivateKey(key) if err != nil { return nil, err } case *rsa.PrivateKey: pemType = "RSA" keyBytes = x509.MarshalPKCS1PrivateKey(key) case ed25519.PrivateKey: var err error pemType = "ED25519" keyBytes, err = x509.MarshalPKCS8PrivateKey(key) if err != nil { return nil, err } default: return nil, fmt.Errorf("unsupported key type: %T", key) } pemKey := pem.Block{Type: pemType + " PRIVATE KEY", Bytes: keyBytes} return pem.EncodeToMemory(&pemKey), nil } // decodePrivateKey loads a PEM-encoded ECC/RSA private key from an array of bytes. // Borrowed from Go standard library, to handle various private key and PEM block types. // https://github.com/golang/go/blob/693748e9fa385f1e2c3b91ca9acbb6c0ad2d133d/src/crypto/tls/tls.go#L291-L308 // https://github.com/golang/go/blob/693748e9fa385f1e2c3b91ca9acbb6c0ad2d133d/src/crypto/tls/tls.go#L238) func decodePrivateKey(keyPEMBytes []byte) (crypto.Signer, error) { keyBlockDER, _ := pem.Decode(keyPEMBytes) if keyBlockDER.Type != "PRIVATE KEY" && !strings.HasSuffix(keyBlockDER.Type, " PRIVATE KEY") { return nil, fmt.Errorf("unknown PEM header %q", keyBlockDER.Type) } if key, err := x509.ParsePKCS1PrivateKey(keyBlockDER.Bytes); err == nil { return key, nil } if key, err := x509.ParsePKCS8PrivateKey(keyBlockDER.Bytes); err == nil { switch key := key.(type) { case *rsa.PrivateKey, *ecdsa.PrivateKey, ed25519.PrivateKey: return key.(crypto.Signer), nil default: return nil, fmt.Errorf("found unknown private key type in PKCS#8 wrapping: %T", key) } } if key, err := x509.ParseECPrivateKey(keyBlockDER.Bytes); err == nil { return key, nil } return nil, fmt.Errorf("unknown private key type") } // parseCertsFromPEMBundle parses a certificate bundle from top to bottom and returns // a slice of x509 certificates. This function will error if no certificates are found. func parseCertsFromPEMBundle(bundle []byte) ([]*x509.Certificate, error) { var certificates []*x509.Certificate var certDERBlock *pem.Block for { certDERBlock, bundle = pem.Decode(bundle) if certDERBlock == nil { break } if certDERBlock.Type == "CERTIFICATE" { cert, err := x509.ParseCertificate(certDERBlock.Bytes) if err != nil { return nil, err } certificates = append(certificates, cert) } } if len(certificates) == 0 { return nil, fmt.Errorf("no certificates found in bundle") } return certificates, nil } // fastHash hashes input using a hashing algorithm that // is fast, and returns the hash as a hex-encoded string. // Do not use this for cryptographic purposes. func fastHash(input []byte) string { h := fnv.New32a() h.Write(input) return fmt.Sprintf("%x", h.Sum32()) } // saveCertResource saves the certificate resource to disk. This // includes the certificate file itself, the private key, and the // metadata file. func (cfg *Config) saveCertResource(cert CertificateResource) error { metaBytes, err := json.MarshalIndent(cert, "", "\t") if err != nil { return fmt.Errorf("encoding certificate metadata: %v", err) } issuerKey := cfg.Issuer.IssuerKey() certKey := cert.NamesKey() all := []keyValue{ { key: StorageKeys.SiteCert(issuerKey, certKey), value: cert.CertificatePEM, }, { key: StorageKeys.SitePrivateKey(issuerKey, certKey), value: cert.PrivateKeyPEM, }, { key: StorageKeys.SiteMeta(issuerKey, certKey), value: metaBytes, }, } return storeTx(cfg.Storage, all) } func (cfg *Config) loadCertResource(certNamesKey string) (CertificateResource, error) { var certRes CertificateResource issuerKey := cfg.Issuer.IssuerKey() certBytes, err := cfg.Storage.Load(StorageKeys.SiteCert(issuerKey, certNamesKey)) if err != nil { return CertificateResource{}, err } certRes.CertificatePEM = certBytes keyBytes, err := cfg.Storage.Load(StorageKeys.SitePrivateKey(issuerKey, certNamesKey)) if err != nil { return CertificateResource{}, err } certRes.PrivateKeyPEM = keyBytes metaBytes, err := cfg.Storage.Load(StorageKeys.SiteMeta(issuerKey, certNamesKey)) if err != nil { return CertificateResource{}, err } err = json.Unmarshal(metaBytes, &certRes) if err != nil { return CertificateResource{}, fmt.Errorf("decoding certificate metadata: %v", err) } // TODO: July 2020 - transition to new ACME lib and cert resource structure; // for a while, we will need to convert old cert resources to new structure certRes, err = cfg.transitionCertMetaToACMEzJuly2020Format(certRes, metaBytes) if err != nil { return certRes, fmt.Errorf("one-time certificate resource transition: %v", err) } return certRes, nil } // TODO: this is a temporary transition helper starting July 2020. // It can go away when we think enough time has passed that most active assets have transitioned. func (cfg *Config) transitionCertMetaToACMEzJuly2020Format(certRes CertificateResource, metaBytes []byte) (CertificateResource, error) { data, ok := certRes.IssuerData.(map[string]interface{}) if !ok { return certRes, nil } if certURL, ok := data["url"].(string); ok && certURL != "" { return certRes, nil } var oldCertRes struct { SANs []string `json:"sans"` IssuerData struct { Domain string `json:"domain"` CertURL string `json:"certUrl"` CertStableURL string `json:"certStableUrl"` } `json:"issuer_data"` } err := json.Unmarshal(metaBytes, &oldCertRes) if err != nil { return certRes, fmt.Errorf("decoding into old certificate resource type: %v", err) } data = map[string]interface{}{ "url": oldCertRes.IssuerData.CertURL, } certRes.IssuerData = data err = cfg.saveCertResource(certRes) if err != nil { return certRes, fmt.Errorf("saving converted certificate resource: %v", err) } return certRes, nil } // hashCertificateChain computes the unique hash of certChain, // which is the chain of DER-encoded bytes. It returns the // hex encoding of the hash. func hashCertificateChain(certChain [][]byte) string { h := sha256.New() for _, certInChain := range certChain { h.Write(certInChain) } return fmt.Sprintf("%x", h.Sum(nil)) } func namesFromCSR(csr *x509.CertificateRequest) []string { var nameSet []string nameSet = append(nameSet, csr.DNSNames...) nameSet = append(nameSet, csr.EmailAddresses...) for _, v := range csr.IPAddresses { nameSet = append(nameSet, v.String()) } for _, v := range csr.URIs { nameSet = append(nameSet, v.String()) } return nameSet } // preferredDefaultCipherSuites returns an appropriate // cipher suite to use depending on hardware support // for AES-NI. // // See https://github.com/mholt/caddy/issues/1674 func preferredDefaultCipherSuites() []uint16 { if cpuid.CPU.AesNi() { return defaultCiphersPreferAES } return defaultCiphersPreferChaCha } var ( defaultCiphersPreferAES = []uint16{ tls.TLS_ECDHE_ECDSA_WITH_AES_256_GCM_SHA384, tls.TLS_ECDHE_RSA_WITH_AES_256_GCM_SHA384, tls.TLS_ECDHE_ECDSA_WITH_AES_128_GCM_SHA256, tls.TLS_ECDHE_RSA_WITH_AES_128_GCM_SHA256, tls.TLS_ECDHE_ECDSA_WITH_CHACHA20_POLY1305, tls.TLS_ECDHE_RSA_WITH_CHACHA20_POLY1305, } defaultCiphersPreferChaCha = []uint16{ tls.TLS_ECDHE_ECDSA_WITH_CHACHA20_POLY1305, tls.TLS_ECDHE_RSA_WITH_CHACHA20_POLY1305, tls.TLS_ECDHE_ECDSA_WITH_AES_256_GCM_SHA384, tls.TLS_ECDHE_RSA_WITH_AES_256_GCM_SHA384, tls.TLS_ECDHE_ECDSA_WITH_AES_128_GCM_SHA256, tls.TLS_ECDHE_RSA_WITH_AES_128_GCM_SHA256, } ) // StandardKeyGenerator is the standard, in-memory key source // that uses crypto/rand. type StandardKeyGenerator struct { // The type of keys to generate. KeyType KeyType } // GenerateKey generates a new private key according to kg.KeyType. func (kg StandardKeyGenerator) GenerateKey() (crypto.PrivateKey, error) { switch kg.KeyType { case ED25519: _, priv, err := ed25519.GenerateKey(rand.Reader) return priv, err case "", P256: return ecdsa.GenerateKey(elliptic.P256(), rand.Reader) case P384: return ecdsa.GenerateKey(elliptic.P384(), rand.Reader) case RSA2048: return rsa.GenerateKey(rand.Reader, 2048) case RSA4096: return rsa.GenerateKey(rand.Reader, 4096) case RSA8192: return rsa.GenerateKey(rand.Reader, 8192) } return nil, fmt.Errorf("unrecognized or unsupported key type: %s", kg.KeyType) } // DefaultKeyGenerator is the default key source. var DefaultKeyGenerator = StandardKeyGenerator{KeyType: P256} // KeyType enumerates the known/supported key types. type KeyType string // Constants for all key types we support. const ( ED25519 = KeyType("ed25519") P256 = KeyType("p256") P384 = KeyType("p384") RSA2048 = KeyType("rsa2048") RSA4096 = KeyType("rsa4096") RSA8192 = KeyType("rsa8192") )