diff --git a/builtin/logical/pki/cert_util.go b/builtin/logical/pki/cert_util.go index e6f49f1a3e..185488a7a1 100644 --- a/builtin/logical/pki/cert_util.go +++ b/builtin/logical/pki/cert_util.go @@ -70,8 +70,61 @@ var ( // OIDs for X.509 certificate extensions used below. oidExtensionSubjectAltName = []int{2, 5, 29, 17} + + // Cloned from https://github.com/golang/go/blob/82c713feb05da594567631972082af2fcba0ee4f/src/crypto/x509/x509.go#L327-L379 + oidSignatureMD2WithRSA = asn1.ObjectIdentifier{1, 2, 840, 113549, 1, 1, 2} + oidSignatureMD5WithRSA = asn1.ObjectIdentifier{1, 2, 840, 113549, 1, 1, 4} + oidSignatureSHA1WithRSA = asn1.ObjectIdentifier{1, 2, 840, 113549, 1, 1, 5} + oidSignatureSHA256WithRSA = asn1.ObjectIdentifier{1, 2, 840, 113549, 1, 1, 11} + oidSignatureSHA384WithRSA = asn1.ObjectIdentifier{1, 2, 840, 113549, 1, 1, 12} + oidSignatureSHA512WithRSA = asn1.ObjectIdentifier{1, 2, 840, 113549, 1, 1, 13} + oidSignatureRSAPSS = asn1.ObjectIdentifier{1, 2, 840, 113549, 1, 1, 10} + oidSignatureDSAWithSHA1 = asn1.ObjectIdentifier{1, 2, 840, 10040, 4, 3} + oidSignatureDSAWithSHA256 = asn1.ObjectIdentifier{2, 16, 840, 1, 101, 3, 4, 3, 2} + oidSignatureECDSAWithSHA1 = asn1.ObjectIdentifier{1, 2, 840, 10045, 4, 1} + oidSignatureECDSAWithSHA256 = asn1.ObjectIdentifier{1, 2, 840, 10045, 4, 3, 2} + oidSignatureECDSAWithSHA384 = asn1.ObjectIdentifier{1, 2, 840, 10045, 4, 3, 3} + oidSignatureECDSAWithSHA512 = asn1.ObjectIdentifier{1, 2, 840, 10045, 4, 3, 4} + oidSignatureEd25519 = asn1.ObjectIdentifier{1, 3, 101, 112} + oidISOSignatureSHA1WithRSA = asn1.ObjectIdentifier{1, 3, 14, 3, 2, 29} + + signatureAlgorithmDetails = []struct { + algo x509.SignatureAlgorithm + name string + oid asn1.ObjectIdentifier + pubKeyAlgo x509.PublicKeyAlgorithm + hash crypto.Hash + }{ + {x509.MD2WithRSA, "MD2-RSA", oidSignatureMD2WithRSA, x509.RSA, crypto.Hash(0) /* no value for MD2 */}, + {x509.MD5WithRSA, "MD5-RSA", oidSignatureMD5WithRSA, x509.RSA, crypto.MD5}, + {x509.SHA1WithRSA, "SHA1-RSA", oidSignatureSHA1WithRSA, x509.RSA, crypto.SHA1}, + {x509.SHA1WithRSA, "SHA1-RSA", oidISOSignatureSHA1WithRSA, x509.RSA, crypto.SHA1}, + {x509.SHA256WithRSA, "SHA256-RSA", oidSignatureSHA256WithRSA, x509.RSA, crypto.SHA256}, + {x509.SHA384WithRSA, "SHA384-RSA", oidSignatureSHA384WithRSA, x509.RSA, crypto.SHA384}, + {x509.SHA512WithRSA, "SHA512-RSA", oidSignatureSHA512WithRSA, x509.RSA, crypto.SHA512}, + {x509.SHA256WithRSAPSS, "SHA256-RSAPSS", oidSignatureRSAPSS, x509.RSA, crypto.SHA256}, + {x509.SHA384WithRSAPSS, "SHA384-RSAPSS", oidSignatureRSAPSS, x509.RSA, crypto.SHA384}, + {x509.SHA512WithRSAPSS, "SHA512-RSAPSS", oidSignatureRSAPSS, x509.RSA, crypto.SHA512}, + {x509.DSAWithSHA1, "DSA-SHA1", oidSignatureDSAWithSHA1, x509.DSA, crypto.SHA1}, + {x509.DSAWithSHA256, "DSA-SHA256", oidSignatureDSAWithSHA256, x509.DSA, crypto.SHA256}, + {x509.ECDSAWithSHA1, "ECDSA-SHA1", oidSignatureECDSAWithSHA1, x509.ECDSA, crypto.SHA1}, + {x509.ECDSAWithSHA256, "ECDSA-SHA256", oidSignatureECDSAWithSHA256, x509.ECDSA, crypto.SHA256}, + {x509.ECDSAWithSHA384, "ECDSA-SHA384", oidSignatureECDSAWithSHA384, x509.ECDSA, crypto.SHA384}, + {x509.ECDSAWithSHA512, "ECDSA-SHA512", oidSignatureECDSAWithSHA512, x509.ECDSA, crypto.SHA512}, + {x509.PureEd25519, "Ed25519", oidSignatureEd25519, x509.Ed25519, crypto.Hash(0) /* no pre-hashing */}, + } ) +func doesPublicKeyAlgoMatchSignatureAlgo(pubKey x509.PublicKeyAlgorithm, algo x509.SignatureAlgorithm) bool { + for _, detail := range signatureAlgorithmDetails { + if detail.algo == algo { + return pubKey == detail.pubKeyAlgo + } + } + + return false +} + func getFormat(data *framework.FieldData) string { format := data.Get("format").(string) switch format { diff --git a/builtin/logical/pki/path_issue_sign.go b/builtin/logical/pki/path_issue_sign.go index 9833605584..d9f73c2716 100644 --- a/builtin/logical/pki/path_issue_sign.go +++ b/builtin/logical/pki/path_issue_sign.go @@ -167,16 +167,6 @@ func buildPathSign(b *backend, pattern string, displayAttrs *framework.DisplayAt Description: `Time of expiration`, Required: true, }, - "private_key": { - Type: framework.TypeString, - Description: `Private key`, - Required: false, - }, - "private_key_type": { - Type: framework.TypeString, - Description: `Private key type`, - Required: false, - }, }, }}, }, @@ -260,16 +250,6 @@ func buildPathIssuerSignVerbatim(b *backend, pattern string, displayAttrs *frame Description: `Time of expiration`, Required: true, }, - "private_key": { - Type: framework.TypeString, - Description: `Private key`, - Required: false, - }, - "private_key_type": { - Type: framework.TypeString, - Description: `Private key type`, - Required: false, - }, }, }}, }, @@ -403,73 +383,23 @@ func (b *backend) pathIssueSignCert(ctx context.Context, req *logical.Request, d } } - signingCB, err := signingBundle.ToCertBundle() - if err != nil { - return nil, fmt.Errorf("error converting raw signing bundle to cert bundle: %w", err) - } - cb, err := parsedBundle.ToCertBundle() if err != nil { return nil, fmt.Errorf("error converting raw cert bundle to cert bundle: %w", err) } - caChainGen := newCaChainOutput(parsedBundle, data) - - respData := map[string]interface{}{ - "expiration": int64(parsedBundle.Certificate.NotAfter.Unix()), - "serial_number": cb.SerialNumber, + resp, err := signIssueApiResponse(data, parsedBundle, signingBundle, warnings) + if err != nil { + return nil, err } - switch format { - case "pem": - respData["issuing_ca"] = signingCB.Certificate - respData["certificate"] = cb.Certificate - if caChainGen.containsChain() { - respData["ca_chain"] = caChainGen.pemEncodedChain() - } - if !useCSR { - respData["private_key"] = cb.PrivateKey - respData["private_key_type"] = cb.PrivateKeyType - } - - case "pem_bundle": - respData["issuing_ca"] = signingCB.Certificate - respData["certificate"] = cb.ToPEMBundle() - if caChainGen.containsChain() { - respData["ca_chain"] = caChainGen.pemEncodedChain() - } - if !useCSR { - respData["private_key"] = cb.PrivateKey - respData["private_key_type"] = cb.PrivateKeyType - } - - case "der": - respData["certificate"] = base64.StdEncoding.EncodeToString(parsedBundle.CertificateBytes) - respData["issuing_ca"] = base64.StdEncoding.EncodeToString(signingBundle.CertificateBytes) - - if caChainGen.containsChain() { - respData["ca_chain"] = caChainGen.derEncodedChain() - } - - if !useCSR { - respData["private_key"] = base64.StdEncoding.EncodeToString(parsedBundle.PrivateKeyBytes) - respData["private_key_type"] = cb.PrivateKeyType - } - default: - return nil, fmt.Errorf("unsupported format: %s", format) - } - - var resp *logical.Response switch { case role.GenerateLease == nil: return nil, fmt.Errorf("generate lease in role is nil") case !*role.GenerateLease: - // If lease generation is disabled do not populate `Secret` field in - // the response - resp = &logical.Response{ - Data: respData, - } + // Use resp from above default: + respData := resp.Data resp = b.Secret(SecretCertsType).Response( respData, map[string]interface{}{ @@ -478,13 +408,6 @@ func (b *backend) pathIssueSignCert(ctx context.Context, req *logical.Request, d resp.Secret.TTL = parsedBundle.Certificate.NotAfter.Sub(time.Now()) } - if data.Get("private_key_format").(string) == "pkcs8" { - err = convertRespToPKCS8(resp) - if err != nil { - return nil, err - } - } - if !role.NoStore { key := "certs/" + normalizeSerial(cb.SerialNumber) certsCounted := b.certsCounted.Load() @@ -556,6 +479,83 @@ func (cac *caChainOutput) derEncodedChain() []string { return derCaChain } +func signIssueApiResponse(data *framework.FieldData, parsedBundle *certutil.ParsedCertBundle, signingBundle *certutil.CAInfoBundle, warnings []string) (*logical.Response, error) { + cb, err := parsedBundle.ToCertBundle() + if err != nil { + return nil, fmt.Errorf("error converting raw cert bundle to cert bundle: %w", err) + } + + signingCB, err := signingBundle.ToCertBundle() + if err != nil { + return nil, fmt.Errorf("error converting raw signing bundle to cert bundle: %w", err) + } + + caChainGen := newCaChainOutput(parsedBundle, data) + includeKey := parsedBundle.PrivateKey != nil + + respData := map[string]interface{}{ + "expiration": parsedBundle.Certificate.NotAfter.Unix(), + "serial_number": cb.SerialNumber, + } + + format := getFormat(data) + switch format { + case "pem": + respData["issuing_ca"] = signingCB.Certificate + respData["certificate"] = cb.Certificate + if caChainGen.containsChain() { + respData["ca_chain"] = caChainGen.pemEncodedChain() + } + if includeKey { + respData["private_key"] = cb.PrivateKey + respData["private_key_type"] = cb.PrivateKeyType + } + + case "pem_bundle": + respData["issuing_ca"] = signingCB.Certificate + respData["certificate"] = cb.ToPEMBundle() + if caChainGen.containsChain() { + respData["ca_chain"] = caChainGen.pemEncodedChain() + } + if includeKey { + respData["private_key"] = cb.PrivateKey + respData["private_key_type"] = cb.PrivateKeyType + } + + case "der": + respData["certificate"] = base64.StdEncoding.EncodeToString(parsedBundle.CertificateBytes) + respData["issuing_ca"] = base64.StdEncoding.EncodeToString(signingBundle.CertificateBytes) + + if caChainGen.containsChain() { + respData["ca_chain"] = caChainGen.derEncodedChain() + } + + if includeKey { + respData["private_key"] = base64.StdEncoding.EncodeToString(parsedBundle.PrivateKeyBytes) + respData["private_key_type"] = cb.PrivateKeyType + } + default: + return nil, fmt.Errorf("unsupported format: %s", format) + } + + resp := &logical.Response{ + Data: respData, + } + + if includeKey { + if keyFormat := data.Get("private_key_format"); keyFormat == "pkcs8" { + err := convertRespToPKCS8(resp) + if err != nil { + return nil, err + } + } + } + + resp = addWarnings(resp, warnings) + + return resp, nil +} + const pathIssueHelpSyn = ` Request a certificate using a certain role with the provided details. `