-
Notifications
You must be signed in to change notification settings - Fork 86
Expand file tree
/
Copy pathtest_x509cert.rb
More file actions
947 lines (807 loc) · 40.1 KB
/
test_x509cert.rb
File metadata and controls
947 lines (807 loc) · 40.1 KB
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
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
458
459
460
461
462
463
464
465
466
467
468
469
470
471
472
473
474
475
476
477
478
479
480
481
482
483
484
485
486
487
488
489
490
491
492
493
494
495
496
497
498
499
500
501
502
503
504
505
506
507
508
509
510
511
512
513
514
515
516
517
518
519
520
521
522
523
524
525
526
527
528
529
530
531
532
533
534
535
536
537
538
539
540
541
542
543
544
545
546
547
548
549
550
551
552
553
554
555
556
557
558
559
560
561
562
563
564
565
566
567
568
569
570
571
572
573
574
575
576
577
578
579
580
581
582
583
584
585
586
587
588
589
590
591
592
593
594
595
596
597
598
599
600
601
602
603
604
605
606
607
608
609
610
611
612
613
614
615
616
617
618
619
620
621
622
623
624
625
626
627
628
629
630
631
632
633
634
635
636
637
638
639
640
641
642
643
644
645
646
647
648
649
650
651
652
653
654
655
656
657
658
659
660
661
662
663
664
665
666
667
668
669
670
671
672
673
674
675
676
677
678
679
680
681
682
683
684
685
686
687
688
689
690
691
692
693
694
695
696
697
698
699
700
701
702
703
704
705
706
707
708
709
710
711
712
713
714
715
716
717
718
719
720
721
722
723
724
725
726
727
728
729
730
731
732
733
734
735
736
737
738
739
740
741
742
743
744
745
746
747
748
749
750
751
752
753
754
755
756
757
758
759
760
761
762
763
764
765
766
767
768
769
770
771
772
773
774
775
776
777
778
779
780
781
782
783
784
785
786
787
788
789
790
791
792
793
794
795
796
797
798
799
800
801
802
803
804
805
806
807
808
809
810
811
812
813
814
815
816
817
818
819
820
821
822
823
824
825
826
827
828
829
830
831
832
833
834
835
836
837
838
839
840
841
842
843
844
845
846
847
848
849
850
851
852
853
854
855
856
857
858
859
860
861
862
863
864
865
866
867
868
869
870
871
872
873
874
875
876
877
878
879
880
881
882
883
884
885
886
887
888
889
890
891
892
893
894
895
896
897
898
899
900
901
902
903
904
905
906
907
908
909
910
911
912
913
914
915
916
917
918
919
920
921
922
923
924
925
926
927
928
929
930
931
932
933
934
935
936
937
938
939
940
941
942
943
944
945
946
947
# coding: US-ASCII
require File.expand_path('../test_helper', File.dirname(__FILE__))
class TestX509Certificate < TestCase
def test_new
cert = OpenSSL::X509::Certificate.new
empty_name = OpenSSL::X509::Name.new
assert_equal empty_name, cert.issuer
assert_equal empty_name, cert.subject
bn = OpenSSL::BN.new('0') unless defined? JRUBY_VERSION
assert_equal bn || OpenSSL::BN.new(0), cert.serial
assert_nil cert.not_before
assert_nil cert.not_after
assert_raise(OpenSSL::X509::CertificateError) { cert.public_key }
end
def test_new_from_java_bytes # was historically supported through the StringHelper.readInput fallback
cert = File.read(File.expand_path('ca.crt', File.dirname(__FILE__)))
fact = java.security.cert.CertificateFactory.getInstance("X.509")
java_cert = fact.generateCertificate(java.io.ByteArrayInputStream.new(cert.to_java_bytes)) # X509Certificate
cert = OpenSSL::X509::Certificate.new(java_cert.getEncoded) # byte[]
assert_equal java_cert.getSerialNumber, cert.serial.to_java
assert_equal java_cert.getPublicKey, cert.public_key.to_java
end
def test_alt_name_extension
cert = OpenSSL::X509::Certificate.new
cert.add_extension OpenSSL::X509::Extension.new('subjectAltName', 'email:self@jruby.org, IP:127.0.0.1', false)
assert_equal 'email:self@jruby.org, IP:127.0.0.1', cert.extensions[0].value
end
def test_cert_extensions # JRUBY-3468
pem_cert = <<END
-----BEGIN CERTIFICATE-----
MIIC/jCCAmegAwIBAgIBATANBgkqhkiG9w0BAQUFADBNMQswCQYDVQQGEwJKUDER
MA8GA1UECgwIY3Rvci5vcmcxFDASBgNVBAsMC0RldmVsb3BtZW50MRUwEwYDVQQD
DAxodHRwLWFjY2VzczIwHhcNMDcwOTExMTM1ODMxWhcNMDkwOTEwMTM1ODMxWjBN
MQswCQYDVQQGEwJKUDERMA8GA1UECgwIY3Rvci5vcmcxFDASBgNVBAsMC0RldmVs
b3BtZW50MRUwEwYDVQQDDAxodHRwLWFjY2VzczIwgZ8wDQYJKoZIhvcNAQEBBQAD
gY0AMIGJAoGBALi66ujWtUCQm5HpMSyr/AAIFYVXC/dmn7C8TR/HMiUuW3waY4uX
LFqCDAGOX4gf177pX+b99t3mpaiAjJuqc858D9xEECzhDWgXdLbhRqWhUOble4RY
c1yWYC990IgXJDMKx7VAuZ3cBhdBxtlE9sb1ZCzmHQsvTy/OoRzcJCrTAgMBAAGj
ge0wgeowDwYDVR0TAQH/BAUwAwEB/zAxBglghkgBhvhCAQ0EJBYiUnVieS9PcGVu
U1NMIEdlbmVyYXRlZCBDZXJ0aWZpY2F0ZTAdBgNVHQ4EFgQUJNE0GGaRKmN2qhnO
FyBWVl4Qj6owDgYDVR0PAQH/BAQDAgEGMHUGA1UdIwRuMGyAFCTRNBhmkSpjdqoZ
zhcgVlZeEI+qoVGkTzBNMQswCQYDVQQGEwJKUDERMA8GA1UECgwIY3Rvci5vcmcx
FDASBgNVBAsMC0RldmVsb3BtZW50MRUwEwYDVQQDDAxodHRwLWFjY2VzczKCAQEw
DQYJKoZIhvcNAQEFBQADgYEAH11tstSUuqFpMqoh/vM5l3Nqb8ygblbqEYQs/iG/
UeQkOZk/P1TxB6Ozn2htJ1srqDpUsncFVZ/ecP19GkeOZ6BmIhppcHhE5WyLBcPX
It5q1BW0PiAzT9LlEGoaiW0nw39so0Pr1whJDfc1t4fjdk+kSiMIzRHbTDvHWfpV
nTA=
-----END CERTIFICATE-----
END
cert = OpenSSL::X509::Certificate.new(pem_cert)
keyid = '24:D1:34:18:66:91:2A:63:76:AA:19:CE:17:20:56:56:5E:10:8F:AA'
cert.extensions.each do |ext|
value = ext.value
crit = ext.critical?
case ext.oid
when "keyUsage"
assert_equal true, crit
assert_equal "Certificate Sign, CRL Sign", value
when "basicConstraints"
assert_equal true, crit
assert_equal "CA:TRUE", value
when "authorityKeyIdentifier"
assert_equal false, crit
expected = "keyid:#{keyid}\n"
# NOTE: this behavior is matched against MRI 1.8.7/1.9.3/2.1.2 :
expected << "DirName:/C=JP/O=ctor.org/OU=Development/CN=http-access2\n"
expected << "serial:01\n"
assert_equal expected, value
when "subjectKeyIdentifier"
assert_equal false, crit
assert_equal keyid, value
when "nsComment"
assert_equal false, crit
assert_equal "Ruby/OpenSSL Generated Certificate", value
end
end
end
def test_aki_extension_to_text
cert = create_self_signed_cert [ %w[CN localhost] ], __method__.to_s
keyid = "97:39:9D:C3:FB:CD:BA:8F:54:0C:90:7B:46:3F:EA:D6:43:75:B1:CB"
assert cert.extensions.size > 0
value = cert.extensions.last.value
assert_equal "keyid:#{keyid}\nDirName:/CN=localhost\nserial:01\n", value
end
def create_self_signed_cert(cn, comment) # cert generation ripped from WEBrick
key = OpenSSL::PKey::RSA.new TEST_KEY_RSA2048
cert = OpenSSL::X509::Certificate.new
cert.version = 2
cert.serial = 1
name = (cn.kind_of? String) ? OpenSSL::X509::Name.parse(cn) : OpenSSL::X509::Name.new(cn)
cert.subject = name
cert.issuer = name
cert.not_before = Time.now
cert.not_after = Time.now + (365*24*60*60)
cert.public_key = key.public_key
ef = OpenSSL::X509::ExtensionFactory.new(nil,cert)
ef.issuer_certificate = cert
cert.extensions = [
ef.create_extension("basicConstraints","CA:FALSE"),
ef.create_extension("keyUsage", "keyEncipherment"),
ef.create_extension("subjectKeyIdentifier", "hash"),
ef.create_extension("extendedKeyUsage", "serverAuth"),
# ef.create_extension("nsComment", comment),
]
aki = ef.create_extension("authorityKeyIdentifier", "keyid:always,issuer:always")
cert.add_extension(aki)
cert.sign(key, OpenSSL::Digest::SHA1.new)
cert
end
def test_resolve_extensions
rsa2048 = OpenSSL::PKey::RSA.new TEST_KEY_RSA2048
ca = OpenSSL::X509::Name.parse("/DC=org/DC=ruby-lang/CN=CA")
ca_exts = [
[ "basicConstraints", "CA:TRUE", true ],
[ "keyUsage", "keyCertSign, cRLSign", true ],
[ "subjectKeyIdentifier", "hash", false ],
[ "authorityKeyIdentifier", "keyid:always", false ],
[ "subjectAltName", "email:self@jruby.org, DNS:jruby.org", false ],
]
now = Time.now
ca_cert = issue_cert(ca, rsa2048, 1, ca_exts, nil, nil,
not_before: now, not_after: now + 3600, digest: OpenSSL::Digest::SHA1.new)
assert_equal 5, ca_cert.extensions.size
cert = OpenSSL::X509::Certificate.new ca_cert.to_der
assert_equal 5, cert.extensions.size
# Java 6/7 seems to maintain same order but Java 8 does definitely not :
#if self.class.java_version.last.to_i == 7
# assert_equal '97:39:9D:C3:FB:CD:BA:8F:54:0C:90:7B:46:3F:EA:D6:43:75:B1:CB', cert.extensions[2].value
# assert_equal 'email:self@jruby.org, DNS:jruby.org', cert.extensions[4].value
#end
exts = cert.extensions.dup
assert ext = exts.find { |e| e.oid == 'basicConstraints' }, "missing 'basicConstraints' among: #{exts.join(', ')}"
assert_equal 'CA:TRUE', ext.value
assert ext.critical?
assert ext = exts.find { |e| e.oid == 'authorityKeyIdentifier' }, "missing 'authorityKeyIdentifier' among: #{exts.join(', ')}"
assert_equal "keyid:97:39:9D:C3:FB:CD:BA:8F:54:0C:90:7B:46:3F:EA:D6:43:75:B1:CB\n", ext.value
assert ! ext.critical?
assert ext = exts.find { |e| e.oid == 'subjectKeyIdentifier' }, "missing 'subjectKeyIdentifier' among: #{exts.join(', ')}"
assert_equal "97:39:9D:C3:FB:CD:BA:8F:54:0C:90:7B:46:3F:EA:D6:43:75:B1:CB", ext.value
assert ! ext.critical?
assert ext = exts.find { |e| e.oid == 'subjectAltName' }, "missing 'subjectAltName' among: #{exts.join(', ')}"
assert_equal 'email:self@jruby.org, DNS:jruby.org', ext.value
end
def test_extensions
rsa2048 = OpenSSL::PKey::RSA.new TEST_KEY_RSA2048
ca = OpenSSL::X509::Name.parse("/DC=org/DC=ruby-lang/CN=CA")
ca_exts = [
[ "basicConstraints", "CA:TRUE", true ],
[ "keyUsage", "keyCertSign, cRLSign", true ],
[ "subjectKeyIdentifier", "hash", false ],
[ "authorityKeyIdentifier", "keyid:always", false ],
[ "extendedKeyUsage", "clientAuth, emailProtection, codeSigning", false ],
[ "subjectAltName", "email:self@jruby.org", false ],
[ "subjectAltName", "IP:174.129.31.214", false ],
[ "subjectAltName", "DNS:jruby.org", false ],
]
now = Time.now
ca_cert = issue_cert(ca, rsa2048, 1, ca_exts, nil, nil,
not_before: now, not_after: now + 3600, digest: OpenSSL::Digest::SHA1.new)
assert_equal 8, ca_cert.extensions.size
ca_cert.extensions.each_with_index do |ext, i|
assert_equal ca_exts[i][0], ext.oid
assert_equal ca_exts[i][2], ext.critical?
case ca_exts[i][1]
when 'keyCertSign, cRLSign'
assert_equal 'Certificate Sign, CRL Sign', ext.value
when 'hash'
#assert_equal '97:39:9D:C3:FB:CD:BA:8F:54:0C:90:7B:46:3F:EA:D6:43:75:B1:CB', ext.value
when 'keyid:always'
assert_equal "keyid:97:39:9D:C3:FB:CD:BA:8F:54:0C:90:7B:46:3F:EA:D6:43:75:B1:CB\n", ext.value
when 'clientAuth, emailProtection, codeSigning'
assert_equal 'TLS Web Client Authentication, E-mail Protection, Code Signing', ext.value
when /IP\:/
# NOTE: probably fine as "IP:174.129.31.214" on JRuby while on MRI :
# assert_equal 'IP Address:174.129.31.214', ext.value
assert_match( /IP.*?:174.129.31.214/, ext.value )
else
assert_equal ca_exts[i][1], ext.value
end
end
end
def test_inspect_to_text
subj = OpenSSL::X509::Name.parse("/DC=org/DC=ruby-lang/CN=TestCA")
key = OpenSSL::PKey::RSA.new TEST_KEY_RSA1024
now = Time.at 1412840060 # Time.now.to_i suppress usec
s = 0xdeadbeafdeadbeafdeadbeafdeadbeaf
exts = [
[ "basicConstraints", "CA:TRUE,pathlen:1", true ],
[ "keyUsage", "keyCertSign, cRLSign", true ],
[ "subjectKeyIdentifier", "hash", false ],
]
dgst = OpenSSL::Digest::SHA1.new # NOTE: does it match MRI ?!
cert = issue_cert(subj, key, s, exts, nil, nil, not_before: now, not_after: now + 3600, digest: dgst)
assert cert.inspect.start_with?('#<OpenSSL::X509::Certificate:')
if defined? JRUBY_VERSION
assert cert.inspect.index('subject=/DC=org/DC=ruby-lang/CN=TestCA, issuer=/DC=org/DC=ruby-lang/CN=TestCA')
assert cert.inspect.index('serial=295990750012446699619010157040970350255')
# TODO this isn't MRI compatible, which gives :
# #<OpenSSL::X509::Certificate:
# subject=#<OpenSSL::X509::Name CN=TestCA,DC=ruby-lang,DC=org>,
# issuer=#<OpenSSL::X509::Name CN=TestCA,DC=ruby-lang,DC=org>,
# serial=#<OpenSSL::BN:0x00005627d4602938>,
# not_before=2014-10-09 07:34:20 UTC,
# not_after=2014-10-09 08:34:20 UTC>
end
#assert cert.inspect.index('not_before=2014-10-09 07:34:20 UTC, not_after=2014-10-09 08:34:20 UTC')
text_without_signature = <<-TEXT
Certificate:
Data:
Version: 3 (0x2)
Serial Number:
de:ad:be:af:de:ad:be:af:de:ad:be:af:de:ad:be:af
Signature Algorithm: sha1WithRSAEncryption
Issuer: DC=org, DC=ruby-lang, CN=TestCA
Validity
Not Before: Oct 9 07:34:20 2014 GMT
Not After : Oct 9 08:34:20 2014 GMT
Subject: DC=org, DC=ruby-lang, CN=TestCA
Subject Public Key Info:
Public Key Algorithm: rsaEncryption
Public-Key: (1024 bit)
Modulus:
00:cb:c2:c4:b0:d4:40:a7:3e:d4:fe:3e:43:a0:1e:
17:06:03:bd:67:c0:2d:bf:9c:bf:39:54:11:a7:46:
a0:f1:3a:a8:d5:87:b0:b1:68:a3:c4:45:81:ec:93:
80:4f:0a:41:37:6e:bb:53:84:f5:9c:f6:48:c7:11:
04:3b:b9:ff:58:d6:b6:c2:cf:49:5a:c8:da:87:cb:
2c:10:11:52:c5:9a:9d:5c:a4:8b:7f:43:78:1e:2e:
ff:19:0f:da:62:86:8c:0a:24:3c:8c:0e:23:7a:02:
b6:14:99:97:33:bd:6e:3d:ef:a3:14:df:e9:79:e0:
4e:a5:17:f2:5f:14:45:39:87
Exponent: 65537 (0x10001)
X509v3 extensions:
X509v3 Basic Constraints: critical
CA:TRUE, pathlen:1
X509v3 Key Usage: critical
Certificate Sign, CRL Sign
X509v3 Subject Key Identifier:\s
D1:FE:F9:FB:F8:AE:1B:C1:60:CB:FA:03:E2:59:6D:D8:73:08:92:13
Signature Algorithm: sha1WithRSAEncryption
TEXT
cert.to_text
assert_equal 2, cert.version
assert_equal 'sha1WithRSAEncryption', cert.signature_algorithm
unless defined? JRUBY_VERSION # TODO "/DC=org,/DC=ruby-lang,/CN=TestCA"
assert_equal text_without_signature, cert.to_text[0, text_without_signature.size]
end
end
def test_to_text_regression
cert = OpenSSL::X509::Certificate.new <<-EOF
-----BEGIN CERTIFICATE-----
MIIFcjCCA1oCCQCa3TLZ9FJORzANBgkqhkiG9w0BAQsFADB6MQswCQYDVQQGEwJV
UzEVMBMGA1UECAwMVGhlIEludGVybmV0MRUwEwYDVQQHDAxUaGUgSW50ZXJuZXQx
FTATBgNVBAoMDE1hbnRpY29yZSBDQTESMBAGA1UECwwJTWFudGljb3JlMRIwEAYD
VQQDDAlsb2NhbGhvc3QwHhcNMTUwNDA2MDc1OTQ2WhcNMTUwNDA3MDc1OTQ2WjB8
MQswCQYDVQQGEwJVUzEVMBMGA1UECAwMVGhlIEludGVybmV0MRUwEwYDVQQHDAxU
aGUgSW50ZXJuZXQxFzAVBgNVBAoMDk1hbnRpY29yZSBIb3N0MRIwEAYDVQQLDAlN
YW50aWNvcmUxEjAQBgNVBAMMCWxvY2FsaG9zdDCCAiIwDQYJKoZIhvcNAQEBBQAD
ggIPADCCAgoCggIBANi9QKfRpmRXkfpcrLaI14gIT6VmDvrphQLhx8+JrEJihKv4
kXR061UFV2K52bfumoD+/vdj9DzQIAKMUXUn+Z8BfAsJR6wgocCE/I0a0NOurzMe
FTgdL6oo8pnxQ5lv7wxhUNMwXJcfcefIqBO91lKwajL9MAiOoHcfK5KNKHyowjqR
+KMUUhps4x0llkqcKZlFnhMuy2bhJDID/6xT07C6fzGH7e0ty+EWVKz7zG0mT4ek
ygZhusSkYOAp7q0FSPdR1KwB/Z5XlUjmrfvsfmSZ1kXWivJchv7cxKwu0c9fRGP5
HHSKmJn8yl5GhFt+RwTlC1O652sJylSyemh46UgNeKn9biBmKO3mgtLdnAT1tY/Z
KGByqRRmzsXcLQCP5HdKAwzjP/Lvf0k4RybmYkY14S9xnDrOojkN8yg9mZ3dzuvZ
ZRxv6EMU/Te86H9FcyBiX7IFR5sXpMTit0T6XOXmhULA9zKlHlEZ13CASjC1nMc1
rWeB6HKFJBS/Ag02it5onbqvkIsbbQqZyTwVT0yH/CYZO1YlNIvDyvmYSNm0avgZ
5pCzdBc3WlX+osVGuWMdilR93kNBI/MQRr+XDnDCXeAb3c43uBaYveYpakUDxJQB
QDSvTmAIFf8GpfyQFgLOO20CZ95LWgr/4PH2C9BPh6hciEbZgomxmuYy3PZ7AgMB
AAEwDQYJKoZIhvcNAQELBQADggIBAErx32pC6o94xIqazKDAJFnevoQjupjaW7Wc
d+QcBD/sZ5Zlfmv/MPeDaO4oGsKHHPecUojwzYg3H+BNLnjg9m7IWwqdaxuVLdt+
5sC3KFHlMtcw42Ux6XfKGecDHisJdEkoP/pPcwcfL7teZbt3dwtUh9h1dOcNFfsl
qMN7mRmDTWZIozncQCmAU6TKreZpGqeN8sTIyjvKXyXFjMhPeP0TOFfIKaUCHbyi
Ze/bREEp67G2XNxdJMAlY407RcQyoAGnbFIf/WBCiR8Y+/pnjc5n1Wc8UXXW6EVm
ObfdNkY/F4w5SPRSthn62msj7yaiAiZQ21Uwl0zb+Axvz2SS6f6NPDEinvyWIAo4
PX0p3ujl5OrYdNCe6/2cft5L73X1mAsBAtkmMXnVACm6cIPv4S3jYUXvpnfOYFCm
r91Lpir5muDzXjtXvXUUCp0Hp7ONr+Y3BI5C+Z+yiq9XHy56jv/az/h/8soEB+g5
7UrdKeDWrSUbOm85VLymiadggX+ZgxxDqCXEUPBFqgLwPe5FMLa07lUSnn2F0sK9
YVdE5HiXeoj+WE+WmRlEM3ZWujqDh8AJgaDip5hltCxWXTEBSLV4gHBKcphBbkf6
XBzK5QOuZdfCC6WJHA2Mesi3yZBbbO5Tw7vPCPdQ97pj0J3Tw1YVRPHMeJKJyF98
7/EXBJpK
-----END CERTIFICATE-----
EOF
assert_equal 0, cert.version
assert_equal 'sha256WithRSAEncryption', cert.signature_algorithm
assert cert.to_text.index('Version: 1 (0x0)')
assert cert.to_text.index('Signature Algorithm: sha256WithRSAEncryption')
end
def test_to_text_issue_322_sigstore_certificate
# https://github.com/jruby/jruby-openssl/issues/322
cert = OpenSSL::X509::Certificate.new <<-EOF
-----BEGIN CERTIFICATE-----
MIIIMDCCB7agAwIBAgIUaUHXj0S4ZNEEjDxaXlzPw/VYQQ4wCgYIKoZIzj0EAwMw
NzEVMBMGA1UEChMMc2lnc3RvcmUuZGV2MR4wHAYDVQQDExVzaWdzdG9yZS1pbnRl
cm1lZGlhdGUwHhcNMjMwOTI3MTYwNDQwWhcNMjMwOTI3MTYxNDQwWjAAMFkwEwYH
KoZIzj0CAQYIKoZIzj0DAQcDQgAEad0Uh6twE3x8YAbfBme0T/G0V2xxIl0rw/uY
8GfamPrQk3AzW9b/TwQMtipyTY2GAPDC7SVbZTxGBd6BtTWUmqOCBtUwggbRMA4G
A1UdDwEB/wQEAwIHgDATBgNVHSUEDDAKBggrBgEFBQcDAzAdBgNVHQ4EFgQUOizU
dUPvmWDSB8LtOjpjyLNKgM0wHwYDVR0jBBgwFoAU39Ppz1YkEZb5qNjpKFWixi4Y
ZD8wgaUGA1UdEQEB/wSBmjCBl4aBlGh0dHBzOi8vZ2l0aHViLmNvbS9zaWdzdG9y
ZS1jb25mb3JtYW5jZS9leHRyZW1lbHktZGFuZ2Vyb3VzLXB1YmxpYy1vaWRjLWJl
YWNvbi8uZ2l0aHViL3dvcmtmbG93cy9leHRyZW1lbHktZGFuZ2Vyb3VzLW9pZGMt
YmVhY29uLnltbEByZWZzL2hlYWRzL21haW4wOQYKKwYBBAGDvzABAQQraHR0cHM6
Ly90b2tlbi5hY3Rpb25zLmdpdGh1YnVzZXJjb250ZW50LmNvbTAfBgorBgEEAYO/
MAECBBF3b3JrZmxvd19kaXNwYXRjaDA2BgorBgEEAYO/MAEDBChmZTdhZGU5MWY0
YzRkNDZjZTc5ODg2ZmE4MGRmODAwNmEzZmFlOWUyMC0GCisGAQQBg78wAQQEH0V4
dHJlbWVseSBkYW5nZXJvdXMgT0lEQyBiZWFjb24wSQYKKwYBBAGDvzABBQQ7c2ln
c3RvcmUtY29uZm9ybWFuY2UvZXh0cmVtZWx5LWRhbmdlcm91cy1wdWJsaWMtb2lk
Yy1iZWFjb24wHQYKKwYBBAGDvzABBgQPcmVmcy9oZWFkcy9tYWluMDsGCisGAQQB
g78wAQgELQwraHR0cHM6Ly90b2tlbi5hY3Rpb25zLmdpdGh1YnVzZXJjb250ZW50
LmNvbTCBpgYKKwYBBAGDvzABCQSBlwyBlGh0dHBzOi8vZ2l0aHViLmNvbS9zaWdz
dG9yZS1jb25mb3JtYW5jZS9leHRyZW1lbHktZGFuZ2Vyb3VzLXB1YmxpYy1vaWRj
LWJlYWNvbi8uZ2l0aHViL3dvcmtmbG93cy9leHRyZW1lbHktZGFuZ2Vyb3VzLW9p
ZGMtYmVhY29uLnltbEByZWZzL2hlYWRzL21haW4wOAYKKwYBBAGDvzABCgQqDChm
ZTdhZGU5MWY0YzRkNDZjZTc5ODg2ZmE4MGRmODAwNmEzZmFlOWUyMB0GCisGAQQB
g78wAQsEDwwNZ2l0aHViLWhvc3RlZDBeBgorBgEEAYO/MAEMBFAMTmh0dHBzOi8v
Z2l0aHViLmNvbS9zaWdzdG9yZS1jb25mb3JtYW5jZS9leHRyZW1lbHktZGFuZ2Vy
b3VzLXB1YmxpYy1vaWRjLWJlYWNvbjA4BgorBgEEAYO/MAENBCoMKGZlN2FkZTkx
ZjRjNGQ0NmNlNzk4ODZmYTgwZGY4MDA2YTNmYWU5ZTIwHwYKKwYBBAGDvzABDgQR
DA9yZWZzL2hlYWRzL21haW4wGQYKKwYBBAGDvzABDwQLDAk2MzI1OTY4OTcwNwYK
KwYBBAGDvzABEAQpDCdodHRwczovL2dpdGh1Yi5jb20vc2lnc3RvcmUtY29uZm9y
bWFuY2UwGQYKKwYBBAGDvzABEQQLDAkxMzE4MDQ1NjMwgaYGCisGAQQBg78wARIE
gZcMgZRodHRwczovL2dpdGh1Yi5jb20vc2lnc3RvcmUtY29uZm9ybWFuY2UvZXh0
cmVtZWx5LWRhbmdlcm91cy1wdWJsaWMtb2lkYy1iZWFjb24vLmdpdGh1Yi93b3Jr
Zmxvd3MvZXh0cmVtZWx5LWRhbmdlcm91cy1vaWRjLWJlYWNvbi55bWxAcmVmcy9o
ZWFkcy9tYWluMDgGCisGAQQBg78wARMEKgwoZmU3YWRlOTFmNGM0ZDQ2Y2U3OTg4
NmZhODBkZjgwMDZhM2ZhZTllMjAhBgorBgEEAYO/MAEUBBMMEXdvcmtmbG93X2Rp
c3BhdGNoMIGBBgorBgEEAYO/MAEVBHMMcWh0dHBzOi8vZ2l0aHViLmNvbS9zaWdz
dG9yZS1jb25mb3JtYW5jZS9leHRyZW1lbHktZGFuZ2Vyb3VzLXB1YmxpYy1vaWRj
LWJlYWNvbi9hY3Rpb25zL3J1bnMvNjMyODQ5OTI2My9hdHRlbXB0cy8xMBYGCisG
AQQBg78wARYECAwGcHVibGljMIGJBgorBgEEAdZ5AgQCBHsEeQB3AHUA3T0wasbH
ETJjGR4cmWc3AqJKXrjePK3/h4pygC8p7o4AAAGK12KksgAABAMARjBEAiB/73GK
v9a3CdW4uBkWhNw1W0YCeLuBLRi/Pv6yrASVpwIgOrK8L2ubaLnXSWAiK76oDmmJ
1MaHKGanSuh13pxW4fgwCgYIKoZIzj0EAwMDaAAwZQIwaG18DfwChTX9hPA/WADa
i9Wh9i3hESo5Nixoff/71AtMwETfBDu2MVN3lqo8o73NAjEAxed8hLxiJdxmZ3ZA
XPOarzmFTZLPC794+i15i7RqInsZ49FtUVLjHuvccINZL63Y
-----END CERTIFICATE-----
EOF
assert_equal 2, cert.version
assert cert.extensions.any? { |ext| ext.oid == '1.3.6.1.4.1.57264.1.13' }
text = cert.to_text
assert text.index('X509v3 Subject Alternative Name: critical')
assert text.index('1.3.6.1.4.1.57264.1.13:')
assert text.index('CT Precertificate SCTs:')
end
def test_to_text_read_back
crt = File.expand_path('ca.crt', File.dirname(__FILE__))
cert = OpenSSL::X509::Certificate.new File.read(crt)
p cert if $VERBOSE
key_id = '8F:B4:94:C8:7F:CB:EF:00:89:B2:F6:C1:BE:44:4B:1C:12:54:3B:28'
assert cert.to_text.index('X509v3 Authority Key Identifier:')
assert cert.to_text.match /X509v3 Authority Key Identifier:\s*?keyid:#{key_id}/m
assert cert.to_text.index('X509v3 Subject Key Identifier:')
assert cert.to_text.match /X509v3 Subject Key Identifier:\s*?#{key_id}/m
end
def test_to_text_npe_regression
# https://github.com/jruby/jruby-openssl/issues/78
key = OpenSSL::PKey::RSA.generate(2048)
issuer = subject = OpenSSL::X509::Name.new
subject.add_entry('C', 'JP')
subject.add_entry('ST', 'Tokyo')
subject.add_entry('L', 'Chiyoda')
subject.add_entry('CN', 'demo.example.com')
digest = OpenSSL::Digest::SHA1.new
cert = OpenSSL::X509::Certificate.new
cert.not_before = Time.at(0)
cert.not_after = Time.now + 5 * 365 * 86400 # 5 years after
cert.public_key = key
cert.serial = 1
cert.issuer = issuer
cert.subject = subject
cert.add_extension OpenSSL::X509::Extension.new('basicConstraints', OpenSSL::ASN1.Sequence([OpenSSL::ASN1::Boolean(true)]))
cert.sign(key, digest)
assert cert.to_text.index('Version: 1 (0x0)')
assert cert.to_text.index('Serial Number: 1 (0x1)')
# TODO
#assert cert.to_text.index('Issuer: C=JP, ST=Tokyo, L=Chiyoda, CN=demo.example.com')
assert_equal 0, cert.version
assert_equal OpenSSL::BN.new(1), cert.serial
end
def test_sign_invalid_arg
issuer = subject = OpenSSL::X509::Name.new
subject.add_entry('C', 'JP')
subject.add_entry('ST', 'Tokyo')
subject.add_entry('L', 'Chiyoda')
subject.add_entry('CN', 'demo.example.com')
cert = OpenSSL::X509::Certificate.new
cert.not_before = Time.at(0)
cert.not_after = Time.now + 1 * 365 * 86400
cert.public_key = pkey = OpenSSL::PKey::RSA.generate(1024)
cert.serial = 1
cert.issuer = issuer
cert.subject = subject
cert.add_extension OpenSSL::X509::Extension.new('basicConstraints', OpenSSL::ASN1.Sequence([OpenSSL::ASN1::Boolean(true)]))
digest = OpenSSL::Digest::SHA1.new
begin
cert.sign(nil, digest)
fail 'expected sign to fail (on pkey)'
rescue StandardError # expected
assert :ok
end
begin
cert.sign(pkey, nil)
fail 'expected sign to fail (on digest)'
rescue TypeError # expected
assert :ok
end
end
def test_sign_cert_default_serial # jruby/jruby#1691
context = OpenSSL::SSL::SSLContext.new
context.verify_mode = OpenSSL::SSL::VERIFY_NONE
context.key = OpenSSL::PKey::RSA.new(1024)
context.cert = OpenSSL::X509::Certificate.new
context.cert.subject = OpenSSL::X509::Name.new( [['CN', 'localhost']] )
context.cert.issuer = context.cert.subject
context.cert.public_key = context.key
context.cert.not_before = Time.now
context.cert.not_after = Time.now + 60 * 60 * 24
if defined? JRUBY_VERSION
begin
res = context.cert.sign(context.key, OpenSSL::Digest::SHA1.new)
rescue OpenSSL::X509::CertificateError
return
end
else
res = context.cert.sign(context.key, OpenSSL::Digest::SHA1.new)
end
# MRI allows (invalid) serial == 0 :
assert res.is_a?(OpenSSL::X509::Certificate)
assert_equal 0, res.serial
end
def test_tbs_precert_bytes
ca = OpenSSL::X509::Name.parse('/DC=org/DC=ruby-lang/CN=CA')
cert = issue_cert(ca, OpenSSL::PKey::RSA.new(TEST_KEY_RSA1024), 1, [], nil, nil)
seq = OpenSSL::ASN1.decode(cert.tbs_bytes)
assert_equal 7, seq.value.size
end
def test_eq
now = Time.now
ca = OpenSSL::X509::Name.parse('/DC=org/DC=ruby-lang/CN=CA')
ee = OpenSSL::X509::Name.parse('/DC=org/DC=example/CN=ServerCert')
ca_key = OpenSSL::PKey::RSA.new(TEST_KEY_RSA1024)
ee_key = OpenSSL::PKey::RSA.new(TEST_KEY_RSA1024)
cacert = issue_cert(ca, ca_key, 1, [], nil, nil,
not_before: now, not_after: now + 3600)
cert1 = issue_cert(ee, ee_key, 2, [], cacert, ca_key,
not_before: now, not_after: now + 3600)
cert2 = issue_cert(ee, ee_key, 2, [], cacert, ca_key,
not_before: now, not_after: now + 3600)
cert3 = issue_cert(ee, ee_key, 3, [], cacert, ca_key,
not_before: now, not_after: now + 3600)
cert4 = issue_cert(ee, ee_key, 2, [], cacert, ca_key,
digest: 'sha512', not_before: now, not_after: now + 3600)
assert_equal false, cert1 == 12345
assert_equal true, cert1 == cert2
assert_equal false, cert1 == cert3
assert_equal false, cert1 == cert4
assert_equal false, cert3 == cert4
end
def test_cert_loading_regression
cert_text = "0\x82\x01\xAD0\x82\x01\xA1\xA0\x03\x02\x01\x02\x02\x01\x010\x03\x06\x01\x000g1\v0\t\x06\x03U\x04\x06\x13\x02US1\x130\x11\x06\x03U\x04\b\f\nCalifornia1\x150\x13\x06\x03U\x04\a\f\fSanta Monica1\x110\x0F\x06\x03U\x04\n\f\bOneLogin1\x190\x17\x06\x03U\x04\x03\f\x10app.onelogin.com0\x1E\x17\r100309095845Z\x17\r150309095845Z0g1\v0\t\x06\x03U\x04\x06\x13\x02US1\x130\x11\x06\x03U\x04\b\f\nCalifornia1\x150\x13\x06\x03U\x04\a\f\fSanta Monica1\x110\x0F\x06\x03U\x04\n\f\bOneLogin1\x190\x17\x06\x03U\x04\x03\f\x10app.onelogin.com0\x81\x9F0\r\x06\t*\x86H\x86\xF7\r\x01\x01\x01\x05\x00\x03\x81\x8D\x000\x81\x89\x02\x81\x81\x00\xE8\xD2\xBBW\xE3?/\x1D\xE7\x0E\x10\xC8\xBD~\xCD\xDE!#\rL\x92G\xDF\xE1f?L\xB1\xBC9\x99\x14\xE5\x84\xD2Zi\x87<>d\xBD\x81\xF9\xBA\x85\xD2\xFF\xAA\x90\xF3Z\x97\xA5\x1D\xB0W\xC0\x93\xA3\x06IP\xB84\xF5\xD7Qu\x19\xFCB\xCA\xA3\xD4\\\x8E\v\x9B%\x13|\xB6m\x9D\xA8\x16\xE6\xBB\xDA\x87\xFF\xE3\xD7\xE9\xBA9\xC5O\xA2\xA7C\xADB\x04\xCA\xA5\x0E\x84\xD0\xA8\xE4\xFA\xDA\xF1\x89\xF2s\xFA1\x95\xAF\x03\xAB1\xAA\xE7y\x02\x03\x01\x00\x010\x03\x06\x01\x00\x03\x01\x00"
assert cert = OpenSSL::X509::Certificate.new(cert_text)
debug cert.to_text
assert cert.to_text.index('Signature Algorithm: 0.0')
end
TEST_KEY_RSA1024 = <<-_end_of_pem_
-----BEGIN RSA PRIVATE KEY-----
MIICXgIBAAKBgQDLwsSw1ECnPtT+PkOgHhcGA71nwC2/nL85VBGnRqDxOqjVh7Cx
aKPERYHsk4BPCkE3brtThPWc9kjHEQQ7uf9Y1rbCz0layNqHyywQEVLFmp1cpIt/
Q3geLv8ZD9pihowKJDyMDiN6ArYUmZczvW4976MU3+l54E6lF/JfFEU5hwIDAQAB
AoGBAKSl/MQarye1yOysqX6P8fDFQt68VvtXkNmlSiKOGuzyho0M+UVSFcs6k1L0
maDE25AMZUiGzuWHyaU55d7RXDgeskDMakD1v6ZejYtxJkSXbETOTLDwUWTn618T
gnb17tU1jktUtU67xK/08i/XodlgnQhs6VoHTuCh3Hu77O6RAkEA7+gxqBuZR572
74/akiW/SuXm0SXPEviyO1MuSRwtI87B02D0qgV8D1UHRm4AhMnJ8MCs1809kMQE
JiQUCrp9mQJBANlt2ngBO14us6NnhuAseFDTBzCHXwUUu1YKHpMMmxpnGqaldGgX
sOZB3lgJsT9VlGf3YGYdkLTNVbogQKlKpB8CQQDiSwkb4vyQfDe8/NpU5Not0fII
8jsDUCb+opWUTMmfbxWRR3FBNu8wnym/m19N4fFj8LqYzHX4KY0oVPu6qvJxAkEA
wa5snNekFcqONLIE4G5cosrIrb74sqL8GbGb+KuTAprzj5z1K8Bm0UW9lTjVDjDi
qRYgZfZSL+x1P/54+xTFSwJAY1FxA/N3QPCXCjPh5YqFxAMQs2VVYTfg+t0MEcJD
dPMQD5JX6g5HKnHFg2mZtoXQrWmJSn7p8GJK8yNTopEErA==
-----END RSA PRIVATE KEY-----
_end_of_pem_
TEST_KEY_RSA2048 = <<-_end_of_pem_
-----BEGIN RSA PRIVATE KEY-----
MIIEpAIBAAKCAQEAuV9ht9J7k4NBs38jOXvvTKY9gW8nLICSno5EETR1cuF7i4pN
s9I1QJGAFAX0BEO4KbzXmuOvfCpD3CU+Slp1enenfzq/t/e/1IRW0wkJUJUFQign
4CtrkJL+P07yx18UjyPlBXb81ApEmAB5mrJVSrWmqbjs07JbuS4QQGGXLc+Su96D
kYKmSNVjBiLxVVSpyZfAY3hD37d60uG+X8xdW5v68JkRFIhdGlb6JL8fllf/A/bl
NwdJOhVr9mESHhwGjwfSeTDPfd8ZLE027E5lyAVX9KZYcU00mOX+fdxOSnGqS/8J
DRh0EPHDL15RcJjV2J6vZjPb0rOYGDoMcH+94wIDAQABAoIBAAzsamqfYQAqwXTb
I0CJtGg6msUgU7HVkOM+9d3hM2L791oGHV6xBAdpXW2H8LgvZHJ8eOeSghR8+dgq
PIqAffo4x1Oma+FOg3A0fb0evyiACyrOk+EcBdbBeLo/LcvahBtqnDfiUMQTpy6V
seSoFCwuN91TSCeGIsDpRjbG1vxZgtx+uI+oH5+ytqJOmfCksRDCkMglGkzyfcl0
Xc5CUhIJ0my53xijEUQl19rtWdMnNnnkdbG8PT3LZlOta5Do86BElzUYka0C6dUc
VsBDQ0Nup0P6rEQgy7tephHoRlUGTYamsajGJaAo1F3IQVIrRSuagi7+YpSpCqsW
wORqorkCgYEA7RdX6MDVrbw7LePnhyuaqTiMK+055/R1TqhB1JvvxJ1CXk2rDL6G
0TLHQ7oGofd5LYiemg4ZVtWdJe43BPZlVgT6lvL/iGo8JnrncB9Da6L7nrq/+Rvj
XGjf1qODCK+LmreZWEsaLPURIoR/Ewwxb9J2zd0CaMjeTwafJo1CZvcCgYEAyCgb
aqoWvUecX8VvARfuA593Lsi50t4MEArnOXXcd1RnXoZWhbx5rgO8/ATKfXr0BK/n
h2GF9PfKzHFm/4V6e82OL7gu/kLy2u9bXN74vOvWFL5NOrOKPM7Kg+9I131kNYOw
Ivnr/VtHE5s0dY7JChYWE1F3vArrOw3T00a4CXUCgYEA0SqY+dS2LvIzW4cHCe9k
IQqsT0yYm5TFsUEr4sA3xcPfe4cV8sZb9k/QEGYb1+SWWZ+AHPV3UW5fl8kTbSNb
v4ng8i8rVVQ0ANbJO9e5CUrepein2MPL0AkOATR8M7t7dGGpvYV0cFk8ZrFx0oId
U0PgYDotF/iueBWlbsOM430CgYEAqYI95dFyPI5/AiSkY5queeb8+mQH62sdcCCr
vd/w/CZA/K5sbAo4SoTj8dLk4evU6HtIa0DOP63y071eaxvRpTNqLUOgmLh+D6gS
Cc7TfLuFrD+WDBatBd5jZ+SoHccVrLR/4L8jeodo5FPW05A+9gnKXEXsTxY4LOUC
9bS4e1kCgYAqVXZh63JsMwoaxCYmQ66eJojKa47VNrOeIZDZvd2BPVf30glBOT41
gBoDG3WMPZoQj9pb7uMcrnvs4APj2FIhMU8U15LcPAj59cD6S6rWnAxO8NFK7HQG
4Jxg3JNNf8ErQoCHb1B3oVdXJkmbJkARoDpBKmTCgKtP8ADYLmVPQw==
-----END RSA PRIVATE KEY-----
_end_of_pem_
def test_cert_subject_hash
cert = OpenSSL::X509::Certificate.new <<-EOF
-----BEGIN CERTIFICATE-----
MIIDdTCCAl2gAwIBAgILBAAAAAABFUtaw5QwDQYJKoZIhvcNAQEFBQAwVzELMAkG
A1UEBhMCQkUxGTAXBgNVBAoTEEdsb2JhbFNpZ24gbnYtc2ExEDAOBgNVBAsTB1Jv
b3QgQ0ExGzAZBgNVBAMTEkdsb2JhbFNpZ24gUm9vdCBDQTAeFw05ODA5MDExMjAw
MDBaFw0yODAxMjgxMjAwMDBaMFcxCzAJBgNVBAYTAkJFMRkwFwYDVQQKExBHbG9i
YWxTaWduIG52LXNhMRAwDgYDVQQLEwdSb290IENBMRswGQYDVQQDExJHbG9iYWxT
aWduIFJvb3QgQ0EwggEiMA0GCSqGSIb3DQEBAQUAA4IBDwAwggEKAoIBAQDaDuaZ
jc6j40+Kfvvxi4Mla+pIH/EqsLmVEQS98GPR4mdmzxzdzxtIK+6NiY6arymAZavp
xy0Sy6scTHAHoT0KMM0VjU/43dSMUBUc71DuxC73/OlS8pF94G3VNTCOXkNz8kHp
1Wrjsok6Vjk4bwY8iGlbKk3Fp1S4bInMm/k8yuX9ifUSPJJ4ltbcdG6TRGHRjcdG
snUOhugZitVtbNV4FpWi6cgKOOvyJBNPc1STE4U6G7weNLWLBYy5d4ux2x8gkasJ
U26Qzns3dLlwR5EiUWMWea6xrkEmCMgZK9FGqkjWZCrXgzT/LCrBbBlDSgeF59N8
9iFo7+ryUp9/k5DPAgMBAAGjQjBAMA4GA1UdDwEB/wQEAwIBBjAPBgNVHRMBAf8E
BTADAQH/MB0GA1UdDgQWBBRge2YaRQ2XyolQL30EzTSo//z9SzANBgkqhkiG9w0B
AQUFAAOCAQEA1nPnfE920I2/7LqivjTFKDK1fPxsnCwrvQmeU79rXqoRSLblCKOz
yj1hTdNGCbM+w6DjY1Ub8rrvrTnhQ7k4o+YviiY776BQVvnGCv04zcQLcFGUl5gE
38NflNUVyRRBnMRddWQVDf9VMOyGj/8N7yy5Y0b2qvzfvGn9LhJIZJrglfCm7ymP
AbEVtQwdpf5pLGkkeB6zpxxxYu7KyJesF12KwvhHhm4qxFYxldBniYUr+WymXUad
DKqC5JlR3XC321Y9YeRq4VzW9v493kHMB65jUr9TU/Qr6cf9tveCX4XSQRjbgbME
HMUfpIBvFSDJ3gyICh3WZlXi/EjJKSZp4A==
-----END CERTIFICATE-----
EOF
assert_equal '5ad8a5d6', cert.subject.hash.to_s(16)
end
def test_ec_public_key
cert_string = File.read(File.expand_path('ec.crt', File.dirname(__FILE__)))
cert = OpenSSL::X509::Certificate.new(cert_string)
assert_same OpenSSL::PKey::EC, cert.public_key.class
end
def test_rsa_public_key
cert_string = File.read(File.expand_path('rsa.crt', File.dirname(__FILE__)))
cert = OpenSSL::X509::Certificate.new(cert_string)
assert_same OpenSSL::PKey::RSA, cert.public_key.class
end
def test_dsa_public_key
cert_string = File.read(File.expand_path('dsa.crt', File.dirname(__FILE__)))
cert = OpenSSL::X509::Certificate.new(cert_string)
assert_same OpenSSL::PKey::DSA, cert.public_key.class
end
def test_crl_distribution_points_to_text
key = OpenSSL::PKey::RSA.new(2048)
subject = "/C=FR/ST=IDF/L=PARIS/O=Company/CN=myhost.example"
cert = OpenSSL::X509::Certificate.new
cert.subject = cert.issuer = OpenSSL::X509::Name.parse(subject)
cert.not_before = Time.now
cert.not_after = Time.now + 365*24*60*60
cert.public_key = key.public_key
cert.serial = 0x0
cert.version = 2
ef = OpenSSL::X509::ExtensionFactory.new
ef.subject_certificate = ef.issuer_certificate = cert
cert.add_extension ef.create_extension('basicConstraints', 'CA:FALSE', true)
cert.add_extension ef.create_extension('keyUsage', 'keyEncipherment,dataEncipherment,digitalSignature')
cert.add_extension ef.create_extension('subjectKeyIdentifier', 'hash')
cert.add_extension ef.create_extension('authorityKeyIdentifier', 'keyid:always,issuer:always')
cert.add_extension ef.create_extension('crlDistributionPoints', "URI:http://example.com")
cert.sign key, OpenSSL::Digest::SHA256.new
# Test that the extension value is properly formatted
crl_ext = cert.extensions.find { |e| e.oid == 'crlDistributionPoints' }
assert_not_nil crl_ext, "crlDistributionPoints extension not found"
assert_equal "Full Name:\n URI:http://example.com", crl_ext.value
# Test that to_text output matches C-Ruby format
text = cert.to_text
assert text.include?('X509v3 CRL Distribution Points:'), "Missing 'X509v3 CRL Distribution Points:' in to_text output"
# Extract the CRL Distribution Points section
lines = text.split("\n")
crl_idx = lines.index { |l| l.include?("X509v3 CRL Distribution Points") }
assert_not_nil crl_idx, "Could not find CRL Distribution Points line"
# Check the formatting matches C-Ruby:
# Line 0: " X509v3 CRL Distribution Points: "
# Line 1: " Full Name:"
# Line 2: " URI:http://example.com"
assert_match(/X509v3 CRL Distribution Points:\s*$/, lines[crl_idx])
assert_match(/^\s+Full Name:\s*$/, lines[crl_idx + 1])
assert_match(/^\s+URI:http:\/\/example\.com/, lines[crl_idx + 2])
end
def test_crl_distribution_points_with_config
key = OpenSSL::PKey::RSA.new(2048)
subject = "/C=FR/ST=IDF/L=PARIS/O=Company/CN=myhost.example"
cert = OpenSSL::X509::Certificate.new
cert.subject = cert.issuer = OpenSSL::X509::Name.parse(subject)
cert.not_before = Time.now
cert.not_after = Time.now + 365*24*60*60
cert.public_key = key.public_key
cert.serial = 0x1
cert.version = 2
# Create a config with CRL distribution points section
config = OpenSSL::Config.new
config['crl_section'] = {
'URI.1' => 'http://crl.example.com/ca.crl',
'URI.2' => 'http://crl2.example.com/ca.crl'
}
ef = OpenSSL::X509::ExtensionFactory.new
ef.subject_certificate = ef.issuer_certificate = cert
ef.config = config
cert.add_extension ef.create_extension('basicConstraints', 'CA:FALSE', true)
cert.add_extension ef.create_extension('subjectKeyIdentifier', 'hash')
# Use @ syntax to reference config section
cert.add_extension ef.create_extension('crlDistributionPoints', '@crl_section')
cert.sign key, OpenSSL::Digest::SHA256.new
# Test that the extension value contains both URIs
crl_ext = cert.extensions.find { |e| e.oid == 'crlDistributionPoints' }
assert_not_nil crl_ext, "crlDistributionPoints extension not found"
value = crl_ext.value
assert value.include?('Full Name:'), "Missing 'Full Name:' in extension value"
assert value.include?('URI:http://crl.example.com/ca.crl'), "Missing first URI in extension value"
assert value.include?('URI:http://crl2.example.com/ca.crl'), "Missing second URI in extension value"
# Test that to_text output is properly formatted
text = cert.to_text
assert text.include?('X509v3 CRL Distribution Points:'), "Missing 'X509v3 CRL Distribution Points:' in to_text output"
assert text.include?('URI:http://crl.example.com/ca.crl'), "Missing first URI in to_text output"
assert text.include?('URI:http://crl2.example.com/ca.crl'), "Missing second URI in to_text output"
end
def test_crl_distribution_points_with_dirname
key = OpenSSL::PKey::RSA.new(2048)
subject = "/C=FR/ST=IDF/L=PARIS/O=Company/CN=myhost.example"
cert = OpenSSL::X509::Certificate.new
cert.subject = cert.issuer = OpenSSL::X509::Name.parse(subject)
cert.not_before = Time.now
cert.not_after = Time.now + 365*24*60*60
cert.public_key = key.public_key
cert.serial = 0x2
cert.version = 2
# Create a config with directory name section
config = OpenSSL::Config.new
config['issuer_sect'] = {
'C' => 'US',
'O' => 'Example Inc',
'CN' => 'Example CA'
}
config['crl_section'] = {
'fullname' => 'dirName:issuer_sect'
}
ef = OpenSSL::X509::ExtensionFactory.new
ef.subject_certificate = ef.issuer_certificate = cert
ef.config = config
cert.add_extension ef.create_extension('basicConstraints', 'CA:FALSE', true)
cert.add_extension ef.create_extension('subjectKeyIdentifier', 'hash')
cert.add_extension ef.create_extension('crlDistributionPoints', 'crl_section')
cert.sign key, OpenSSL::Digest::SHA256.new
# Test that the extension value contains the directory name
crl_ext = cert.extensions.find { |e| e.oid == 'crlDistributionPoints' }
assert_not_nil crl_ext, "crlDistributionPoints extension not found"
value = crl_ext.value
assert value.include?('Full Name:'), "Missing 'Full Name:' in extension value"
assert value.include?('DirName:'), "Missing 'DirName:' in extension value"
end
def test_authority_info_access_to_text
key = OpenSSL::PKey::RSA.new(2048)
subject = "/C=FR/ST=IDF/L=PARIS/O=Company/CN=myhost.example"
cert = OpenSSL::X509::Certificate.new
cert.subject = cert.issuer = OpenSSL::X509::Name.parse(subject)
cert.not_before = Time.now
cert.not_after = Time.now + 365*24*60*60
cert.public_key = key.public_key
cert.serial = 0x3
cert.version = 2
ef = OpenSSL::X509::ExtensionFactory.new
ef.subject_certificate = ef.issuer_certificate = cert
cert.add_extension ef.create_extension('authorityInfoAccess',
'OCSP;URI:http://ocsp.example.com,caIssuers;URI:http://ca.example.com/ca.crt')
cert.sign key, OpenSSL::Digest::SHA256.new
aia_ext = cert.extensions.find { |e| e.oid == 'authorityInfoAccess' }
assert_not_nil aia_ext, 'authorityInfoAccess extension not found'
assert_equal "OCSP - URI:http://ocsp.example.com\nCA Issuers - URI:http://ca.example.com/ca.crt", aia_ext.value
text = cert.to_text
assert text.include?('Authority Information Access:'), "Missing 'Authority Information Access:' in to_text output"
assert text.include?('OCSP - URI:http://ocsp.example.com'), 'Missing OCSP URI in to_text output'
assert text.include?('CA Issuers - URI:http://ca.example.com/ca.crt'), 'Missing CA Issuers URI in to_text output'
end
def test_authority_info_access_ocsp_uris # GH-210
cert = OpenSSL::X509::Certificate.new(Fixtures.read_file('x509', 'login_live_com_aia.pem'))
aia_ext = cert.extensions.find { |e| e.oid == 'authorityInfoAccess' }
assert_not_nil aia_ext, 'authorityInfoAccess extension not found'
expected = "OCSP - URI:http://ocsp.digicert.com\nCA Issuers - URI:http://cacerts.digicert.com/DigiCertSHA2SecureServerCA.crt"
assert_equal expected, aia_ext.value
assert_equal ['http://ocsp.digicert.com'], cert.ocsp_uris
assert_equal ['http://cacerts.digicert.com/DigiCertSHA2SecureServerCA.crt'], cert.ca_issuer_uris
assert_not_match(/#<OpenSSL::ASN1::/, aia_ext.value)
end
def test_dup_preserves_extensions
rsa2048 = OpenSSL::PKey::RSA.new TEST_KEY_RSA2048
ca = OpenSSL::X509::Name.parse("/DC=org/DC=ruby-lang/CN=CA")
ca_exts = [
[ "basicConstraints", "CA:TRUE", true ],
[ "keyUsage", "keyCertSign, cRLSign", true ],
[ "subjectKeyIdentifier", "hash", false ],
]
now = Time.now
cert = issue_cert(ca, rsa2048, 1, ca_exts, nil, nil,
not_before: now, not_after: now + 3600)
duped = cert.dup
assert_equal cert.extensions.size, duped.extensions.size,
"dup should preserve extensions"
assert_equal cert.extensions.map(&:oid).sort, duped.extensions.map(&:oid).sort
end
def test_dup_preserves_subject_and_issuer
rsa2048 = OpenSSL::PKey::RSA.new TEST_KEY_RSA2048
ca = OpenSSL::X509::Name.parse("/DC=org/DC=ruby-lang/CN=CA")
now = Time.now
cert = issue_cert(ca, rsa2048, 42, [], nil, nil,
not_before: now, not_after: now + 3600)
duped = cert.dup
assert_equal cert.subject.to_s, duped.subject.to_s
assert_equal cert.issuer.to_s, duped.issuer.to_s
assert_equal cert.serial, duped.serial
assert_equal cert.version, duped.version
assert_equal cert.not_before.to_i, duped.not_before.to_i
assert_equal cert.not_after.to_i, duped.not_after.to_i
end
def test_dup_produces_independent_copy
rsa2048 = OpenSSL::PKey::RSA.new TEST_KEY_RSA2048
ca = OpenSSL::X509::Name.parse("/DC=org/DC=ruby-lang/CN=CA")
ca_exts = [
[ "basicConstraints", "CA:TRUE", true ],
[ "subjectKeyIdentifier", "hash", false ],
]
now = Time.now
cert = issue_cert(ca, rsa2048, 1, ca_exts, nil, nil,
not_before: now, not_after: now + 3600)
duped = cert.dup
# Modifying the dup's extensions should not affect the original
duped.extensions = []
assert_equal 2, cert.extensions.size,
"modifying duped cert's extensions should not affect original"
end
def test_dup_to_der_matches_original
rsa2048 = OpenSSL::PKey::RSA.new TEST_KEY_RSA2048
ca = OpenSSL::X509::Name.parse("/DC=org/DC=ruby-lang/CN=CA")
ca_exts = [
[ "basicConstraints", "CA:TRUE", true ],
[ "keyUsage", "keyCertSign, cRLSign", true ],
[ "subjectKeyIdentifier", "hash", false ],
]
now = Time.now
cert = issue_cert(ca, rsa2048, 1, ca_exts, nil, nil,
not_before: now, not_after: now + 3600)
duped = cert.dup
assert_equal cert.to_der, duped.to_der
end
def test_dup_preserves_live_subject_after_signed_cert_name_is_mutated
rsa2048 = OpenSSL::PKey::RSA.new TEST_KEY_RSA2048
ca = OpenSSL::X509::Name.parse("/DC=org/DC=ruby-lang/CN=CA")
now = Time.now
cert = issue_cert(ca, rsa2048, 1, [], nil, nil,
not_before: now, not_after: now + 3600)
cert.subject.add_entry("O", "mutated")
duped = cert.dup
assert_equal cert.subject.to_a, duped.subject.to_a
end
def test_dup_preserves_live_extensions_after_signed_cert_is_modified
rsa2048 = OpenSSL::PKey::RSA.new TEST_KEY_RSA2048
ca = OpenSSL::X509::Name.parse("/DC=org/DC=ruby-lang/CN=CA")
ca_exts = [
[ "basicConstraints", "CA:TRUE", true ],
[ "subjectKeyIdentifier", "hash", false ],
]
now = Time.now
cert = issue_cert(ca, rsa2048, 1, ca_exts, nil, nil,
not_before: now, not_after: now + 3600)
cert.extensions = []
duped = cert.dup
assert_equal [], duped.extensions.map(&:oid)
end
def test_dup_unsigned_cert_preserves_fields
rsa2048 = OpenSSL::PKey::RSA.new TEST_KEY_RSA2048
cert = OpenSSL::X509::Certificate.new
cert.version = 2
cert.serial = 99
cert.subject = OpenSSL::X509::Name.parse("/CN=unsigned")
cert.issuer = OpenSSL::X509::Name.parse("/CN=issuer")
cert.not_before = Time.now
cert.not_after = Time.now + 3600
cert.public_key = rsa2048.public_key
duped = cert.dup
assert_equal cert.subject.to_s, duped.subject.to_s
assert_equal cert.issuer.to_s, duped.issuer.to_s
assert_equal cert.serial, duped.serial
assert_equal cert.version, duped.version
assert_equal cert.public_key.to_der, duped.public_key.to_der
end
def test_dup_unsigned_cert_deep_copies_names
cert = OpenSSL::X509::Certificate.new
cert.subject = OpenSSL::X509::Name.parse("/CN=unsigned")
cert.issuer = OpenSSL::X509::Name.parse("/CN=issuer")
duped = cert.dup
duped.subject.add_entry("O", "mutated")
duped.issuer.add_entry("O", "mutated")
assert_equal "/CN=unsigned", cert.subject.to_s
assert_equal "/CN=issuer", cert.issuer.to_s
assert_equal "/CN=unsigned/O=mutated", duped.subject.to_s
assert_equal "/CN=issuer/O=mutated", duped.issuer.to_s
end
end