|
- // 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")
- )
|