r509 0.8

Sign up to get free protection for your applications and to get access to all the features.
Files changed (162) hide show
  1. data/README.md +447 -0
  2. data/Rakefile +38 -0
  3. data/bin/r509 +96 -0
  4. data/bin/r509-parse +35 -0
  5. data/doc/R509.html +154 -0
  6. data/doc/R509/Cert.html +3954 -0
  7. data/doc/R509/Cert/Extensions.html +360 -0
  8. data/doc/R509/Cert/Extensions/AuthorityInfoAccess.html +391 -0
  9. data/doc/R509/Cert/Extensions/AuthorityKeyIdentifier.html +148 -0
  10. data/doc/R509/Cert/Extensions/BasicConstraints.html +482 -0
  11. data/doc/R509/Cert/Extensions/CrlDistributionPoints.html +316 -0
  12. data/doc/R509/Cert/Extensions/ExtendedKeyUsage.html +780 -0
  13. data/doc/R509/Cert/Extensions/KeyUsage.html +1230 -0
  14. data/doc/R509/Cert/Extensions/SubjectAlternativeName.html +467 -0
  15. data/doc/R509/Cert/Extensions/SubjectKeyIdentifier.html +216 -0
  16. data/doc/R509/CertificateAuthority.html +126 -0
  17. data/doc/R509/CertificateAuthority/Signer.html +855 -0
  18. data/doc/R509/Config.html +127 -0
  19. data/doc/R509/Config/CaConfig.html +2144 -0
  20. data/doc/R509/Config/CaConfigPool.html +599 -0
  21. data/doc/R509/Config/CaProfile.html +656 -0
  22. data/doc/R509/Config/SubjectItemPolicy.html +578 -0
  23. data/doc/R509/Crl.html +126 -0
  24. data/doc/R509/Crl/Administrator.html +2077 -0
  25. data/doc/R509/Crl/Parser.html +1224 -0
  26. data/doc/R509/Csr.html +2248 -0
  27. data/doc/R509/IOHelpers.html +564 -0
  28. data/doc/R509/MessageDigest.html +396 -0
  29. data/doc/R509/NameSanitizer.html +319 -0
  30. data/doc/R509/Ocsp.html +128 -0
  31. data/doc/R509/Ocsp/Request.html +126 -0
  32. data/doc/R509/Ocsp/Request/Nonce.html +160 -0
  33. data/doc/R509/Ocsp/Response.html +837 -0
  34. data/doc/R509/OidMapper.html +393 -0
  35. data/doc/R509/PrivateKey.html +1647 -0
  36. data/doc/R509/R509Error.html +134 -0
  37. data/doc/R509/Spki.html +1424 -0
  38. data/doc/R509/Subject.html +836 -0
  39. data/doc/R509/Validity.html +160 -0
  40. data/doc/R509/Validity/Checker.html +320 -0
  41. data/doc/R509/Validity/DefaultChecker.html +283 -0
  42. data/doc/R509/Validity/DefaultWriter.html +330 -0
  43. data/doc/R509/Validity/Status.html +561 -0
  44. data/doc/R509/Validity/Writer.html +394 -0
  45. data/doc/_index.html +501 -0
  46. data/doc/class_list.html +53 -0
  47. data/doc/css/common.css +1 -0
  48. data/doc/css/full_list.css +57 -0
  49. data/doc/css/style.css +328 -0
  50. data/doc/file.README.html +534 -0
  51. data/doc/file.r509.html +149 -0
  52. data/doc/file_list.html +58 -0
  53. data/doc/frames.html +28 -0
  54. data/doc/index.html +534 -0
  55. data/doc/js/app.js +208 -0
  56. data/doc/js/full_list.js +173 -0
  57. data/doc/js/jquery.js +4 -0
  58. data/doc/methods_list.html +1932 -0
  59. data/doc/top-level-namespace.html +112 -0
  60. data/lib/r509.rb +22 -0
  61. data/lib/r509/cert.rb +414 -0
  62. data/lib/r509/cert/extensions.rb +309 -0
  63. data/lib/r509/certificateauthority.rb +290 -0
  64. data/lib/r509/config.rb +407 -0
  65. data/lib/r509/crl.rb +379 -0
  66. data/lib/r509/csr.rb +324 -0
  67. data/lib/r509/exceptions.rb +5 -0
  68. data/lib/r509/io_helpers.rb +52 -0
  69. data/lib/r509/messagedigest.rb +49 -0
  70. data/lib/r509/ocsp.rb +85 -0
  71. data/lib/r509/oidmapper.rb +32 -0
  72. data/lib/r509/privatekey.rb +185 -0
  73. data/lib/r509/spki.rb +112 -0
  74. data/lib/r509/subject.rb +133 -0
  75. data/lib/r509/validity.rb +92 -0
  76. data/lib/r509/version.rb +4 -0
  77. data/r509.yaml +73 -0
  78. data/spec/cert/extensions_spec.rb +632 -0
  79. data/spec/cert_spec.rb +321 -0
  80. data/spec/certificate_authority_spec.rb +260 -0
  81. data/spec/config_spec.rb +349 -0
  82. data/spec/crl_spec.rb +215 -0
  83. data/spec/csr_spec.rb +302 -0
  84. data/spec/fixtures.rb +233 -0
  85. data/spec/fixtures/cert1.der +0 -0
  86. data/spec/fixtures/cert1.pem +24 -0
  87. data/spec/fixtures/cert1_public_key_modulus.txt +1 -0
  88. data/spec/fixtures/cert3.p12 +0 -0
  89. data/spec/fixtures/cert3.pem +28 -0
  90. data/spec/fixtures/cert3_key.pem +27 -0
  91. data/spec/fixtures/cert3_key_des3.pem +30 -0
  92. data/spec/fixtures/cert4.pem +14 -0
  93. data/spec/fixtures/cert5.pem +30 -0
  94. data/spec/fixtures/cert6.pem +26 -0
  95. data/spec/fixtures/cert_expired.pem +26 -0
  96. data/spec/fixtures/cert_not_yet_valid.pem +26 -0
  97. data/spec/fixtures/cert_san.pem +27 -0
  98. data/spec/fixtures/cert_san2.pem +22 -0
  99. data/spec/fixtures/config_pool_test_minimal.yaml +15 -0
  100. data/spec/fixtures/config_test.yaml +41 -0
  101. data/spec/fixtures/config_test_engine_key.yaml +7 -0
  102. data/spec/fixtures/config_test_engine_no_key_name.yaml +6 -0
  103. data/spec/fixtures/config_test_minimal.yaml +7 -0
  104. data/spec/fixtures/config_test_password.yaml +7 -0
  105. data/spec/fixtures/config_test_various.yaml +100 -0
  106. data/spec/fixtures/crl_list_file.txt +1 -0
  107. data/spec/fixtures/crl_with_reason.pem +17 -0
  108. data/spec/fixtures/csr1.der +0 -0
  109. data/spec/fixtures/csr1.pem +17 -0
  110. data/spec/fixtures/csr1_key.der +0 -0
  111. data/spec/fixtures/csr1_key.pem +27 -0
  112. data/spec/fixtures/csr1_key_encrypted_des3.pem +30 -0
  113. data/spec/fixtures/csr1_newlines.pem +32 -0
  114. data/spec/fixtures/csr1_no_begin_end.pem +15 -0
  115. data/spec/fixtures/csr1_public_key_modulus.txt +1 -0
  116. data/spec/fixtures/csr2.pem +15 -0
  117. data/spec/fixtures/csr2_key.pem +27 -0
  118. data/spec/fixtures/csr3.pem +16 -0
  119. data/spec/fixtures/csr4.pem +25 -0
  120. data/spec/fixtures/csr_dsa.pem +15 -0
  121. data/spec/fixtures/csr_invalid_signature.pem +13 -0
  122. data/spec/fixtures/dsa_key.pem +20 -0
  123. data/spec/fixtures/key4.pem +27 -0
  124. data/spec/fixtures/key4_encrypted_des3.pem +30 -0
  125. data/spec/fixtures/missing_key_identifier_ca.cer +21 -0
  126. data/spec/fixtures/missing_key_identifier_ca.key +27 -0
  127. data/spec/fixtures/ocsptest.r509.local.pem +27 -0
  128. data/spec/fixtures/ocsptest.r509.local_ocsp_request.der +0 -0
  129. data/spec/fixtures/ocsptest2.r509.local.pem +27 -0
  130. data/spec/fixtures/second_ca.cer +26 -0
  131. data/spec/fixtures/second_ca.key +27 -0
  132. data/spec/fixtures/spkac.der +0 -0
  133. data/spec/fixtures/spkac.txt +1 -0
  134. data/spec/fixtures/spkac_dsa.txt +1 -0
  135. data/spec/fixtures/stca.pem +22 -0
  136. data/spec/fixtures/stca_ocsp_request.der +0 -0
  137. data/spec/fixtures/stca_ocsp_response.der +0 -0
  138. data/spec/fixtures/test1.csr +17 -0
  139. data/spec/fixtures/test_ca.cer +22 -0
  140. data/spec/fixtures/test_ca.key +28 -0
  141. data/spec/fixtures/test_ca.p12 +0 -0
  142. data/spec/fixtures/test_ca_des3.key +30 -0
  143. data/spec/fixtures/test_ca_ocsp.cer +26 -0
  144. data/spec/fixtures/test_ca_ocsp.key +27 -0
  145. data/spec/fixtures/test_ca_ocsp.p12 +0 -0
  146. data/spec/fixtures/test_ca_ocsp_chain.txt +48 -0
  147. data/spec/fixtures/test_ca_ocsp_response.der +0 -0
  148. data/spec/fixtures/test_ca_subroot.cer +26 -0
  149. data/spec/fixtures/test_ca_subroot.key +27 -0
  150. data/spec/fixtures/test_ca_subroot_ocsp.cer +25 -0
  151. data/spec/fixtures/test_ca_subroot_ocsp.key +27 -0
  152. data/spec/fixtures/test_ca_subroot_ocsp_response.der +0 -0
  153. data/spec/fixtures/unknown_oid.csr +17 -0
  154. data/spec/message_digest_spec.rb +89 -0
  155. data/spec/ocsp_spec.rb +111 -0
  156. data/spec/oid_mapper_spec.rb +31 -0
  157. data/spec/privatekey_spec.rb +198 -0
  158. data/spec/spec_helper.rb +14 -0
  159. data/spec/spki_spec.rb +157 -0
  160. data/spec/subject_spec.rb +203 -0
  161. data/spec/validity_spec.rb +98 -0
  162. metadata +257 -0
