Compare issuer certificates using cert, signature algo and signature fields (#15285)

* Move existing test helpers into a new test_helpers.go file within PKI

* Compare issuer certificates by cert, signature algo and signature

 - Instead of comparing the strings of a certificate, instead leverage
   the Go Raw attribute within a parsed certificate to compare. The Raw
   attribute is a byte array of an ASN.1 DER containing the cert,
   signature algo and signature.
 - Rework a bit of the importIssuers function as well to fail checks on the
   inbound issuer earlier as well as load keys/issuers just before we need
   them
This commit is contained in:
Steven Clark
2022-05-11 13:04:54 -04:00
committed by GitHub
parent 229681f767
commit 4ea9745eea
6 changed files with 298 additions and 248 deletions

View File

@@ -9,16 +9,12 @@ import (
"crypto/elliptic"
"crypto/rand"
"crypto/rsa"
"crypto/sha256"
"crypto/sha512"
"crypto/x509"
"crypto/x509/pkix"
"encoding/base64"
"encoding/json"
"encoding/pem"
"fmt"
"hash"
"io/ioutil"
"math"
"math/big"
mathrand "math/rand"
@@ -2780,23 +2776,6 @@ func TestBackend_SignSelfIssued_DifferentTypes(t *testing.T) {
}
}
func getSelfSigned(t *testing.T, subject, issuer *x509.Certificate, key *rsa.PrivateKey) (string, *x509.Certificate) {
t.Helper()
selfSigned, err := x509.CreateCertificate(rand.Reader, subject, issuer, key.Public(), key)
if err != nil {
t.Fatal(err)
}
cert, err := x509.ParseCertificate(selfSigned)
if err != nil {
t.Fatal(err)
}
pemSS := strings.TrimSpace(string(pem.EncodeToMemory(&pem.Block{
Type: "CERTIFICATE",
Bytes: selfSigned,
})))
return pemSS, cert
}
// This is a really tricky test because the Go stdlib asn1 package is incapable
// of doing the right thing with custom OID SANs (see comments in the package,
// it's readily admitted that it's too magic) but that means that any
@@ -4028,70 +4007,6 @@ func TestBackend_RevokePlusTidy_Intermediate(t *testing.T) {
}
}
func getParsedCrl(t *testing.T, client *api.Client, mountPoint string) *pkix.CertificateList {
path := fmt.Sprintf("/v1/%s/crl", mountPoint)
return getParsedCrlAtPath(t, client, path)
}
func getParsedCrlForIssuer(t *testing.T, client *api.Client, mountPoint string, issuer string) *pkix.CertificateList {
path := fmt.Sprintf("/v1/%v/issuer/%v/crl/der", mountPoint, issuer)
crl := getParsedCrlAtPath(t, client, path)
// Now fetch the issuer as well and verify the certificate
path = fmt.Sprintf("/v1/%v/issuer/%v/der", mountPoint, issuer)
req := client.NewRequest("GET", path)
resp, err := client.RawRequest(req)
if err != nil {
t.Fatal(err)
}
defer resp.Body.Close()
certBytes, err := ioutil.ReadAll(resp.Body)
if err != nil {
t.Fatalf("err: %s", err)
}
if len(certBytes) == 0 {
t.Fatalf("expected certificate in response body")
}
cert, err := x509.ParseCertificate(certBytes)
if err != nil {
t.Fatal(err)
}
if cert == nil {
t.Fatalf("expected parsed certificate")
}
if err := cert.CheckCRLSignature(crl); err != nil {
t.Fatalf("expected valid signature on CRL for issuer %v: %v", issuer, crl)
}
return crl
}
func getParsedCrlAtPath(t *testing.T, client *api.Client, path string) *pkix.CertificateList {
req := client.NewRequest("GET", path)
resp, err := client.RawRequest(req)
if err != nil {
t.Fatal(err)
}
defer resp.Body.Close()
crlBytes, err := ioutil.ReadAll(resp.Body)
if err != nil {
t.Fatalf("err: %s", err)
}
if len(crlBytes) == 0 {
t.Fatalf("expected CRL in response body")
}
crl, err := x509.ParseDERCRL(crlBytes)
if err != nil {
t.Fatal(err)
}
return crl
}
func TestBackend_Root_FullCAChain(t *testing.T) {
testCases := []struct {
testName string
@@ -5074,90 +4989,3 @@ var (
edCAKey string
edCACert string
)
func mountPKIEndpoint(t *testing.T, client *api.Client, path string) {
var err error
err = client.Sys().Mount(path, &api.MountInput{
Type: "pki",
Config: api.MountConfigInput{
DefaultLeaseTTL: "16h",
MaxLeaseTTL: "32h",
},
})
require.NoError(t, err, "failed mounting pki endpoint")
}
func requireSignedBy(t *testing.T, cert *x509.Certificate, key crypto.PublicKey) {
switch key.(type) {
case *rsa.PublicKey:
requireRSASignedBy(t, cert, key.(*rsa.PublicKey))
case *ecdsa.PublicKey:
requireECDSASignedBy(t, cert, key.(*ecdsa.PublicKey))
case ed25519.PublicKey:
requireED25519SignedBy(t, cert, key.(ed25519.PublicKey))
default:
require.Fail(t, "unknown public key type %#v", key)
}
}
func requireRSASignedBy(t *testing.T, cert *x509.Certificate, key *rsa.PublicKey) {
require.Contains(t, []x509.SignatureAlgorithm{x509.SHA256WithRSA, x509.SHA512WithRSA},
cert.SignatureAlgorithm, "only sha256 signatures supported")
var hasher hash.Hash
var hashAlgo crypto.Hash
switch cert.SignatureAlgorithm {
case x509.SHA256WithRSA:
hasher = sha256.New()
hashAlgo = crypto.SHA256
case x509.SHA512WithRSA:
hasher = sha512.New()
hashAlgo = crypto.SHA512
}
hasher.Write(cert.RawTBSCertificate)
hashData := hasher.Sum(nil)
err := rsa.VerifyPKCS1v15(key, hashAlgo, hashData, cert.Signature)
require.NoError(t, err, "the certificate was not signed by the expected public rsa key.")
}
func requireECDSASignedBy(t *testing.T, cert *x509.Certificate, key *ecdsa.PublicKey) {
require.Contains(t, []x509.SignatureAlgorithm{x509.ECDSAWithSHA256, x509.ECDSAWithSHA512},
cert.SignatureAlgorithm, "only ecdsa signatures supported")
var hasher hash.Hash
switch cert.SignatureAlgorithm {
case x509.ECDSAWithSHA256:
hasher = sha256.New()
case x509.ECDSAWithSHA512:
hasher = sha512.New()
}
hasher.Write(cert.RawTBSCertificate)
hashData := hasher.Sum(nil)
verify := ecdsa.VerifyASN1(key, hashData, cert.Signature)
require.True(t, verify, "the certificate was not signed by the expected public ecdsa key.")
}
func requireED25519SignedBy(t *testing.T, cert *x509.Certificate, key ed25519.PublicKey) {
require.Equal(t, x509.PureEd25519, cert.SignatureAlgorithm)
ed25519.Verify(key, cert.RawTBSCertificate, cert.Signature)
}
func parseCert(t *testing.T, pemCert string) *x509.Certificate {
block, _ := pem.Decode([]byte(pemCert))
require.NotNil(t, block, "failed to decode PEM block")
cert, err := x509.ParseCertificate(block.Bytes)
require.NoError(t, err)
return cert
}
func requireMatchingPublicKeys(t *testing.T, cert *x509.Certificate, key crypto.PublicKey) {
certPubKey := cert.PublicKey
require.True(t, reflect.DeepEqual(certPubKey, key),
"public keys mismatched: got: %v, expected: %v", certPubKey, key)
}