2015-11-18 15:16:09 +00:00
package pki
import (
2018-01-08 18:31:38 +00:00
"context"
2021-09-14 15:07:27 +00:00
"crypto"
"crypto/ecdsa"
"crypto/elliptic"
2017-09-01 03:07:15 +00:00
"crypto/rand"
2021-09-14 15:07:27 +00:00
"crypto/rsa"
2017-09-01 03:07:15 +00:00
"crypto/x509"
2015-11-18 15:16:09 +00:00
"encoding/base64"
2017-09-01 03:07:15 +00:00
"encoding/pem"
2021-09-14 15:07:27 +00:00
"errors"
2015-11-18 15:16:09 +00:00
"fmt"
2017-09-01 03:07:15 +00:00
"reflect"
2018-03-18 20:00:51 +00:00
"strings"
2017-08-31 19:46:13 +00:00
"time"
2015-11-18 15:16:09 +00:00
2022-01-27 18:06:34 +00:00
"golang.org/x/crypto/ed25519"
2019-07-19 01:04:56 +00:00
"github.com/hashicorp/vault/sdk/helper/certutil"
2019-04-13 07:44:06 +00:00
"github.com/hashicorp/vault/sdk/framework"
2019-04-12 21:54:35 +00:00
"github.com/hashicorp/vault/sdk/helper/errutil"
"github.com/hashicorp/vault/sdk/logical"
2015-11-18 15:16:09 +00:00
)
func pathGenerateRoot ( b * backend ) * framework . Path {
ret := & framework . Path {
Pattern : "root/generate/" + framework . GenericNameRegex ( "exported" ) ,
2022-02-25 18:26:34 +00:00
Operations : map [ logical . Operation ] framework . OperationHandler {
logical . UpdateOperation : & framework . PathOperation {
Callback : b . pathCAGenerateRoot ,
// Read more about why these flags are set in backend.go
ForwardPerformanceStandby : true ,
ForwardPerformanceSecondary : true ,
} ,
2015-11-18 15:16:09 +00:00
} ,
HelpSynopsis : pathGenerateRootHelpSyn ,
HelpDescription : pathGenerateRootHelpDesc ,
}
ret . Fields = addCACommonFields ( map [ string ] * framework . FieldSchema { } )
ret . Fields = addCAKeyGenerationFields ( ret . Fields )
ret . Fields = addCAIssueFields ( ret . Fields )
return ret
}
2017-08-15 18:00:40 +00:00
func pathDeleteRoot ( b * backend ) * framework . Path {
ret := & framework . Path {
Pattern : "root" ,
2022-02-25 18:26:34 +00:00
Operations : map [ logical . Operation ] framework . OperationHandler {
logical . DeleteOperation : & framework . PathOperation {
Callback : b . pathCADeleteRoot ,
// Read more about why these flags are set in backend.go
ForwardPerformanceStandby : true ,
ForwardPerformanceSecondary : true ,
} ,
2017-08-15 18:00:40 +00:00
} ,
HelpSynopsis : pathDeleteRootHelpSyn ,
HelpDescription : pathDeleteRootHelpDesc ,
}
return ret
}
2015-11-18 15:16:09 +00:00
func pathSignIntermediate ( b * backend ) * framework . Path {
ret := & framework . Path {
Pattern : "root/sign-intermediate" ,
2022-02-25 18:26:34 +00:00
Operations : map [ logical . Operation ] framework . OperationHandler {
logical . UpdateOperation : & framework . PathOperation {
Callback : b . pathCASignIntermediate ,
} ,
2015-11-18 15:16:09 +00:00
} ,
HelpSynopsis : pathSignIntermediateHelpSyn ,
HelpDescription : pathSignIntermediateHelpDesc ,
}
ret . Fields = addCACommonFields ( map [ string ] * framework . FieldSchema { } )
ret . Fields = addCAIssueFields ( ret . Fields )
ret . Fields [ "csr" ] = & framework . FieldSchema {
Type : framework . TypeString ,
Default : "" ,
Description : ` PEM-format CSR to be signed. ` ,
}
ret . Fields [ "use_csr_values" ] = & framework . FieldSchema {
Type : framework . TypeBool ,
Default : false ,
Description : ` If true , then :
1 ) Subject information , including names and alternate
names , will be preserved from the CSR rather than
using values provided in the other parameters to
this path ;
2 ) Any key usages requested in the CSR will be
added to the basic set of key usages used for CA
certs signed by this path ; for instance ,
2022-02-25 19:06:41 +00:00
the non - repudiation flag ;
3 ) Extensions requested in the CSR will be copied
into the issued certificate . ` ,
2015-11-18 15:16:09 +00:00
}
return ret
}
2017-08-31 19:46:13 +00:00
func pathSignSelfIssued ( b * backend ) * framework . Path {
ret := & framework . Path {
Pattern : "root/sign-self-issued" ,
2022-02-25 18:26:34 +00:00
Operations : map [ logical . Operation ] framework . OperationHandler {
logical . UpdateOperation : & framework . PathOperation {
Callback : b . pathCASignSelfIssued ,
} ,
2017-09-01 03:07:15 +00:00
} ,
Fields : map [ string ] * framework . FieldSchema {
2021-04-08 16:43:39 +00:00
"certificate" : {
2017-09-01 03:07:15 +00:00
Type : framework . TypeString ,
Description : ` PEM-format self-issued certificate to be signed. ` ,
} ,
2022-01-27 18:06:34 +00:00
"require_matching_certificate_algorithms" : {
2021-09-14 15:07:27 +00:00
Type : framework . TypeBool ,
2021-11-01 19:42:25 +00:00
Default : false ,
Description : ` If true, require the public key algorithm of the signer to match that of the self issued certificate. ` ,
2021-09-14 15:07:27 +00:00
} ,
2017-08-31 19:46:13 +00:00
} ,
HelpSynopsis : pathSignSelfIssuedHelpSyn ,
HelpDescription : pathSignSelfIssuedHelpDesc ,
}
return ret
}
2018-01-08 18:31:38 +00:00
func ( b * backend ) pathCADeleteRoot ( ctx context . Context , req * logical . Request , data * framework . FieldData ) ( * logical . Response , error ) {
2018-01-19 06:44:44 +00:00
return nil , req . Storage . Delete ( ctx , "config/ca_bundle" )
2017-08-15 18:00:40 +00:00
}
2018-01-08 18:31:38 +00:00
func ( b * backend ) pathCAGenerateRoot ( ctx context . Context , req * logical . Request , data * framework . FieldData ) ( * logical . Response , error ) {
2015-11-18 15:16:09 +00:00
var err error
2018-01-19 06:44:44 +00:00
entry , err := req . Storage . Get ( ctx , "config/ca_bundle" )
2017-08-15 18:00:40 +00:00
if err != nil {
return nil , err
}
if entry != nil {
2018-05-09 14:29:54 +00:00
resp := & logical . Response { }
2022-02-22 19:39:21 +00:00
resp . AddWarning ( fmt . Sprintf ( "Refusing to generate a root certificate over an existing root certificate. " +
"If you really want to destroy the original root certificate, please issue a delete against %s root." , req . MountPoint ) )
2018-05-09 14:29:54 +00:00
return resp , nil
2017-08-15 18:00:40 +00:00
}
2022-03-03 21:30:18 +00:00
exported , format , role , errorResp := b . getGenerationParams ( ctx , data , req . MountPoint )
2015-11-18 15:16:09 +00:00
if errorResp != nil {
return errorResp , nil
}
maxPathLengthIface , ok := data . GetOk ( "max_path_length" )
if ok {
maxPathLength := maxPathLengthIface . ( int )
role . MaxPathLength = & maxPathLength
}
2019-05-09 15:43:11 +00:00
input := & inputBundle {
2018-02-16 22:19:34 +00:00
req : req ,
apiData : data ,
role : role ,
}
2021-09-15 16:59:12 +00:00
parsedBundle , err := generateCert ( ctx , b , input , nil , true , b . Backend . GetRandomReader ( ) )
2015-11-18 15:16:09 +00:00
if err != nil {
switch err . ( type ) {
2016-07-28 19:19:27 +00:00
case errutil . UserError :
2015-11-18 15:16:09 +00:00
return logical . ErrorResponse ( err . Error ( ) ) , nil
2022-01-27 04:06:25 +00:00
default :
return nil , err
2015-11-18 15:16:09 +00:00
}
}
cb , err := parsedBundle . ToCertBundle ( )
if err != nil {
2021-04-22 15:20:59 +00:00
return nil , fmt . Errorf ( "error converting raw cert bundle to cert bundle: %w" , err )
2015-11-18 15:16:09 +00:00
}
2016-05-09 23:53:28 +00:00
resp := & logical . Response {
Data : map [ string ] interface { } {
2015-11-18 15:16:09 +00:00
"expiration" : int64 ( parsedBundle . Certificate . NotAfter . Unix ( ) ) ,
2015-11-19 21:51:27 +00:00
"serial_number" : cb . SerialNumber ,
2015-11-18 15:16:09 +00:00
} ,
2016-05-09 23:53:28 +00:00
}
2015-11-18 15:16:09 +00:00
switch format {
case "pem" :
2016-02-01 18:19:41 +00:00
resp . Data [ "certificate" ] = cb . Certificate
2016-09-28 00:50:17 +00:00
resp . Data [ "issuing_ca" ] = cb . Certificate
2015-11-18 15:16:09 +00:00
if exported {
resp . Data [ "private_key" ] = cb . PrivateKey
resp . Data [ "private_key_type" ] = cb . PrivateKeyType
}
2016-02-01 18:19:41 +00:00
case "pem_bundle" :
2016-09-28 00:50:17 +00:00
resp . Data [ "issuing_ca" ] = cb . Certificate
2016-02-01 18:19:41 +00:00
if exported {
resp . Data [ "private_key" ] = cb . PrivateKey
resp . Data [ "private_key_type" ] = cb . PrivateKeyType
2016-09-28 00:50:17 +00:00
resp . Data [ "certificate" ] = fmt . Sprintf ( "%s\n%s" , cb . PrivateKey , cb . Certificate )
2016-02-01 18:19:41 +00:00
} else {
2016-09-28 00:50:17 +00:00
resp . Data [ "certificate" ] = cb . Certificate
2016-02-01 18:19:41 +00:00
}
2015-11-18 15:16:09 +00:00
case "der" :
resp . Data [ "certificate" ] = base64 . StdEncoding . EncodeToString ( parsedBundle . CertificateBytes )
2016-09-28 00:50:17 +00:00
resp . Data [ "issuing_ca" ] = base64 . StdEncoding . EncodeToString ( parsedBundle . CertificateBytes )
2015-11-18 15:16:09 +00:00
if exported {
resp . Data [ "private_key" ] = base64 . StdEncoding . EncodeToString ( parsedBundle . PrivateKeyBytes )
resp . Data [ "private_key_type" ] = cb . PrivateKeyType
}
}
2017-11-06 17:05:07 +00:00
if data . Get ( "private_key_format" ) . ( string ) == "pkcs8" {
err = convertRespToPKCS8 ( resp )
if err != nil {
return nil , err
}
}
2015-11-19 21:51:27 +00:00
// Store it as the CA bundle
2017-08-15 18:00:40 +00:00
entry , err = logical . StorageEntryJSON ( "config/ca_bundle" , cb )
2015-11-18 15:16:09 +00:00
if err != nil {
return nil , err
}
2018-01-19 06:44:44 +00:00
err = req . Storage . Put ( ctx , entry )
2015-11-18 15:16:09 +00:00
if err != nil {
return nil , err
}
2015-11-19 21:51:27 +00:00
// Also store it as just the certificate identified by serial number, so it
// can be revoked
2018-01-19 06:44:44 +00:00
err = req . Storage . Put ( ctx , & logical . StorageEntry {
2017-05-03 14:12:58 +00:00
Key : "certs/" + normalizeSerial ( cb . SerialNumber ) ,
2015-11-19 21:51:27 +00:00
Value : parsedBundle . CertificateBytes ,
} )
if err != nil {
2021-04-22 15:20:59 +00:00
return nil , fmt . Errorf ( "unable to store certificate locally: %w" , err )
2015-11-19 21:51:27 +00:00
}
2015-11-18 15:16:09 +00:00
// For ease of later use, also store just the certificate at a known
2015-11-19 21:51:27 +00:00
// location
2015-11-18 15:16:09 +00:00
entry . Key = "ca"
entry . Value = parsedBundle . CertificateBytes
2018-01-19 06:44:44 +00:00
err = req . Storage . Put ( ctx , entry )
2015-11-18 15:16:09 +00:00
if err != nil {
return nil , err
}
2015-11-19 21:51:27 +00:00
// Build a fresh CRL
2018-08-21 15:20:57 +00:00
err = buildCRL ( ctx , b , req , true )
2015-11-18 15:16:09 +00:00
if err != nil {
return nil , err
}
if parsedBundle . Certificate . MaxPathLen == 0 {
resp . AddWarning ( "Max path length of the generated certificate is zero. This certificate cannot be used to issue intermediate CA certificates." )
}
return resp , nil
}
2018-01-08 18:31:38 +00:00
func ( b * backend ) pathCASignIntermediate ( ctx context . Context , req * logical . Request , data * framework . FieldData ) ( * logical . Response , error ) {
2015-11-18 15:16:09 +00:00
var err error
format := getFormat ( data )
if format == "" {
return logical . ErrorResponse (
` The "format" path parameter must be "pem" or "der" ` ,
) , nil
}
role := & roleEntry {
Add role parameter to restrict issuance of wildcard certificates (#14238)
* Add new AllowWildcardCertificate field to PKI role
This field allows the PKI role to control whether or not issuance of
wildcard certificates are allowed. We default (both on migration and
new role creation) to the less secure true value for backwards
compatibility with existing Vault versions.
Signed-off-by: Alexander Scheel <alex.scheel@hashicorp.com>
* Refactor sanitizedName to reducedName
Per comment, this variable name was confusing during the reproduction
and subsequent fix of the earlier vulnerability and associated bug
report. Because the common name isn't necessarily _sanitized_ in any way
(and indeed must be considered in relation to other parts or the whole),
but portions of the entire name are removed, reducedName appears to make
the most sense.
Signed-off-by: Alexander Scheel <alex.scheel@hashicorp.com>
* Enforce AllowWildcardCertificates during issuance
This commit adds the bulk of correctly validating wildcard certificate
Common Names during issuance according to RFC 6125 Section 6.4.3
semantics. As part of this, support for RFC 2818-conforming wildcard
certificates (wherein there are almost no restrictions on issuance) has
been removed.
Note that this flag does take precedence over AllowAnyName, giving a
little more safety in wildcard issuance in this case.
Signed-off-by: Alexander Scheel <alex.scheel@hashicorp.com>
* Update test cases to conform with RFC 6125
Test cases 19, 70+71, and 83+84 didn't conform with the RFC 6125, and so
should've been rejected under strict conformance. For 70+71 and 83+84,
we previously conditioned around the value of AllowSubdomains (allowing
issuance when true), but they likely should've been rejected either way.
Additionally, update the notes about globs matching wildcard
certificates to notate this is indeed the case.
Signed-off-by: Alexander Scheel <alex.scheel@hashicorp.com>
* Check AllowWildcardCertifciates in issuance tests
This allows for regression tests to cover the new
AllowWildcardCertificate conditional. We add additional test cases
ensuring that wildcard issuance is properly forbidden in all relevant
scenarios, while allowing the existing test cases to validate that
wildcard status doesn't affect non-wildcard certificates.
Signed-off-by: Alexander Scheel <alex.scheel@hashicorp.com>
* Add Wildcard allowance during signing operations
When using sign-verbatim, sign-intermediate, or getting certificate
generation parameters, set AllowWildcardCertificates to mirror existing
policies.
Signed-off-by: Alexander Scheel <alex.scheel@hashicorp.com>
* Add changelog entry
Signed-off-by: Alexander Scheel <alex.scheel@hashicorp.com>
2022-02-24 13:41:56 +00:00
OU : data . Get ( "ou" ) . ( [ ] string ) ,
Organization : data . Get ( "organization" ) . ( [ ] string ) ,
Country : data . Get ( "country" ) . ( [ ] string ) ,
Locality : data . Get ( "locality" ) . ( [ ] string ) ,
Province : data . Get ( "province" ) . ( [ ] string ) ,
StreetAddress : data . Get ( "street_address" ) . ( [ ] string ) ,
PostalCode : data . Get ( "postal_code" ) . ( [ ] string ) ,
TTL : time . Duration ( data . Get ( "ttl" ) . ( int ) ) * time . Second ,
AllowLocalhost : true ,
AllowAnyName : true ,
AllowIPSANs : true ,
AllowWildcardCertificates : new ( bool ) ,
EnforceHostnames : false ,
KeyType : "any" ,
AllowedOtherSANs : [ ] string { "*" } ,
AllowedSerialNumbers : [ ] string { "*" } ,
AllowedURISANs : [ ] string { "*" } ,
AllowExpirationPastCA : true ,
NotAfter : data . Get ( "not_after" ) . ( string ) ,
2015-11-18 15:16:09 +00:00
}
Add role parameter to restrict issuance of wildcard certificates (#14238)
* Add new AllowWildcardCertificate field to PKI role
This field allows the PKI role to control whether or not issuance of
wildcard certificates are allowed. We default (both on migration and
new role creation) to the less secure true value for backwards
compatibility with existing Vault versions.
Signed-off-by: Alexander Scheel <alex.scheel@hashicorp.com>
* Refactor sanitizedName to reducedName
Per comment, this variable name was confusing during the reproduction
and subsequent fix of the earlier vulnerability and associated bug
report. Because the common name isn't necessarily _sanitized_ in any way
(and indeed must be considered in relation to other parts or the whole),
but portions of the entire name are removed, reducedName appears to make
the most sense.
Signed-off-by: Alexander Scheel <alex.scheel@hashicorp.com>
* Enforce AllowWildcardCertificates during issuance
This commit adds the bulk of correctly validating wildcard certificate
Common Names during issuance according to RFC 6125 Section 6.4.3
semantics. As part of this, support for RFC 2818-conforming wildcard
certificates (wherein there are almost no restrictions on issuance) has
been removed.
Note that this flag does take precedence over AllowAnyName, giving a
little more safety in wildcard issuance in this case.
Signed-off-by: Alexander Scheel <alex.scheel@hashicorp.com>
* Update test cases to conform with RFC 6125
Test cases 19, 70+71, and 83+84 didn't conform with the RFC 6125, and so
should've been rejected under strict conformance. For 70+71 and 83+84,
we previously conditioned around the value of AllowSubdomains (allowing
issuance when true), but they likely should've been rejected either way.
Additionally, update the notes about globs matching wildcard
certificates to notate this is indeed the case.
Signed-off-by: Alexander Scheel <alex.scheel@hashicorp.com>
* Check AllowWildcardCertifciates in issuance tests
This allows for regression tests to cover the new
AllowWildcardCertificate conditional. We add additional test cases
ensuring that wildcard issuance is properly forbidden in all relevant
scenarios, while allowing the existing test cases to validate that
wildcard status doesn't affect non-wildcard certificates.
Signed-off-by: Alexander Scheel <alex.scheel@hashicorp.com>
* Add Wildcard allowance during signing operations
When using sign-verbatim, sign-intermediate, or getting certificate
generation parameters, set AllowWildcardCertificates to mirror existing
policies.
Signed-off-by: Alexander Scheel <alex.scheel@hashicorp.com>
* Add changelog entry
Signed-off-by: Alexander Scheel <alex.scheel@hashicorp.com>
2022-02-24 13:41:56 +00:00
* role . AllowWildcardCertificates = true
2015-11-18 15:16:09 +00:00
if cn := data . Get ( "common_name" ) . ( string ) ; len ( cn ) == 0 {
role . UseCSRCommonName = true
}
var caErr error
2022-01-27 04:06:25 +00:00
signingBundle , caErr := fetchCAInfo ( ctx , b , req )
2022-02-22 19:39:21 +00:00
if caErr != nil {
switch caErr . ( type ) {
case errutil . UserError :
return nil , errutil . UserError { Err : fmt . Sprintf (
"could not fetch the CA certificate (was one set?): %s" , caErr ) }
default :
return nil , errutil . InternalError { Err : fmt . Sprintf (
"error fetching CA certificate: %s" , caErr ) }
}
2015-11-18 15:16:09 +00:00
}
useCSRValues := data . Get ( "use_csr_values" ) . ( bool )
maxPathLengthIface , ok := data . GetOk ( "max_path_length" )
if ok {
maxPathLength := maxPathLengthIface . ( int )
role . MaxPathLength = & maxPathLength
}
2019-05-09 15:43:11 +00:00
input := & inputBundle {
req : req ,
apiData : data ,
role : role ,
2018-02-16 22:19:34 +00:00
}
2019-05-09 15:43:11 +00:00
parsedBundle , err := signCert ( b , input , signingBundle , true , useCSRValues )
2015-11-18 15:16:09 +00:00
if err != nil {
switch err . ( type ) {
2016-07-28 19:19:27 +00:00
case errutil . UserError :
2015-11-18 15:16:09 +00:00
return logical . ErrorResponse ( err . Error ( ) ) , nil
2022-02-22 19:39:21 +00:00
default :
return nil , errutil . InternalError { Err : fmt . Sprintf (
"error signing cert: %s" , err ) }
2015-11-18 15:16:09 +00:00
}
}
2016-09-28 00:50:17 +00:00
if err := parsedBundle . Verify ( ) ; err != nil {
2021-04-22 15:20:59 +00:00
return nil , fmt . Errorf ( "verification of parsed bundle failed: %w" , err )
2016-09-28 00:50:17 +00:00
}
signingCB , err := signingBundle . ToCertBundle ( )
if err != nil {
2021-04-22 15:20:59 +00:00
return nil , fmt . Errorf ( "error converting raw signing bundle to cert bundle: %w" , err )
2016-09-28 00:50:17 +00:00
}
2015-11-18 15:16:09 +00:00
cb , err := parsedBundle . ToCertBundle ( )
if err != nil {
2021-04-22 15:20:59 +00:00
return nil , fmt . Errorf ( "error converting raw cert bundle to cert bundle: %w" , err )
2015-11-18 15:16:09 +00:00
}
2016-05-09 23:53:28 +00:00
resp := & logical . Response {
Data : map [ string ] interface { } {
2015-11-18 15:16:09 +00:00
"expiration" : int64 ( parsedBundle . Certificate . NotAfter . Unix ( ) ) ,
"serial_number" : cb . SerialNumber ,
} ,
2016-05-09 23:53:28 +00:00
}
2015-11-18 15:16:09 +00:00
2017-09-13 15:42:45 +00:00
if signingBundle . Certificate . NotAfter . Before ( parsedBundle . Certificate . NotAfter ) {
resp . AddWarning ( "The expiration time for the signed certificate is after the CA's expiration time. If the new certificate is not treated as a root, validation paths with the certificate past the issuing CA's expiration time will fail." )
}
2016-02-01 18:19:41 +00:00
switch format {
case "pem" :
resp . Data [ "certificate" ] = cb . Certificate
2016-09-28 00:50:17 +00:00
resp . Data [ "issuing_ca" ] = signingCB . Certificate
if cb . CAChain != nil && len ( cb . CAChain ) > 0 {
resp . Data [ "ca_chain" ] = cb . CAChain
}
2016-02-01 18:19:41 +00:00
case "pem_bundle" :
2016-09-28 00:50:17 +00:00
resp . Data [ "certificate" ] = cb . ToPEMBundle ( )
resp . Data [ "issuing_ca" ] = signingCB . Certificate
if cb . CAChain != nil && len ( cb . CAChain ) > 0 {
resp . Data [ "ca_chain" ] = cb . CAChain
}
2016-02-01 18:19:41 +00:00
case "der" :
2015-11-18 15:16:09 +00:00
resp . Data [ "certificate" ] = base64 . StdEncoding . EncodeToString ( parsedBundle . CertificateBytes )
2016-09-28 00:50:17 +00:00
resp . Data [ "issuing_ca" ] = base64 . StdEncoding . EncodeToString ( signingBundle . CertificateBytes )
var caChain [ ] string
for _ , caCert := range parsedBundle . CAChain {
caChain = append ( caChain , base64 . StdEncoding . EncodeToString ( caCert . Bytes ) )
}
if caChain != nil && len ( caChain ) > 0 {
resp . Data [ "ca_chain" ] = cb . CAChain
}
2015-11-18 15:16:09 +00:00
}
2018-01-19 06:44:44 +00:00
err = req . Storage . Put ( ctx , & logical . StorageEntry {
2017-05-03 14:12:58 +00:00
Key : "certs/" + normalizeSerial ( cb . SerialNumber ) ,
2015-11-18 15:16:09 +00:00
Value : parsedBundle . CertificateBytes ,
} )
if err != nil {
2021-04-22 15:20:59 +00:00
return nil , fmt . Errorf ( "unable to store certificate locally: %w" , err )
2015-11-18 15:16:09 +00:00
}
if parsedBundle . Certificate . MaxPathLen == 0 {
resp . AddWarning ( "Max path length of the signed certificate is zero. This certificate cannot be used to issue intermediate CA certificates." )
}
return resp , nil
}
2018-01-08 18:31:38 +00:00
func ( b * backend ) pathCASignSelfIssued ( ctx context . Context , req * logical . Request , data * framework . FieldData ) ( * logical . Response , error ) {
2017-09-01 03:07:15 +00:00
var err error
certPem := data . Get ( "certificate" ) . ( string )
block , _ := pem . Decode ( [ ] byte ( certPem ) )
if block == nil || len ( block . Bytes ) == 0 {
return logical . ErrorResponse ( "certificate could not be PEM-decoded" ) , nil
}
certs , err := x509 . ParseCertificates ( block . Bytes )
if err != nil {
return logical . ErrorResponse ( fmt . Sprintf ( "error parsing certificate: %s" , err ) ) , nil
}
if len ( certs ) != 1 {
return logical . ErrorResponse ( fmt . Sprintf ( "%d certificates found in PEM file, expected 1" , len ( certs ) ) ) , nil
}
cert := certs [ 0 ]
if ! cert . IsCA {
return logical . ErrorResponse ( "given certificate is not a CA certificate" ) , nil
}
if ! reflect . DeepEqual ( cert . Issuer , cert . Subject ) {
return logical . ErrorResponse ( "given certificate is not self-issued" ) , nil
}
var caErr error
2022-01-27 04:06:25 +00:00
signingBundle , caErr := fetchCAInfo ( ctx , b , req )
2022-02-22 19:39:21 +00:00
if caErr != nil {
switch caErr . ( type ) {
case errutil . UserError :
return nil , errutil . UserError { Err : fmt . Sprintf (
"could not fetch the CA certificate (was one set?): %s" , caErr ) }
default :
return nil , errutil . InternalError { Err : fmt . Sprintf ( "error fetching CA certificate: %s" , caErr ) }
}
2017-09-01 03:07:15 +00:00
}
signingCB , err := signingBundle . ToCertBundle ( )
if err != nil {
2021-04-22 15:20:59 +00:00
return nil , fmt . Errorf ( "error converting raw signing bundle to cert bundle: %w" , err )
2017-09-01 03:07:15 +00:00
}
2019-05-09 15:43:11 +00:00
urls := & certutil . URLEntries { }
2017-09-01 03:07:15 +00:00
if signingBundle . URLs != nil {
urls = signingBundle . URLs
}
cert . IssuingCertificateURL = urls . IssuingCertificates
cert . CRLDistributionPoints = urls . CRLDistributionPoints
cert . OCSPServer = urls . OCSPServers
2021-09-14 15:07:27 +00:00
// If the requested signature algorithm isn't the same as the signing certificate, and
// the user has requested a cross-algorithm signature, reset the template's signing algorithm
// to that of the signing key
signingPubType , signingAlgorithm , err := publicKeyType ( signingBundle . Certificate . PublicKey )
if err != nil {
return nil , fmt . Errorf ( "error determining signing certificate algorithm type: %e" , err )
}
certPubType , _ , err := publicKeyType ( cert . PublicKey )
if err != nil {
return nil , fmt . Errorf ( "error determining template algorithm type: %e" , err )
}
if signingPubType != certPubType {
2021-11-01 19:42:25 +00:00
b , ok := data . GetOk ( "require_matching_certificate_algorithms" )
if ! ok || ! b . ( bool ) {
2021-09-14 15:07:27 +00:00
cert . SignatureAlgorithm = signingAlgorithm
2021-11-01 19:42:25 +00:00
} else {
return nil , fmt . Errorf ( "signing certificate's public key algorithm (%s) does not match submitted certificate's (%s), and require_matching_certificate_algorithms is true" ,
signingPubType . String ( ) , certPubType . String ( ) )
2021-09-14 15:07:27 +00:00
}
}
2017-09-13 15:42:45 +00:00
newCert , err := x509 . CreateCertificate ( rand . Reader , cert , signingBundle . Certificate , cert . PublicKey , signingBundle . PrivateKey )
2017-09-01 03:07:15 +00:00
if err != nil {
2021-04-22 15:20:59 +00:00
return nil , fmt . Errorf ( "error signing self-issued certificate: %w" , err )
2017-09-01 03:07:15 +00:00
}
if len ( newCert ) == 0 {
return nil , fmt . Errorf ( "nil cert was created when signing self-issued certificate" )
}
pemCert := pem . EncodeToMemory ( & pem . Block {
Type : "CERTIFICATE" ,
Bytes : newCert ,
} )
return & logical . Response {
Data : map [ string ] interface { } {
2018-03-18 20:00:51 +00:00
"certificate" : strings . TrimSpace ( string ( pemCert ) ) ,
2017-09-01 03:07:15 +00:00
"issuing_ca" : signingCB . Certificate ,
} ,
} , nil
}
2021-09-14 15:07:27 +00:00
// Adapted from similar code in https://github.com/golang/go/blob/4a4221e8187189adcc6463d2d96fe2e8da290132/src/crypto/x509/x509.go#L1342,
// may need to be updated in the future.
func publicKeyType ( pub crypto . PublicKey ) ( pubType x509 . PublicKeyAlgorithm , sigAlgo x509 . SignatureAlgorithm , err error ) {
switch pub := pub . ( type ) {
case * rsa . PublicKey :
pubType = x509 . RSA
sigAlgo = x509 . SHA256WithRSA
case * ecdsa . PublicKey :
pubType = x509 . ECDSA
switch pub . Curve {
case elliptic . P224 ( ) , elliptic . P256 ( ) :
sigAlgo = x509 . ECDSAWithSHA256
case elliptic . P384 ( ) :
sigAlgo = x509 . ECDSAWithSHA384
case elliptic . P521 ( ) :
sigAlgo = x509 . ECDSAWithSHA512
default :
err = errors . New ( "x509: unknown elliptic curve" )
}
case ed25519 . PublicKey :
pubType = x509 . Ed25519
sigAlgo = x509 . PureEd25519
default :
err = errors . New ( "x509: only RSA, ECDSA and Ed25519 keys supported" )
}
return
}
2015-11-18 15:16:09 +00:00
const pathGenerateRootHelpSyn = `
Generate a new CA certificate and private key used for signing .
`
const pathGenerateRootHelpDesc = `
See the API documentation for more information .
`
2017-08-15 18:00:40 +00:00
const pathDeleteRootHelpSyn = `
Deletes the root CA key to allow a new one to be generated .
`
const pathDeleteRootHelpDesc = `
See the API documentation for more information .
`
2015-11-18 15:16:09 +00:00
const pathSignIntermediateHelpSyn = `
Issue an intermediate CA certificate based on the provided CSR .
`
const pathSignIntermediateHelpDesc = `
2017-08-31 19:46:13 +00:00
see the API documentation for more information .
`
const pathSignSelfIssuedHelpSyn = `
Signs another CA ' s self - issued certificate .
`
const pathSignSelfIssuedHelpDesc = `
Signs another CA ' s self - issued certificate . This is most often used for rolling roots ; unless you know you need this you probably want to use sign - intermediate instead .
2017-09-13 15:42:45 +00:00
Note that this is a very privileged operation and should be extremely restricted in terms of who is allowed to use it . All values will be taken directly from the incoming certificate and only verification that it is self - issued will be performed .
Configured URLs for CRLs / OCSP / etc . will be copied over and the issuer will be this mount ' s CA cert . Other than that , all other values will be used verbatim .
2015-11-18 15:16:09 +00:00
`