@@ -0,0 +1,92 @@
1
+ require 'openssl'
2
+
3
+ #Module for holding classes for writing and reading certificate validity information (used for serving OCSP responses)
4
+ module R509::Validity
5
+ #mapping from OpenSSL
6
+ VALID = OpenSSL::OCSP::V_CERTSTATUS_GOOD
7
+ REVOKED = OpenSSL::OCSP::V_CERTSTATUS_REVOKED
8
+ UNKNOWN = OpenSSL::OCSP::V_CERTSTATUS_UNKNOWN
9
+
10
+ #data about the status of a certificate
11
+ class Status
12
+ attr_reader :status, :revocation_time, :revocation_reason
13
+
14
+ def initialize(options={})
15
+ @status = options[:status]
16
+ @revocation_time = options[:revocation_time] || nil
17
+ @revocation_reason = options[:revocation_reason] || 0
18
+
19
+ if (@status == R509::Validity::REVOKED and @revocation_time.nil?)
20
+ @revocation_time = Time.now.to_i
21
+ end
22
+ end
23
+
24
+ # @return [OpenSSL::OCSP::STATUS] OpenSSL status constants when passing R509 constants
25
+ def ocsp_status
26
+ case @status
27
+ when R509::Validity::VALID
28
+ OpenSSL::OCSP::V_CERTSTATUS_GOOD
29
+ when R509::Validity::REVOKED
30
+ OpenSSL::OCSP::V_CERTSTATUS_REVOKED
31
+ when R509::Validity::UNKNOWN
32
+ OpenSSL::OCSP::V_CERTSTATUS_UNKNOWN
33
+ else
34
+ OpenSSL::OCSP::V_CERTSTATUS_UNKNOWN
35
+ end
36
+ end
37
+ end
38
+
39
+ #abstract base class for a Writer
40
+ class Writer
41
+ def issue(issuer, serial)
42
+ raise NotImplementedError, "You must call #issue on a subclass of Writer"
43
+ end
44
+
45
+ def revoke(issuer, serial, reason)
46
+ raise NotImplementedError, "You must call #revoke on a subclass of Writer"
47
+ end
48
+
49
+ # is_available? is meant to be implemented to check if the backend store you choose to implement is currently working.
50
+ # see r509-ocsp-responder and r509-validity-redis for an example of use
51
+ def is_available?
52
+ raise NotImplementedError, "You must call #is_available? on a subclass of Writer"
53
+ end
54
+ end
55
+
56
+ #abstract base class for a Checker
57
+ class Checker
58
+ def check(issuer, serial)
59
+ raise NotImplementedError, "You must call #check on a subclass of Checker"
60
+ end
61
+
62
+ # is_available? is meant to be implemented to check if the backend store you choose to implement is currently working.
63
+ # see r509-ocsp-responder and r509-validity-redis for an example of use
64
+ def is_available?
65
+ raise NotImplementedError, "You must call #is_available? on a subclass of Checker"
66
+ end
67
+ end
68
+
69
+ #default implementaton of the Checker class. Used for tests. DO NOT USE OTHERWISE
70
+ class DefaultChecker < R509::Validity::Checker
71
+ def check(issuer, serial)
72
+ R509::Validity::Status.new(:status => R509::Validity::VALID)
73
+ end
74
+
75
+ def is_available?
76
+ true
77
+ end
78
+ end
79
+
80
+ #default implementaton of the Writer class. Does nothing (obviously)
81
+ class DefaultWriter < R509::Validity::Writer
82
+ def issue(issuer, serial)
83
+ end
84
+
85
+ def revoke(issuer, serial, reason)
86
+ end
87
+
88
+ def is_available?
89
+ true
90
+ end
91
+ end
92
+ end
@@ -0,0 +1,4 @@
1
+ module R509
2
+ #The version of the r509 gem
3
+ VERSION="0.8"
4
+ end
data/r509.yaml ADDED
@@ -0,0 +1,73 @@
1
+ certificate_authorities: {
2
+ test_ca: {
3
+ ca_cert: {
4
+ cert: 'spec/fixtures/test_ca.cer',
5
+ key: 'spec/fixtures/test_ca.key'
6
+ },
7
+ ocsp_cert: {
8
+ :pkcs12: 'spec/fixtures/test_ca_ocsp.p12',
9
+ :password: 'r509'
10
+ },
11
+ ocsp_location: 'URI:http://ocsp.domain.com',
12
+ ocsp_chain: 'spec/fixtures/test_ca_ocsp_chain.txt',
13
+ ocsp_start_skew_seconds: 3600,
14
+ ocsp_validity_hours: 168,
15
+ cdp_location: 'URI:http://crl.domain.com/test_ca.crl',
16
+ crl_list: 'spec/fixtures/test_ca_crl_list.txt',
17
+ crl_number: 'spec/fixtures/test_ca_crl_number.txt',
18
+ crl_validity_hours: 168, #7 days
19
+ message_digest: 'SHA1', #SHA1, SHA256, SHA512 supported. MD5 too, but you really shouldn't use that unless you have a good reason
20
+ profiles: {
21
+ server: {
22
+ basic_constraints: "CA:FALSE",
23
+ key_usage: [digitalSignature,keyEncipherment],
24
+ extended_key_usage: [serverAuth],
25
+ certificate_policies: [ [ "policyIdentifier=2.16.840.1.9999999999.1.2.3.4.1", "CPS.1=http://example.com/cps"] ],
26
+ subject_item_policy: {
27
+ CN: "required",
28
+ O: "required",
29
+ OU: "optional",
30
+ ST: "required",
31
+ C: "required",
32
+ L: "required"
33
+ }
34
+ },
35
+ client: {
36
+ basic_constraints: "CA:FALSE",
37
+ key_usage: [digitalSignature,keyEncipherment],
38
+ extended_key_usage: [clientAuth],
39
+ certificate_policies: [ [ "policyIdentifier=2.16.840.1.9999999999.1.2.3.4.2", "CPS.1=http://example.com/cps"] ]
40
+ },
41
+ email: {
42
+ basic_constraints: "CA:FALSE",
43
+ key_usage: [digitalSignature,keyEncipherment],
44
+ extended_key_usage: [emailProtection],
45
+ certificate_policies: [ [ "policyIdentifier=2.16.840.1.9999999999.1.2.3.4.3", "CPS.1=http://example.com/cps"] ]
46
+ },
47
+ clientserver: {
48
+ basic_constraints: "CA:FALSE",
49
+ key_usage: [digitalSignature,keyEncipherment],
50
+ extended_key_usage: [serverAuth,clientAuth],
51
+ certificate_policies: [ [ "policyIdentifier=2.16.840.1.9999999999.1.2.3.4.4", "CPS.1=http://example.com/cps"] ]
52
+ },
53
+ codesigning: {
54
+ basic_constraints: "CA:FALSE",
55
+ key_usage: [digitalSignature],
56
+ extended_key_usage: [codeSigning],
57
+ certificate_policies: [ [ "policyIdentifier=2.16.840.1.9999999999.1.2.3.4.5", "CPS.1=http://example.com/cps"] ]
58
+ },
59
+ timestamping: {
60
+ basic_constraints: "CA:FALSE",
61
+ key_usage: [digitalSignature],
62
+ extended_key_usage: [timeStamping],
63
+ certificate_policies: [ [ "policyIdentifier=2.16.840.1.9999999999.1.2.3.4.6", "CPS.1=http://example.com/cps"] ]
64
+ },
65
+ subroot: {
66
+ basic_constraints: "CA:TRUE,pathlen:0",
67
+ key_usage: [keyCertSign,cRLSign],
68
+ extended_key_usage: [],
69
+ certificate_policies: [ ]
70
+ }
71
+ }
72
+ }
73
+ }
@@ -0,0 +1,632 @@
1
+ require 'spec_helper'
2
+
3
+ include R509::Cert::Extensions
4
+
5
+ shared_examples_for "a correctly implemented wrap_openssl_extensions" do
6
+ before :each do
7
+ @r509_extensions = R509::Cert::Extensions.wrap_openssl_extensions( @openssl_extensions )
8
+
9
+ @r509_classes = [ BasicConstraints, KeyUsage, ExtendedKeyUsage,
10
+ SubjectKeyIdentifier, AuthorityKeyIdentifier,
11
+ SubjectAlternativeName, AuthorityInfoAccess,
12
+ CrlDistributionPoints ]
13
+ end
14
+
15
+ it "should not have returned values that aren't R509 extensions" do
16
+ classes = @r509_extensions.values.map { |ext| ext.class }
17
+ non_r509_classes = classes.reject { |ext_class| @r509_classes.include?(ext_class) }
18
+ non_r509_classes.should == []
19
+ end
20
+
21
+ it "should have returned the right number of extensions" do
22
+ @r509_extensions.count.should == @wrappable_extensions.count
23
+ end
24
+
25
+ it "should not have returned keys improperly mapped to values" do
26
+ incorrect_mappings = @r509_extensions.select { |key_class,ext| ext.class != key_class }
27
+ incorrect_mappings = {} if incorrect_mappings == [] # compatibility for old versions of Ruby
28
+ incorrect_mappings.should == {}
29
+ end
30
+
31
+ it "should not have failed to map an implemented extension" do
32
+ missing_extensions = []
33
+ @wrappable_extensions.each do |openssl_ext|
34
+ if (@r509_extensions.select {|r509_class,r509_ext| r509_ext.oid == openssl_ext.oid}) == {}
35
+ missing_extensions << openssl_ext.oid
36
+ end
37
+ end
38
+
39
+ missing_extensions.should == []
40
+ end
41
+ end
42
+
43
+ shared_examples_for "a correctly implemented get_unknown_extensions" do
44
+ it "should not have returned values that are R509 extensions" do
45
+ R509::Cert::Extensions.get_unknown_extensions( @openssl_extensions ).should == @unknown_extensions
46
+ end
47
+ end
48
+
49
+ shared_examples_for "a correct R509 BasicConstraints object" do
50
+ before :all do
51
+ extension_name = "basicConstraints"
52
+ klass = BasicConstraints
53
+ openssl_ext = OpenSSL::X509::Extension.new( extension_name, @extension_value )
54
+ @r509_ext = klass.new( openssl_ext )
55
+ end
56
+
57
+ it "is_ca? should correctly report whether it's a CA certificate" do
58
+ @r509_ext.is_ca?.should == @is_ca
59
+ end
60
+
61
+ it "the path length should be correct" do
62
+ @r509_ext.path_length.should == @pathlen
63
+ end
64
+
65
+ it "allows_sub_ca? should correctly report whether its path length allows it to issue CA certs" do
66
+ @r509_ext.allows_sub_ca?.should == @allows_sub_ca
67
+ end
68
+ end
69
+
70
+ shared_examples_for "a correct R509 KeyUsage object" do
71
+ before :all do
72
+ extension_name = "keyUsage"
73
+ klass = KeyUsage
74
+ openssl_ext = OpenSSL::X509::Extension.new( extension_name, @extension_value )
75
+ @r509_ext = klass.new( openssl_ext )
76
+ end
77
+
78
+ it "allowed_uses should be non-nil" do
79
+ @r509_ext.allowed_uses.should_not == nil
80
+ end
81
+
82
+ it "allowed_uses should be correct" do
83
+ @r509_ext.allowed_uses.should == @allowed_uses
84
+ end
85
+
86
+ it "the individual allowed-use functions should be correct" do
87
+ @r509_ext.digital_signature?.should == @allowed_uses.include?( KeyUsage::AU_DIGITAL_SIGNATURE )
88
+ @r509_ext.non_repudiation?.should == @allowed_uses.include?( KeyUsage::AU_NON_REPUDIATION )
89
+ @r509_ext.key_encipherment?.should == @allowed_uses.include?( KeyUsage::AU_KEY_ENCIPHERMENT )
90
+ @r509_ext.data_encipherment?.should == @allowed_uses.include?( KeyUsage::AU_DATA_ENCIPHERMENT )
91
+ @r509_ext.key_agreement?.should == @allowed_uses.include?( KeyUsage::AU_KEY_AGREEMENT )
92
+ @r509_ext.certificate_sign?.should == @allowed_uses.include?( KeyUsage::AU_CERTIFICATE_SIGN )
93
+ @r509_ext.crl_sign?.should == @allowed_uses.include?( KeyUsage::AU_CRL_SIGN )
94
+ @r509_ext.encipher_only?.should == @allowed_uses.include?( KeyUsage::AU_ENCIPHER_ONLY )
95
+ @r509_ext.decipher_only?.should == @allowed_uses.include?( KeyUsage::AU_DECIPHER_ONLY )
96
+ end
97
+ end
98
+
99
+ shared_examples_for "a correct R509 ExtendedKeyUsage object" do
100
+ before :all do
101
+ extension_name = "extendedKeyUsage"
102
+ klass = ExtendedKeyUsage
103
+ openssl_ext = OpenSSL::X509::Extension.new( extension_name, @extension_value )
104
+ @r509_ext = klass.new( openssl_ext )
105
+ end
106
+
107
+ it "allowed_uses should be non-nil" do
108
+ @r509_ext.allowed_uses.should_not == nil
109
+ end
110
+
111
+ it "allowed_uses should be correct" do
112
+ @r509_ext.allowed_uses.should == @allowed_uses
113
+ end
114
+
115
+ it "the individual allowed-use functions should be correct" do
116
+ @r509_ext.web_server_authentication?.should == @allowed_uses.include?( ExtendedKeyUsage::AU_WEB_SERVER_AUTH )
117
+ @r509_ext.web_client_authentication?.should == @allowed_uses.include?( ExtendedKeyUsage::AU_WEB_CLIENT_AUTH )
118
+ @r509_ext.code_signing?.should == @allowed_uses.include?( ExtendedKeyUsage::AU_CODE_SIGNING )
119
+ @r509_ext.email_protection?.should == @allowed_uses.include?( ExtendedKeyUsage::AU_EMAIL_PROTECTION )
120
+ end
121
+ end
122
+
123
+ shared_examples_for "a correct R509 SubjectKeyIdentifier object" do
124
+ before :all do
125
+ extension_name = "subjectKeyIdentifier"
126
+ klass = SubjectKeyIdentifier
127
+ openssl_ext = OpenSSL::X509::Extension.new( extension_name, @extension_value )
128
+ @r509_ext = klass.new( openssl_ext )
129
+ end
130
+
131
+ it "key should be correct" do
132
+ @r509_ext.key.should == @key
133
+ end
134
+ end
135
+
136
+ shared_examples_for "a correct R509 AuthorityKeyIdentifier object" do
137
+ before :all do
138
+ extension_name = "authorityKeyIdentifier"
139
+ klass = AuthorityKeyIdentifier
140
+ openssl_ext = OpenSSL::X509::Extension.new( extension_name, @extension_value )
141
+ @r509_ext = klass.new( openssl_ext )
142
+ end
143
+
144
+ #TODO
145
+ end
146
+
147
+ shared_examples_for "a correct R509 SubjectAlternativeName object" do
148
+ before :all do
149
+ extension_name = "subjectAltName"
150
+ klass = SubjectAlternativeName
151
+ openssl_ext = OpenSSL::X509::Extension.new( extension_name, @extension_value )
152
+ @r509_ext = klass.new( openssl_ext )
153
+ end
154
+
155
+ it "dns_names should be correct" do
156
+ @r509_ext.dns_names.should == @dns_names
157
+ end
158
+
159
+ it "ip_addresses should be correct" do
160
+ @r509_ext.ip_addresses.should == @ip_addresses
161
+ end
162
+
163
+ it "uris should be correct" do
164
+ @r509_ext.uris.should == @uris
165
+ end
166
+ end
167
+
168
+ shared_examples_for "a correct R509 AuthorityInfoAccess object" do
169
+ before :all do
170
+ extension_name = "authorityInfoAccess"
171
+ klass = AuthorityInfoAccess
172
+ openssl_ext = OpenSSL::X509::Extension.new( extension_name, @extension_value )
173
+ @r509_ext = klass.new( openssl_ext )
174
+ end
175
+
176
+ it "ca_issuers_uri should be correct" do
177
+ @r509_ext.ca_issuers_uris.should == @ca_issuers_uris
178
+ end
179
+
180
+ it "ocsp_uri should be correct" do
181
+ @r509_ext.ocsp_uris.should == @ocsp_uris
182
+ end
183
+ end
184
+
185
+ shared_examples_for "a correct R509 CrlDistributionPoints object" do
186
+ before :all do
187
+ extension_name = "crlDistributionPoints"
188
+ klass = CrlDistributionPoints
189
+ openssl_ext = OpenSSL::X509::Extension.new( extension_name, @extension_value )
190
+ @r509_ext = klass.new( openssl_ext )
191
+ end
192
+
193
+ it "crl_uri should be correct" do
194
+ @r509_ext.crl_uris.should == @crl_uris
195
+ end
196
+ end
197
+
198
+
199
+ describe R509::Cert::Extensions do
200
+ include R509::Cert::Extensions
201
+
202
+ context "Class functions" do
203
+ context "#wrap_openssl_extensions and #get_unknown_extensions" do
204
+ context "with no extensions" do
205
+ before :each do
206
+ @wrappable_extensions = []
207
+ @unknown_extensions = []
208
+
209
+ @openssl_extensions = @wrappable_extensions + @unknown_extensions
210
+ end
211
+
212
+ it_should_behave_like "a correctly implemented wrap_openssl_extensions"
213
+ it_should_behave_like "a correctly implemented get_unknown_extensions"
214
+ end
215
+
216
+ context "with one implemented extension" do
217
+ before :each do
218
+ @wrappable_extensions = []
219
+ @wrappable_extensions << OpenSSL::X509::Extension.new( "basicConstraints", "CA:TRUE;pathlen:0" )
220
+
221
+ @unknown_extensions = []
222
+
223
+ @openssl_extensions = @wrappable_extensions + @unknown_extensions
224
+ end
225
+
226
+ it_should_behave_like "a correctly implemented wrap_openssl_extensions"
227
+ it_should_behave_like "a correctly implemented get_unknown_extensions"
228
+ end
229
+
230
+ context "with all implemented extensions" do
231
+ before :each do
232
+ @wrappable_extensions = []
233
+ @wrappable_extensions << OpenSSL::X509::Extension.new( "basicConstraints", "CA:TRUE;pathlen:0" )
234
+ @wrappable_extensions << OpenSSL::X509::Extension.new( "keyUsage", KeyUsage::AU_DIGITAL_SIGNATURE )
235
+ @wrappable_extensions << OpenSSL::X509::Extension.new( "extendedKeyUsage", ExtendedKeyUsage::AU_WEB_SERVER_AUTH )
236
+ @wrappable_extensions << OpenSSL::X509::Extension.new( "subjectKeyIdentifier", "00:11:22:33:44:55:66:77:88:99:00:AA:BB:CC:DD:EE:FF:00:11:22" )
237
+ @wrappable_extensions << OpenSSL::X509::Extension.new( "authorityKeyIdentifier", "keyid:always" )
238
+ @wrappable_extensions << OpenSSL::X509::Extension.new( "subjectAltName", "DNS:www.test.local" )
239
+ @wrappable_extensions << OpenSSL::X509::Extension.new( "authorityInfoAccess", "CA Issuers - URI:http://www.test.local" )
240
+ @wrappable_extensions << OpenSSL::X509::Extension.new( "crlDistributionPoints", "URI:http://www.test.local" )
241
+
242
+ @unknown_extensions = []
243
+
244
+ @openssl_extensions = @wrappable_extensions + @unknown_extensions
245
+ end
246
+
247
+ it_should_behave_like "a correctly implemented wrap_openssl_extensions"
248
+ it_should_behave_like "a correctly implemented get_unknown_extensions"
249
+ end
250
+
251
+ context "with an unimplemented extension" do
252
+ before :each do
253
+ @wrappable_extensions = []
254
+
255
+ @unknown_extensions = []
256
+ @unknown_extensions << OpenSSL::X509::Extension.new( "issuerAltName", "DNS:www.test.local" )
257
+
258
+ @openssl_extensions = @wrappable_extensions + @unknown_extensions
259
+ end
260
+
261
+ it_should_behave_like "a correctly implemented wrap_openssl_extensions"
262
+ it_should_behave_like "a correctly implemented get_unknown_extensions"
263
+ end
264
+
265
+ context "with implemented and unimplemented extensions" do
266
+ before :each do
267
+ @wrappable_extensions = []
268
+ @wrappable_extensions << OpenSSL::X509::Extension.new( "basicConstraints", "CA:TRUE;pathlen:0" )
269
+
270
+ @unknown_extensions = []
271
+ @unknown_extensions << OpenSSL::X509::Extension.new( "issuerAltName", "DNS:www.test.local" )
272
+
273
+ @openssl_extensions = @wrappable_extensions + @unknown_extensions
274
+ end
275
+
276
+ it_should_behave_like "a correctly implemented wrap_openssl_extensions"
277
+ it_should_behave_like "a correctly implemented get_unknown_extensions"
278
+ end
279
+
280
+ context "with multiple extensions of an implemented type" do
281
+ before :each do
282
+ @wrappable_extensions = []
283
+ @wrappable_extensions << OpenSSL::X509::Extension.new( "basicConstraints", "CA:TRUE;pathlen:0" )
284
+ @wrappable_extensions << OpenSSL::X509::Extension.new( "basicConstraints", "CA:TRUE;pathlen:1" )
285
+
286
+ @unknown_extensions = []
287
+ @unknown_extensions << OpenSSL::X509::Extension.new( "issuerAltName", "DNS:www.test.local" )
288
+
289
+ @openssl_extensions = @wrappable_extensions + @unknown_extensions
290
+ end
291
+
292
+ it "should raise an ArgumentError for #wrap_openssl_extensions" do
293
+ expect {
294
+ R509::Cert::Extensions.wrap_openssl_extensions( @openssl_extensions )
295
+ }.to raise_error(ArgumentError)
296
+ end
297
+ it_should_behave_like "a correctly implemented get_unknown_extensions"
298
+ end
299
+
300
+ context "with multiple extensions of an unimplemented type" do
301
+ before :each do
302
+ @wrappable_extensions = []
303
+ @wrappable_extensions << OpenSSL::X509::Extension.new( "basicConstraints", "CA:TRUE;pathlen:0" )
304
+
305
+ @unknown_extensions = []
306
+ @unknown_extensions << OpenSSL::X509::Extension.new( "issuerAltName", "DNS:www.test.local" )
307
+ @unknown_extensions << OpenSSL::X509::Extension.new( "issuerAltName", "DNS:www2.test.local" )
308
+
309
+ @openssl_extensions = @wrappable_extensions + @unknown_extensions
310
+ end
311
+
312
+ it_should_behave_like "a correctly implemented wrap_openssl_extensions"
313
+ it_should_behave_like "a correctly implemented get_unknown_extensions"
314
+ end
315
+ end
316
+ end
317
+
318
+ context "BasicConstraints" do
319
+ context "with constraints for a CA certificate" do
320
+ before :all do
321
+ @extension_value = "CA:TRUE;pathlen:1"
322
+ @is_ca = true
323
+ @pathlen = 1
324
+ @allows_sub_ca = true
325
+ end
326
+
327
+ it_should_behave_like "a correct R509 BasicConstraints object"
328
+ end
329
+
330
+ context "with constraints for a sub-CA certificate" do
331
+ before :all do
332
+ @extension_value = "CA:TRUE;pathlen:0"
333
+ @is_ca = true
334
+ @pathlen = 0
335
+ @allows_sub_ca = false
336
+ end
337
+
338
+ it_should_behave_like "a correct R509 BasicConstraints object"
339
+ end
340
+
341
+ context "with constraints for a non-CA certificate" do
342
+ before :all do
343
+ @extension_value = "CA:FALSE"
344
+ @is_ca = false
345
+ @pathlen = nil
346
+ @allows_sub_ca = false
347
+ end
348
+
349
+ it_should_behave_like "a correct R509 BasicConstraints object"
350
+ end
351
+ end
352
+
353
+ context "KeyUsage" do
354
+ context "with one allowed use" do
355
+ before :all do
356
+ @allowed_uses = [ KeyUsage::AU_DIGITAL_SIGNATURE ]
357
+ @extension_value = @allowed_uses.join( ", " )
358
+ end
359
+
360
+ it_should_behave_like "a correct R509 KeyUsage object"
361
+ end
362
+
363
+ context "with some allowed uses" do
364
+ before :all do
365
+ # this spec and the one below alternate the uses
366
+ @allowed_uses = [ KeyUsage::AU_DIGITAL_SIGNATURE, KeyUsage::AU_KEY_ENCIPHERMENT, KeyUsage::AU_KEY_AGREEMENT, KeyUsage::AU_CRL_SIGN, KeyUsage::AU_DECIPHER_ONLY ]
367
+ @extension_value = @allowed_uses.join( ", " )
368
+ end
369
+
370
+ it_should_behave_like "a correct R509 KeyUsage object"
371
+ end
372
+
373
+ context "with some different allowed uses" do
374
+ before :all do
375
+ @allowed_uses = [ KeyUsage::AU_NON_REPUDIATION, KeyUsage::AU_DATA_ENCIPHERMENT, KeyUsage::AU_CERTIFICATE_SIGN, KeyUsage::AU_ENCIPHER_ONLY ]
376
+ @extension_value = @allowed_uses.join( ", " )
377
+ end
378
+
379
+ it_should_behave_like "a correct R509 KeyUsage object"
380
+ end
381
+
382
+ context "with all allowed uses" do
383
+ before :all do
384
+ @allowed_uses = [ KeyUsage::AU_DIGITAL_SIGNATURE, KeyUsage::AU_KEY_ENCIPHERMENT,
385
+ KeyUsage::AU_KEY_AGREEMENT, KeyUsage::AU_CRL_SIGN, KeyUsage::AU_DECIPHER_ONLY,
386
+ KeyUsage::AU_NON_REPUDIATION, KeyUsage::AU_DATA_ENCIPHERMENT,
387
+ KeyUsage::AU_CERTIFICATE_SIGN, KeyUsage::AU_ENCIPHER_ONLY ]
388
+ @extension_value = @allowed_uses.join( ", " )
389
+ end
390
+
391
+ it_should_behave_like "a correct R509 KeyUsage object"
392
+ end
393
+ end
394
+
395
+ context "ExtendedKeyUsage" do
396
+ context "with one allowed use" do
397
+ before :all do
398
+ @allowed_uses = [ ExtendedKeyUsage::AU_WEB_SERVER_AUTH ]
399
+ @extension_value = @allowed_uses.join( ", " )
400
+ end
401
+
402
+ it_should_behave_like "a correct R509 ExtendedKeyUsage object"
403
+ end
404
+
405
+ context "with some allowed uses" do
406
+ before :all do
407
+ # this spec and the one below alternate the uses
408
+ @allowed_uses = [ ExtendedKeyUsage::AU_WEB_SERVER_AUTH, ExtendedKeyUsage::AU_CODE_SIGNING ]
409
+ @extension_value = @allowed_uses.join( ", " )
410
+ end
411
+
412
+ it_should_behave_like "a correct R509 ExtendedKeyUsage object"
413
+ end
414
+
415
+ context "with some different allowed uses" do
416
+ before :all do
417
+ @allowed_uses = [ ExtendedKeyUsage::AU_WEB_CLIENT_AUTH, ExtendedKeyUsage::AU_EMAIL_PROTECTION ]
418
+ @extension_value = @allowed_uses.join( ", " )
419
+ end
420
+
421
+ it_should_behave_like "a correct R509 ExtendedKeyUsage object"
422
+ end
423
+
424
+ context "with all allowed uses" do
425
+ before :all do
426
+ @allowed_uses = [ ExtendedKeyUsage::AU_WEB_SERVER_AUTH, ExtendedKeyUsage::AU_CODE_SIGNING,
427
+ ExtendedKeyUsage::AU_WEB_CLIENT_AUTH, ExtendedKeyUsage::AU_EMAIL_PROTECTION ]
428
+ @extension_value = @allowed_uses.join( ", " )
429
+ end
430
+
431
+ it_should_behave_like "a correct R509 ExtendedKeyUsage object"
432
+ end
433
+ end
434
+
435
+ context "SubjectKeyIdentifier" do
436
+ before :all do
437
+ @extension_value = "00:11:22:33:44:55:66:77:88:99:00:AA:BB:CC:DD:EE:FF:00:11:22"
438
+ @key = @extension_value
439
+ end
440
+
441
+ it_should_behave_like "a correct R509 SubjectKeyIdentifier object"
442
+ end
443
+
444
+ context "AuthorityKeyIdentifier" do
445
+ before :all do
446
+ @extension_value = "keyid:always"
447
+ end
448
+
449
+ it_should_behave_like "a correct R509 AuthorityKeyIdentifier object"
450
+ end
451
+
452
+ context "SubjectAlternativeName" do
453
+ context "with a DNS alternative name only" do
454
+ before :all do
455
+ @dns_names = ["www.test.local"]
456
+ @ip_addresses = []
457
+ @uris = []
458
+ @extension_value = "DNS:#{@dns_names.join(",DNS:")}"
459
+ end
460
+
461
+ it_should_behave_like "a correct R509 SubjectAlternativeName object"
462
+ end
463
+
464
+ context "with multiple DNS alternative names only" do
465
+ before :all do
466
+ @dns_names = ["www.test.local", "www2.test.local"]
467
+ @ip_addresses = []
468
+ @uris = []
469
+ @extension_value = "DNS:#{@dns_names.join(",DNS:")}"
470
+ end
471
+
472
+ it_should_behave_like "a correct R509 SubjectAlternativeName object"
473
+ end
474
+
475
+ context "with an IP address alternative name only" do
476
+ before :all do
477
+ @dns_names = []
478
+ @ip_addresses = ["10.1.2.3"]
479
+ @uris = []
480
+ @extension_value = "IP:#{@ip_addresses.join(",IP:")}"
481
+ end
482
+
483
+ it_should_behave_like "a correct R509 SubjectAlternativeName object"
484
+ end
485
+
486
+ context "with multiple IP address alternative names only" do
487
+ before :all do
488
+ @dns_names = []
489
+ @ip_addresses = ["10.1.2.3", "10.1.2.4"]
490
+ @uris = []
491
+ @extension_value = "IP:#{@ip_addresses.join(",IP:")}"
492
+ end
493
+
494
+ it_should_behave_like "a correct R509 SubjectAlternativeName object"
495
+ end
496
+
497
+ context "with a URI alternative name only" do
498
+ before :all do
499
+ @dns_names = []
500
+ @ip_addresses = []
501
+ @uris = ["http://www.test.local"]
502
+ @extension_value = "URI:#{@uris.join(",URI:")}"
503
+ end
504
+
505
+ it_should_behave_like "a correct R509 SubjectAlternativeName object"
506
+ end
507
+
508
+ context "with multiple URI alternative names only" do
509
+ before :all do
510
+ @dns_names = []
511
+ @ip_addresses = []
512
+ @uris = ["http://www.test.local","http://www2.test.local"]
513
+ @extension_value = "URI:#{@uris.join(",URI:")}"
514
+ end
515
+
516
+ it_should_behave_like "a correct R509 SubjectAlternativeName object"
517
+ end
518
+
519
+ context "with multiple different alternative names" do
520
+ before :all do
521
+ @dns_names = ["www.test.local"]
522
+ @ip_addresses = ["10.1.2.3"]
523
+ @uris = ["http://www.test.local"]
524
+ @extension_value = "DNS:#{@dns_names.join(",DNS:")},IP:#{@ip_addresses.join(",IP:")},URI:#{@uris.join(",URI:")}"
525
+ end
526
+
527
+ it_should_behave_like "a correct R509 SubjectAlternativeName object"
528
+ end
529
+
530
+ context "with multiple different alternative names with trailing newlines" do
531
+ before :all do
532
+ @dns_names = ["www.test.local"]
533
+ @ip_addresses = ["10.1.2.3"]
534
+ @uris = ["http://www.test.local"]
535
+ @extension_value = "DNS:#{@dns_names.join("\n,DNS:")}\n,IP:#{@ip_addresses.join("\n,IP:")}\n,URI:#{@uris.join("\n,URI:")}\n"
536
+ end
537
+
538
+ it_should_behave_like "a correct R509 SubjectAlternativeName object"
539
+ end
540
+ end
541
+ context "AuthorityInfoAccess" do
542
+ context "with a CA Issuers URI only" do
543
+ before :all do
544
+ @ca_issuers_uris = ["http://www.test.local/ca.cert"]
545
+ @ocsp_uris = []
546
+ @extension_value = "CA Issuers - URI:#{@ca_issuers_uris.join(",URI:")}"
547
+ end
548
+
549
+ it_should_behave_like "a correct R509 AuthorityInfoAccess object"
550
+ end
551
+
552
+ context "with multiple CA Issuers URIs only" do
553
+ before :all do
554
+ @ca_issuers_uris = ["http://www.test.local/ca.cert", "http://www.test.local/subca.cert"]
555
+ @ocsp_uris = []
556
+ @extension_value = "CA Issuers - URI:#{@ca_issuers_uris.join(",CA Issuers - URI:")}"
557
+ end
558
+
559
+ it_should_behave_like "a correct R509 AuthorityInfoAccess object"
560
+ end
561
+
562
+ context "with an OCSP URI only" do
563
+ before :all do
564
+ @ca_issuers_uris = []
565
+ @ocsp_uris = ["http://www.test.local"]
566
+ @extension_value = "OCSP - URI:#{@ocsp_uris.join(",URI:")}"
567
+ end
568
+
569
+ it_should_behave_like "a correct R509 AuthorityInfoAccess object"
570
+ end
571
+
572
+ context "with multiple OCSP URIs only" do
573
+ before :all do
574
+ @ca_issuers_uris = []
575
+ @ocsp_uris = ["http://www.test.local", "http://www2.test.local"]
576
+ @extension_value = "OCSP - URI:#{@ocsp_uris.join(",OCSP - URI:")}"
577
+ end
578
+
579
+ it_should_behave_like "a correct R509 AuthorityInfoAccess object"
580
+ end
581
+
582
+ context "with both a CA Issuers URI and an OCSP URI" do
583
+ before :all do
584
+ @ca_issuers_uris = ["http://www.test.local/ca.cert"]
585
+ @ocsp_uris = ["http://www.test.local"]
586
+ @extension_value = "CA Issuers - URI:#{@ca_issuers_uris.join(",CA Issuers - URI:")},OCSP - URI:#{@ocsp_uris.join(",URI:")}"
587
+ end
588
+
589
+ it_should_behave_like "a correct R509 AuthorityInfoAccess object"
590
+ end
591
+
592
+ context "with both a CA Issuers URI and an OCSP URI with trailing newlines" do
593
+ before :all do
594
+ @ca_issuers_uris = ["http://www.test.local/ca.cert"]
595
+ @ocsp_uris = ["http://www.test.local"]
596
+ @extension_value = "CA Issuers - URI:#{@ca_issuers_uris.join("\n,CA Issuers - URI:")}\n,OCSP - URI:#{@ocsp_uris.join("\n,URI:")}\n"
597
+ end
598
+
599
+ it_should_behave_like "a correct R509 AuthorityInfoAccess object"
600
+ end
601
+ end
602
+
603
+ context "CrlDistributionPoints" do
604
+ context "wtih a single CRL URI" do
605
+ before :all do
606
+ @crl_uris = ["http://www.test.local/ca.crl"]
607
+ @extension_value = "URI:#{@crl_uris.join(",URI:")}"
608
+ end
609
+
610
+ it_should_behave_like "a correct R509 CrlDistributionPoints object"
611
+ end
612
+
613
+ context "wtih multiple CRL URIs" do
614
+ before :all do
615
+ @crl_uris = ["http://www.test.local/ca.crl", "http://www.test.local/subca.crl"]
616
+ @extension_value = "URI:#{@crl_uris.join(",URI:")}"
617
+ end
618
+
619
+ it_should_behave_like "a correct R509 CrlDistributionPoints object"
620
+ end
621
+
622
+ context "wtih multiple CRL URIs and trailing newlines" do
623
+ before :all do
624
+ @crl_uris = ["http://www.test.local/ca.crl", "http://www.test.local/subca.crl"]
625
+ @extension_value = "URI:#{@crl_uris.join("\n,URI:")}\n"
626
+ end
627
+
628
+ it_should_behave_like "a correct R509 CrlDistributionPoints object"
629
+ end
630
+ end
631
+
632
+ end