Skip to content

Commit

Permalink
Refactor sign/issue response logic to be usable by CIEPS codebase (#2…
Browse files Browse the repository at this point in the history
…2053)

- Extract out the sign/issue certificate (non-CA) response into
   a function that can be used by the CIEPS sign/issue API handlers.
  • Loading branch information
stevendpclark authored Jul 25, 2023
1 parent df05956 commit 6ff7d38
Show file tree
Hide file tree
Showing 2 changed files with 135 additions and 82 deletions.
53 changes: 53 additions & 0 deletions builtin/logical/pki/cert_util.go
Original file line number Diff line number Diff line change
Expand Up @@ -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 {
Expand Down
164 changes: 82 additions & 82 deletions builtin/logical/pki/path_issue_sign.go
Original file line number Diff line number Diff line change
Expand Up @@ -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,
},
},
}},
},
Expand Down Expand Up @@ -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,
},
},
}},
},
Expand Down Expand Up @@ -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,
}

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)
resp, err := signIssueApiResponse(data, parsedBundle, signingBundle, warnings)
if err != nil {
return nil, err
}

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{}{
Expand All @@ -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()
Expand Down Expand Up @@ -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.
`
Expand Down

0 comments on commit 6ff7d38

Please sign in to comment.