r509 0.8

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
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