istio.io/istio@v0.0.0-20240520182934-d79c90f27776/security/pkg/pki/util/verify_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  	"crypto/x509"
    19  	"log"
    20  	"os"
    21  	"strings"
    22  	"testing"
    23  	"time"
    24  )
    25  
    26  func loadPEMFile(path string) string {
    27  	b, err := os.ReadFile(path)
    28  	if err != nil {
    29  		log.Fatalf("failed to load the pem file = %v, err = %v", path, err)
    30  	}
    31  	return string(b)
    32  }
    33  
    34  var (
    35  	key             = loadPEMFile("../testdata/key-10y.pem")
    36  	keyMismatch     = loadPEMFile("../testdata/key-mismatch.pem")
    37  	keyBad          = loadPEMFile("../testdata/key-verify-fail.pem")
    38  	certChainBad    = loadPEMFile("../testdata/cert-verify-fail.pem")
    39  	certChainNoRoot = loadPEMFile("../testdata/cert-noroot.pem")
    40  	certChain       = loadPEMFile("../testdata/cert-chain-10y.pem")
    41  	rootCertBad     = loadPEMFile("../testdata/root-verify-fail.pem")
    42  	rootCert        = loadPEMFile("../testdata/root-cert-10y.pem")
    43  	verifyField1    = &VerifyFields{
    44  		Host: "",
    45  	}
    46  
    47  	verifyField2 = &VerifyFields{
    48  		Host: "spiffe",
    49  	}
    50  
    51  	notBefore = &VerifyFields{
    52  		NotBefore: time.Unix(0, 0),
    53  		Host:      "spiffe://cluster.local/ns/default/sa/default",
    54  	}
    55  
    56  	ttl = &VerifyFields{
    57  		TTL:  time.Duration(0),
    58  		Host: "spiffe://cluster.local/ns/default/sa/default",
    59  	}
    60  
    61  	extKeyUsage = &VerifyFields{
    62  		TTL:  time.Duration(1),
    63  		Host: "spiffe://cluster.local/ns/default/sa/default",
    64  	}
    65  
    66  	keyUsage = &VerifyFields{
    67  		ExtKeyUsage: []x509.ExtKeyUsage{1, 2},
    68  		KeyUsage:    2,
    69  		Host:        "spiffe://cluster.local/ns/default/sa/default",
    70  	}
    71  
    72  	isCA = &VerifyFields{
    73  		ExtKeyUsage: []x509.ExtKeyUsage{1, 2},
    74  		KeyUsage:    5,
    75  		IsCA:        true,
    76  		Host:        "spiffe://cluster.local/ns/default/sa/default",
    77  	}
    78  
    79  	org = &VerifyFields{
    80  		ExtKeyUsage: []x509.ExtKeyUsage{1, 2},
    81  		KeyUsage:    5,
    82  		Org:         "bad",
    83  		Host:        "spiffe://cluster.local/ns/default/sa/default",
    84  	}
    85  
    86  	success = &VerifyFields{
    87  		ExtKeyUsage: []x509.ExtKeyUsage{1, 2},
    88  		KeyUsage:    5,
    89  		Host:        "spiffe://cluster.local/ns/default/sa/default",
    90  	}
    91  )
    92  
    93  func TestVerifyCert(t *testing.T) {
    94  	testCases := map[string]struct {
    95  		privPem        []byte
    96  		certChainPem   []byte
    97  		rootCertPem    []byte
    98  		expectedFields *VerifyFields
    99  		expectedErr    string
   100  	}{
   101  		"Root cert bad": {
   102  			privPem:        nil,
   103  			certChainPem:   nil,
   104  			rootCertPem:    []byte(rootCertBad),
   105  			expectedFields: verifyField1,
   106  			expectedErr:    "failed to parse root certificate",
   107  		},
   108  		"Cert chain bad": {
   109  			privPem:        nil,
   110  			certChainPem:   []byte(certChainBad),
   111  			rootCertPem:    []byte(rootCert),
   112  			expectedFields: verifyField1,
   113  			expectedErr:    "failed to parse certificate chain",
   114  		},
   115  		"Failed to verify cert chain": {
   116  			privPem:        nil,
   117  			certChainPem:   []byte(certChainNoRoot),
   118  			rootCertPem:    []byte(rootCert),
   119  			expectedFields: verifyField2,
   120  			expectedErr:    "failed to verify certificate: x509:",
   121  		},
   122  		"Failed to verify key": {
   123  			privPem:        []byte(keyBad),
   124  			certChainPem:   []byte(certChain),
   125  			rootCertPem:    []byte(rootCert),
   126  			expectedFields: verifyField2,
   127  			expectedErr:    "invalid PEM-encoded key",
   128  		},
   129  		"Failed to match key/cert": {
   130  			privPem:        []byte(keyMismatch),
   131  			certChainPem:   []byte(certChain),
   132  			rootCertPem:    []byte(rootCert),
   133  			expectedFields: verifyField2,
   134  			expectedErr:    "the generated private RSA key and cert doesn't match",
   135  		},
   136  		"Wrong SAN": {
   137  			privPem:        []byte(key),
   138  			certChainPem:   []byte(certChain),
   139  			rootCertPem:    []byte(rootCert),
   140  			expectedFields: verifyField2,
   141  			expectedErr:    "the certificate doesn't have the expected SAN for: spiffe",
   142  		},
   143  		"Timestamp error": {
   144  			privPem:        []byte(key),
   145  			certChainPem:   []byte(certChain),
   146  			rootCertPem:    []byte(rootCert),
   147  			expectedFields: notBefore,
   148  			expectedErr:    "unexpected value for 'NotBefore' field",
   149  		},
   150  		"TTL error": {
   151  			privPem:        []byte(key),
   152  			certChainPem:   []byte(certChain),
   153  			rootCertPem:    []byte(rootCert),
   154  			expectedFields: extKeyUsage,
   155  			expectedErr:    "unexpected value for 'NotAfter' - 'NotBefore'",
   156  		},
   157  		"extKeyUsage error": {
   158  			privPem:        []byte(key),
   159  			certChainPem:   []byte(certChain),
   160  			rootCertPem:    []byte(rootCert),
   161  			expectedFields: ttl,
   162  			expectedErr:    "unexpected value for 'ExtKeyUsage' field",
   163  		},
   164  		"KeyUsage Error": {
   165  			privPem:        []byte(key),
   166  			certChainPem:   []byte(certChain),
   167  			rootCertPem:    []byte(rootCert),
   168  			expectedFields: keyUsage,
   169  			expectedErr:    "unexpected value for 'KeyUsage' field",
   170  		},
   171  		"IsCA error": {
   172  			privPem:        []byte(key),
   173  			certChainPem:   []byte(certChain),
   174  			rootCertPem:    []byte(rootCert),
   175  			expectedFields: isCA,
   176  			expectedErr:    "unexpected value for 'IsCA' field",
   177  		},
   178  		"Org error": {
   179  			privPem:        []byte(key),
   180  			certChainPem:   []byte(certChain),
   181  			rootCertPem:    []byte(rootCert),
   182  			expectedFields: org,
   183  			expectedErr:    "unexpected value for 'Organization' field",
   184  		},
   185  		"Succeeded": {
   186  			privPem:        []byte(key),
   187  			certChainPem:   []byte(certChain),
   188  			rootCertPem:    []byte(rootCert),
   189  			expectedFields: success,
   190  			expectedErr:    "",
   191  		},
   192  	}
   193  	for id, tc := range testCases {
   194  		err := VerifyCertificate(
   195  			tc.privPem, tc.certChainPem, tc.rootCertPem, tc.expectedFields)
   196  		if err != nil {
   197  			if len(tc.expectedErr) == 0 {
   198  				t.Errorf("%s: Unexpected error: %v", id, err)
   199  			} else if !strings.Contains(err.Error(), tc.expectedErr) {
   200  				t.Errorf("%s: Unexpected error: %v VS (expected) %s", id, err, tc.expectedErr)
   201  			}
   202  		} else if len(tc.expectedErr) != 0 {
   203  			t.Errorf("%s: Expected error %s but succeeded", id, tc.expectedErr)
   204  		}
   205  	}
   206  }
   207  
   208  func TestCertExpired(t *testing.T) {
   209  	testCases := map[string]struct {
   210  		filepath string
   211  		expected bool
   212  	}{
   213  		"Expired Cert": {
   214  			filepath: "../testdata/expired-cert.pem",
   215  			expected: true,
   216  		},
   217  		"Not Expired Cert": {
   218  			filepath: "../testdata/notexpired-cert.pem",
   219  			expected: false,
   220  		},
   221  	}
   222  	for id, tc := range testCases {
   223  		t.Run(id, func(t *testing.T) {
   224  			certExpired, err := IsCertExpired(tc.filepath)
   225  			if err != nil {
   226  				t.Fatalf("failed to check the cert, err is: %v", err)
   227  			}
   228  			if certExpired != tc.expected {
   229  				t.Errorf("isCertExpired: get %v, want %v", certExpired, tc.expected)
   230  			}
   231  		})
   232  	}
   233  }