github.com/hashicorp/vault/sdk@v0.11.0/helper/ocsp/ocsp_test.go (about)

     1  // Copyright (c) 2017-2022 Snowflake Computing Inc. All rights reserved.
     2  
     3  package ocsp
     4  
     5  import (
     6  	"bytes"
     7  	"context"
     8  	"crypto"
     9  	"crypto/ecdsa"
    10  	"crypto/elliptic"
    11  	"crypto/rand"
    12  	"crypto/tls"
    13  	"crypto/x509"
    14  	"crypto/x509/pkix"
    15  	"errors"
    16  	"fmt"
    17  	"io"
    18  	"io/ioutil"
    19  	"math/big"
    20  	"net"
    21  	"net/http"
    22  	"net/http/httptest"
    23  	"net/url"
    24  	"testing"
    25  	"time"
    26  
    27  	"github.com/hashicorp/go-hclog"
    28  	"github.com/hashicorp/go-retryablehttp"
    29  	lru "github.com/hashicorp/golang-lru"
    30  	"github.com/stretchr/testify/require"
    31  	"golang.org/x/crypto/ocsp"
    32  )
    33  
    34  func TestOCSP(t *testing.T) {
    35  	targetURL := []string{
    36  		"https://sfcdev1.blob.core.windows.net/",
    37  		"https://sfctest0.snowflakecomputing.com/",
    38  		"https://s3-us-west-2.amazonaws.com/sfc-snowsql-updates/?prefix=1.1/windows_x86_64",
    39  	}
    40  
    41  	conf := VerifyConfig{
    42  		OcspFailureMode: FailOpenFalse,
    43  	}
    44  	c := New(testLogFactory, 10)
    45  	transports := []*http.Transport{
    46  		newInsecureOcspTransport(nil),
    47  		c.NewTransport(&conf),
    48  	}
    49  
    50  	for _, tgt := range targetURL {
    51  		c.ocspResponseCache, _ = lru.New2Q(10)
    52  		for _, tr := range transports {
    53  			c := &http.Client{
    54  				Transport: tr,
    55  				Timeout:   30 * time.Second,
    56  			}
    57  			req, err := http.NewRequest("GET", tgt, bytes.NewReader(nil))
    58  			if err != nil {
    59  				t.Fatalf("fail to create a request. err: %v", err)
    60  			}
    61  			res, err := c.Do(req)
    62  			if err != nil {
    63  				t.Fatalf("failed to GET contents. err: %v", err)
    64  			}
    65  			defer res.Body.Close()
    66  			_, err = ioutil.ReadAll(res.Body)
    67  			if err != nil {
    68  				t.Fatalf("failed to read content body for %v", tgt)
    69  			}
    70  
    71  		}
    72  	}
    73  }
    74  
    75  /**
    76  // Used for development, requires an active Vault with PKI setup
    77  func TestMultiOCSP(t *testing.T) {
    78  
    79  	targetURL := []string{
    80  		"https://localhost:8200/v1/pki/ocsp",
    81  		"https://localhost:8200/v1/pki/ocsp",
    82  		"https://localhost:8200/v1/pki/ocsp",
    83  	}
    84  
    85  	b, _ := pem.Decode([]byte(vaultCert))
    86  	caCert, _ := x509.ParseCertificate(b.Bytes)
    87  	conf := VerifyConfig{
    88  		OcspFailureMode:     FailOpenFalse,
    89  		QueryAllServers:     true,
    90  		OcspServersOverride: targetURL,
    91  		ExtraCas:            []*x509.Certificate{caCert},
    92  	}
    93  	c := New(testLogFactory, 10)
    94  	transports := []*http.Transport{
    95  		newInsecureOcspTransport(conf.ExtraCas),
    96  		c.NewTransport(&conf),
    97  	}
    98  
    99  	tgt := "https://localhost:8200/v1/pki/ca/pem"
   100  	c.ocspResponseCache, _ = lru.New2Q(10)
   101  	for _, tr := range transports {
   102  		c := &http.Client{
   103  			Transport: tr,
   104  			Timeout:   30 * time.Second,
   105  		}
   106  		req, err := http.NewRequest("GET", tgt, bytes.NewReader(nil))
   107  		if err != nil {
   108  			t.Fatalf("fail to create a request. err: %v", err)
   109  		}
   110  		res, err := c.Do(req)
   111  		if err != nil {
   112  			t.Fatalf("failed to GET contents. err: %v", err)
   113  		}
   114  		defer res.Body.Close()
   115  		_, err = ioutil.ReadAll(res.Body)
   116  		if err != nil {
   117  			t.Fatalf("failed to read content body for %v", tgt)
   118  		}
   119  	}
   120  }
   121  */
   122  
   123  func TestUnitEncodeCertIDGood(t *testing.T) {
   124  	targetURLs := []string{
   125  		"faketestaccount.snowflakecomputing.com:443",
   126  		"s3-us-west-2.amazonaws.com:443",
   127  		"sfcdev1.blob.core.windows.net:443",
   128  	}
   129  	for _, tt := range targetURLs {
   130  		chainedCerts := getCert(tt)
   131  		for i := 0; i < len(chainedCerts)-1; i++ {
   132  			subject := chainedCerts[i]
   133  			issuer := chainedCerts[i+1]
   134  			ocspServers := subject.OCSPServer
   135  			if len(ocspServers) == 0 {
   136  				t.Fatalf("no OCSP server is found. cert: %v", subject.Subject)
   137  			}
   138  			ocspReq, err := ocsp.CreateRequest(subject, issuer, &ocsp.RequestOptions{})
   139  			if err != nil {
   140  				t.Fatalf("failed to create OCSP request. err: %v", err)
   141  			}
   142  			var ost *ocspStatus
   143  			_, ost = extractCertIDKeyFromRequest(ocspReq)
   144  			if ost.err != nil {
   145  				t.Fatalf("failed to extract cert ID from the OCSP request. err: %v", ost.err)
   146  			}
   147  			// better hash. Not sure if the actual OCSP server accepts this, though.
   148  			ocspReq, err = ocsp.CreateRequest(subject, issuer, &ocsp.RequestOptions{Hash: crypto.SHA512})
   149  			if err != nil {
   150  				t.Fatalf("failed to create OCSP request. err: %v", err)
   151  			}
   152  			_, ost = extractCertIDKeyFromRequest(ocspReq)
   153  			if ost.err != nil {
   154  				t.Fatalf("failed to extract cert ID from the OCSP request. err: %v", ost.err)
   155  			}
   156  			// tweaked request binary
   157  			ocspReq, err = ocsp.CreateRequest(subject, issuer, &ocsp.RequestOptions{Hash: crypto.SHA512})
   158  			if err != nil {
   159  				t.Fatalf("failed to create OCSP request. err: %v", err)
   160  			}
   161  			ocspReq[10] = 0 // random change
   162  			_, ost = extractCertIDKeyFromRequest(ocspReq)
   163  			if ost.err == nil {
   164  				t.Fatal("should have failed")
   165  			}
   166  		}
   167  	}
   168  }
   169  
   170  func TestUnitCheckOCSPResponseCache(t *testing.T) {
   171  	c := New(testLogFactory, 10)
   172  	dummyKey0 := certIDKey{
   173  		NameHash:      "dummy0",
   174  		IssuerKeyHash: "dummy0",
   175  		SerialNumber:  "dummy0",
   176  	}
   177  	dummyKey := certIDKey{
   178  		NameHash:      "dummy1",
   179  		IssuerKeyHash: "dummy1",
   180  		SerialNumber:  "dummy1",
   181  	}
   182  	currentTime := float64(time.Now().UTC().Unix())
   183  	c.ocspResponseCache.Add(dummyKey0, &ocspCachedResponse{time: currentTime})
   184  	subject := &x509.Certificate{}
   185  	issuer := &x509.Certificate{}
   186  	ost, err := c.checkOCSPResponseCache(&dummyKey, subject, issuer)
   187  	if err != nil {
   188  		t.Fatal(err)
   189  	}
   190  	if ost.code != ocspMissedCache {
   191  		t.Fatalf("should have failed. expected: %v, got: %v", ocspMissedCache, ost.code)
   192  	}
   193  	// old timestamp
   194  	c.ocspResponseCache.Add(dummyKey, &ocspCachedResponse{time: float64(1395054952)})
   195  	ost, err = c.checkOCSPResponseCache(&dummyKey, subject, issuer)
   196  	if err != nil {
   197  		t.Fatal(err)
   198  	}
   199  	if ost.code != ocspCacheExpired {
   200  		t.Fatalf("should have failed. expected: %v, got: %v", ocspCacheExpired, ost.code)
   201  	}
   202  
   203  	// invalid validity
   204  	c.ocspResponseCache.Add(dummyKey, &ocspCachedResponse{time: float64(currentTime - 1000)})
   205  	ost, err = c.checkOCSPResponseCache(&dummyKey, subject, nil)
   206  	if err == nil && isValidOCSPStatus(ost.code) {
   207  		t.Fatalf("should have failed.")
   208  	}
   209  }
   210  
   211  func TestUnitExpiredOCSPResponse(t *testing.T) {
   212  	rootCaKey, rootCa, leafCert := createCaLeafCerts(t)
   213  
   214  	expiredOcspResponse := http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
   215  		now := time.Now()
   216  		ocspRes := ocsp.Response{
   217  			SerialNumber: big.NewInt(2),
   218  			ThisUpdate:   now.Add(-1 * time.Hour),
   219  			NextUpdate:   now.Add(-30 * time.Minute),
   220  			Status:       ocsp.Good,
   221  		}
   222  		response, err := ocsp.CreateResponse(rootCa, rootCa, ocspRes, rootCaKey)
   223  		if err != nil {
   224  			_, _ = w.Write(ocsp.InternalErrorErrorResponse)
   225  			t.Fatalf("failed generating OCSP response: %v", err)
   226  		}
   227  		_, _ = w.Write(response)
   228  	})
   229  	ts := httptest.NewServer(expiredOcspResponse)
   230  	defer ts.Close()
   231  
   232  	logFactory := func() hclog.Logger {
   233  		return hclog.NewNullLogger()
   234  	}
   235  	client := New(logFactory, 100)
   236  
   237  	ctx := context.Background()
   238  
   239  	config := &VerifyConfig{
   240  		OcspEnabled:         true,
   241  		OcspServersOverride: []string{ts.URL},
   242  		OcspFailureMode:     FailOpenFalse,
   243  		QueryAllServers:     false,
   244  	}
   245  
   246  	status, err := client.GetRevocationStatus(ctx, leafCert, rootCa, config)
   247  	require.ErrorContains(t, err, "invalid validity",
   248  		"Expected error got response: %v, %v", status, err)
   249  }
   250  
   251  func createCaLeafCerts(t *testing.T) (*ecdsa.PrivateKey, *x509.Certificate, *x509.Certificate) {
   252  	rootCaKey, err := ecdsa.GenerateKey(elliptic.P256(), rand.Reader)
   253  	require.NoError(t, err, "failed generated root key for CA")
   254  
   255  	// Validate we reject CSRs that contain CN that aren't in the original order
   256  	cr := &x509.Certificate{
   257  		Subject:               pkix.Name{CommonName: "Root Cert"},
   258  		SerialNumber:          big.NewInt(1),
   259  		IsCA:                  true,
   260  		BasicConstraintsValid: true,
   261  		SignatureAlgorithm:    x509.ECDSAWithSHA256,
   262  		NotBefore:             time.Now().Add(-1 * time.Second),
   263  		NotAfter:              time.Now().AddDate(1, 0, 0),
   264  		KeyUsage:              x509.KeyUsageCertSign | x509.KeyUsageCRLSign,
   265  		ExtKeyUsage:           []x509.ExtKeyUsage{x509.ExtKeyUsageOCSPSigning},
   266  	}
   267  	rootCaBytes, err := x509.CreateCertificate(rand.Reader, cr, cr, &rootCaKey.PublicKey, rootCaKey)
   268  	require.NoError(t, err, "failed generating root ca")
   269  
   270  	rootCa, err := x509.ParseCertificate(rootCaBytes)
   271  	require.NoError(t, err, "failed parsing root ca")
   272  
   273  	leafKey, err := ecdsa.GenerateKey(elliptic.P256(), rand.Reader)
   274  	require.NoError(t, err, "failed generated leaf key")
   275  
   276  	cr = &x509.Certificate{
   277  		Subject:            pkix.Name{CommonName: "Leaf Cert"},
   278  		SerialNumber:       big.NewInt(2),
   279  		SignatureAlgorithm: x509.ECDSAWithSHA256,
   280  		NotBefore:          time.Now().Add(-1 * time.Second),
   281  		NotAfter:           time.Now().AddDate(1, 0, 0),
   282  		KeyUsage:           x509.KeyUsageDigitalSignature,
   283  		ExtKeyUsage:        []x509.ExtKeyUsage{x509.ExtKeyUsageServerAuth},
   284  	}
   285  	leafCertBytes, err := x509.CreateCertificate(rand.Reader, cr, rootCa, &leafKey.PublicKey, rootCaKey)
   286  	require.NoError(t, err, "failed generating root ca")
   287  
   288  	leafCert, err := x509.ParseCertificate(leafCertBytes)
   289  	require.NoError(t, err, "failed parsing root ca")
   290  	return rootCaKey, rootCa, leafCert
   291  }
   292  
   293  func TestUnitValidateOCSP(t *testing.T) {
   294  	ocspRes := &ocsp.Response{}
   295  	ost, err := validateOCSP(ocspRes)
   296  	if err == nil && isValidOCSPStatus(ost.code) {
   297  		t.Fatalf("should have failed.")
   298  	}
   299  
   300  	currentTime := time.Now()
   301  	ocspRes.ThisUpdate = currentTime.Add(-2 * time.Hour)
   302  	ocspRes.NextUpdate = currentTime.Add(2 * time.Hour)
   303  	ocspRes.Status = ocsp.Revoked
   304  	ost, err = validateOCSP(ocspRes)
   305  	if err != nil {
   306  		t.Fatal(err)
   307  	}
   308  
   309  	if ost.code != ocspStatusRevoked {
   310  		t.Fatalf("should have failed. expected: %v, got: %v", ocspStatusRevoked, ost.code)
   311  	}
   312  	ocspRes.Status = ocsp.Good
   313  	ost, err = validateOCSP(ocspRes)
   314  	if err != nil {
   315  		t.Fatal(err)
   316  	}
   317  
   318  	if ost.code != ocspStatusGood {
   319  		t.Fatalf("should have success. expected: %v, got: %v", ocspStatusGood, ost.code)
   320  	}
   321  	ocspRes.Status = ocsp.Unknown
   322  	ost, err = validateOCSP(ocspRes)
   323  	if err != nil {
   324  		t.Fatal(err)
   325  	}
   326  	if ost.code != ocspStatusUnknown {
   327  		t.Fatalf("should have failed. expected: %v, got: %v", ocspStatusUnknown, ost.code)
   328  	}
   329  	ocspRes.Status = ocsp.ServerFailed
   330  	ost, err = validateOCSP(ocspRes)
   331  	if err != nil {
   332  		t.Fatal(err)
   333  	}
   334  	if ost.code != ocspStatusOthers {
   335  		t.Fatalf("should have failed. expected: %v, got: %v", ocspStatusOthers, ost.code)
   336  	}
   337  }
   338  
   339  func TestUnitEncodeCertID(t *testing.T) {
   340  	var st *ocspStatus
   341  	_, st = extractCertIDKeyFromRequest([]byte{0x1, 0x2})
   342  	if st.code != ocspFailedDecomposeRequest {
   343  		t.Fatalf("failed to get OCSP status. expected: %v, got: %v", ocspFailedDecomposeRequest, st.code)
   344  	}
   345  }
   346  
   347  func getCert(addr string) []*x509.Certificate {
   348  	tcpConn, err := net.DialTimeout("tcp", addr, 40*time.Second)
   349  	if err != nil {
   350  		panic(err)
   351  	}
   352  	defer tcpConn.Close()
   353  
   354  	err = tcpConn.SetDeadline(time.Now().Add(10 * time.Second))
   355  	if err != nil {
   356  		panic(err)
   357  	}
   358  	config := tls.Config{InsecureSkipVerify: true, ServerName: addr}
   359  
   360  	conn := tls.Client(tcpConn, &config)
   361  	defer conn.Close()
   362  
   363  	err = conn.Handshake()
   364  	if err != nil {
   365  		panic(err)
   366  	}
   367  
   368  	state := conn.ConnectionState()
   369  
   370  	return state.PeerCertificates
   371  }
   372  
   373  func TestOCSPRetry(t *testing.T) {
   374  	c := New(testLogFactory, 10)
   375  	certs := getCert("s3-us-west-2.amazonaws.com:443")
   376  	dummyOCSPHost := &url.URL{
   377  		Scheme: "https",
   378  		Host:   "dummyOCSPHost",
   379  	}
   380  	client := &fakeHTTPClient{
   381  		cnt:     3,
   382  		success: true,
   383  		body:    []byte{1, 2, 3},
   384  		logger:  hclog.New(hclog.DefaultOptions),
   385  		t:       t,
   386  	}
   387  	res, b, st, err := c.retryOCSP(
   388  		context.TODO(),
   389  		client, fakeRequestFunc,
   390  		dummyOCSPHost,
   391  		make(map[string]string), []byte{0}, certs[len(certs)-1])
   392  	if err == nil {
   393  		fmt.Printf("should fail: %v, %v, %v\n", res, b, st)
   394  	}
   395  	client = &fakeHTTPClient{
   396  		cnt:     30,
   397  		success: true,
   398  		body:    []byte{1, 2, 3},
   399  		logger:  hclog.New(hclog.DefaultOptions),
   400  		t:       t,
   401  	}
   402  	res, b, st, err = c.retryOCSP(
   403  		context.TODO(),
   404  		client, fakeRequestFunc,
   405  		dummyOCSPHost,
   406  		make(map[string]string), []byte{0}, certs[len(certs)-1])
   407  	if err == nil {
   408  		fmt.Printf("should fail: %v, %v, %v\n", res, b, st)
   409  	}
   410  }
   411  
   412  type tcCanEarlyExit struct {
   413  	results       []*ocspStatus
   414  	resultLen     int
   415  	retFailOpen   *ocspStatus
   416  	retFailClosed *ocspStatus
   417  }
   418  
   419  func TestCanEarlyExitForOCSP(t *testing.T) {
   420  	testcases := []tcCanEarlyExit{
   421  		{ // 0
   422  			results: []*ocspStatus{
   423  				{
   424  					code: ocspStatusGood,
   425  				},
   426  				{
   427  					code: ocspStatusGood,
   428  				},
   429  				{
   430  					code: ocspStatusGood,
   431  				},
   432  			},
   433  			retFailOpen:   nil,
   434  			retFailClosed: nil,
   435  		},
   436  		{ // 1
   437  			results: []*ocspStatus{
   438  				{
   439  					code: ocspStatusRevoked,
   440  					err:  errors.New("revoked"),
   441  				},
   442  				{
   443  					code: ocspStatusGood,
   444  				},
   445  				{
   446  					code: ocspStatusGood,
   447  				},
   448  			},
   449  			retFailOpen:   &ocspStatus{ocspStatusRevoked, errors.New("revoked")},
   450  			retFailClosed: &ocspStatus{ocspStatusRevoked, errors.New("revoked")},
   451  		},
   452  		{ // 2
   453  			results: []*ocspStatus{
   454  				{
   455  					code: ocspStatusUnknown,
   456  					err:  errors.New("unknown"),
   457  				},
   458  				{
   459  					code: ocspStatusGood,
   460  				},
   461  				{
   462  					code: ocspStatusGood,
   463  				},
   464  			},
   465  			retFailOpen:   nil,
   466  			retFailClosed: &ocspStatus{ocspStatusUnknown, errors.New("unknown")},
   467  		},
   468  		{ // 3: not taken as revoked if any invalid OCSP response (ocspInvalidValidity) is included.
   469  			results: []*ocspStatus{
   470  				{
   471  					code: ocspStatusRevoked,
   472  					err:  errors.New("revoked"),
   473  				},
   474  				{
   475  					code: ocspInvalidValidity,
   476  				},
   477  				{
   478  					code: ocspStatusGood,
   479  				},
   480  			},
   481  			retFailOpen:   nil,
   482  			retFailClosed: &ocspStatus{ocspStatusRevoked, errors.New("revoked")},
   483  		},
   484  		{ // 4: not taken as revoked if the number of results don't match the expected results.
   485  			results: []*ocspStatus{
   486  				{
   487  					code: ocspStatusRevoked,
   488  					err:  errors.New("revoked"),
   489  				},
   490  				{
   491  					code: ocspStatusGood,
   492  				},
   493  			},
   494  			resultLen:     3,
   495  			retFailOpen:   nil,
   496  			retFailClosed: &ocspStatus{ocspStatusRevoked, errors.New("revoked")},
   497  		},
   498  	}
   499  	c := New(testLogFactory, 10)
   500  	for idx, tt := range testcases {
   501  		expectedLen := len(tt.results)
   502  		if tt.resultLen > 0 {
   503  			expectedLen = tt.resultLen
   504  		}
   505  		r := c.canEarlyExitForOCSP(tt.results, expectedLen, &VerifyConfig{OcspFailureMode: FailOpenTrue})
   506  		if !(tt.retFailOpen == nil && r == nil) && !(tt.retFailOpen != nil && r != nil && tt.retFailOpen.code == r.code) {
   507  			t.Fatalf("%d: failed to match return. expected: %v, got: %v", idx, tt.retFailOpen, r)
   508  		}
   509  		r = c.canEarlyExitForOCSP(tt.results, expectedLen, &VerifyConfig{OcspFailureMode: FailOpenFalse})
   510  		if !(tt.retFailClosed == nil && r == nil) && !(tt.retFailClosed != nil && r != nil && tt.retFailClosed.code == r.code) {
   511  			t.Fatalf("%d: failed to match return. expected: %v, got: %v", idx, tt.retFailClosed, r)
   512  		}
   513  	}
   514  }
   515  
   516  var testLogger = hclog.New(hclog.DefaultOptions)
   517  
   518  func testLogFactory() hclog.Logger {
   519  	return testLogger
   520  }
   521  
   522  type fakeHTTPClient struct {
   523  	cnt        int    // number of retry
   524  	success    bool   // return success after retry in cnt times
   525  	timeout    bool   // timeout
   526  	body       []byte // return body
   527  	t          *testing.T
   528  	logger     hclog.Logger
   529  	redirected bool
   530  }
   531  
   532  func (c *fakeHTTPClient) Do(_ *retryablehttp.Request) (*http.Response, error) {
   533  	c.cnt--
   534  	if c.cnt < 0 {
   535  		c.cnt = 0
   536  	}
   537  	c.t.Log("fakeHTTPClient.cnt", c.cnt)
   538  
   539  	var retcode int
   540  	if !c.redirected {
   541  		c.redirected = true
   542  		c.cnt++
   543  		retcode = 405
   544  	} else if c.success && c.cnt == 1 {
   545  		retcode = 200
   546  	} else {
   547  		if c.timeout {
   548  			// simulate timeout
   549  			time.Sleep(time.Second * 1)
   550  			return nil, &fakeHTTPError{
   551  				err:     "Whatever reason (Client.Timeout exceeded while awaiting headers)",
   552  				timeout: true,
   553  			}
   554  		}
   555  		retcode = 0
   556  	}
   557  
   558  	ret := &http.Response{
   559  		StatusCode: retcode,
   560  		Body:       &fakeResponseBody{body: c.body},
   561  	}
   562  	return ret, nil
   563  }
   564  
   565  type fakeHTTPError struct {
   566  	err     string
   567  	timeout bool
   568  }
   569  
   570  func (e *fakeHTTPError) Error() string   { return e.err }
   571  func (e *fakeHTTPError) Timeout() bool   { return e.timeout }
   572  func (e *fakeHTTPError) Temporary() bool { return true }
   573  
   574  type fakeResponseBody struct {
   575  	body []byte
   576  	cnt  int
   577  }
   578  
   579  func (b *fakeResponseBody) Read(p []byte) (n int, err error) {
   580  	if b.cnt == 0 {
   581  		copy(p, b.body)
   582  		b.cnt = 1
   583  		return len(b.body), nil
   584  	}
   585  	b.cnt = 0
   586  	return 0, io.EOF
   587  }
   588  
   589  func (b *fakeResponseBody) Close() error {
   590  	return nil
   591  }
   592  
   593  func fakeRequestFunc(_, _ string, _ interface{}) (*retryablehttp.Request, error) {
   594  	return nil, nil
   595  }
   596  
   597  const vaultCert = `-----BEGIN CERTIFICATE-----
   598  MIIDuTCCAqGgAwIBAgIUA6VeVD1IB5rXcCZRAqPO4zr/GAMwDQYJKoZIhvcNAQEL
   599  BQAwcjELMAkGA1UEBhMCVVMxCzAJBgNVBAgMAlZBMREwDwYDVQQHDAhTb21lQ2l0
   600  eTESMBAGA1UECgwJTXlDb21wYW55MRMwEQYDVQQLDApNeURpdmlzaW9uMRowGAYD
   601  VQQDDBF3d3cuY29uaHVnZWNvLmNvbTAeFw0yMjA5MDcxOTA1MzdaFw0yNDA5MDYx
   602  OTA1MzdaMHIxCzAJBgNVBAYTAlVTMQswCQYDVQQIDAJWQTERMA8GA1UEBwwIU29t
   603  ZUNpdHkxEjAQBgNVBAoMCU15Q29tcGFueTETMBEGA1UECwwKTXlEaXZpc2lvbjEa
   604  MBgGA1UEAwwRd3d3LmNvbmh1Z2Vjby5jb20wggEiMA0GCSqGSIb3DQEBAQUAA4IB
   605  DwAwggEKAoIBAQDL9qzEXi4PIafSAqfcwcmjujFvbG1QZbI8swxnD+w8i4ufAQU5
   606  LDmvMrGo3ZbhJ0mCihYmFxpjhRdP2raJQ9TysHlPXHtDRpr9ckWTKBz2oIfqVtJ2
   607  qzteQkWCkDAO7kPqzgCFsMeoMZeONRkeGib0lEzQAbW/Rqnphg8zVVkyQ71DZ7Pc
   608  d5WkC2E28kKcSramhWfVFpxG3hSIrLOX2esEXteLRzKxFPf+gi413JZFKYIWrebP
   609  u5t0++MLNpuX322geoki4BWMjQsd47XILmxZ4aj33ScZvdrZESCnwP76hKIxg9mO
   610  lMxrqSWKVV5jHZrElSEj9LYJgDO1Y6eItn7hAgMBAAGjRzBFMAsGA1UdDwQEAwIE
   611  MDATBgNVHSUEDDAKBggrBgEFBQcDATAhBgNVHREEGjAYggtleGFtcGxlLmNvbYIJ
   612  bG9jYWxob3N0MA0GCSqGSIb3DQEBCwUAA4IBAQA5dPdf5SdtMwe2uSspO/EuWqbM
   613  497vMQBW1Ey8KRKasJjhvOVYMbe7De5YsnW4bn8u5pl0zQGF4hEtpmifAtVvziH/
   614  K+ritQj9VVNbLLCbFcg+b0kfjt4yrDZ64vWvIeCgPjG1Kme8gdUUWgu9dOud5gdx
   615  qg/tIFv4TRS/eIIymMlfd9owOD3Ig6S5fy4NaAJFAwXf8+3Rzuc+e7JSAPgAufjh
   616  tOTWinxvoiOLuYwo9CyGgq4qKBFsrY0aE0gdA7oTQkpbEbo2EbqiWUl/PTCl1Y4Z
   617  nSZ0n+4q9QC9RLrWwYTwh838d5RVLUst2mBKSA+vn7YkqmBJbdBC6nkd7n7H
   618  -----END CERTIFICATE-----
   619  `