istio.io/istio@v0.0.0-20240520182934-d79c90f27776/security/pkg/pki/util/generate_csr_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 "crypto/ecdsa" 19 "crypto/rsa" 20 "crypto/x509" 21 "encoding/pem" 22 "errors" 23 "reflect" 24 "strings" 25 "testing" 26 ) 27 28 func TestGenCSR(t *testing.T) { 29 // Options to generate a CSR. 30 cases := map[string]struct { 31 csrOptions CertOptions 32 err error 33 }{ 34 "GenCSR with RSA": { 35 csrOptions: CertOptions{ 36 Host: "test_ca.com", 37 Org: "MyOrg", 38 RSAKeySize: 2048, 39 }, 40 }, 41 "GenCSR with EC": { 42 csrOptions: CertOptions{ 43 Host: "test_ca.com", 44 Org: "MyOrg", 45 ECSigAlg: EcdsaSigAlg, 46 }, 47 }, 48 "GenCSR with EC errors due to invalid signature algorithm": { 49 csrOptions: CertOptions{ 50 Host: "test_ca.com", 51 Org: "MyOrg", 52 ECSigAlg: "ED25519", 53 }, 54 err: errors.New("csr cert generation fails due to unsupported EC signature algorithm"), 55 }, 56 } 57 58 for id, tc := range cases { 59 csrPem, _, err := GenCSR(tc.csrOptions) 60 if err != nil { 61 if tc.err != nil { 62 if err.Error() == tc.err.Error() { 63 continue 64 } 65 t.Fatalf("%s: expected error to match expected error: %v", id, err) 66 } else { 67 t.Errorf("%s: failed to gen CSR", id) 68 } 69 } 70 71 pemBlock, _ := pem.Decode(csrPem) 72 if pemBlock == nil { 73 t.Fatalf("%s: failed to decode csr", id) 74 } 75 csr, err := x509.ParseCertificateRequest(pemBlock.Bytes) 76 if err != nil { 77 t.Fatalf("%s: failed to parse csr", id) 78 } 79 if err = csr.CheckSignature(); err != nil { 80 t.Errorf("%s: csr signature is invalid", id) 81 } 82 if csr.Subject.Organization[0] != "MyOrg" { 83 t.Errorf("%s: csr subject does not match", id) 84 } 85 if !strings.HasSuffix(string(csr.Extensions[0].Value), "test_ca.com") { 86 t.Errorf("%s: csr host does not match", id) 87 } 88 if tc.csrOptions.ECSigAlg != "" { 89 if tc.csrOptions.ECSigAlg != EcdsaSigAlg { 90 t.Errorf("%s: Only ECDSA signature algorithms are currently supported", id) 91 } 92 if reflect.TypeOf(csr.PublicKey) != reflect.TypeOf(&ecdsa.PublicKey{}) { 93 t.Errorf("%s: decoded PKCS#8 returned unexpected key type: %T", id, csr.PublicKey) 94 } 95 } else if reflect.TypeOf(csr.PublicKey) != reflect.TypeOf(&rsa.PublicKey{}) { 96 t.Errorf("%s: decoded PKCS#8 returned unexpected key type: %T", id, csr.PublicKey) 97 } 98 } 99 } 100 101 func TestGenCSRPKCS8Key(t *testing.T) { 102 // Options to generate a CSR. 103 cases := map[string]struct { 104 csrOptions CertOptions 105 }{ 106 "PKCS8Key with RSA": { 107 csrOptions: CertOptions{ 108 Host: "test_ca.com", 109 Org: "MyOrg", 110 RSAKeySize: 2048, 111 PKCS8Key: true, 112 }, 113 }, 114 "PKCS8Key with EC": { 115 csrOptions: CertOptions{ 116 Host: "test_ca.com", 117 Org: "MyOrg", 118 ECSigAlg: EcdsaSigAlg, 119 PKCS8Key: true, 120 }, 121 }, 122 } 123 124 for id, tc := range cases { 125 csrPem, keyPem, err := GenCSR(tc.csrOptions) 126 if err != nil { 127 t.Errorf("%s: failed to gen CSR", id) 128 } 129 130 pemBlock, _ := pem.Decode(csrPem) 131 if pemBlock == nil { 132 t.Fatalf("%s: failed to decode csr", id) 133 } 134 csr, err := x509.ParseCertificateRequest(pemBlock.Bytes) 135 if err != nil { 136 t.Fatalf("%s: failed to parse csr", id) 137 } 138 if err = csr.CheckSignature(); err != nil { 139 t.Errorf("%s: csr signature is invalid", id) 140 } 141 if csr.Subject.Organization[0] != "MyOrg" { 142 t.Errorf("%s: csr subject does not match", id) 143 } 144 if !strings.HasSuffix(string(csr.Extensions[0].Value), "test_ca.com") { 145 t.Errorf("%s: csr host does not match", id) 146 } 147 148 keyPemBlock, _ := pem.Decode(keyPem) 149 if keyPemBlock == nil { 150 t.Fatalf("%s: failed to decode private key PEM", id) 151 } 152 key, err := x509.ParsePKCS8PrivateKey(keyPemBlock.Bytes) 153 if err != nil { 154 t.Errorf("%s: failed to parse PKCS#8 private key", id) 155 } 156 if tc.csrOptions.ECSigAlg != "" { 157 if tc.csrOptions.ECSigAlg != EcdsaSigAlg { 158 t.Errorf("%s: Only ECDSA signature algorithms are currently supported", id) 159 } 160 if reflect.TypeOf(key) != reflect.TypeOf(&ecdsa.PrivateKey{}) { 161 t.Errorf("%s: decoded PKCS#8 returned unexpected key type: %T", id, key) 162 } 163 } else if reflect.TypeOf(key) != reflect.TypeOf(&rsa.PrivateKey{}) { 164 t.Errorf("%s: decoded PKCS#8 returned unexpected key type: %T", id, key) 165 } 166 } 167 } 168 169 func TestGenCSRWithInvalidOption(t *testing.T) { 170 // Options with invalid Key size. 171 csrOptions := CertOptions{ 172 Host: "test_ca.com", 173 Org: "MyOrg", 174 RSAKeySize: -1, 175 } 176 177 csr, priv, err := GenCSR(csrOptions) 178 179 if err == nil || csr != nil || priv != nil { 180 t.Errorf("Should have failed") 181 } 182 } 183 184 func TestGenCSRTemplateForDualUse(t *testing.T) { 185 tt := map[string]struct { 186 host string 187 expectedCN string 188 }{ 189 "Single host": { 190 host: "bla.com", 191 expectedCN: "bla.com", 192 }, 193 "Multiple hosts": { 194 host: "a.org,b.net,c.groups", 195 expectedCN: "a.org", 196 }, 197 } 198 199 for _, tc := range tt { 200 opts := CertOptions{ 201 Host: tc.host, 202 Org: "MyOrg", 203 RSAKeySize: 512, 204 IsDualUse: true, 205 } 206 207 csr, err := GenCSRTemplate(opts) 208 if err != nil { 209 t.Error(err) 210 } 211 212 if csr.Subject.CommonName != tc.expectedCN { 213 t.Errorf("unexpected value for 'CommonName' field: want %v but got %v", tc.expectedCN, csr.Subject.CommonName) 214 } 215 } 216 }