1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38 package test
39
40 import (
41 "bytes"
42 "context"
43 "crypto/rsa"
44 "crypto/x509"
45 "encoding/hex"
46 "encoding/pem"
47 "fmt"
48 "io"
49 "math/big"
50 "os"
51 "strings"
52 "testing"
53
54
55
56 "github.com/miekg/pkcs11"
57 . "github.com/sigstore/cosign/v2/cmd/cosign/cli/pkcs11cli"
58 "github.com/sigstore/cosign/v2/pkg/cosign/pkcs11key"
59 "github.com/stretchr/testify/require"
60 )
61
62 var (
63 modulePath = "/usr/local/lib/softhsm/libsofthsm2.so"
64 tokenLabel = "My Token"
65 pin = "1234"
66 keyLabel = "My Key"
67
68 keyID = "355d2d0b569a2a0169e46b82e172cf99aca41400"
69 uri = ""
70 )
71
72 func init() {
73 if x := os.Getenv("SOFTHSM_LIB"); x != "" {
74 modulePath = x
75 }
76 if x := os.Getenv("SOFTHSM_TOKENLABEL"); x != "" {
77 tokenLabel = x
78 }
79 if x := os.Getenv("SOFTHSM_PIN"); x != "" {
80 pin = x
81 }
82 if x := os.Getenv("SOFTHSM_KEYLABEL"); x != "" {
83 keyLabel = x
84 }
85 if x := os.Getenv("SOFTHSM_CONF"); x == "" {
86 os.Setenv("SOFTHSM_CONF", "/etc/softhsm.conf")
87 }
88 if x := os.Getenv("SOFTHSM2_CONF"); x == "" {
89 os.Setenv("SOFTHSM2_CONF", "/etc/softhsm2.conf")
90 }
91
92 keyIDBytes, _ := hex.DecodeString(keyID)
93 pkcs11UriConfig := pkcs11key.NewPkcs11UriConfigFromInput(modulePath, nil, tokenLabel, []byte(keyLabel), keyIDBytes, pin)
94 uri, _ = pkcs11UriConfig.Construct()
95 }
96
97 func TestParsePKCS11URI(t *testing.T) {
98 _ = context.Background()
99
100 uriString := "pkcs11:"
101 uriString += "library-manufacturer=manufacturer;library-description=description;library-version=1;"
102 uriString += "slot-manufacturer=manufacturer;slot-description=description;slot-id=1;"
103 uriString += "manufacturer=manufacturer;model=model;serial=12345678;token=token%20label;"
104 uriString += "type=private;object=key%20label;id=%6b%65%79%5f%69%64"
105 uriString += "?"
106 uriString += "module-path=/path/to/some%20folder/libmodule.so&module-name=libmodule.so&"
107 uriString += "pin-value=1234&pin-source=/path/to/pinfile"
108
109 pkcs11UriConfig := pkcs11key.NewPkcs11UriConfig()
110 must(pkcs11UriConfig.Parse(uriString), t)
111 require.Equal(t, pkcs11UriConfig.KeyID, []byte("key_id"))
112 require.Equal(t, pkcs11UriConfig.KeyLabel, []byte("key label"))
113 require.Equal(t, pkcs11UriConfig.ModulePath, "/path/to/some folder/libmodule.so")
114 require.Equal(t, pkcs11UriConfig.Pin, "1234")
115 require.Equal(t, *pkcs11UriConfig.SlotID, 1)
116 require.Equal(t, pkcs11UriConfig.TokenLabel, "token label")
117 }
118
119 func TestConstructPKCS11URI(t *testing.T) {
120 _ = context.Background()
121
122 uri := "pkcs11:token=token%20label;slot-id=1;id=%6b%65%79%5f%69%64;object=key%20label"
123 uri += "?"
124 uri += "module-path=/path/to/some%20folder/libmodule.so&pin-value=1234"
125
126 slotID := 1
127 pkcs11UriConfig := pkcs11key.NewPkcs11UriConfigFromInput("/path/to/some folder/libmodule.so", &slotID, "token label", []byte("key label"), []byte("key_id"), "1234")
128 uriString, err := pkcs11UriConfig.Construct()
129 require.NoError(t, err)
130 require.Equal(t, uri, uriString)
131 }
132
133 func TestListTokensCmd(t *testing.T) {
134 ctx := context.Background()
135
136 tokens, err := GetTokens(ctx, modulePath)
137 if err != nil {
138 t.Fatal(err)
139 }
140
141 bTokenFound := false
142 for _, token := range tokens {
143 if token.TokenInfo.Label == tokenLabel {
144 bTokenFound = true
145 break
146 }
147 }
148
149 if !bTokenFound {
150 t.Fatalf("token with label '%s' not found", tokenLabel)
151 }
152 }
153
154 func TestListKeysUrisCmd(t *testing.T) {
155 ctx := context.Background()
156
157 tokens, err := GetTokens(ctx, modulePath)
158 if err != nil {
159 t.Fatal(err)
160 }
161
162 bTokenFound := false
163 var slotID uint
164 for _, token := range tokens {
165 if token.TokenInfo.Label == tokenLabel {
166 bTokenFound = true
167 slotID = token.Slot
168 break
169 }
170 }
171 if !bTokenFound {
172 t.Fatalf("token with label '%s' not found", tokenLabel)
173 }
174
175 err = importKey(slotID)
176 if err != nil {
177 t.Fatal(err)
178 }
179 defer deleteKey(slotID)
180
181 keysInfo, err := GetKeysInfo(ctx, modulePath, slotID, pin)
182 if err != nil {
183 t.Fatal(err)
184 }
185
186 bKeyFound := false
187 for _, keyInfo := range keysInfo {
188 if hex.EncodeToString(keyInfo.KeyID) == keyID && string(keyInfo.KeyLabel) == keyLabel {
189 foundUriConfig := pkcs11key.NewPkcs11UriConfig()
190 err = foundUriConfig.Parse(keyInfo.KeyURI)
191 if err != nil {
192 t.Fatal(err)
193 }
194
195 uriConfig := pkcs11key.NewPkcs11UriConfig()
196 err = uriConfig.Parse(uri)
197 if err != nil {
198 t.Fatal(err)
199 }
200
201 if foundUriConfig.TokenLabel == uriConfig.TokenLabel &&
202 bytes.Compare(foundUriConfig.KeyID, uriConfig.KeyID) == 0 &&
203 bytes.Compare(foundUriConfig.KeyLabel, uriConfig.KeyLabel) == 0 &&
204 foundUriConfig.ModulePath == uriConfig.ModulePath &&
205 foundUriConfig.Pin == uriConfig.Pin {
206 bKeyFound = true
207 }
208
209 break
210 }
211 }
212
213 if !bKeyFound {
214 t.Fatalf("key not found")
215 }
216 }
217
218 func TestCertificateIgnored(t *testing.T) {
219 ctx := context.Background()
220
221 tokens, err := GetTokens(ctx, modulePath)
222 if err != nil {
223 t.Fatal(err)
224 }
225
226 bTokenFound := false
227 var slotID uint
228 for _, token := range tokens {
229 if token.TokenInfo.Label == tokenLabel {
230 bTokenFound = true
231 slotID = token.Slot
232 break
233 }
234 }
235 if !bTokenFound {
236 t.Fatalf("token with label '%s' not found", tokenLabel)
237 }
238
239 err = importKey(slotID)
240 if err != nil {
241 t.Fatal(err)
242 }
243 defer deleteKey(slotID)
244
245 pkcs11UriConfig := pkcs11key.NewPkcs11UriConfig()
246 err = pkcs11UriConfig.Parse(uri)
247 if err != nil {
248 t.Fatal(err)
249 }
250
251 const envvar = "COSIGN_PKCS11_IGNORE_CERTIFICATE"
252
253 if err := os.Setenv(envvar, "1"); err != nil {
254 t.Fatal(err)
255 }
256
257 defer os.Setenv(envvar, "")
258
259 sk, err := pkcs11key.GetKeyWithURIConfig(pkcs11UriConfig, true)
260 if err != nil {
261 t.Fatal(err)
262 }
263
264 defer sk.Close()
265
266 cert, err := sk.Certificate()
267 if err != nil {
268 t.Fatal(err)
269 }
270
271 if cert != nil {
272 t.Fatalf("expected certificate to be ignored while loading")
273 }
274 }
275
276 func TestSignAndVerify(t *testing.T) {
277 ctx := context.Background()
278
279 tokens, err := GetTokens(ctx, modulePath)
280 if err != nil {
281 t.Fatal(err)
282 }
283
284 bTokenFound := false
285 var slotID uint
286 for _, token := range tokens {
287 if token.TokenInfo.Label == tokenLabel {
288 bTokenFound = true
289 slotID = token.Slot
290 break
291 }
292 }
293 if !bTokenFound {
294 t.Fatalf("token with label '%s' not found", tokenLabel)
295 }
296
297 err = importKey(slotID)
298 if err != nil {
299 t.Fatal(err)
300 }
301 defer deleteKey(slotID)
302
303 pkcs11UriConfig := pkcs11key.NewPkcs11UriConfig()
304 err = pkcs11UriConfig.Parse(uri)
305 if err != nil {
306 t.Fatal(err)
307 }
308
309 sk, err := pkcs11key.GetKeyWithURIConfig(pkcs11UriConfig, true)
310 if err != nil {
311 t.Fatal(err)
312 }
313 defer sk.Close()
314
315 sv, err := sk.SignerVerifier()
316 if err != nil {
317 t.Fatal(err)
318 }
319
320 v, err := sk.Verifier()
321 if err != nil {
322 t.Fatal(err)
323 }
324
325 sig, err := sv.SignMessage(bytes.NewReader([]byte("hello, world!")))
326 if err != nil {
327 t.Fatal(err)
328 }
329
330 err = v.VerifySignature(bytes.NewReader(sig), bytes.NewReader([]byte("hello, world!")))
331 if err != nil {
332 t.Fatal(err)
333 }
334 }
335
336 var newPublicKeyAttrs = []*pkcs11.Attribute{
337 pkcs11.NewAttribute(pkcs11.CKA_CLASS, pkcs11.CKO_PUBLIC_KEY),
338 pkcs11.NewAttribute(pkcs11.CKA_TOKEN, true),
339 pkcs11.NewAttribute(pkcs11.CKA_PRIVATE, false),
340 pkcs11.NewAttribute(pkcs11.CKA_VERIFY, true),
341 }
342
343 var newPrivateKeyAttrs = []*pkcs11.Attribute{
344 pkcs11.NewAttribute(pkcs11.CKA_CLASS, pkcs11.CKO_PRIVATE_KEY),
345 pkcs11.NewAttribute(pkcs11.CKA_TOKEN, true),
346 pkcs11.NewAttribute(pkcs11.CKA_PRIVATE, true),
347 pkcs11.NewAttribute(pkcs11.CKA_SENSITIVE, true),
348 pkcs11.NewAttribute(pkcs11.CKA_EXTRACTABLE, false),
349 pkcs11.NewAttribute(pkcs11.CKA_SIGN, true),
350 }
351
352 func rsaImportAttrs(priv *rsa.PrivateKey) (pubAttrs, privAttrs []*pkcs11.Attribute) {
353 pubAttrs = []*pkcs11.Attribute{
354 pkcs11.NewAttribute(pkcs11.CKA_PUBLIC_EXPONENT, big.NewInt(int64(priv.E)).Bytes()),
355 pkcs11.NewAttribute(pkcs11.CKA_MODULUS, priv.N.Bytes()),
356 }
357 privAttrs = []*pkcs11.Attribute{
358 pkcs11.NewAttribute(pkcs11.CKA_PUBLIC_EXPONENT, big.NewInt(int64(priv.E)).Bytes()),
359 pkcs11.NewAttribute(pkcs11.CKA_MODULUS, priv.N.Bytes()),
360 pkcs11.NewAttribute(pkcs11.CKA_PRIVATE_EXPONENT, priv.D.Bytes()),
361 pkcs11.NewAttribute(pkcs11.CKA_PRIME_1, priv.Primes[0].Bytes()),
362 pkcs11.NewAttribute(pkcs11.CKA_PRIME_2, priv.Primes[1].Bytes()),
363 pkcs11.NewAttribute(pkcs11.CKA_EXPONENT_1, priv.Precomputed.Dp.Bytes()),
364 pkcs11.NewAttribute(pkcs11.CKA_EXPONENT_2, priv.Precomputed.Dq.Bytes()),
365 pkcs11.NewAttribute(pkcs11.CKA_COEFFICIENT, priv.Precomputed.Qinv.Bytes()),
366 }
367 return
368 }
369
370 func attrConcat(attrSets ...[]*pkcs11.Attribute) []*pkcs11.Attribute {
371 ret := make([]*pkcs11.Attribute, 0)
372 for _, attrs := range attrSets {
373 ret = append(ret, attrs...)
374 }
375 return ret
376 }
377
378 func initPKCS11(modulePath string) (*pkcs11.Ctx, error) {
379 ctx := pkcs11.New(modulePath)
380 if ctx == nil {
381 return nil, fmt.Errorf("unable to load PKCS#11 module")
382 }
383
384 err := ctx.Initialize()
385 if err != nil {
386 return nil, fmt.Errorf("unable to initialize PKCS#11 module")
387 }
388
389 return ctx, nil
390 }
391
392 func importKey(slotID uint) error {
393 var pemBytes []byte
394 var priv interface{}
395
396 ctx, err := initPKCS11(modulePath)
397 if err != nil {
398 return err
399 }
400 defer func() {
401 ctx.Finalize()
402 ctx.Destroy()
403 }()
404
405 keyIDBytes, err := hex.DecodeString(keyID)
406 if err != nil {
407 return err
408 }
409 keyLabelBytes := []byte(keyLabel)
410
411 r := strings.NewReader(rsaPrivKey)
412 pemBytes, err = io.ReadAll(r)
413 if err != nil {
414 return fmt.Errorf("unable to read pem")
415 }
416 block, _ := pem.Decode(pemBytes)
417 if block == nil {
418 return fmt.Errorf("unable to decode pem")
419 }
420 priv, err = x509.ParsePKCS8PrivateKey(block.Bytes)
421 if err != nil {
422 return fmt.Errorf("unable to parse pem")
423 }
424 privKey, ok := priv.(*rsa.PrivateKey)
425 if !ok {
426 return fmt.Errorf("unable to load key")
427 }
428
429 session, err := ctx.OpenSession(slotID, pkcs11.CKF_SERIAL_SESSION|pkcs11.CKF_RW_SESSION)
430 if err != nil {
431 return fmt.Errorf("unable to open session")
432 }
433 defer ctx.CloseSession(session)
434 err = ctx.Login(session, pkcs11.CKU_USER, pin)
435 if err != nil {
436 return fmt.Errorf("unable to login")
437 }
438 defer ctx.Logout(session)
439
440 keyType := pkcs11.CKK_RSA
441 pubTypeAttrs, privTypeAttrs := rsaImportAttrs(privKey)
442 commonAttrs := []*pkcs11.Attribute{
443 pkcs11.NewAttribute(pkcs11.CKA_KEY_TYPE, keyType),
444 pkcs11.NewAttribute(pkcs11.CKA_ID, keyIDBytes),
445 pkcs11.NewAttribute(pkcs11.CKA_LABEL, keyLabelBytes),
446 }
447 pubAttrs := attrConcat(commonAttrs, newPublicKeyAttrs, pubTypeAttrs)
448 privAttrs := attrConcat(commonAttrs, newPrivateKeyAttrs, privTypeAttrs)
449 pubHandle, err := ctx.CreateObject(session, pubAttrs)
450 if err != nil {
451 return fmt.Errorf("unable to create public key")
452 }
453 _, err = ctx.CreateObject(session, privAttrs)
454 if err != nil {
455 ctx.DestroyObject(session, pubHandle)
456 return fmt.Errorf("unable to create private key")
457 }
458
459 return nil
460 }
461
462 func deleteKey(slotID uint) error {
463 var handles []pkcs11.ObjectHandle
464
465 ctx, err := initPKCS11(modulePath)
466 if err != nil {
467 return err
468 }
469 defer func() {
470 ctx.Finalize()
471 ctx.Destroy()
472 }()
473
474 keyIDBytes, err := hex.DecodeString(keyID)
475 if err != nil {
476 return err
477 }
478 keyLabelBytes := []byte(keyLabel)
479
480 session, err := ctx.OpenSession(slotID, pkcs11.CKF_SERIAL_SESSION|pkcs11.CKF_RW_SESSION)
481 if err != nil {
482 return fmt.Errorf("unable to open session")
483 }
484 defer ctx.CloseSession(session)
485 err = ctx.Login(session, pkcs11.CKU_USER, pin)
486 if err != nil {
487 return fmt.Errorf("unable to login")
488 }
489 defer ctx.Logout(session)
490
491 maxHandlePerFind := 20
492 publicAttrs := []*pkcs11.Attribute{
493 pkcs11.NewAttribute(pkcs11.CKA_CLASS, pkcs11.CKO_PUBLIC_KEY),
494 pkcs11.NewAttribute(pkcs11.CKA_ID, keyIDBytes),
495 pkcs11.NewAttribute(pkcs11.CKA_LABEL, keyLabelBytes),
496 }
497 if err = ctx.FindObjectsInit(session, publicAttrs); err != nil {
498 return fmt.Errorf("unable to initialize find objects")
499 }
500 handles, _, err = ctx.FindObjects(session, maxHandlePerFind)
501 if err != nil {
502 return fmt.Errorf("unable to find objects")
503 }
504 err = ctx.FindObjectsFinal(session)
505 if err != nil {
506 return fmt.Errorf("unable to finalize find objects")
507 }
508 if len(handles) == 1 {
509 ctx.DestroyObject(session, handles[0])
510 if err != nil {
511 return fmt.Errorf("unable to destroy public key")
512 }
513 }
514
515 privAttrs := []*pkcs11.Attribute{
516 pkcs11.NewAttribute(pkcs11.CKA_CLASS, pkcs11.CKO_PRIVATE_KEY),
517 pkcs11.NewAttribute(pkcs11.CKA_ID, keyIDBytes),
518 pkcs11.NewAttribute(pkcs11.CKA_LABEL, keyLabelBytes),
519 }
520 if err = ctx.FindObjectsInit(session, privAttrs); err != nil {
521 return fmt.Errorf("unable to initialize find objects")
522 }
523 handles, _, err = ctx.FindObjects(session, maxHandlePerFind)
524 if err != nil {
525 return fmt.Errorf("unable to find objects")
526 }
527 err = ctx.FindObjectsFinal(session)
528 if err != nil {
529 return fmt.Errorf("unable to finalize find objects")
530 }
531 if len(handles) == 1 {
532 ctx.DestroyObject(session, handles[0])
533 if err != nil {
534 return fmt.Errorf("unable to destroy private key")
535 }
536 }
537
538 return nil
539 }
540
541 func must(err error, t *testing.T) {
542 t.Helper()
543 if err != nil {
544 t.Fatal(err)
545 }
546 }
547
548 func mustErr(err error, t *testing.T) {
549 t.Helper()
550 if err == nil {
551 t.Fatal("expected error")
552 }
553 }
554
555 const rsaPrivKey = `-----BEGIN PRIVATE KEY-----
556 MIIEvgIBADANBgkqhkiG9w0BAQEFAASCBKgwggSkAgEAAoIBAQDZJZ44vB04D2wm
557 xz+3upmuWelrTWcceVC2v6fkBo9dIR9IejolFY+CsMF1Rc5LGXG3XStQHRrbmq1w
558 UxC8jsIOK7gI2xI9IOwCgyaQun3J+1VQc6eZxHLGQfTTNq7Vx67VOG8V8d3RhN7L
559 BvAMT5U55254bUgH0KVx5C1ybLcX6BdGaABCunh7tV+thgwEZSbr2/t0Tf8QneMr
560 eHojTKZp7/d90TH8KF+/FiPWJWWv5OVhjpZPwTWqUgL+6pgrMKUSWD/92JSDIZe6
561 UjxASE4JgnJWMQUhkerJ7j5P16gjdAwJAAt5m3L6wdfVQG2aZ9CJzUowk12ly4Dc
562 Dx73/UufAgMBAAECggEAF8iA/eHMqXk29UBZgDwV3PzIDhKaOoonBv0S3GzDgwW/
563 sWaBu9ISt9O4PKn6oEsXI2g2+D1X1bmpSWYvrRdNtdOgAohMBRn3/4Zx0OQ8JsU6
564 YOdp8fOMRp6uu/t/RrbqNTxLHnIxQ2N0K3SFEjQdOgxZEyOVAhYeKM0/FQtHOnzj
565 WoyZHT8pV3mr6WnxBw/4u/1Ahfau7fs6aVJLECc9jGF/6e7aQeb+yEeLrHayml8e
566 sbBx4l/1LqU/2S7SQrWtQ+fi+/MlgxvLh0XC7tTPP6I3cTetyMZime9EwwDiPebX
567 PLUgo8Kf/sHzd/25G9M3Yz+UCLemcPSMUjBUQTPtYQKBgQD8lnpjekyeOjNCdRVP
568 5w6h1wGN4aC4bCksZ89HKpHc44+3AjDT/aVviory+CyOj05qbXDdpNnNh+jl5llM
569 yDw15WIvSsXFx3UQ467VVrBKm7vr+k1LGgLJ2fSFbZUTyLvwW4NpP26KDW6SitZ8
570 B9lkepTZ0G4Eao51VgidHsulKQKBgQDcFJNAIctqUWDli4tA5L0G5tiypcAA7iIZ
571 0h2YK+7eOU2f3r8aaywbPhcRn+cKlrf3iV4BCZAv59WEJqq1HOlzU92jkmZspYPq
572 8kSZLaaiDIBw+vwV4prHDSdZFEY+hHq5eULPIgVm/M474JcghetkVt8pG3ee+Dml
573 o6zUrZr7hwKBgQDCiXbrpObbuoF+PsTSTGfFl923k74ALDWt4KoQ6qV61bz7O3G1
574 5BYFiVOo/CD9Dzxa1b1mx6+ED5f9cOL4MwPEks2DFPircgoknucpomGWpMkgXyAm
575 pnrdUcN0/Egj+6db4G+eoN8W7m9p6Ap3bmgtbge0lkYVmqfrkP6DXJOFuQKBgHA/
576 hkMFeYyGaRdqruGwSMEGaKvlYiKXUok8459DeReavn61y16cHujeKEHy/pImATqd
577 s3Zv/DyS0BIQ7qxlTKRnt/m/p8HuQXRJkLdX009/dNsrB/vZkfvIN7N1ZcZpJ3cF
578 5A9lWMAIXN+pUythYofQzw1WVxKbpDtZWcM3sH5tAoGBAMHgZdtmIyllx/1BbYSg
579 Emxj3LekvZL0e7afeod9f977ZETt/imaejnJNnGOPeSbtLSPfhwonLEp+5XmICzt
580 lJZAF8iP2m1n9h8sZga5rZQ0JgiwVNFNwde4sp1pD5UcFrYepHRxKPo50eJi3rhR
581 SwNAKWa96qm5o8BaQu/aRMRu
582 -----END PRIVATE KEY-----`
583
584 const rsaCert = `-----BEGIN CERTIFICATE-----
585 MIIDazCCAlOgAwIBAgIUL7BdF7HSUwEAdqElJjVLQYd2OekwDQYJKoZIhvcNAQEL
586 BQAwRTELMAkGA1UEBhMCQVUxEzARBgNVBAgMClNvbWUtU3RhdGUxITAfBgNVBAoM
587 GEludGVybmV0IFdpZGdpdHMgUHR5IEx0ZDAeFw0yMTExMDIxNzM3MzJaFw0zMTEw
588 MzExNzM3MzJaMEUxCzAJBgNVBAYTAkFVMRMwEQYDVQQIDApTb21lLVN0YXRlMSEw
589 HwYDVQQKDBhJbnRlcm5ldCBXaWRnaXRzIFB0eSBMdGQwggEiMA0GCSqGSIb3DQEB
590 AQUAA4IBDwAwggEKAoIBAQDZJZ44vB04D2wmxz+3upmuWelrTWcceVC2v6fkBo9d
591 IR9IejolFY+CsMF1Rc5LGXG3XStQHRrbmq1wUxC8jsIOK7gI2xI9IOwCgyaQun3J
592 +1VQc6eZxHLGQfTTNq7Vx67VOG8V8d3RhN7LBvAMT5U55254bUgH0KVx5C1ybLcX
593 6BdGaABCunh7tV+thgwEZSbr2/t0Tf8QneMreHojTKZp7/d90TH8KF+/FiPWJWWv
594 5OVhjpZPwTWqUgL+6pgrMKUSWD/92JSDIZe6UjxASE4JgnJWMQUhkerJ7j5P16gj
595 dAwJAAt5m3L6wdfVQG2aZ9CJzUowk12ly4DcDx73/UufAgMBAAGjUzBRMB0GA1Ud
596 DgQWBBRokgD44sdsSGQEQcbJ3vrCrXTIcTAfBgNVHSMEGDAWgBRokgD44sdsSGQE
597 QcbJ3vrCrXTIcTAPBgNVHRMBAf8EBTADAQH/MA0GCSqGSIb3DQEBCwUAA4IBAQA8
598 +CpbIGi4ycCcSeomBzGVXsTFgFutqqvh3BFQ1u6bPlV7hIlFd11zzgWBeKKxREJn
599 z3SipT1qGX+uP4iVhUux94f2rQCV25mJNRKft2phAUylMr+laiO7IkHFB1zzJTfz
600 Bi9gm55HGvGCIdSWFkLZ/MUNCMj3WtPrUYl5jqFgDDmCpLctmPoN4vxSa0of3apv
601 ILH8jSsN5XbL8G1hsT/IGlRRbzoiLCKgCp6e6TjZSq/Y+JWGyw/+sZJMI8Mg4Mje
602 054uJhD29xmbfxdYxrMWLAFb6yoWVbDJPdECFf9uwOXyDZ8bGd48frTdUU3Rb+m3
603 5Hue2g5US98p2jnJiv75
604 -----END CERTIFICATE-----`
605
View as plain text