istio.io/istio@v0.0.0-20240520182934-d79c90f27776/security/pkg/pki/util/generate_cert_test.go (about)

     1  // Copyright Istio Authors
     2  //
     3  // Licensed under the Apache License, Version 2.0 (the "License");
     4  // you may not use this file except in compliance with the License.
     5  // You may obtain a copy of the License at
     6  //
     7  //     http://www.apache.org/licenses/LICENSE-2.0
     8  //
     9  // Unless required by applicable law or agreed to in writing, software
    10  // distributed under the License is distributed on an "AS IS" BASIS,
    11  // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
    12  // See the License for the specific language governing permissions and
    13  // limitations under the License.
    14  
    15  package util
    16  
    17  import (
    18  	"bytes"
    19  	"crypto"
    20  	"crypto/ecdsa"
    21  	"crypto/ed25519"
    22  	"crypto/elliptic"
    23  	"crypto/rand"
    24  	"crypto/rsa"
    25  	"crypto/x509"
    26  	"crypto/x509/pkix"
    27  	"strings"
    28  	"testing"
    29  	"time"
    30  )
    31  
    32  var (
    33  	now     = time.Now().Round(time.Second).UTC()
    34  	certPem = `-----BEGIN CERTIFICATE-----
    35  MIIC5jCCAc6gAwIBAgIRAIDngVC9z3HRR4DdOvnKO38wDQYJKoZIhvcNAQELBQAw
    36  HDEaMBgGA1UEChMRazhzLmNsdXN0ZXIubG9jYWwwHhcNMTcxMTE1MDAzMzUyWhcN
    37  MjcxMTEzMDAzMzUyWjAcMRowGAYDVQQKExFrOHMuY2x1c3Rlci5sb2NhbDCCASIw
    38  DQYJKoZIhvcNAQEBBQADggEPADCCAQoCggEBAOLNvPT59LqfuJFZEkHNg5BABXqX
    39  Yy0yu/t60lsd+Z43eTjEctnhyk45/4KE909wSVdzrq6jvlWCki/iHLkbnZ9Bfk0E
    40  mGwP2TOjihOPWH9F6i8yO6GI5wqeQki7yiT/NozMo/vSNrso0Xa8WoQSN6svziP8
    41  b9OeSIIMWIa8F1vD1EOvyHYlZHPMw/IJCqAxQef50FpVu2sB8t4FKeswyv0+Twh+
    42  J75hB9OiDnM1G8Ex3An4G6KeUX8ptuJS6aLemuZrqOG6dsaG4HrC6OuIuxfyRbe2
    43  zJyyHeOnGhozGVXS9TpCp3Mkr54NyKl4+p3XfeVtuBeG7UUvHS7EvS+2Bl0CAwEA
    44  AaMjMCEwDgYDVR0PAQH/BAQDAgIEMA8GA1UdEwEB/wQFMAMBAf8wDQYJKoZIhvcN
    45  AQELBQADggEBAEe3XmOAod4CoLkOWNFP6RbtSO3jDO6bzV0qOioS8Yj55eQ78hR9
    46  R14TG5+QCHXz4W3FQMsgEg1OQodmw6lhupkvQn1ZP/zf3a/kfTuK0VOIzqeKe4TI
    47  IgsccmELXGdxojN23311/tAcq3d8pSTKusH7KNwAQWmerkxB6wzSHTiJWICFJzs4
    48  RWeVWm0l72yZcYFaZ/LBkn+gRyV88r51BR+IR7sMDB7k6hsdMWdxvNESr1h9JU+Q
    49  NbOwbkIREzozcpaJ2eSiksLkPIxh8/zaULUpPbVMOeOIybUK4iW+K2FyibCc5r9d
    50  vbw9mUuRBuYCROUaNv2/TAkauxVPCYPq7Ow=
    51  -----END CERTIFICATE-----`
    52  )
    53  
    54  func TestGenCertKeyFromOptions(t *testing.T) {
    55  	// set "notBefore" to be one hour ago, this ensures the issued certificate to
    56  	// be valid as of now.
    57  	caCertNotBefore := now.Add(-time.Hour)
    58  	caCertTTL := 24 * time.Hour
    59  	host := "test_ca.com"
    60  
    61  	// Options to generate a CA cert with RSA.
    62  	rsaCaCertOptions := CertOptions{
    63  		Host:         host,
    64  		NotBefore:    caCertNotBefore,
    65  		TTL:          caCertTTL,
    66  		SignerCert:   nil,
    67  		SignerPriv:   nil,
    68  		Org:          "MyOrg",
    69  		IsCA:         true,
    70  		IsSelfSigned: true,
    71  		IsClient:     false,
    72  		IsServer:     true,
    73  		RSAKeySize:   2048,
    74  	}
    75  
    76  	rsaCaCertPem, rsaCaPrivPem, err := GenCertKeyFromOptions(rsaCaCertOptions)
    77  	if err != nil {
    78  		t.Fatal(err)
    79  	}
    80  
    81  	// Options to generate a CA cert with EC.
    82  	ecCaCertOptions := CertOptions{
    83  		Host:         host,
    84  		NotBefore:    caCertNotBefore,
    85  		TTL:          caCertTTL,
    86  		SignerCert:   nil,
    87  		SignerPriv:   nil,
    88  		Org:          "MyOrg",
    89  		IsCA:         true,
    90  		IsSelfSigned: true,
    91  		IsClient:     false,
    92  		IsServer:     true,
    93  		ECSigAlg:     EcdsaSigAlg,
    94  	}
    95  
    96  	ecCaCertPem, ecCaPrivPem, err := GenCertKeyFromOptions(ecCaCertOptions)
    97  	if err != nil {
    98  		t.Fatal(err)
    99  	}
   100  
   101  	fields := &VerifyFields{
   102  		NotBefore:   caCertNotBefore,
   103  		TTL:         caCertTTL,
   104  		ExtKeyUsage: []x509.ExtKeyUsage{x509.ExtKeyUsageServerAuth},
   105  		KeyUsage:    x509.KeyUsageCertSign,
   106  		IsCA:        true,
   107  		Org:         "MyOrg",
   108  		Host:        host,
   109  	}
   110  	if VerifyCertificate(rsaCaPrivPem, rsaCaCertPem, rsaCaCertPem, fields) != nil {
   111  		t.Fatal(err)
   112  	}
   113  
   114  	if VerifyCertificate(ecCaPrivPem, ecCaCertPem, ecCaCertPem, fields) != nil {
   115  		t.Fatal(err)
   116  	}
   117  
   118  	rsaCaCert, err := ParsePemEncodedCertificate(rsaCaCertPem)
   119  	if err != nil {
   120  		t.Fatal(err)
   121  	}
   122  
   123  	ecCaCert, err := ParsePemEncodedCertificate(ecCaCertPem)
   124  	if err != nil {
   125  		t.Fatal(err)
   126  	}
   127  
   128  	rsaCaPriv, err := ParsePemEncodedKey(rsaCaPrivPem)
   129  	if err != nil {
   130  		t.Fatal(err)
   131  	}
   132  
   133  	ecCaPriv, err := ParsePemEncodedKey(ecCaPrivPem)
   134  	if err != nil {
   135  		t.Fatal(err)
   136  	}
   137  
   138  	notBefore := now.Add(-5 * time.Minute)
   139  	ttl := time.Hour
   140  	cases := map[string]struct {
   141  		certOptions  CertOptions
   142  		verifyFields *VerifyFields
   143  	}{
   144  		// These certs are signed by the CA cert
   145  		"RSA: Server cert with DNS SAN": {
   146  			certOptions: CertOptions{
   147  				Host:         "test_server.com",
   148  				NotBefore:    notBefore,
   149  				TTL:          ttl,
   150  				SignerCert:   rsaCaCert,
   151  				SignerPriv:   rsaCaPriv,
   152  				Org:          "",
   153  				IsCA:         false,
   154  				IsSelfSigned: false,
   155  				IsClient:     false,
   156  				IsServer:     true,
   157  				RSAKeySize:   2048,
   158  			},
   159  			verifyFields: &VerifyFields{
   160  				ExtKeyUsage: []x509.ExtKeyUsage{x509.ExtKeyUsageServerAuth},
   161  				IsCA:        false,
   162  				KeyUsage:    x509.KeyUsageDigitalSignature | x509.KeyUsageKeyEncipherment,
   163  				NotBefore:   notBefore,
   164  				TTL:         ttl,
   165  				Org:         "MyOrg",
   166  			},
   167  		},
   168  		"RSA: Server and client cert with DNS SAN": {
   169  			certOptions: CertOptions{
   170  				Host:         "test_client.com",
   171  				NotBefore:    notBefore,
   172  				TTL:          ttl,
   173  				SignerCert:   rsaCaCert,
   174  				SignerPriv:   rsaCaPriv,
   175  				Org:          "",
   176  				IsCA:         false,
   177  				IsSelfSigned: false,
   178  				IsClient:     true,
   179  				IsServer:     true,
   180  				RSAKeySize:   2048,
   181  			},
   182  			verifyFields: &VerifyFields{
   183  				ExtKeyUsage: []x509.ExtKeyUsage{x509.ExtKeyUsageServerAuth, x509.ExtKeyUsageClientAuth},
   184  				IsCA:        false,
   185  				KeyUsage:    x509.KeyUsageDigitalSignature | x509.KeyUsageKeyEncipherment,
   186  				NotBefore:   notBefore,
   187  				TTL:         ttl,
   188  				Org:         "MyOrg",
   189  			},
   190  		},
   191  		"RSA: Server cert with IP SAN": {
   192  			certOptions: CertOptions{
   193  				Host:         "1.2.3.4",
   194  				NotBefore:    notBefore,
   195  				TTL:          ttl,
   196  				SignerCert:   rsaCaCert,
   197  				SignerPriv:   rsaCaPriv,
   198  				Org:          "",
   199  				IsCA:         false,
   200  				IsSelfSigned: false,
   201  				IsClient:     false,
   202  				IsServer:     true,
   203  				RSAKeySize:   2048,
   204  			},
   205  			verifyFields: &VerifyFields{
   206  				ExtKeyUsage: []x509.ExtKeyUsage{x509.ExtKeyUsageServerAuth},
   207  				IsCA:        false,
   208  				KeyUsage:    x509.KeyUsageDigitalSignature | x509.KeyUsageKeyEncipherment,
   209  				NotBefore:   notBefore,
   210  				TTL:         ttl,
   211  				Org:         "MyOrg",
   212  			},
   213  		},
   214  		"RSA: Client cert with URI SAN": {
   215  			certOptions: CertOptions{
   216  				Host:         "spiffe://domain/ns/bar/sa/foo",
   217  				NotBefore:    notBefore,
   218  				TTL:          ttl,
   219  				SignerCert:   rsaCaCert,
   220  				SignerPriv:   rsaCaPriv,
   221  				Org:          "",
   222  				IsCA:         false,
   223  				IsSelfSigned: false,
   224  				IsClient:     true,
   225  				IsServer:     true,
   226  				RSAKeySize:   2048,
   227  			},
   228  			verifyFields: &VerifyFields{
   229  				ExtKeyUsage: []x509.ExtKeyUsage{x509.ExtKeyUsageServerAuth, x509.ExtKeyUsageClientAuth},
   230  				IsCA:        false,
   231  				KeyUsage:    x509.KeyUsageDigitalSignature | x509.KeyUsageKeyEncipherment,
   232  				NotBefore:   notBefore,
   233  				TTL:         ttl,
   234  				Org:         "MyOrg",
   235  			},
   236  		},
   237  		"RSA: Server cert with DNS for webhook": {
   238  			certOptions: CertOptions{
   239  				Host:         "spiffe://domain/ns/bar/sa/foo,bar.foo.svcs",
   240  				NotBefore:    notBefore,
   241  				TTL:          ttl,
   242  				SignerCert:   rsaCaCert,
   243  				SignerPriv:   rsaCaPriv,
   244  				Org:          "",
   245  				IsCA:         false,
   246  				IsSelfSigned: false,
   247  				IsClient:     false,
   248  				IsServer:     true,
   249  				RSAKeySize:   2048,
   250  			},
   251  			verifyFields: &VerifyFields{
   252  				ExtKeyUsage: []x509.ExtKeyUsage{x509.ExtKeyUsageServerAuth},
   253  				IsCA:        false,
   254  				KeyUsage:    x509.KeyUsageDigitalSignature | x509.KeyUsageKeyEncipherment,
   255  				NotBefore:   notBefore,
   256  				TTL:         ttl,
   257  				Org:         "MyOrg",
   258  			},
   259  		},
   260  		"RSA: Generate cert with multiple host names": {
   261  			certOptions: CertOptions{
   262  				Host:       "a,b",
   263  				NotBefore:  notBefore,
   264  				TTL:        ttl,
   265  				SignerCert: rsaCaCert,
   266  				SignerPriv: rsaCaPriv,
   267  				RSAKeySize: 2048,
   268  			},
   269  			verifyFields: &VerifyFields{
   270  				IsCA:     false,
   271  				KeyUsage: x509.KeyUsageDigitalSignature | x509.KeyUsageKeyEncipherment,
   272  			},
   273  		},
   274  		"RSA: Generate dual-use cert": {
   275  			certOptions: CertOptions{
   276  				Host:         "spiffe://domain/ns/bar/sa/foo",
   277  				NotBefore:    notBefore,
   278  				TTL:          ttl,
   279  				SignerCert:   rsaCaCert,
   280  				SignerPriv:   rsaCaPriv,
   281  				Org:          "",
   282  				IsCA:         false,
   283  				IsSelfSigned: false,
   284  				IsClient:     true,
   285  				IsServer:     true,
   286  				RSAKeySize:   2048,
   287  				IsDualUse:    true,
   288  			},
   289  			verifyFields: &VerifyFields{
   290  				ExtKeyUsage: []x509.ExtKeyUsage{x509.ExtKeyUsageServerAuth, x509.ExtKeyUsageClientAuth},
   291  				IsCA:        false,
   292  				KeyUsage:    x509.KeyUsageDigitalSignature | x509.KeyUsageKeyEncipherment,
   293  				NotBefore:   notBefore,
   294  				TTL:         ttl,
   295  				Org:         "MyOrg",
   296  				CommonName:  "spiffe://domain/ns/bar/sa/foo",
   297  			},
   298  		},
   299  		"RSA: Generate dual-use cert with multiple host names": {
   300  			certOptions: CertOptions{
   301  				Host:         "a,b,c",
   302  				NotBefore:    notBefore,
   303  				TTL:          ttl,
   304  				SignerCert:   rsaCaCert,
   305  				SignerPriv:   rsaCaPriv,
   306  				Org:          "",
   307  				IsCA:         false,
   308  				IsSelfSigned: false,
   309  				IsClient:     true,
   310  				IsServer:     true,
   311  				RSAKeySize:   2048,
   312  				IsDualUse:    true,
   313  			},
   314  			verifyFields: &VerifyFields{
   315  				ExtKeyUsage: []x509.ExtKeyUsage{x509.ExtKeyUsageServerAuth, x509.ExtKeyUsageClientAuth},
   316  				IsCA:        false,
   317  				KeyUsage:    x509.KeyUsageDigitalSignature | x509.KeyUsageKeyEncipherment,
   318  				NotBefore:   notBefore,
   319  				TTL:         ttl,
   320  				Org:         "MyOrg",
   321  				CommonName:  "a", // only first host used for CN
   322  			},
   323  		},
   324  		"RSA: Generate PKCS8 private key": {
   325  			certOptions: CertOptions{
   326  				Host:         "spiffe://domain/ns/bar/sa/foo",
   327  				NotBefore:    notBefore,
   328  				TTL:          ttl,
   329  				SignerCert:   rsaCaCert,
   330  				SignerPriv:   rsaCaPriv,
   331  				Org:          "",
   332  				IsCA:         false,
   333  				IsSelfSigned: false,
   334  				IsClient:     true,
   335  				IsServer:     true,
   336  				RSAKeySize:   2048,
   337  				PKCS8Key:     true,
   338  			},
   339  			verifyFields: &VerifyFields{
   340  				ExtKeyUsage: []x509.ExtKeyUsage{x509.ExtKeyUsageServerAuth, x509.ExtKeyUsageClientAuth},
   341  				IsCA:        false,
   342  				KeyUsage:    x509.KeyUsageDigitalSignature | x509.KeyUsageKeyEncipherment,
   343  				NotBefore:   notBefore,
   344  				TTL:         ttl,
   345  				Org:         "MyOrg",
   346  			},
   347  		},
   348  		"EC: Server cert with DNS SAN": {
   349  			certOptions: CertOptions{
   350  				Host:         "test_server.com",
   351  				NotBefore:    notBefore,
   352  				TTL:          ttl,
   353  				SignerCert:   ecCaCert,
   354  				SignerPriv:   ecCaPriv,
   355  				Org:          "",
   356  				IsCA:         false,
   357  				IsSelfSigned: false,
   358  				IsClient:     false,
   359  				IsServer:     true,
   360  				ECSigAlg:     EcdsaSigAlg,
   361  			},
   362  			verifyFields: &VerifyFields{
   363  				ExtKeyUsage: []x509.ExtKeyUsage{x509.ExtKeyUsageServerAuth},
   364  				IsCA:        false,
   365  				KeyUsage:    x509.KeyUsageDigitalSignature | x509.KeyUsageKeyEncipherment,
   366  				NotBefore:   notBefore,
   367  				TTL:         ttl,
   368  				Org:         "MyOrg",
   369  			},
   370  		},
   371  		"EC: Server and client cert with DNS SAN": {
   372  			certOptions: CertOptions{
   373  				Host:         "test_client.com",
   374  				NotBefore:    notBefore,
   375  				TTL:          ttl,
   376  				SignerCert:   ecCaCert,
   377  				SignerPriv:   ecCaPriv,
   378  				Org:          "",
   379  				IsCA:         false,
   380  				IsSelfSigned: false,
   381  				IsClient:     true,
   382  				IsServer:     true,
   383  				ECSigAlg:     EcdsaSigAlg,
   384  			},
   385  			verifyFields: &VerifyFields{
   386  				ExtKeyUsage: []x509.ExtKeyUsage{x509.ExtKeyUsageServerAuth, x509.ExtKeyUsageClientAuth},
   387  				IsCA:        false,
   388  				KeyUsage:    x509.KeyUsageDigitalSignature | x509.KeyUsageKeyEncipherment,
   389  				NotBefore:   notBefore,
   390  				TTL:         ttl,
   391  				Org:         "MyOrg",
   392  			},
   393  		},
   394  		"EC: Server cert with IP SAN": {
   395  			certOptions: CertOptions{
   396  				Host:         "1.2.3.4",
   397  				NotBefore:    notBefore,
   398  				TTL:          ttl,
   399  				SignerCert:   ecCaCert,
   400  				SignerPriv:   ecCaPriv,
   401  				Org:          "",
   402  				IsCA:         false,
   403  				IsSelfSigned: false,
   404  				IsClient:     false,
   405  				IsServer:     true,
   406  				ECSigAlg:     EcdsaSigAlg,
   407  			},
   408  			verifyFields: &VerifyFields{
   409  				ExtKeyUsage: []x509.ExtKeyUsage{x509.ExtKeyUsageServerAuth},
   410  				IsCA:        false,
   411  				KeyUsage:    x509.KeyUsageDigitalSignature | x509.KeyUsageKeyEncipherment,
   412  				NotBefore:   notBefore,
   413  				TTL:         ttl,
   414  				Org:         "MyOrg",
   415  			},
   416  		},
   417  		"EC: Client cert with URI SAN": {
   418  			certOptions: CertOptions{
   419  				Host:         "spiffe://domain/ns/bar/sa/foo",
   420  				NotBefore:    notBefore,
   421  				TTL:          ttl,
   422  				SignerCert:   ecCaCert,
   423  				SignerPriv:   ecCaPriv,
   424  				Org:          "",
   425  				IsCA:         false,
   426  				IsSelfSigned: false,
   427  				IsClient:     true,
   428  				IsServer:     true,
   429  				ECSigAlg:     EcdsaSigAlg,
   430  			},
   431  			verifyFields: &VerifyFields{
   432  				ExtKeyUsage: []x509.ExtKeyUsage{x509.ExtKeyUsageServerAuth, x509.ExtKeyUsageClientAuth},
   433  				IsCA:        false,
   434  				KeyUsage:    x509.KeyUsageDigitalSignature | x509.KeyUsageKeyEncipherment,
   435  				NotBefore:   notBefore,
   436  				TTL:         ttl,
   437  				Org:         "MyOrg",
   438  			},
   439  		},
   440  		"EC: Server cert with DNS for webhook": {
   441  			certOptions: CertOptions{
   442  				Host:         "spiffe://domain/ns/bar/sa/foo,bar.foo.svcs",
   443  				NotBefore:    notBefore,
   444  				TTL:          ttl,
   445  				SignerCert:   ecCaCert,
   446  				SignerPriv:   ecCaPriv,
   447  				Org:          "",
   448  				IsCA:         false,
   449  				IsSelfSigned: false,
   450  				IsClient:     false,
   451  				IsServer:     true,
   452  				ECSigAlg:     EcdsaSigAlg,
   453  			},
   454  			verifyFields: &VerifyFields{
   455  				ExtKeyUsage: []x509.ExtKeyUsage{x509.ExtKeyUsageServerAuth},
   456  				IsCA:        false,
   457  				KeyUsage:    x509.KeyUsageDigitalSignature | x509.KeyUsageKeyEncipherment,
   458  				NotBefore:   notBefore,
   459  				TTL:         ttl,
   460  				Org:         "MyOrg",
   461  			},
   462  		},
   463  		"EC: Generate cert with multiple host names": {
   464  			certOptions: CertOptions{
   465  				Host:       "a,b",
   466  				NotBefore:  notBefore,
   467  				TTL:        ttl,
   468  				SignerCert: ecCaCert,
   469  				SignerPriv: ecCaPriv,
   470  				ECSigAlg:   EcdsaSigAlg,
   471  			},
   472  			verifyFields: &VerifyFields{
   473  				IsCA:     false,
   474  				KeyUsage: x509.KeyUsageDigitalSignature | x509.KeyUsageKeyEncipherment,
   475  			},
   476  		},
   477  		"EC: Generate dual-use cert": {
   478  			certOptions: CertOptions{
   479  				Host:         "spiffe://domain/ns/bar/sa/foo",
   480  				NotBefore:    notBefore,
   481  				TTL:          ttl,
   482  				SignerCert:   ecCaCert,
   483  				SignerPriv:   ecCaPriv,
   484  				Org:          "",
   485  				IsCA:         false,
   486  				IsSelfSigned: false,
   487  				IsClient:     true,
   488  				IsServer:     true,
   489  				ECSigAlg:     EcdsaSigAlg,
   490  				IsDualUse:    true,
   491  			},
   492  			verifyFields: &VerifyFields{
   493  				ExtKeyUsage: []x509.ExtKeyUsage{x509.ExtKeyUsageServerAuth, x509.ExtKeyUsageClientAuth},
   494  				IsCA:        false,
   495  				KeyUsage:    x509.KeyUsageDigitalSignature | x509.KeyUsageKeyEncipherment,
   496  				NotBefore:   notBefore,
   497  				TTL:         ttl,
   498  				Org:         "MyOrg",
   499  				CommonName:  "spiffe://domain/ns/bar/sa/foo",
   500  			},
   501  		},
   502  		"EC: Generate dual-use cert with multiple host names": {
   503  			certOptions: CertOptions{
   504  				Host:         "a,b,c",
   505  				NotBefore:    notBefore,
   506  				TTL:          ttl,
   507  				SignerCert:   ecCaCert,
   508  				SignerPriv:   ecCaPriv,
   509  				Org:          "",
   510  				IsCA:         false,
   511  				IsSelfSigned: false,
   512  				IsClient:     true,
   513  				IsServer:     true,
   514  				ECSigAlg:     EcdsaSigAlg,
   515  				IsDualUse:    true,
   516  			},
   517  			verifyFields: &VerifyFields{
   518  				ExtKeyUsage: []x509.ExtKeyUsage{x509.ExtKeyUsageServerAuth, x509.ExtKeyUsageClientAuth},
   519  				IsCA:        false,
   520  				KeyUsage:    x509.KeyUsageDigitalSignature | x509.KeyUsageKeyEncipherment,
   521  				NotBefore:   notBefore,
   522  				TTL:         ttl,
   523  				Org:         "MyOrg",
   524  				CommonName:  "a", // only first host used for CN
   525  			},
   526  		},
   527  		"EC: Generate PKCS8 private key": {
   528  			certOptions: CertOptions{
   529  				Host:         "spiffe://domain/ns/bar/sa/foo",
   530  				NotBefore:    notBefore,
   531  				TTL:          ttl,
   532  				SignerCert:   ecCaCert,
   533  				SignerPriv:   ecCaPriv,
   534  				Org:          "",
   535  				IsCA:         false,
   536  				IsSelfSigned: false,
   537  				IsClient:     true,
   538  				IsServer:     true,
   539  				ECSigAlg:     EcdsaSigAlg,
   540  				PKCS8Key:     true,
   541  			},
   542  			verifyFields: &VerifyFields{
   543  				ExtKeyUsage: []x509.ExtKeyUsage{x509.ExtKeyUsageServerAuth, x509.ExtKeyUsageClientAuth},
   544  				IsCA:        false,
   545  				KeyUsage:    x509.KeyUsageDigitalSignature | x509.KeyUsageKeyEncipherment,
   546  				NotBefore:   notBefore,
   547  				TTL:         ttl,
   548  				Org:         "MyOrg",
   549  			},
   550  		},
   551  	}
   552  
   553  	for id, c := range cases {
   554  		t.Run(id, func(t *testing.T) {
   555  			certOptions := c.certOptions
   556  			certPem, privPem, err := GenCertKeyFromOptions(certOptions)
   557  			if err != nil {
   558  				t.Errorf("[%s] cert/key generation error: %v", id, err)
   559  			}
   560  
   561  			for _, host := range strings.Split(certOptions.Host, ",") {
   562  				c.verifyFields.Host = host
   563  				root := rsaCaCertPem
   564  				if c.certOptions.ECSigAlg != "" {
   565  					root = ecCaCertPem
   566  				}
   567  				if err := VerifyCertificate(privPem, certPem, root, c.verifyFields); err != nil {
   568  					t.Errorf("[%s] cert verification error: %v", id, err)
   569  				}
   570  			}
   571  		})
   572  	}
   573  }
   574  
   575  func TestGenCertFromCSR(t *testing.T) {
   576  	keyFile := "../testdata/key.pem"
   577  	certFile := "../testdata/cert.pem"
   578  	keycert, err := NewVerifiedKeyCertBundleFromFile(certFile, keyFile, nil, certFile)
   579  	if err != nil {
   580  		t.Errorf("Failed to load CA key and cert from files: %s, %s", keyFile, certFile)
   581  	}
   582  	signingCert, signingKey, _, _ := keycert.GetAll()
   583  
   584  	// Then generates signee's key pairs.
   585  	rsaSigneeKey, err := rsa.GenerateKey(rand.Reader, 1024)
   586  	if err != nil {
   587  		t.Errorf("failed to generate signee key pair %v", err)
   588  	}
   589  	ecdsaSigneeKey, err := ecdsa.GenerateKey(elliptic.P256(), rand.Reader)
   590  	if err != nil {
   591  		t.Errorf("failed to generate signee key pair %v", err)
   592  	}
   593  	_, ed25519SigneeKey, err := ed25519.GenerateKey(rand.Reader)
   594  	if err != nil {
   595  		t.Errorf("failed to generate signee key pair %v", err)
   596  	}
   597  
   598  	cases := []struct {
   599  		name        string
   600  		subjectIDs  []string
   601  		signeeKey   crypto.Signer
   602  		csrTemplate *x509.CertificateRequest
   603  	}{
   604  		{
   605  			name:       "Single subject ID",
   606  			subjectIDs: []string{"spiffe://test.com/abc/def"},
   607  			signeeKey:  rsaSigneeKey,
   608  			csrTemplate: &x509.CertificateRequest{
   609  				SignatureAlgorithm: x509.SHA256WithRSA,
   610  				DNSNames:           []string{"name_in_csr"},
   611  				Version:            3,
   612  			},
   613  		},
   614  		{
   615  			name:       "Two subject IDs",
   616  			subjectIDs: []string{"spiffe://test.com/abc/def", "test.com"},
   617  			signeeKey:  rsaSigneeKey,
   618  			csrTemplate: &x509.CertificateRequest{
   619  				SignatureAlgorithm: x509.SHA256WithRSA,
   620  				DNSNames:           []string{"name_in_csr"},
   621  				Version:            3,
   622  			},
   623  		},
   624  		{
   625  			name:       "Common name in CSR",
   626  			subjectIDs: []string{"test.com"},
   627  			signeeKey:  rsaSigneeKey,
   628  			csrTemplate: &x509.CertificateRequest{
   629  				Subject:            pkix.Name{CommonName: "common_name"},
   630  				SignatureAlgorithm: x509.SHA256WithRSA,
   631  				DNSNames:           []string{"name_in_csr"},
   632  				Version:            3,
   633  			},
   634  		},
   635  		{
   636  			name:       "Use ECDSA Signee Key",
   637  			subjectIDs: []string{"test.com"},
   638  			signeeKey:  ecdsaSigneeKey,
   639  			csrTemplate: &x509.CertificateRequest{
   640  				Subject:            pkix.Name{CommonName: "common_name"},
   641  				SignatureAlgorithm: x509.ECDSAWithSHA256,
   642  				DNSNames:           []string{"name_in_csr"},
   643  				Version:            3,
   644  			},
   645  		},
   646  		{
   647  			name:       "Use ED25519 Signee Key",
   648  			subjectIDs: []string{"test.com"},
   649  			signeeKey:  ed25519SigneeKey,
   650  			csrTemplate: &x509.CertificateRequest{
   651  				Subject:            pkix.Name{CommonName: "common_name"},
   652  				SignatureAlgorithm: x509.PureEd25519,
   653  				DNSNames:           []string{"name_in_csr"},
   654  				Version:            3,
   655  			},
   656  		},
   657  	}
   658  
   659  	for _, c := range cases {
   660  		derBytes, err := x509.CreateCertificateRequest(rand.Reader, c.csrTemplate, c.signeeKey)
   661  		if err != nil {
   662  			t.Errorf("failed to create certificate request %v", err)
   663  		}
   664  		csr, err := x509.ParseCertificateRequest(derBytes)
   665  		if err != nil {
   666  			t.Errorf("failed to parse certificate request %v", err)
   667  		}
   668  		derBytes, err = GenCertFromCSR(csr, signingCert, c.signeeKey.Public(), *signingKey, c.subjectIDs, time.Hour, false)
   669  		if err != nil {
   670  			t.Errorf("failed to GenCertFromCSR, error %v", err)
   671  		}
   672  
   673  		// Verify the certificate.
   674  		out, err := x509.ParseCertificate(derBytes)
   675  		if err != nil {
   676  			t.Errorf("failed to parse generated certificate %v", err)
   677  		}
   678  		if len(c.csrTemplate.Subject.CommonName) == 0 {
   679  			if len(out.Subject.CommonName) > 0 {
   680  				t.Errorf("Common name should be empty, but got %s", out.Subject.CommonName)
   681  			}
   682  		} else if out.Subject.CommonName != c.subjectIDs[0] {
   683  			t.Errorf("Unmatched common name, expected %s, got %s", c.subjectIDs[0], out.Subject.CommonName)
   684  		}
   685  		if len(out.Subject.Organization) > 0 {
   686  			t.Errorf("Organization should be empty, but got %s", out.Subject.Organization)
   687  		}
   688  
   689  		ids, err := ExtractIDs(out.Extensions)
   690  		if err != nil {
   691  			t.Errorf("failed to extract IDs from cert extension: %v", err)
   692  		}
   693  		if len(c.subjectIDs) != len(ids) {
   694  			t.Errorf("Wrong number of IDs encoded. Expected %d, but got %d.", len(c.subjectIDs), len(ids))
   695  		}
   696  		if len(c.subjectIDs) == 1 && c.subjectIDs[0] != ids[0] {
   697  			t.Errorf("incorrect ID encoded: %v VS (expected) %v", ids[0], c.subjectIDs[0])
   698  		}
   699  		if len(c.subjectIDs) == 2 {
   700  			if !(c.subjectIDs[0] == ids[0] && c.subjectIDs[1] == ids[1] || c.subjectIDs[0] == ids[1] && c.subjectIDs[1] == ids[0]) {
   701  				t.Errorf("incorrect IDs encoded: %v, %v VS (expected) %v, %v", ids[0], ids[1], c.subjectIDs[0], c.subjectIDs[1])
   702  			}
   703  		}
   704  		pool := x509.NewCertPool()
   705  		pool.AddCert(signingCert)
   706  		vo := x509.VerifyOptions{
   707  			Roots: pool,
   708  		}
   709  		if _, err := out.Verify(vo); err != nil {
   710  			t.Errorf("verification of the signed certificate failed %v", err)
   711  		}
   712  	}
   713  }
   714  
   715  func TestLoadSignerCredsFromFiles(t *testing.T) {
   716  	testCases := map[string]struct {
   717  		certFile    string
   718  		keyFile     string
   719  		expectedErr string
   720  	}{
   721  		"Good certificates": {
   722  			certFile:    "../testdata/cert.pem",
   723  			keyFile:     "../testdata/key.pem",
   724  			expectedErr: "",
   725  		},
   726  		"Missing cert files": {
   727  			certFile:    "../testdata/cert-not-exist.pem",
   728  			keyFile:     "../testdata/key.pem",
   729  			expectedErr: "certificate file reading failure (open ../testdata/cert-not-exist.pem: no such file or directory)",
   730  		},
   731  		"Missing key files": {
   732  			certFile:    "../testdata/cert.pem",
   733  			keyFile:     "../testdata/key-not-exist.pem",
   734  			expectedErr: "private key file reading failure (open ../testdata/key-not-exist.pem: no such file or directory)",
   735  		},
   736  		"Bad cert files": {
   737  			certFile:    "../testdata/cert-parse-fail.pem",
   738  			keyFile:     "../testdata/key.pem",
   739  			expectedErr: "pem encoded cert parsing failure (invalid PEM encoded certificate)",
   740  		},
   741  		"Bad key files": {
   742  			certFile:    "../testdata/cert.pem",
   743  			keyFile:     "../testdata/key-parse-fail.pem",
   744  			expectedErr: "pem encoded key parsing failure (invalid PEM-encoded key)",
   745  		},
   746  	}
   747  
   748  	for id, tc := range testCases {
   749  		cert, key, err := LoadSignerCredsFromFiles(tc.certFile, tc.keyFile)
   750  		if len(tc.expectedErr) > 0 {
   751  			if err == nil {
   752  				t.Errorf("[%s] Succeeded. Error expected: %v", id, err)
   753  			} else if err.Error() != tc.expectedErr {
   754  				t.Errorf("[%s] incorrect error message: %s VS (expected) %s",
   755  					id, err.Error(), tc.expectedErr)
   756  			}
   757  			continue
   758  		} else if err != nil {
   759  			t.Fatalf("[%s] Unexpected Error: %v", id, err)
   760  		}
   761  
   762  		if cert == nil || key == nil {
   763  			t.Errorf("[%s] Failed to load signer credentials from files: %v, %v", id, tc.certFile, tc.keyFile)
   764  		}
   765  	}
   766  }
   767  
   768  // TestAppendRootCerts verifies that AppendRootCerts works properly.
   769  func TestAppendRootCerts(t *testing.T) {
   770  	testCases := map[string]struct {
   771  		pemCert          []byte
   772  		rootFile         string
   773  		expectedErr      string
   774  		expectedRootCert []byte
   775  	}{
   776  		"Empty pem cert and root file": {
   777  			pemCert:          []byte{},
   778  			rootFile:         "",
   779  			expectedErr:      "",
   780  			expectedRootCert: []byte{},
   781  		},
   782  		"Non empty root file": {
   783  			pemCert:          []byte{},
   784  			rootFile:         "../testdata/cert.pem",
   785  			expectedErr:      "",
   786  			expectedRootCert: []byte(certPem + "\n"),
   787  		},
   788  		"Non empty pem cert": {
   789  			pemCert:          []byte(certPem),
   790  			rootFile:         "",
   791  			expectedErr:      "",
   792  			expectedRootCert: []byte(certPem),
   793  		},
   794  		"Non empty pem cert and non empty root file": {
   795  			pemCert:          []byte(certPem),
   796  			rootFile:         "../testdata/cert.pem",
   797  			expectedErr:      "",
   798  			expectedRootCert: append([]byte(certPem+"\n"), []byte(certPem+"\n")...),
   799  		},
   800  		"Not existing root file": {
   801  			pemCert:          []byte{},
   802  			rootFile:         "../testdata/notexistcert.pem",
   803  			expectedErr:      "",
   804  			expectedRootCert: []byte{},
   805  		},
   806  	}
   807  
   808  	for id, tc := range testCases {
   809  		rc, err := AppendRootCerts(tc.pemCert, tc.rootFile)
   810  		if len(tc.expectedErr) > 0 {
   811  			if err == nil {
   812  				t.Errorf("[%s] Succeeded. Error expected: %s", id, tc.expectedErr)
   813  			} else if err.Error() != tc.expectedErr {
   814  				t.Errorf("[%s] incorrect error message: %s VS (expected) %s",
   815  					id, err.Error(), tc.expectedErr)
   816  			}
   817  		} else if err != nil {
   818  			t.Errorf("[%s] Unexpected error: %s", id, err.Error())
   819  		}
   820  		if !bytes.Equal(rc, tc.expectedRootCert) {
   821  			t.Errorf("[%s] root cert does not match. %v VS (expected) %v", id, rc, tc.expectedRootCert)
   822  		}
   823  	}
   824  }
   825  
   826  // TestGenRootCertFromExistingKey creates original root certificate and private key, and then
   827  // uses the private key to generate a new root certificate. Verifies that the new root certificate
   828  // matches old root certificate except lifetime changes.
   829  func TestGenRootCertFromExistingKey(t *testing.T) {
   830  	// Generate root certificate and private key
   831  	caCertTTL := 24 * time.Hour
   832  	oldOrg := "old org"
   833  	caKeySize := 2048
   834  	caCertOptions := CertOptions{
   835  		TTL:          caCertTTL,
   836  		Org:          oldOrg,
   837  		IsCA:         true,
   838  		IsSelfSigned: true,
   839  		RSAKeySize:   caKeySize,
   840  		IsDualUse:    false,
   841  	}
   842  	oldRootCertPem, oldRootKeyPem, err := GenCertKeyFromOptions(caCertOptions)
   843  	if err != nil {
   844  		t.Errorf("failed to generate root certificate from options: %v", err)
   845  	}
   846  
   847  	// Rotate root certificate using the old private key.
   848  	// 1. get cert option from old root certificate.
   849  	oldCertOptions, err := GetCertOptionsFromExistingCert(oldRootCertPem)
   850  	if err != nil {
   851  		t.Errorf("failed to generate cert options from existing root certificate: %v", err)
   852  	}
   853  	// 2. create cert option for new root certificate.
   854  	defaultOrg := "default org"
   855  	// Verify that changing RSA key size does not change private key, as the key is reused.
   856  	defaultRSAKeySize := 4096
   857  	// Create a default cert options
   858  	newCertOptions := CertOptions{
   859  		TTL:           caCertTTL,
   860  		SignerPrivPem: oldRootKeyPem,
   861  		Org:           defaultOrg,
   862  		IsCA:          true,
   863  		IsSelfSigned:  true,
   864  		RSAKeySize:    defaultRSAKeySize,
   865  		IsDualUse:     false,
   866  	}
   867  	// Merge cert options.
   868  	newCertOptions = MergeCertOptions(newCertOptions, oldCertOptions)
   869  	if newCertOptions.Org != oldOrg && newCertOptions.Org == defaultOrg {
   870  		t.Error("Org in cert options should be overwritten")
   871  	}
   872  	// 3. create new root certificate.
   873  	newRootCertPem, newRootKeyPem, err := GenRootCertFromExistingKey(newCertOptions)
   874  	if err != nil {
   875  		t.Errorf("failed to generate root certificate from existing key: %v", err)
   876  	}
   877  
   878  	// Verifies that private key does not change, and certificates match.
   879  	if !bytes.Equal(oldRootKeyPem, newRootKeyPem) {
   880  		t.Errorf("private key should not change")
   881  	}
   882  	keyLen, err := getPublicKeySizeInBits(newRootKeyPem)
   883  	if err != nil {
   884  		t.Errorf("failed to parse private key: %v", err)
   885  	}
   886  	if keyLen != caKeySize {
   887  		t.Errorf("Public key size should not change, (got %d) vs (expected %d)",
   888  			keyLen, caKeySize)
   889  	}
   890  
   891  	oldRootCert, _ := ParsePemEncodedCertificate(oldRootCertPem)
   892  	newRootCert, _ := ParsePemEncodedCertificate(newRootCertPem)
   893  	if oldRootCert.Subject.String() != newRootCert.Subject.String() {
   894  		t.Errorf("certificate Subject does not match (old: %s) vs (new: %s)",
   895  			oldRootCert.Subject.String(), newRootCert.Subject.String())
   896  	}
   897  	if oldRootCert.Issuer.String() != newRootCert.Issuer.String() {
   898  		t.Errorf("certificate Issuer does not match (old: %s) vs (new: %s)",
   899  			oldRootCert.Issuer.String(), newRootCert.Issuer.String())
   900  	}
   901  	if oldRootCert.IsCA != newRootCert.IsCA {
   902  		t.Errorf("certificate IsCA does not match (old: %t) vs (new: %t)",
   903  			oldRootCert.IsCA, newRootCert.IsCA)
   904  	}
   905  	if oldRootCert.Version != newRootCert.Version {
   906  		t.Errorf("certificate Version does not match (old: %d) vs (new: %d)",
   907  			oldRootCert.Version, newRootCert.Version)
   908  	}
   909  	if oldRootCert.PublicKeyAlgorithm != newRootCert.PublicKeyAlgorithm {
   910  		t.Errorf("public key algorithm does not match (old: %s) vs (new: %s)",
   911  			oldRootCert.PublicKeyAlgorithm.String(), newRootCert.PublicKeyAlgorithm.String())
   912  	}
   913  }
   914  
   915  func getPublicKeySizeInBits(keyPem []byte) (int, error) {
   916  	privateKey, err := ParsePemEncodedKey(keyPem)
   917  	if err != nil {
   918  		return 0, err
   919  	}
   920  	k := privateKey.(*rsa.PrivateKey)
   921  	return k.PublicKey.Size() * 8, nil
   922  }
   923  
   924  // TestMergeCertOptions verifies that cert option fields are overwritten.
   925  func TestMergeCertOptions(t *testing.T) {
   926  	certTTL := 240 * time.Hour
   927  	org := "old org"
   928  	keySize := 512
   929  	defaultCertOptions := CertOptions{
   930  		TTL:          certTTL,
   931  		Org:          org,
   932  		IsCA:         true,
   933  		IsSelfSigned: true,
   934  		RSAKeySize:   keySize,
   935  		IsDualUse:    false,
   936  	}
   937  
   938  	deltaCertTTL := 1 * time.Hour
   939  	deltaOrg := "delta org"
   940  	deltaKeySize := 1024
   941  	deltaCertOptions := CertOptions{
   942  		TTL:          deltaCertTTL,
   943  		Org:          deltaOrg,
   944  		IsCA:         true,
   945  		IsSelfSigned: true,
   946  		RSAKeySize:   deltaKeySize,
   947  		IsDualUse:    true,
   948  	}
   949  
   950  	mergedCertOptions := MergeCertOptions(defaultCertOptions, deltaCertOptions)
   951  	if mergedCertOptions.Org != deltaCertOptions.Org {
   952  		t.Errorf("Org does not match, (get %s) vs (expected %s)",
   953  			mergedCertOptions.Org, deltaCertOptions.Org)
   954  	}
   955  	if mergedCertOptions.TTL != defaultCertOptions.TTL {
   956  		t.Errorf("TTL does not match, (get %s) vs (expected %s)",
   957  			mergedCertOptions.TTL.String(), deltaCertOptions.TTL.String())
   958  	}
   959  	if mergedCertOptions.IsCA != defaultCertOptions.IsCA {
   960  		t.Errorf("IsCA does not match, (get %t) vs (expected %t)",
   961  			mergedCertOptions.IsCA, deltaCertOptions.IsCA)
   962  	}
   963  	if mergedCertOptions.RSAKeySize != defaultCertOptions.RSAKeySize {
   964  		t.Errorf("TTL does not match, (get %d) vs (expected %d)",
   965  			mergedCertOptions.RSAKeySize, deltaCertOptions.RSAKeySize)
   966  	}
   967  	if mergedCertOptions.IsDualUse != defaultCertOptions.IsDualUse {
   968  		t.Errorf("IsDualUse does not match, (get %t) vs (expected %t)",
   969  			mergedCertOptions.IsDualUse, deltaCertOptions.IsDualUse)
   970  	}
   971  }