123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442 |
- package initca
- import (
- "bytes"
- "crypto/ecdsa"
- "crypto/rsa"
- "os"
- "strings"
- "testing"
- "time"
- "github.com/cloudflare/cfssl/config"
- "github.com/cloudflare/cfssl/csr"
- "github.com/cloudflare/cfssl/helpers"
- "github.com/cloudflare/cfssl/signer"
- "github.com/cloudflare/cfssl/signer/local"
- )
- var validKeyParams = []csr.KeyRequest{
- {A: "rsa", S: 2048},
- {A: "rsa", S: 3072},
- {A: "rsa", S: 4096},
- {A: "ecdsa", S: 256},
- {A: "ecdsa", S: 384},
- {A: "ecdsa", S: 521},
- {A: "ed25519"},
- }
- var validCAConfigs = []csr.CAConfig{
- {PathLength: 0, PathLenZero: true},
- {PathLength: 0, PathLenZero: false},
- {PathLength: 2},
- {PathLength: 2, Expiry: "1h"},
- // invalid PathLenZero value will be ignored
- {PathLength: 2, PathLenZero: true},
- }
- var invalidCAConfig = csr.CAConfig{
- PathLength: 2,
- // Expiry must be a duration string
- Expiry: "2116/12/31",
- }
- var csrFiles = []string{
- "testdata/rsa2048.csr",
- "testdata/rsa3072.csr",
- "testdata/rsa4096.csr",
- "testdata/ecdsa256.csr",
- "testdata/ecdsa384.csr",
- "testdata/ecdsa521.csr",
- "testdata/ed25519.csr",
- }
- var testRSACAFile = "testdata/5min-rsa.pem"
- var testRSACAKeyFile = "testdata/5min-rsa-key.pem"
- var testECDSACAFile = "testdata/5min-ecdsa.pem"
- var testECDSACAKeyFile = "testdata/5min-ecdsa-key.pem"
- var testED25519CAFile = "testdata/5min-ed25519.pem"
- var testED25519CAKeyFile = "testdata/5min-ed25519-key.pem"
- var invalidCryptoParams = []csr.KeyRequest{
- // Weak Key
- {A: "rsa", S: 1024},
- // Bad param
- {A: "rsaCrypto", S: 2048},
- {A: "ecdsa", S: 2000},
- }
- func TestInitCA(t *testing.T) {
- var req *csr.CertificateRequest
- hostname := "cloudflare.com"
- crl := "http://crl.cloudflare.com/655c6a9b-01c6-4eea-bf21-be690cc315e0.crl" // cert_uuid.crl
- for _, param := range validKeyParams {
- for _, caconfig := range validCAConfigs {
- req = &csr.CertificateRequest{
- Names: []csr.Name{
- {
- C: "US",
- ST: "California",
- L: "San Francisco",
- O: "CloudFlare",
- OU: "Systems Engineering",
- },
- },
- CN: hostname,
- Hosts: []string{hostname, "www." + hostname},
- KeyRequest: ¶m,
- CA: &caconfig,
- CRL: crl,
- }
- certBytes, _, keyBytes, err := New(req)
- if err != nil {
- t.Fatal("InitCA failed:", err)
- }
- key, err := helpers.ParsePrivateKeyPEM(keyBytes)
- if err != nil {
- t.Fatal("InitCA private key parsing failed:", err)
- }
- cert, err := helpers.ParseCertificatePEM(certBytes)
- if err != nil {
- t.Fatal("InitCA cert parsing failed:", err)
- }
- // Verify if the CRL is set
- crlSet := false
- for _, certCrl := range cert.CRLDistributionPoints {
- if certCrl == crl {
- crlSet = true
- break
- }
- }
- if !crlSet {
- t.Fatal("Missing CRL on certificate")
- }
- // Verify key parameters.
- switch req.KeyRequest.Algo() {
- case "rsa":
- if cert.PublicKey.(*rsa.PublicKey).N.BitLen() != param.Size() {
- t.Fatal("Cert key length mismatch.")
- }
- if key.(*rsa.PrivateKey).N.BitLen() != param.Size() {
- t.Fatal("Private key length mismatch.")
- }
- case "ecdsa":
- if cert.PublicKey.(*ecdsa.PublicKey).Curve.Params().BitSize != param.Size() {
- t.Fatal("Cert key length mismatch.")
- }
- if key.(*ecdsa.PrivateKey).Curve.Params().BitSize != param.Size() {
- t.Fatal("Private key length mismatch.")
- }
- }
- // Verify CA MaxPathLen
- if caconfig.PathLength == 0 && cert.MaxPathLenZero != caconfig.PathLenZero {
- t.Fatalf("fail to init a CA cert with specified CA pathlen zero: expect %v, got %v", caconfig.PathLenZero, cert.MaxPathLenZero)
- }
- if caconfig.PathLength != 0 {
- if cert.MaxPathLen != caconfig.PathLength {
- t.Fatalf("fail to init a CA cert with specified CA pathlen: expect %d, got %d", caconfig.PathLength, cert.MaxPathLen)
- }
- if cert.MaxPathLenZero != false {
- t.Fatalf("fail to init a CA cert with specified CA pathlen zero: expect false, got %t", cert.MaxPathLenZero)
- }
- }
- // Replace the default CAPolicy with a test (short expiry) version and add a crl
- CAPolicy = func() *config.Signing {
- return &config.Signing{
- Default: &config.SigningProfile{
- Usage: []string{"cert sign", "crl sign"},
- ExpiryString: "300s",
- Expiry: 300 * time.Second,
- CAConstraint: config.CAConstraint{IsCA: true},
- CRL: crl,
- },
- }
- }
- // Start a signer
- s, err := local.NewSigner(key, cert, signer.DefaultSigAlgo(key), nil)
- if err != nil {
- t.Fatal("Signer Creation error:", err)
- }
- s.SetPolicy(CAPolicy())
- // Sign RSA, ECDSA and ed25519 customer CSRs.
- for _, csrFile := range csrFiles {
- csrBytes, err := os.ReadFile(csrFile)
- if err != nil {
- t.Fatal("CSR loading error:", err)
- }
- req := signer.SignRequest{
- Request: string(csrBytes),
- Hosts: signer.SplitHosts(hostname),
- Profile: "",
- Label: "",
- }
- bytes, err := s.Sign(req)
- if err != nil {
- t.Fatal(err)
- }
- customerCert, _ := helpers.ParseCertificatePEM(bytes)
- if customerCert.SignatureAlgorithm != s.SigAlgo() {
- t.Fatal("Signature Algorithm mismatch")
- }
- err = customerCert.CheckSignatureFrom(cert)
- if err != nil {
- t.Fatal("Signing CSR failed.", err)
- }
- }
- }
- }
- }
- func TestInvalidCAConfig(t *testing.T) {
- hostname := "example.com"
- req := &csr.CertificateRequest{
- Names: []csr.Name{
- {
- C: "US",
- ST: "California",
- L: "San Francisco",
- O: "CloudFlare",
- OU: "Systems Engineering",
- },
- },
- CN: hostname,
- Hosts: []string{hostname, "www." + hostname},
- KeyRequest: &validKeyParams[0],
- CA: &invalidCAConfig,
- }
- _, _, _, err := New(req)
- if err == nil {
- t.Fatalf("InitCA with bad CAConfig should fail: %v", invalidCAConfig)
- }
- }
- func TestInvalidCryptoParams(t *testing.T) {
- var req *csr.CertificateRequest
- hostname := "cloudflare.com"
- for _, invalidParam := range invalidCryptoParams {
- req = &csr.CertificateRequest{
- Names: []csr.Name{
- {
- C: "US",
- ST: "California",
- L: "San Francisco",
- O: "CloudFlare",
- OU: "Systems Engineering",
- },
- },
- CN: hostname,
- Hosts: []string{hostname, "www." + hostname},
- KeyRequest: &invalidParam,
- }
- _, _, _, err := New(req)
- if err == nil {
- t.Fatalf("InitCA with bad CAConfig should fail: %v", invalidCAConfig)
- }
- if !strings.Contains(err.Error(), `"code":2400`) {
- t.Fatal(err)
- }
- }
- }
- type validation struct {
- r *csr.CertificateRequest
- v bool
- }
- var testValidations = []validation{
- {&csr.CertificateRequest{}, false},
- {&csr.CertificateRequest{
- CN: "test CA",
- }, true},
- {&csr.CertificateRequest{
- Names: []csr.Name{{}},
- }, false},
- {&csr.CertificateRequest{
- Names: []csr.Name{
- {O: "Example CA"},
- },
- }, true},
- }
- func TestValidations(t *testing.T) {
- for i, tv := range testValidations {
- err := validator(tv.r)
- if tv.v && err != nil {
- t.Fatalf("%v", err)
- }
- if !tv.v && err == nil {
- t.Fatalf("%d: expected error, but no error was reported", i)
- }
- }
- }
- func TestRenewRSA(t *testing.T) {
- certPEM, err := RenewFromPEM(testRSACAFile, testRSACAKeyFile)
- if err != nil {
- t.Fatal(err)
- }
- // must parse ok
- cert, err := helpers.ParseCertificatePEM(certPEM)
- if err != nil {
- t.Fatal(err)
- }
- if !cert.IsCA {
- t.Fatal("renewed CA certificate is not CA")
- }
- // cert expiry must be 5 minutes
- expiry := cert.NotAfter.Sub(cert.NotBefore).Seconds()
- if expiry >= 301 || expiry <= 299 {
- t.Fatal("expiry is not correct:", expiry)
- }
- // check subject
- if cert.Subject.CommonName != "" {
- t.Fatal("Bad CommonName")
- }
- if len(cert.Subject.Country) != 1 || cert.Subject.Country[0] != "US" {
- t.Fatal("Bad Subject")
- }
- if len(cert.Subject.Organization) != 1 || cert.Subject.Organization[0] != "CloudFlare, Inc." {
- t.Fatal("Bad Subject")
- }
- }
- func TestRenewED25519(t *testing.T) {
- certPEM, err := RenewFromPEM(testED25519CAFile, testED25519CAKeyFile)
- if err != nil {
- t.Fatal(err)
- }
- // must parse ok
- cert, err := helpers.ParseCertificatePEM(certPEM)
- if err != nil {
- t.Fatal(err)
- }
- if !cert.IsCA {
- t.Fatal("renewed CA certificate is not CA")
- }
- // cert expiry must be 5 minutes
- expiry := cert.NotAfter.Sub(cert.NotBefore).Seconds()
- if expiry >= 301 || expiry <= 299 {
- t.Fatal("expiry is not correct:", expiry)
- }
- // check subject
- if cert.Subject.CommonName != "" {
- t.Fatal("Bad CommonName")
- }
- if len(cert.Subject.Country) != 1 || cert.Subject.Country[0] != "US" {
- t.Fatal("Bad Subject")
- }
- if len(cert.Subject.Organization) != 1 || cert.Subject.Organization[0] != "CloudFlare, Inc." {
- t.Fatal("Bad Subject")
- }
- }
- func TestRenewECDSA(t *testing.T) {
- certPEM, err := RenewFromPEM(testECDSACAFile, testECDSACAKeyFile)
- if err != nil {
- t.Fatal(err)
- }
- // must parse ok
- cert, err := helpers.ParseCertificatePEM(certPEM)
- if err != nil {
- t.Fatal(err)
- }
- if !cert.IsCA {
- t.Fatal("renewed CA certificate is not CA")
- }
- // cert expiry must be 5 minutes
- expiry := cert.NotAfter.Sub(cert.NotBefore).Seconds()
- if expiry >= 301 || expiry <= 299 {
- t.Fatal("expiry is not correct:", expiry)
- }
- // check subject
- if cert.Subject.CommonName != "" {
- t.Fatal("Bad CommonName")
- }
- if len(cert.Subject.Country) != 1 || cert.Subject.Country[0] != "US" {
- t.Fatal("Bad Subject")
- }
- if len(cert.Subject.Organization) != 1 || cert.Subject.Organization[0] != "CloudFlare, Inc." {
- t.Fatal("Bad Subject")
- }
- }
- func TestRenewMismatch(t *testing.T) {
- _, err := RenewFromPEM(testECDSACAFile, testRSACAKeyFile)
- if err == nil {
- t.Fatal("Fail to detect cert/key mismatch")
- }
- }
- func TestRenew(t *testing.T) {
- in, err := os.ReadFile(testECDSACAFile)
- if err != nil {
- t.Fatal(err)
- }
- cert, err := helpers.ParseCertificatePEM(in)
- if err != nil {
- t.Fatal(err)
- }
- in, err = os.ReadFile(testECDSACAKeyFile)
- if err != nil {
- t.Fatal(err)
- }
- priv, err := helpers.ParsePrivateKeyPEM(in)
- if err != nil {
- t.Fatal(err)
- }
- renewed, err := Update(cert, priv)
- if err != nil {
- t.Fatal(err)
- }
- newCert, err := helpers.ParseCertificatePEM(renewed)
- if err != nil {
- t.Fatal(err)
- }
- if !bytes.Equal(newCert.RawSubjectPublicKeyInfo, cert.RawSubjectPublicKeyInfo) {
- t.Fatal("Update returned a certificate with different subject public key info")
- }
- if !bytes.Equal(newCert.RawSubject, cert.RawSubject) {
- t.Fatal("Update returned a certificate with different subject info")
- }
- if !bytes.Equal(newCert.RawIssuer, cert.RawIssuer) {
- t.Fatal("Update returned a certificate with different issuer info")
- }
- }
|