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 }