├── .gitignore ├── .rspec ├── .travis.yml ├── CHANGELOG.md ├── Gemfile ├── LICENSE ├── README.md ├── Rakefile ├── autotest └── discover.rb ├── lib ├── openssl-extensions.rb └── openssl-extensions │ ├── all.rb │ ├── bn.rb │ ├── pkey.rb │ ├── pkey │ ├── dsa.rb │ ├── pkey.rb │ └── rsa.rb │ ├── ssl.rb │ ├── ssl │ └── ssl_socket.rb │ ├── version.rb │ ├── x509.rb │ └── x509 │ ├── authority_key_identifier.rb │ ├── certificate.rb │ ├── certificate_chain.rb │ ├── name.rb │ └── request.rb ├── openssl-extensions.gemspec └── spec ├── fixtures ├── certificate_requests │ ├── 1024.csr │ ├── challenge.csr │ ├── envylabs.csr │ ├── geocerts.csr │ └── sans.csr ├── certificates │ ├── app1.hongkongpost.com.pem │ ├── bgthelpdesk.braxtongrant.com.pem │ ├── equifax-secure-ca.pem │ ├── geotrust-extended-validation-ssl-ca.pem │ ├── geotrust-primary-certification-authority.pem │ ├── globalsign-root-ca.pem │ ├── hongkong-post-e-cert-ca-1.pem │ ├── hongkong-post-e-cert-ca.pem │ ├── hongkong-post-root-ca-1.pem │ ├── hongkong-post-root-ca.pem │ ├── www.geocerts.com.pem │ └── www.twongo.com.pem └── keys │ ├── dsa.key │ └── rsa.key ├── models ├── openssl-extensions │ ├── pkey │ │ └── pkey_spec.rb │ ├── ssl │ │ └── ssl_socket_spec.rb │ └── x509 │ │ ├── authority_key_identifier_spec.rb │ │ ├── certificate_chain_spec.rb │ │ ├── certificate_spec.rb │ │ ├── name_spec.rb │ │ └── request_spec.rb └── openssl-extensions_spec.rb ├── requests └── openssl │ ├── bn_spec.rb │ ├── pkey │ └── pkey_spec.rb │ ├── ssl │ └── ssl_socket_spec.rb │ └── x509 │ ├── certificate_spec.rb │ ├── name_spec.rb │ └── request_spec.rb ├── spec_helper.rb └── support ├── certificate_request_fixtures.rb ├── pkey_fixtures.rb └── ssl_certificate_fixtures.rb /.gitignore: -------------------------------------------------------------------------------- 1 | Gemfile.lock 2 | .bundle 3 | 4 | .DS_Store 5 | .*.swp 6 | tmp 7 | pkg 8 | *.gem 9 | 10 | .ruby-version 11 | .ruby-gemset 12 | .rbenv-version 13 | .rvmrc 14 | -------------------------------------------------------------------------------- /.rspec: -------------------------------------------------------------------------------- 1 | --color 2 | -------------------------------------------------------------------------------- /.travis.yml: -------------------------------------------------------------------------------- 1 | rvm: 2 | - 1.8.7 3 | - 1.9.2 4 | - 1.9.3 5 | - ree 6 | - rbx-18mode 7 | - rbx-19mode 8 | script: "rake spec" 9 | notifications: 10 | email: 11 | on_success: change 12 | on_failure: always 13 | -------------------------------------------------------------------------------- /CHANGELOG.md: -------------------------------------------------------------------------------- 1 | # openssl-extensions changelog 2 | 3 | ## [HEAD][unreleased] / unreleased 4 | 5 | * No significant changes. 6 | 7 | ## [1.2.1][v1.2.1] / 2013-06-11 8 | 9 | * Fix strength calculation with X509 certificates encrypted with DSA keys. 10 | * Loosen the RSpec dependency requirements to ~> 2.x. 11 | * Fix failing specs in CRL distribution points. 12 | 13 | ## [1.2.0][v1.2.0] / 2011-11-03 14 | 15 | * Extended OpenSSL::BN to provide a #to_hex helper, a shortcut for to_s(16) 16 | \[[pyrat][pyrat]\] 17 | * Extended OpenSSL::X509::Certificate, adding #authority_info_access and 18 | #crl_distribution_points methods \[[pyrat][pyrat]\] 19 | 20 | ## [1.1.0][v1.1.0] / 2011-01-20 21 | 22 | * Extended OpenSSL::PKey::PKey to add equality methods 23 | 24 | ## 1.0.0 / 2011-01-17 25 | 26 | * Initial major release. 27 | * Extended OpenSSL::X509::Request, OpenSSL::X509::Certificate, 28 | OpenSSL::X509::Name 29 | * Added OpenSSLExtensions::X509::CertificateChain and 30 | OpenSSLExtensions::X509::AuthorityKeyIdentifier 31 | 32 | 33 | [unreleased]: https://github.com/envylabs/openssl-extensions/compare/v1.2.1...master 34 | [v1.2.1]: https://github.com/envylabs/openssl-extensions/compare/v1.2.0...v1.2.1 35 | [v1.2.0]: https://github.com/envylabs/openssl-extensions/compare/v1.1.0...v1.2.0 36 | [v1.1.0]: https://github.com/envylabs/openssl-extensions/compare/v1.0.0...v1.1.0 37 | 38 | [pyrat]: https://github.com/pyrat 39 | -------------------------------------------------------------------------------- /Gemfile: -------------------------------------------------------------------------------- 1 | source "http://rubygems.org" 2 | gemspec 3 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | Copyright (c) 2010 Envy Labs LLC 2 | 3 | Permission is hereby granted, free of charge, to any person obtaining 4 | a copy of this software and associated documentation files (the 5 | "Software"), to deal in the Software without restriction, including 6 | without limitation the rights to use, copy, modify, merge, publish, 7 | distribute, sublicense, and/or sell copies of the Software, and to 8 | permit persons to whom the Software is furnished to do so, subject to 9 | the following conditions: 10 | 11 | The above copyright notice and this permission notice shall be 12 | included in all copies or substantial portions of the Software. 13 | 14 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, 15 | EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF 16 | MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND 17 | NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE 18 | LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION 19 | OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION 20 | WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. 21 | 22 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # OpenSSL Extensions 2 | 3 | [![Gem Version](https://badge.fury.io/rb/openssl-extensions.png)](http://badge.fury.io/rb/openssl-extensions) 4 | [![Build Status](https://secure.travis-ci.org/envylabs/openssl-extensions.png?branch=master)](http://travis-ci.org/envylabs/openssl-extensions) 5 | [![Code Climate](https://codeclimate.com/github/envylabs/openssl-extensions.png)](https://codeclimate.com/github/envylabs/openssl-extensions) 6 | 7 | This library generally provides helper methods which makes working with 8 | OpenSSL a little more bearable. It does, however, provide some additional 9 | structures (such as a CertificateChain) which extend the traditional 10 | features of the library. 11 | 12 | ## Installation 13 | 14 | With [Bundler](http://gembundler.com): 15 | 16 | ```ruby 17 | gem 'openssl-extensions', :require => 'openssl-extensions/all' 18 | ``` 19 | 20 | With standard RubyGems: 21 | 22 | ```shell 23 | gem install openssl-extensions 24 | ``` 25 | 26 | ```ruby 27 | require 'rubygems' 28 | require 'openssl-extensions/all' 29 | ``` 30 | 31 | Once required, the extensions are automatically applied. 32 | 33 | ## Usage 34 | 35 | In general, this extension library should be somewhat transparent to you. 36 | It does not directly provide many classes with which you might interact. 37 | Instead, it extends the current classes provided by Ruby's OpenSSL library 38 | (being OpenSSL::X509::Request, OpenSSL::X509::Certificate, and 39 | OpenSSL::X509::NAME). 40 | 41 | Below is a simple example exercising a few helpers provided by this 42 | library: 43 | 44 | ```ruby 45 | csr_body = File.read('example.csr') # assuming this is valid and exists 46 | request = OpenSSL::X509::Request.new(csr_body) 47 | 48 | request.subject.common_name # => "example.com" 49 | request.subject.organization # => "Example Corp" 50 | request.subject.locality # => "Orlando" 51 | request.subject.region # => "Florida" 52 | request.subject.country # => "US" 53 | request.subject.location # => "Orlando, Florida, US" 54 | 55 | request.strength # => 2048 56 | request.challenge_password? # => false 57 | request.subject_alternative_names # => ['example.com', 'www.example.com'] 58 | ``` 59 | 60 | ## Supported Ruby Implementations 61 | 62 | This OpenSSL extension library currently supports (and is continuously tested 63 | against) the following Ruby implementations: 64 | 65 | * [MRI 1.8.7][mri] 66 | * [MRI 1.9.2][mri] 67 | * [MRI 1.9.3][mri] 68 | * [Ruby Enterprise Edition][ree] 69 | * [Rubinius][rubinius] 70 | 71 | The following implementations are known to be incompatible: 72 | 73 | * [JRuby][jruby] 74 | 75 | ## License 76 | 77 | Released under the MIT License. See the LICENSE file for further details. 78 | 79 | [mri]: http://www.ruby-lang.org/ 80 | [ree]: http://www.rubyenterpriseedition.com/ 81 | [rubinius]: http://rubini.us/ 82 | [jruby]: http://jruby.org/ 83 | -------------------------------------------------------------------------------- /Rakefile: -------------------------------------------------------------------------------- 1 | require 'bundler/gem_tasks' 2 | require 'rspec/core/rake_task' 3 | 4 | RSpec::Core::RakeTask.new(:spec) 5 | task :default => :spec 6 | -------------------------------------------------------------------------------- /autotest/discover.rb: -------------------------------------------------------------------------------- 1 | Autotest.add_discovery { "rspec2" } 2 | -------------------------------------------------------------------------------- /lib/openssl-extensions.rb: -------------------------------------------------------------------------------- 1 | module OpenSSLExtensions 2 | ## 3 | # Ensures that the current Ruby was compiled with OpenSSL support enabled. 4 | # 5 | def self.check_dependencies! 6 | begin 7 | require 'openssl' 8 | rescue LoadError 9 | $stderr.puts "OpenSSLExtensions requires Ruby to be compiled with OpenSSL support." 10 | exit(1) 11 | end 12 | end 13 | end 14 | -------------------------------------------------------------------------------- /lib/openssl-extensions/all.rb: -------------------------------------------------------------------------------- 1 | require 'openssl-extensions' 2 | OpenSSLExtensions.check_dependencies! 3 | 4 | require 'openssl-extensions/pkey/pkey' 5 | require 'openssl-extensions/pkey/dsa' 6 | require 'openssl-extensions/pkey/rsa' 7 | require 'openssl-extensions/x509/certificate' 8 | require 'openssl-extensions/x509/certificate_chain' 9 | require 'openssl-extensions/x509/request' 10 | require 'openssl-extensions/x509/name' 11 | require 'openssl-extensions/ssl/ssl_socket' 12 | require 'openssl-extensions/bn' 13 | -------------------------------------------------------------------------------- /lib/openssl-extensions/bn.rb: -------------------------------------------------------------------------------- 1 | require 'openssl-extensions' 2 | 3 | module OpenSSLExtensions 4 | module BN 5 | ## 6 | # OpenSSL deals with serials in HEX format. 7 | # This gives you the ability to get this hex serial if you need to work with 8 | # certificate information directly with OpenSSL 9 | def to_hex 10 | to_s(16).upcase 11 | end 12 | end 13 | end 14 | 15 | OpenSSL::BN.send(:include, OpenSSLExtensions::BN) 16 | -------------------------------------------------------------------------------- /lib/openssl-extensions/pkey.rb: -------------------------------------------------------------------------------- 1 | require 'openssl-extensions' 2 | 3 | module OpenSSLExtensions::PKey 4 | end 5 | -------------------------------------------------------------------------------- /lib/openssl-extensions/pkey/dsa.rb: -------------------------------------------------------------------------------- 1 | require 'openssl-extensions' 2 | require 'openssl-extensions/pkey' 3 | 4 | ## 5 | # Extends OpenSSL::PKey::RSA with helper methods. 6 | # 7 | module OpenSSLExtensions::PKey::DSA 8 | def strength 9 | p.num_bits 10 | end 11 | end 12 | 13 | OpenSSL::PKey::DSA.send(:include, OpenSSLExtensions::PKey::DSA) 14 | -------------------------------------------------------------------------------- /lib/openssl-extensions/pkey/pkey.rb: -------------------------------------------------------------------------------- 1 | require 'openssl-extensions' 2 | require 'openssl-extensions/pkey' 3 | 4 | ## 5 | # Extends OpenSSL::PKey::PKey and its submodules with helper methods. 6 | # 7 | module OpenSSLExtensions::PKey::PKey 8 | UnknownAlgorithmError = Class.new(RuntimeError) 9 | 10 | ## 11 | # Equality is tested by comparing the instances' +hash+. 12 | # 13 | def ==(other) 14 | other.kind_of?(OpenSSL::PKey::PKey) && 15 | self.hash == other.hash 16 | end 17 | alias_method :eql?, :== 18 | 19 | ## 20 | # Override the default Object#hash to identify uniqueness of the key. 21 | # This uses a hash of the PEM. 22 | # 23 | def hash 24 | to_pem.hash 25 | end 26 | 27 | ## 28 | # Returns the strength of the public key in number of bits. 29 | # 30 | def strength 31 | raise UnknownAlgorithmError 32 | end 33 | end 34 | 35 | OpenSSL::PKey::PKey.send(:include, OpenSSLExtensions::PKey::PKey) 36 | -------------------------------------------------------------------------------- /lib/openssl-extensions/pkey/rsa.rb: -------------------------------------------------------------------------------- 1 | require 'openssl-extensions' 2 | require 'openssl-extensions/pkey' 3 | 4 | ## 5 | # Extends OpenSSL::PKey::RSA with helper methods. 6 | # 7 | module OpenSSLExtensions::PKey::RSA 8 | def strength 9 | n.num_bits 10 | end 11 | end 12 | 13 | OpenSSL::PKey::RSA.send(:include, OpenSSLExtensions::PKey::RSA) 14 | -------------------------------------------------------------------------------- /lib/openssl-extensions/ssl.rb: -------------------------------------------------------------------------------- 1 | require 'openssl-extensions' 2 | 3 | module OpenSSLExtensions::SSL 4 | end 5 | -------------------------------------------------------------------------------- /lib/openssl-extensions/ssl/ssl_socket.rb: -------------------------------------------------------------------------------- 1 | require 'openssl-extensions' 2 | require 'openssl-extensions/ssl' 3 | require 'openssl-extensions/x509/certificate_chain' 4 | 5 | module OpenSSLExtensions::SSL::SSLSocket 6 | def self.included(base) 7 | base.send(:alias_method, 8 | :peer_cert_chain_without_openssl_extension, 9 | :peer_cert_chain) 10 | base.send(:alias_method, 11 | :peer_cert_chain, 12 | :peer_cert_chain_with_openssl_extension) 13 | end 14 | 15 | ## 16 | # Rather than returning the default, unsorted Array of 17 | # OpenSSL::X509::Certificate instances, this will filter that 18 | # Array through the OpenSSLExtensions::X509::CertificateChain. 19 | # 20 | def peer_cert_chain_with_openssl_extension 21 | OpenSSLExtensions::X509::CertificateChain. 22 | new(peer_cert, peer_cert_chain_without_openssl_extension) 23 | end 24 | end 25 | 26 | OpenSSL::SSL::SSLSocket.send(:include, OpenSSLExtensions::SSL::SSLSocket) 27 | -------------------------------------------------------------------------------- /lib/openssl-extensions/version.rb: -------------------------------------------------------------------------------- 1 | module OpenSSLExtensions 2 | Version = '1.2.1' 3 | end 4 | -------------------------------------------------------------------------------- /lib/openssl-extensions/x509.rb: -------------------------------------------------------------------------------- 1 | require 'openssl-extensions' 2 | 3 | module OpenSSLExtensions::X509 4 | end 5 | -------------------------------------------------------------------------------- /lib/openssl-extensions/x509/authority_key_identifier.rb: -------------------------------------------------------------------------------- 1 | require 'openssl-extensions' 2 | require 'openssl-extensions/x509' 3 | 4 | ## 5 | # Returned with requesting an OpenSSLExtensions::X509::Certificate.authority_key_identifier. 6 | # If available, this collects the issuer_name (issuer's common name), 7 | # serial_number, and key_id (fingerprint). 8 | # 9 | class OpenSSLExtensions::X509::AuthorityKeyIdentifier 10 | 11 | attr_reader :issuer_name, :serial_number, :key_id 12 | alias :serial :serial_number 13 | 14 | def initialize(extension_string) 15 | parse(extension_string.dup) if extension_string 16 | end 17 | 18 | 19 | private 20 | 21 | 22 | def common_name(input) 23 | if input 24 | name = input.split('/'). 25 | collect { |v| v.split('=') }. 26 | detect { |id, val| id == 'CN' } 27 | name[1] if name 28 | end 29 | end 30 | 31 | def parse(string) 32 | Hash[string.scan(%r{(\w+):([^,\n]+)})].tap do |h| 33 | @issuer_name = common_name(strip(h['DirName'])) 34 | @serial_number = strip(h['serial']) 35 | @key_id = strip(h['keyid']) 36 | end 37 | end 38 | 39 | def strip(input) 40 | input ? input.to_s.strip : nil 41 | end 42 | end 43 | -------------------------------------------------------------------------------- /lib/openssl-extensions/x509/certificate.rb: -------------------------------------------------------------------------------- 1 | require 'openssl-extensions' 2 | require 'openssl-extensions/x509' 3 | require 'openssl-extensions/x509/authority_key_identifier' 4 | 5 | ## 6 | # Extends OpenSSL::X509::Certificate with shortcut methods. 7 | # 8 | module OpenSSLExtensions::X509::Certificate 9 | ## 10 | # Equality is tested by comparing the generated PEM signatures. 11 | # 12 | def ==(other) 13 | to_pem == other.to_pem 14 | end 15 | alias_method :eql?, :== 16 | 17 | ## 18 | # Returns +true+ if this certificate is authorized to sign for other certificates (useful for determining CA roots 19 | # and intermediary certificates). 20 | # 21 | def allows_certificate_signing? 22 | usage = read_extension_by_oid('keyUsage') 23 | usage.nil? || !!(usage.match(%r{\bCertificate Sign\b})) 24 | end 25 | 26 | def authority_key_identifier 27 | OpenSSLExtensions::X509::AuthorityKeyIdentifier.new(read_extension_by_oid('authorityKeyIdentifier')) 28 | end 29 | 30 | ## 31 | # Override the default Object#hash to identify uniqueness of the 32 | # Certificate. This uses a hash of the certificate PEM. 33 | # 34 | def hash 35 | to_pem.hash 36 | end 37 | 38 | ## 39 | # Returns +true+ if the certificate given is the issuer certificate for this certificate. 40 | # 41 | def issuing_certificate?(issuer) 42 | (self.authority_key_identifier.key_id && 43 | issuer.subject_key_identifier && 44 | self.authority_key_identifier.key_id == issuer.subject_key_identifier) || 45 | (!self.authority_key_identifier.key_id && 46 | self.issuer.common_name == issuer.subject.common_name && 47 | self.issuer.country == issuer.subject.country && 48 | self.issuer.organization == issuer.subject.organization) 49 | end 50 | 51 | ## 52 | # Returns +true+ if this certificate is a root certificate (it is its 53 | # own issuer). 54 | # 55 | def root? 56 | issuer.to_s == subject.to_s && 57 | (subject_key_identifier && authority_key_identifier.key_id ? subject_key_identifier == authority_key_identifier.key_id : true) 58 | end 59 | 60 | ## 61 | # Returns the bit strength of the public certificate. 62 | # 63 | def strength 64 | public_key.strength 65 | end 66 | 67 | ## 68 | # Returns a collection of subject alternative names on the certificate. 69 | # If no alternative names were provided, then this returns an empty set. 70 | # 71 | def subject_alternative_names 72 | names_string = read_extension_by_oid('subjectAltName') 73 | names_string ? names_string.scan(%r{DNS:([^,]+)}).flatten : [] 74 | end 75 | alias :sans :subject_alternative_names 76 | 77 | def subject_key_identifier 78 | read_extension_by_oid('subjectKeyIdentifier') 79 | end 80 | 81 | ## 82 | # This can be used for getting OCSP Urls for revocation checks. 83 | def authority_info_access 84 | read_extension_by_oid('authorityInfoAccess') 85 | end 86 | 87 | def crl_distribution_points 88 | read_extension_by_oid('crlDistributionPoints') 89 | end 90 | 91 | ## 92 | # Returns the SSL version used by the certificate. Most likely, this 93 | # will return +3+, since version +1+ was unreleased, and version +2+ was 94 | # abandoned in 1995. 95 | # 96 | # See http://en.wikipedia.org/wiki/Secure_Sockets_Layer. 97 | # 98 | #-- 99 | # OPTIMIZE: This should really use a call directly to the OpenSSL library, but will require becoming a compiled gem. 100 | #++ 101 | # 102 | def ssl_version 103 | if to_text =~ %r{^\s+Version: (\d+)}m 104 | $1.to_i 105 | end 106 | end 107 | 108 | 109 | protected 110 | 111 | 112 | def read_extension_by_oid(oid) 113 | (extensions.detect { |e| e.to_a.first == oid } || []).to_a[1] 114 | end 115 | end 116 | 117 | OpenSSL::X509::Certificate.send(:include, OpenSSLExtensions::X509::Certificate) 118 | -------------------------------------------------------------------------------- /lib/openssl-extensions/x509/certificate_chain.rb: -------------------------------------------------------------------------------- 1 | require 'openssl-extensions' 2 | require 'openssl-extensions/x509' 3 | require 'openssl-extensions/x509/certificate' 4 | 5 | ## 6 | # Provides a thin wrapper to an Array which contains the full certificate 7 | # chain. This array, however, has been reorganized to be in the proper 8 | # order for the chain as follows: 9 | # 10 | # [Site Certificate, Intermediary #1, ..., CA Root] 11 | # 12 | # Where +Intermediary #1+ is the issuing certificate of the 13 | # +Site Certificate+, followed by +#2+ which issued +#1+, down to the 14 | # final root signing certificate in last position. 15 | # 16 | class OpenSSLExtensions::X509::CertificateChain 17 | instance_methods.each { |m| undef_method m unless m =~ /(^__|^send$|^object_id$)/ } 18 | 19 | def initialize(peer_certificate, certificates) 20 | @certificates = [] 21 | reorganize!(peer_certificate, certificates) 22 | end 23 | 24 | 25 | private 26 | 27 | 28 | def method_missing(method, *args, &block) 29 | @certificates.send(method, *args, &block) 30 | end 31 | 32 | def reorganize!(site_certificate, certificates) 33 | return unless site_certificate && !certificates.empty? 34 | certificate = nil 35 | 36 | @certificates << (certificates.delete(site_certificate) || site_certificate || certificates.delete(certificates.detect { |c| c.subject_key_identifier.nil? })) 37 | certificate = @certificates.first 38 | 39 | until certificate.nil? 40 | if certificate = certificates.detect { |authority| authority.allows_certificate_signing? && certificate.issuing_certificate?(authority) } 41 | @certificates << certificates.delete(certificate) 42 | else 43 | authority = nil 44 | end 45 | end 46 | end 47 | end 48 | -------------------------------------------------------------------------------- /lib/openssl-extensions/x509/name.rb: -------------------------------------------------------------------------------- 1 | require 'openssl-extensions' 2 | require 'openssl-extensions/x509' 3 | 4 | ## 5 | # Extends OpenSSL::X509::Name with additional shortcut methods. 6 | # 7 | module OpenSSLExtensions::X509::Name 8 | def organization 9 | read_entry_by_oid('O') 10 | end 11 | 12 | def organizational_unit 13 | read_entry_by_oid('OU') 14 | end 15 | 16 | def common_name 17 | read_entry_by_oid('CN') 18 | end 19 | 20 | def country 21 | read_entry_by_oid('C') 22 | end 23 | 24 | def email 25 | read_entry_by_oid('emailAddress') 26 | end 27 | 28 | def locality 29 | read_entry_by_oid('L') 30 | end 31 | 32 | def location 33 | [locality, state, country].compact.join(', ') 34 | end 35 | 36 | def state 37 | read_entry_by_oid('ST') 38 | end 39 | alias :region :state 40 | 41 | 42 | protected 43 | 44 | 45 | def read_entry_by_oid(oid) 46 | (to_a.detect { |e| e.first == oid } || [])[1] 47 | end 48 | end 49 | 50 | OpenSSL::X509::Name.send(:include, OpenSSLExtensions::X509::Name) 51 | -------------------------------------------------------------------------------- /lib/openssl-extensions/x509/request.rb: -------------------------------------------------------------------------------- 1 | require 'openssl-extensions' 2 | require 'openssl-extensions/x509' 3 | 4 | ## 5 | # Extends OpenSSL::X509::Request with shortcut methods. 6 | # 7 | module OpenSSLExtensions::X509::Request 8 | 9 | ## 10 | # Equality is tested by comparing the generated PEM signatures. 11 | # 12 | def ==(other) 13 | to_pem == other.to_pem 14 | end 15 | alias_method :eql?, :== 16 | 17 | ## 18 | # Returns +true+ if the signing request were generated with a challenge 19 | # password. 20 | # 21 | def challenge_password? 22 | !read_attributes_by_oid('challengePassword').nil? 23 | end 24 | 25 | ## 26 | # Override the default Object#hash to identify uniqueness of the 27 | # Request. This uses a hash of the PEM. 28 | # 29 | def hash 30 | to_pem.hash 31 | end 32 | 33 | ## 34 | # Returns the bit strength of the public key used for the signing 35 | # request. 36 | # 37 | def strength 38 | public_key.n.num_bits 39 | end 40 | 41 | ## 42 | # Returns a collection of subject alternative names requested. If no 43 | # alternative names were requested, this returns an empty set. 44 | # 45 | def subject_alternative_names 46 | @_subject_alternative_names ||= begin 47 | if attribute = read_attributes_by_oid('extReq', 'msExtReq') 48 | set = OpenSSL::ASN1.decode(attribute.value) 49 | seq = set.value.first 50 | if sans = seq.value.collect { |asn1ext| OpenSSL::X509::Extension.new(asn1ext).to_a }.detect { |e| e.first == 'subjectAltName' } 51 | sans[1].gsub(/DNS:/,'').split(', ') 52 | else 53 | [] 54 | end 55 | else 56 | [] 57 | end 58 | end 59 | end 60 | alias :sans :subject_alternative_names 61 | 62 | 63 | protected 64 | 65 | 66 | def read_attributes_by_oid(*oids) 67 | attributes.detect { |a| oids.include?(a.oid) } 68 | end 69 | end 70 | 71 | OpenSSL::X509::Request.send(:include, OpenSSLExtensions::X509::Request) 72 | -------------------------------------------------------------------------------- /openssl-extensions.gemspec: -------------------------------------------------------------------------------- 1 | # -*- encoding: utf-8 -*- 2 | lib = File.expand_path('../lib/', __FILE__) 3 | $LOAD_PATH.unshift(lib) unless $LOAD_PATH.include?(lib) 4 | require 'openssl-extensions/version' 5 | 6 | Gem::Specification.new do |spec| 7 | spec.name = 'openssl-extensions' 8 | spec.version = OpenSSLExtensions::Version 9 | spec.authors = ["Envy Labs"] 10 | spec.email = [""] 11 | spec.summary = 'Helper methods and extensions for OpenSSL to make the interface more intuitive.' 12 | spec.description = 'This library patches OpenSSL to add helper methods and extensions to OpenSSL objects with the intention of making the interface more intuitive.' 13 | spec.homepage = 'http://github.com/envylabs/openssl-extensions' 14 | spec.license = 'MIT' 15 | 16 | spec.add_development_dependency 'rspec', '~> 2.4' 17 | 18 | spec.files = `git ls-files`.split($/) 19 | spec.executables = spec.files.grep(%r{^bin/}) { |f| File.basename(f) } 20 | spec.test_files = spec.files.grep(%r{^(test|spec|features)/}) 21 | spec.require_paths = ["lib"] 22 | end 23 | -------------------------------------------------------------------------------- /spec/fixtures/certificate_requests/1024.csr: -------------------------------------------------------------------------------- 1 | -----BEGIN CERTIFICATE REQUEST----- 2 | MIIBtzCCASACAQAwdzELMAkGA1UEBhMCVVMxEDAOBgNVBAgTB0dlb3JnaWExEDAO 3 | BgNVBAcTB0F0bGFudGExFTATBgNVBAoTDEdlb0NlcnRzIEluYzESMBAGA1UECxMJ 4 | U1NMIFNhbGVzMRkwFwYDVQQDExB3d3cuZ2VvY2VydHMuY29tMIGfMA0GCSqGSIb3 5 | DQEBAQUAA4GNADCBiQKBgQDdlqVkMMohJ1kUqBxqZJojCFtJFht7f4JDs5kURI7Q 6 | SCpN6eB/KLqP4x8skx3FGktU/Vgsdri9OeLS71cyw389eJyxmDFroicE0pyAd2o6 7 | dZGh1x9/7AJuIDwolhyNAhHLtKppTRh1LIUoU2ZU9xuywN9QzI5Yj29pNBaszsbE 8 | JwIDAQABoAAwDQYJKoZIhvcNAQEFBQADgYEAC3LXB167uc3gXxMSn+aMk6FxnAaB 9 | o7FMPV//zpk7gqijWxDmDaiIKp5cCKzoaC6M53vR00cx4O65V5La9U39I5NFqmqP 10 | m/x5k6iO35VPTrqbO0ZXM2YsoDarjNnYBYBwLd/MpjpVZIHIRcyndK6gWrU15T4I 11 | cfsIpGfkscraHNA= 12 | -----END CERTIFICATE REQUEST----- 13 | -------------------------------------------------------------------------------- /spec/fixtures/certificate_requests/challenge.csr: -------------------------------------------------------------------------------- 1 | -----BEGIN CERTIFICATE REQUEST----- 2 | MIIB+zCCAWQCAQAwgaUxCzAJBgNVBAYTAkdCMRIwEAYDVQQIEwlCZXJrc2hpcmUx 3 | EDAOBgNVBAcTB05ld2J1cnkxFzAVBgNVBAoTDk15IENvbXBhbnkgTHRkMQ4wDAYD 4 | VQQLEwVTYWxlczEnMCUGA1UEAxMed3d3LnVuc3VwcG9ydGVkLWV4dGVudGlvbnMu 5 | bmV0MR4wHAYJKoZIhvcNAQkBFg91c2VyQGRvbWFpbi5jb20wgZ8wDQYJKoZIhvcN 6 | AQEBBQADgY0AMIGJAoGBAK7A15mxNCj+NoDbOYgiDp5V3i3+I3qww73iwy6UjA8e 7 | /+xOvdLCsXapBCQEz6Zj+AUTPIqVeo6WZbZSHBocBCztRqj5B17gudsaBnV+wJ7h 8 | Cqy+/TWG95DwlwDSBkoNL5aaB1ytwDJke2+s77RUrI26teTCqdXWJ4EzvOEiZUFZ 9 | AgMBAAGgFTATBgkqhkiG9w0BCQcxBgwEKiZeJTANBgkqhkiG9w0BAQUFAAOBgQBA 10 | Idns2ji1Mgs/DMEnBejSpD/cXFQytIZw0hcthhhRYzeZC44SeLoOQl7zusySAnlq 11 | Pk3NonCEGHYDTw7A3s4RNxsfhEnCddB3hvW07tJzFq/CAyC8kack/3zCzfTT6ZCg 12 | E6vUd3TRLLjR2KYz34fsvH7VExIm6ilNUzyvIu2oCQ== 13 | -----END CERTIFICATE REQUEST----- 14 | 15 | -------------------------------------------------------------------------------- /spec/fixtures/certificate_requests/envylabs.csr: -------------------------------------------------------------------------------- 1 | -----BEGIN CERTIFICATE REQUEST----- 2 | MIIDETCCAfkCAQAwgZgxCzAJBgNVBAYTAlVTMRAwDgYDVQQIEwdGbG9yaWRhMRAw 3 | DgYDVQQHEwdPcmxhbmRvMRYwFAYDVQQKEw1FbnZ5IExhYnMgTExDMREwDwYDVQQL 4 | EwhJbnRlcm5ldDEVMBMGA1UEAxMMZW52eWxhYnMuY29tMSMwIQYJKoZIhvcNAQkB 5 | FhRzdXBwb3J0QGVudnlsYWJzLmNvbTCCASIwDQYJKoZIhvcNAQEBBQADggEPADCC 6 | AQoCggEBALlhCULQ4sdyrB48Fz7+FZ8c7IYJAXpJU/bnsTeRvf2je5xD9ZOpQbLA 7 | lprXaDWWd28LJOenQ7zxPGNGhcsdSnZ/zZautNVh9hgTmqfOCqZO6UV3atmrR3Wc 8 | 69ZRbIEKf6IjwWpA1IjIa4fI82UVXL2k0R2ZZyz1gvBapa50WEWqYvzv385q9NAP 9 | lqqZsrh11Sl1jxdk5+a9y74TOyFRwPHH2ZVVAYurZ0JuiAbyiKD+XruL9A2eXw1c 10 | dAuUiOsmoXrt9lYyXhwcgrrgpdt/uP4+fn5HR+QOEPrB6lX48O3r06KkoYNJ3PKw 11 | +UhhJpiBK8hn7IR5nHKSb0fq+6Rd+08CAwEAAaAzMBcGCSqGSIb3DQEJBzEKEwhw 12 | YXNzd29yZDAYBgkqhkiG9w0BCQIxCxMJRW52eSBMYWJzMA0GCSqGSIb3DQEBBQUA 13 | A4IBAQCE17fnA6ew4gddqo4yXArRjkmYjSWR7/fc620tSEfo6DXS0ToZfDcT/Qo2 14 | O9i2BLNJjz2zvGzuH39vlFqFy+YYf2EIUZb4NLPAWacdB0XKNX4S8dwUlNW7q9Ji 15 | SdDI3ggIyUusI9Uw8MQWLgcso3T3f3jIpTLAB+4uy7V4xWNTVrqlcf/bEIlJt/gA 16 | Seg7hhHWrhmbJ0UjV8CBs/7IITzpkUZbNyuT0XwjU/WbadiSvB3G9jc8yhWPQmo+ 17 | LhNnqXFgShvlkUpugjyz2igUzR9uRZwUG1iHMC+0l7mZ2DD0HKdWeAnMdfVJMSGc 18 | UhWy1VRgbLSjxJNAOQZSJwta+q/h 19 | -----END CERTIFICATE REQUEST----- 20 | -------------------------------------------------------------------------------- /spec/fixtures/certificate_requests/geocerts.csr: -------------------------------------------------------------------------------- 1 | -----BEGIN CERTIFICATE REQUEST----- 2 | MIICvDCCAaQCAQAwdzELMAkGA1UEBhMCVVMxEDAOBgNVBAgTB0dlb3JnaWExEDAO 3 | BgNVBAcTB0F0bGFudGExFTATBgNVBAoTDEdlb0NlcnRzIEluYzESMBAGA1UECxMJ 4 | U1NMIFNhbGVzMRkwFwYDVQQDExB3d3cuZ2VvY2VydHMuY29tMIIBIjANBgkqhkiG 5 | 9w0BAQEFAAOCAQ8AMIIBCgKCAQEAuXWzKA84WdxAhyOSsJgyh4Xa6UCItYcEanPL 6 | 1NP5vAsDXyoGlKry49hRDGQkX6SQ+ER3NLYw7jjADn0eB9wIe3RRABRaHzaGTR4s 7 | aFuRlBFahWq7WlRXtyGGM/3LO93GXbN/ZGqZVVzkP/4UhPge6mEs81mTmVAiuLan 8 | RrNnakt/6wJuJg/leyaXtQ82RHE6Oge+h8LaEbHUtfx2Z8ooiwAr4hk4rAH3AqQf 9 | mWiEF+XDIEDI0fBWcUCEXTxpfthojP6JuRC1PS1X9kk5P4ol3nSDlDl4YS4x6LF8 10 | OYhZyBMLxCat9YueS7T8vx64+ilwhXevsqVVu84HHx1TDupLjwIDAQABoAAwDQYJ 11 | KoZIhvcNAQEFBQADggEBAF9xFJQK0R7m75Qw2LWjQ5jI/V1XlOca0G61TKyuYXdn 12 | LpJkIo8cpRnU7/GCEM/lSAmIx2sQeIwpzGErwqLVlq3GnsbbDEkaclJ9hAtalDIc 13 | UrH8Crso1GehjNy08r8UppveG+b/sD7L2GDf7Pxqdxb3x+vt5osmo0l12BFf/85s 14 | K5tjLVnkqjtbMdESgOPvML0Ppq+q9M4TURDqs8zbMSJBu10oZDgxOyGw53JA7yI3 15 | PgiRWlylJlblXTpHm/jvPXaEMeCJ9CrO0z4bMF61A52yFW2+jFmA1L58a7OQ68Kg 16 | u+witRMeQm42iOZGES2p9d/wLPwKXgRPzcvgNyiRLiI= 17 | -----END CERTIFICATE REQUEST----- 18 | -------------------------------------------------------------------------------- /spec/fixtures/certificate_requests/sans.csr: -------------------------------------------------------------------------------- 1 | -----BEGIN NEW CERTIFICATE REQUEST----- 2 | MIIE3TCCA8UCAQAwga8xGTAXBgNVBAMMEG1haWwuc2lwY2hlbS5jb20xCzAJBgNV 3 | BAsMAklUMTwwOgYDVQQKDDNTYXVkaSBJbnRlcm5hdGlvbmFsIFBldHJvY2hlbWlj 4 | YWwgQ29tcGFueSAoU0lQQ0hFTSkxHzAdBgNVBAcMFkp1YmFpbCBJbmR1c3RyaWFs 5 | IENpdHkxGTAXBgNVBAgMEEVhc3Rlcm4gUHJvdmluY2UxCzAJBgNVBAYTAlNBMIIB 6 | IjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEAv3FqEVQCm0bBsA1YQX+/cq0y 7 | 2ZBPMsrQtP5S8F8bi2S8b6+BmsPuOIo7pdl7KYmpiD228mrpQqyrwkX7oBkNn94u 8 | TnxUlNJ+ach8Ni+V42O0IAALtYBd1JNoaCqmpr4+xmtWvdFow3cDTSYmqPapY9iN 9 | 2fxDm7VdNv9WMV89CrZg0a6F2p+PaKn8hOR1guax2AYnyVNx9DZJYiHqRfGXyWM1 10 | dIZaHTL/RtOzhrzxOvU/u+qulzWsSIslrz36rDqNTdDHGZNbKzix1etA0qP349/B 11 | jqIN7lP4+8pjaPp/Ygit6M2wuCTFhq5rm5SHanBlzCCq8pyhraQCjTvdNH0JRQID 12 | AQABoIIB5jAaBgorBgEEAYI3DQIDMQwWCjYuMS43NjAwLjIwZQYJKwYBBAGCNxUU 13 | MVgwVgIBBQwYc2lwYy1jYXMwMS5zaXBjaGVtLmxvY2FsDBNTSVBDSEVNXFNJUEMt 14 | Q0FTMDEkDCJNaWNyb3NvZnQuRXhjaGFuZ2UuU2VydmljZUhvc3QuZXhlMHIGCisG 15 | AQQBgjcNAgIxZDBiAgEBHloATQBpAGMAcgBvAHMAbwBmAHQAIABSAFMAQQAgAFMA 16 | QwBoAGEAbgBuAGUAbAAgAEMAcgB5AHAAdABvAGcAcgBhAHAAaABpAGMAIABQAHIA 17 | bwB2AGkAZABlAHIDAQAwgewGCSqGSIb3DQEJDjGB3jCB2zAOBgNVHQ8BAf8EBAMC 18 | BaAwgZsGA1UdEQSBkzCBkIISbWFpbC5zaXBjaGVtLmxvY2FsghBtYWlsLnNpcGNo 19 | ZW0uY29tggtzaXBjaGVtLmNvbYIaYXV0b2Rpc2NvdmVyLnNpcGNoZW0ubG9jYWyC 20 | GGF1dG9kaXNjb3Zlci5zaXBjaGVtLmNvbYIKc2lwYy1jYXMwMYIKc2lwYy1jYXMw 21 | MoINc2lwY2hlbS5sb2NhbDAMBgNVHRMBAf8EAjAAMB0GA1UdDgQWBBSqTt2+024w 22 | mEJ1nXZpJKMvJAb/uTANBgkqhkiG9w0BAQUFAAOCAQEAIMAxnkhyqkmnqdXfWVSV 23 | ucYeulY0UWlxquBO91n39Vx7f8dDuTlxLXnO4N0GYgkHQWB+NJOXpGhowRc9iJLV 24 | uKFzCbF/t5JekmGU+Vh3+Lns/09pa4zs1LAU+lvfEosI0ay8iBRP7aN0h4H6W7Y9 25 | cmh3Z1OemLy3HvbHkplbNN0agEpcITOzvugN5SnGaXS44271VSas10pBWD27CjlA 26 | nsP4Q1n8OHO9I83UTGqjqzMjMnFMZ7DcFIzEoW8M6TqJQsQVpZ+6WxGKG/1z63Ka 27 | OGbJUk+wK5KJjp8fkB1gSlQhvEyDF6DFcQCjbcssfn06hobLuiF2aNb9YDFUf+s/ 28 | 9g== 29 | -----END NEW CERTIFICATE REQUEST----- 30 | 31 | -------------------------------------------------------------------------------- /spec/fixtures/certificates/app1.hongkongpost.com.pem: -------------------------------------------------------------------------------- 1 | -----BEGIN CERTIFICATE----- 2 | MIIEHTCCAwWgAwIBAgIDI2WeMA0GCSqGSIb3DQEBBQUAMEkxCzAJBgNVBAYTAkhL 3 | MRYwFAYDVQQKEw1Ib25na29uZyBQb3N0MSIwIAYDVQQDExlIb25na29uZyBQb3N0 4 | IGUtQ2VydCBDQSAxMB4XDTA5MDgxMDA4NDYwOVoXDTExMDgyOTE2MDAwMFowgcMx 5 | CzAJBgNVBAYTAkhLMSYwJAYDVQQKEx1Ib25na29uZyBQb3N0IGUtQ2VydCAoU2Vy 6 | dmVyKTETMBEGA1UECxMKMDAwMDA3NzE1NjElMCMGA1UECxMcMDAwMDAwMDAwMDAw 7 | MDAwMDAwMDAwMDAwSEtQTzEhMB8GA1UECxMYSG9uZyBLb25nIFNBUiBHb3Zlcm5t 8 | ZW50MQ0wCwYDVQQLEwRIS1BPMR4wHAYDVQQDExVhcHAxLmhvbmdrb25ncG9zdC5j 9 | b20wgZ8wDQYJKoZIhvcNAQEBBQADgY0AMIGJAoGBAKB5YgVYDWFfeQSg7Bd3cWDK 10 | UnArAqfNqGC59KxwwY5tpy8QQNpI8l/SHDpDqJ0E/4pNFEDFNSv2c/VaORmxybqI 11 | mbP6iUZuxjBa3YMfDYMuVjvA+cw7JGsyE3hPz3OJlkzTIgB4J7Hvbo+XlKn8a/N4 12 | rBeNVQm0QBdBagctkVKTAgMBAAGjggEVMIIBETA+BgNVHSAENzA1MDMGCisGAQQB 13 | /R4BARAwJTAjBggrBgEFBQcCARYXd3d3Lmhvbmdrb25ncG9zdC5nb3YuaGswCQYD 14 | VR0TBAIwADARBglghkgBhvhCAQEEBAMCBkAwDgYDVR0PAQH/BAQDAgUgMFoGA1Ud 15 | IwRTMFGhS6RJMEcxCzAJBgNVBAYTAkhLMRYwFAYDVQQKEw1Ib25na29uZyBQb3N0 16 | MSAwHgYDVQQDExdIb25na29uZyBQb3N0IFJvb3QgQ0EgMYICA+0wRQYDVR0fBD4w 17 | PDA6oDigNoY0aHR0cDovL2NybDEuaG9uZ2tvbmdwb3N0Lmdvdi5oay9jcmwvZUNl 18 | cnRDQTFDUkwyLmNybDANBgkqhkiG9w0BAQUFAAOCAQEAsyO3KMxHle+AyWM8Ku5v 19 | Sz2uXaR8YmadOIBc8MKe522j6eq2MORC4EQ/ObCJ2vzyq8NoJ6aUtIi8k3He9B00 20 | pRKa9uAtgFGW1HVr6soGWvr4UcSVCDYs6C58+XEgbzCiurO5IIaprQCjOptkTLa/ 21 | x4QXkF6YVvtYXufjILl1i5RPr6YmwByeGxJnPKUhNuDsqJ9PfGqq3wfBX7ogEwG7 22 | Hu9L2Kbk9P7+OvqU7flh9GfxhPOTyFvN9itwcmImIScM9EG59JsZOBZHN4SeUzWk 23 | cYrNNv1NTqzBfFWKnX1aAuzeSC3tQP472gC+NgTcL84aBBjCk+2xmwP9OLdQyrkd 24 | ZQ== 25 | -----END CERTIFICATE----- 26 | 27 | -------------------------------------------------------------------------------- /spec/fixtures/certificates/bgthelpdesk.braxtongrant.com.pem: -------------------------------------------------------------------------------- 1 | -----BEGIN CERTIFICATE----- 2 | MIIDJDCCAuGgAwIBAgIET6p/CDALBgcqhkjOOAQDBQAwdTELMAkGA1UEBhMCVVMx 3 | CzAJBgNVBAgTAk1EMRcwFQYDVQQHEw5NYXJyaW90dHN2aWxsZTEMMAoGA1UEChMD 4 | QkdUMQswCQYDVQQLEwJJVDElMCMGA1UEAxMcYmd0aGVscGRlc2suYnJheHRvbmdy 5 | YW50LmNvbTAeFw0xMjA1MDkxNDI4MjRaFw0xNTA1MDkxNDI4MjRaMHUxCzAJBgNV 6 | BAYTAlVTMQswCQYDVQQIEwJNRDEXMBUGA1UEBxMOTWFycmlvdHRzdmlsbGUxDDAK 7 | BgNVBAoTA0JHVDELMAkGA1UECxMCSVQxJTAjBgNVBAMTHGJndGhlbHBkZXNrLmJy 8 | YXh0b25ncmFudC5jb20wggG3MIIBLAYHKoZIzjgEATCCAR8CgYEA/X9TgR11EilS 9 | 30qcLuzk5/YRt1I870QAwx4/gLZRJmlFXUAiUftZPY1Y+r/F9bow9subVWzXgTuA 10 | HTRv8mZgt2uZUKWkn5/oBHsQIsJPu6nX/rfGG/g7V+fGqKYVDwT7g/bTxR7DAjVU 11 | E1oWkTL2dfOuK2HXKu/yIgMZndFIAccCFQCXYFCPFSMLzLKSuYKi64QL8Fgc9QKB 12 | gQD34aCF1ps93su8q1w2uFe5eZSvu/o66oL5V0wLPQeCZ1FZV4661FlP5nEHEIGA 13 | tEkWcSPoTCgWE7fPCTKMyKbhPBZ6i1R8jSjgo64eK7OmdZFuo38L+iE1YvH7YnoB 14 | JDvMpPG+qFGQiaiD3+Fa5Z8GkotmXoB7VSVkAUw7/s9JKgOBhAACgYBAQ5zGDMvY 15 | d0s/pZMeeViCgd4R6GxTpJ6+PmRcZd+Qt9X05XvueffnKtORUmnfCrgr3PyxxEoD 16 | Nlapvpv9jH3HDfWhVNFIj70PsZdYJ1GMOudg8pfxCxfRjtD1upxfNeJ1d7DH3q0j 17 | Oxq3lNQle3rhi6nH+7sTAluc8NoRFJ/5YTALBgcqhkjOOAQDBQADMAAwLQIVAInO 18 | kQcVY0d0SUFKr7GGiF7o+zD2AhRL7oFGOAWewa/KiS+2n6JSlDwrjA== 19 | -----END CERTIFICATE----- 20 | -------------------------------------------------------------------------------- /spec/fixtures/certificates/equifax-secure-ca.pem: -------------------------------------------------------------------------------- 1 | -----BEGIN CERTIFICATE----- 2 | MIIDIDCCAomgAwIBAgIENd70zzANBgkqhkiG9w0BAQUFADBOMQswCQYDVQQGEwJV 3 | UzEQMA4GA1UEChMHRXF1aWZheDEtMCsGA1UECxMkRXF1aWZheCBTZWN1cmUgQ2Vy 4 | dGlmaWNhdGUgQXV0aG9yaXR5MB4XDTk4MDgyMjE2NDE1MVoXDTE4MDgyMjE2NDE1 5 | MVowTjELMAkGA1UEBhMCVVMxEDAOBgNVBAoTB0VxdWlmYXgxLTArBgNVBAsTJEVx 6 | dWlmYXggU2VjdXJlIENlcnRpZmljYXRlIEF1dGhvcml0eTCBnzANBgkqhkiG9w0B 7 | AQEFAAOBjQAwgYkCgYEAwV2xWGcIYu6gmi0fCG2RFGiYCh7+2gRvE4RiIcPRfM6f 8 | BeC4AfBONOziipUEZKzxa1NfBbPLZ4C/QgKO/t0BCezhABRP/PvwDN1Dulsr4R+A 9 | cJkVV5MW8Q+XarfCaCMczE1ZMKxRHjuvK9buY0V7xdlfUNLjUA86iOe/FP3gx7kC 10 | AwEAAaOCAQkwggEFMHAGA1UdHwRpMGcwZaBjoGGkXzBdMQswCQYDVQQGEwJVUzEQ 11 | MA4GA1UEChMHRXF1aWZheDEtMCsGA1UECxMkRXF1aWZheCBTZWN1cmUgQ2VydGlm 12 | aWNhdGUgQXV0aG9yaXR5MQ0wCwYDVQQDEwRDUkwxMBoGA1UdEAQTMBGBDzIwMTgw 13 | ODIyMTY0MTUxWjALBgNVHQ8EBAMCAQYwHwYDVR0jBBgwFoAUSOZo+SvSspXXR9gj 14 | IBBPM5iQn9QwHQYDVR0OBBYEFEjmaPkr0rKV10fYIyAQTzOYkJ/UMAwGA1UdEwQF 15 | MAMBAf8wGgYJKoZIhvZ9B0EABA0wCxsFVjMuMGMDAgbAMA0GCSqGSIb3DQEBBQUA 16 | A4GBAFjOKer89961zgK5F7WF0bnj4JXMJTENAKaSbn+2kmOeUJXRmm/kEd5jhW6Y 17 | 7qj/WsjTVbJmcVfewCHrPSqnI0kBBIZCe/zuf6IWUrVnZ9NA2zsmWLIodz2uFHdh 18 | 1voqZiegDfqnc1zqcPGUIWVEX/r87yloqaKHee9570+sB3c4 19 | -----END CERTIFICATE----- 20 | 21 | -------------------------------------------------------------------------------- /spec/fixtures/certificates/geotrust-extended-validation-ssl-ca.pem: -------------------------------------------------------------------------------- 1 | -----BEGIN CERTIFICATE----- 2 | MIIEnDCCA4SgAwIBAgIQaUiiayAapCHomLHEksfFjjANBgkqhkiG9w0BAQUFADBY 3 | MQswCQYDVQQGEwJVUzEWMBQGA1UEChMNR2VvVHJ1c3QgSW5jLjExMC8GA1UEAxMo 4 | R2VvVHJ1c3QgUHJpbWFyeSBDZXJ0aWZpY2F0aW9uIEF1dGhvcml0eTAeFw0wNjEx 5 | MjkwMDAwMDBaFw0xNjExMjgyMzU5NTlaMIGFMQswCQYDVQQGEwJVUzEVMBMGA1UE 6 | ChMMR2VvVHJ1c3QgSW5jMTEwLwYDVQQLEyhTZWUgd3d3Lmdlb3RydXN0LmNvbS9y 7 | ZXNvdXJjZXMvY3BzIChjKTA2MSwwKgYDVQQDEyNHZW9UcnVzdCBFeHRlbmRlZCBW 8 | YWxpZGF0aW9uIFNTTCBDQTCCASIwDQYJKoZIhvcNAQEBBQADggEPADCCAQoCggEB 9 | AMLv7ewLLXKKdGhzNm4QqH5If1i7eGfc7XvWfKZPPZ9dbwrQoLRl/b7Tv3e2lKWC 10 | /4GVnSgQBuzCtJCqWlFMc9lrdKg1SfSmNoDUXHWennwBx4ycgciGgxqOvQATotz/ 11 | pXiqdywhYgiXP4C992ekedt91z5uttWWuZiGTnpn4pOv2qXRJ/vxZsMqAwy2x4Id 12 | Ofs83ik2cV3hqLUWOXwb/3uG9YCSleADO6pE+/QAteWp4voY+YSaweH2Lg6BixQp 13 | NP8fVWCIpJnGb28EOTp1pKceWN+3/8maHXDbg6DTgxstbSqQW6NjkXO1/52CekHz 14 | 06ovCw2fz0TAXseha8+ulNsCAwEAAaOCATIwggEuMB0GA1UdDgQWBBQoxOuP8V95 15 | kKMrVcNWTn1rU3IsGDA9BggrBgEFBQcBAQQxMC8wLQYIKwYBBQUHMAGGIWh0dHA6 16 | Ly9FVlNlY3VyZS1vY3NwLmdlb3RydXN0LmNvbTASBgNVHRMBAf8ECDAGAQH/AgEA 17 | MEYGA1UdIAQ/MD0wOwYEVR0gADAzMDEGCCsGAQUFBwIBFiVodHRwOi8vd3d3Lmdl 18 | b3RydXN0LmNvbS9yZXNvdXJjZXMvY3BzMEEGA1UdHwQ6MDgwNqA0oDKGMGh0dHA6 19 | Ly9FVlNlY3VyZS1jcmwuZ2VvdHJ1c3QuY29tL0dlb1RydXN0UENBLmNybDAOBgNV 20 | HQ8BAf8EBAMCAQYwHwYDVR0jBBgwFoAULNVQQZcVi/CPNmFbSvtr2ZnJM5IwDQYJ 21 | KoZIhvcNAQEFBQADggEBAAJgoxYSndgcGeRaN2z/Mpg3Rk+8gXyAw8qJKgD+Xj7s 22 | uowrH6uVa5GUIaBgHwIG+s8XbfiVq814IxSWwJ0fG+tQ4WVCitKzya2Aw2fPtFgb 23 | 1QTkWP40ReD7pIQii+niN0yY8Qv/pIlT0U3AaEjXWYcaO3310Pkjcspg/cMiFfCa 24 | lVhvfCST7KUSPbQbAejuae1Ba1LLmrdcFdG9BkB64AyXy2Dngl9qX95JhFZqr3yw 25 | S62MTw95oMwRPCXnRr960C+IyL/rlAtqdTN/cwC4EnAjXlV/RVseELECaNgnQM8k 26 | CeJldM6JRI17KJBorqzCOMhWDTOIKH9U/Dw8UAmTPTg= 27 | -----END CERTIFICATE----- 28 | 29 | -------------------------------------------------------------------------------- /spec/fixtures/certificates/geotrust-primary-certification-authority.pem: -------------------------------------------------------------------------------- 1 | -----BEGIN CERTIFICATE----- 2 | MIIDizCCAvSgAwIBAgIDBo4dMA0GCSqGSIb3DQEBBQUAME4xCzAJBgNVBAYTAlVT 3 | MRAwDgYDVQQKEwdFcXVpZmF4MS0wKwYDVQQLEyRFcXVpZmF4IFNlY3VyZSBDZXJ0 4 | aWZpY2F0ZSBBdXRob3JpdHkwHhcNMDYxMTI4MTYwODMxWhcNMTgwODIxMTUwODMx 5 | WjBYMQswCQYDVQQGEwJVUzEWMBQGA1UEChMNR2VvVHJ1c3QgSW5jLjExMC8GA1UE 6 | AxMoR2VvVHJ1c3QgUHJpbWFyeSBDZXJ0aWZpY2F0aW9uIEF1dGhvcml0eTCCASIw 7 | DQYJKoZIhvcNAQEBBQADggEPADCCAQoCggEBAL64FXv/1Hx9Z62DZHvIQlMt3/aE 8 | CCBh1gFZapxEEa/vdv2Vfs5hMLt6g18CvQFmyu4VjW+hMJy9oYWelDrzVogAMc/Y 9 | 7mqWAtntA4z7dW3n6rhVFgUWmvTgXrGIwGSFXBVNiMe3uuB16a0FPZ3HiUjguyjI 10 | A+Ewk2ReUsBZcCI1V4iK8ZUKg9e8MXMBNO3vRnHgawKoNXJrl5tm4MsceV/YGgRo 11 | HkcC5p1g4jaXAd/ONZLfvmfHbXdZO4+d1pAVlLxCNBDBOfmxJz5+1op1xbKvltOi 12 | 3pvkmL594emBrbZv/NcO2uA0sA0ad+fjCJjvWPqchLc2r8LfrNL0EAZwcTUCAwEA 13 | AaOB6DCB5TAOBgNVHQ8BAf8EBAMCAQYwHQYDVR0OBBYEFCzVUEGXFYvwjzZhW0r7 14 | a9mZyTOSMB8GA1UdIwQYMBaAFEjmaPkr0rKV10fYIyAQTzOYkJ/UMA8GA1UdEwEB 15 | /wQFMAMBAf8wOgYDVR0fBDMwMTAvoC2gK4YpaHR0cDovL2NybC5nZW90cnVzdC5j 16 | b20vY3Jscy9zZWN1cmVjYS5jcmwwRgYDVR0gBD8wPTA7BgRVHSAAMDMwMQYIKwYB 17 | BQUHAgEWJWh0dHA6Ly93d3cuZ2VvdHJ1c3QuY29tL3Jlc291cmNlcy9jcHMwDQYJ 18 | KoZIhvcNAQEFBQADgYEAe2AG6d2nHSkI7xH51Ts80itTyz7tvnZgZEig5svoScMa 19 | v92txUy9U0hVQdsYsU47OmgsJFpB9cipRKYyKS11+E3yUI7w4pvp4eQ7cLcyiduo 20 | OcVbaFa9BBXDtssbJEqn/MTVjbaY3QP2sbOU2j9SoKRQBspFZ07/8UGJQAA2fnk= 21 | -----END CERTIFICATE----- 22 | 23 | -------------------------------------------------------------------------------- /spec/fixtures/certificates/globalsign-root-ca.pem: -------------------------------------------------------------------------------- 1 | -----BEGIN CERTIFICATE----- 2 | MIIDdTCCAl2gAwIBAgILBAAAAAABFUtaw5QwDQYJKoZIhvcNAQEFBQAwVzELMAkG 3 | A1UEBhMCQkUxGTAXBgNVBAoTEEdsb2JhbFNpZ24gbnYtc2ExEDAOBgNVBAsTB1Jv 4 | b3QgQ0ExGzAZBgNVBAMTEkdsb2JhbFNpZ24gUm9vdCBDQTAeFw05ODA5MDExMjAw 5 | MDBaFw0yODAxMjgxMjAwMDBaMFcxCzAJBgNVBAYTAkJFMRkwFwYDVQQKExBHbG9i 6 | YWxTaWduIG52LXNhMRAwDgYDVQQLEwdSb290IENBMRswGQYDVQQDExJHbG9iYWxT 7 | aWduIFJvb3QgQ0EwggEiMA0GCSqGSIb3DQEBAQUAA4IBDwAwggEKAoIBAQDaDuaZ 8 | jc6j40+Kfvvxi4Mla+pIH/EqsLmVEQS98GPR4mdmzxzdzxtIK+6NiY6arymAZavp 9 | xy0Sy6scTHAHoT0KMM0VjU/43dSMUBUc71DuxC73/OlS8pF94G3VNTCOXkNz8kHp 10 | 1Wrjsok6Vjk4bwY8iGlbKk3Fp1S4bInMm/k8yuX9ifUSPJJ4ltbcdG6TRGHRjcdG 11 | snUOhugZitVtbNV4FpWi6cgKOOvyJBNPc1STE4U6G7weNLWLBYy5d4ux2x8gkasJ 12 | U26Qzns3dLlwR5EiUWMWea6xrkEmCMgZK9FGqkjWZCrXgzT/LCrBbBlDSgeF59N8 13 | 9iFo7+ryUp9/k5DPAgMBAAGjQjBAMA4GA1UdDwEB/wQEAwIBBjAPBgNVHRMBAf8E 14 | BTADAQH/MB0GA1UdDgQWBBRge2YaRQ2XyolQL30EzTSo//z9SzANBgkqhkiG9w0B 15 | AQUFAAOCAQEA1nPnfE920I2/7LqivjTFKDK1fPxsnCwrvQmeU79rXqoRSLblCKOz 16 | yj1hTdNGCbM+w6DjY1Ub8rrvrTnhQ7k4o+YviiY776BQVvnGCv04zcQLcFGUl5gE 17 | 38NflNUVyRRBnMRddWQVDf9VMOyGj/8N7yy5Y0b2qvzfvGn9LhJIZJrglfCm7ymP 18 | AbEVtQwdpf5pLGkkeB6zpxxxYu7KyJesF12KwvhHhm4qxFYxldBniYUr+WymXUad 19 | DKqC5JlR3XC321Y9YeRq4VzW9v493kHMB65jUr9TU/Qr6cf9tveCX4XSQRjbgbME 20 | HMUfpIBvFSDJ3gyICh3WZlXi/EjJKSZp4A== 21 | -----END CERTIFICATE----- 22 | 23 | -------------------------------------------------------------------------------- /spec/fixtures/certificates/hongkong-post-e-cert-ca-1.pem: -------------------------------------------------------------------------------- 1 | -----BEGIN CERTIFICATE----- 2 | MIIDMjCCAhqgAwIBAgICA+0wDQYJKoZIhvcNAQEFBQAwRzELMAkGA1UEBhMCSEsx 3 | FjAUBgNVBAoTDUhvbmdrb25nIFBvc3QxIDAeBgNVBAMTF0hvbmdrb25nIFBvc3Qg 4 | Um9vdCBDQSAxMB4XDTAzMDUxNTA3MjkzNFoXDTEzMDUxNTA3MjkxMFowSTELMAkG 5 | A1UEBhMCSEsxFjAUBgNVBAoTDUhvbmdrb25nIFBvc3QxIjAgBgNVBAMTGUhvbmdr 6 | b25nIFBvc3QgZS1DZXJ0IENBIDEwggEiMA0GCSqGSIb3DQEBAQUAA4IBDwAwggEK 7 | AoIBAQDMuy41bQKNRsp21OVxWu3JI4Vp48VgeVriKqLBhmdOird5LD9FhsiIStw0 8 | mOXZVuwJ8HmpuE4phvqIpSuxKhz/eCCTWO0duIa7XZ36uEv3vWJowjkcmn/r+6V3 9 | AN1Ts/1Ga39eqVIIJGMVDpFAH1vS3II7YOoYWJkfbwgIxSJ80bVxroNEzP2T2bW/ 10 | m4JhFhGC2WBHM8mobpvqqRFPQ1uc+w7bFMs3mKc+zq71FjTu0s3NAZu3LXIdMYn2 11 | pOM0agz8J3Yn/3Stru3UiC7HASupAcasbIAf6edrroYf4ZvQQp4oiaG9bGEchjyw 12 | ghGQPn9MFq+AzV1SkCjl/LPd3FFRAgMBAAGjJjAkMBIGA1UdEwEB/wQIMAYBAf8C 13 | AQAwDgYDVR0PAQH/BAQDAgHGMA0GCSqGSIb3DQEBBQUAA4IBAQChcHcWuSHwYRMl 14 | RFbLCt0ee9zmataVUMaCpSNwHutmvaYbnwDe30ipJkUzxBlTcWgHp5zKTZLzU8go 15 | rsA+pHpznU2VLD+Sr1OOpefJ+t04ZmkAYpTCsi65SEEL3dhFRF/fHtTK/4JKSHuZ 16 | 3oyuHwbjONGRPos212h/EFw20vR5R2acK8l5/6iPkl+jKhmfqsHd5Ve/JWN3G05+ 17 | 4KkrAgLFjCXUh9PmFTeGsR7GELuL6xQC33udGnLDRALdumafePAMbKqYLUb6Ftrz 18 | Fb9gih/+sTUt+hWme/Bas3Vwl7oswqzMDaJG4q/vqO5w48LPvE1nKEMoVw5nzJ+h 19 | f+4cB/TZ 20 | -----END CERTIFICATE----- 21 | 22 | -------------------------------------------------------------------------------- /spec/fixtures/certificates/hongkong-post-e-cert-ca.pem: -------------------------------------------------------------------------------- 1 | -----BEGIN CERTIFICATE----- 2 | MIIDLTCCAhWgAwIBAgIBBDANBgkqhkiG9w0BAQUFADBFMQswCQYDVQQGEwJISzEW 3 | MBQGA1UEChMNSG9uZ2tvbmcgUG9zdDEeMBwGA1UEAxMVSG9uZ2tvbmcgUG9zdCBS 4 | b290IENBMB4XDTAwMDExNjA4NDMwMFoXDTEwMDExNjIzNTkwMFowRzELMAkGA1UE 5 | BhMCSEsxFjAUBgNVBAoTDUhvbmdrb25nIFBvc3QxIDAeBgNVBAMTF0hvbmdrb25n 6 | IFBvc3QgZS1DZXJ0IENBMIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEA 7 | 0Nimo3Osj9gWM+WFpQ5PksMquQ8CXOUdpHCOl9l5KvGZqdSH3QhYhYDe0MmM+mSO 8 | Oz1HHxjDniDmEZuzTveQdhlkOYuW5zAhNy7R+kXOnEku0Tmvrd6/YEHQxThgTXSk 9 | wF0vcd4zMrajUNQUv+d1QbL40Gg6mV3T3wbZG9gk4KblakGd/znx/EeNAGaLRrjU 10 | GNn3l/DmxzEf80uBtLYBsBhUzCA24SqpMh5x3waEyN/kNzjb7NT0XI0hKs0yGE32 11 | R7hrkHM/RSTa7in0AqLy/gJaPBIypzpY+5h/XRMmef0yAggWThOJJtWhJC8/AwOF 12 | wPfAkZtJ71+f8+2c6t7x7wIDAQABoyYwJDAOBgNVHQ8BAf8EBAMCAQYwEgYDVR0T 13 | AQH/BAgwBgEB/wIBADANBgkqhkiG9w0BAQUFAAOCAQEAbaENyadgv8R5k2qUGWCm 14 | 4iA3XLu/P8qo6sulk+M+bKpL8WmU+Pl36yeBZax+QJTrHrdqgvgaZNvfmRykOvVm 15 | OZMGLSG9DxejqZtydEC5kGCqr3XmdQgkM8END5YDZNaOlj92WAVnuF7lw0nM54w9 16 | 1URkLN7I6tbAbD4X66HwKB2JotO9EPLbdVrMZIPOj7wNMWdxbBImmHIizH3mD7HV 17 | VdjLQNgh5ErB2mDouKLwHX/ok2GiiNRC8vNqBgV7cJDs58c23Pm7q2TjjmkAMCc0 18 | dy0QCKffH1ncMcPkVha1EhIx35HMyDa0RPJlVjW0M9LWMzfup9luU3uYKd1EG4g+ 19 | fA== 20 | -----END CERTIFICATE----- 21 | 22 | -------------------------------------------------------------------------------- /spec/fixtures/certificates/hongkong-post-root-ca-1.pem: -------------------------------------------------------------------------------- 1 | -----BEGIN CERTIFICATE----- 2 | MIIDMDCCAhigAwIBAgICA+gwDQYJKoZIhvcNAQEFBQAwRzELMAkGA1UEBhMCSEsx 3 | FjAUBgNVBAoTDUhvbmdrb25nIFBvc3QxIDAeBgNVBAMTF0hvbmdrb25nIFBvc3Qg 4 | Um9vdCBDQSAxMB4XDTAzMDUxNTA1MTMxNFoXDTIzMDUxNTA0NTIyOVowRzELMAkG 5 | A1UEBhMCSEsxFjAUBgNVBAoTDUhvbmdrb25nIFBvc3QxIDAeBgNVBAMTF0hvbmdr 6 | b25nIFBvc3QgUm9vdCBDQSAxMIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKC 7 | AQEArP84tulmAknjorThkPlAj3n54r15/gK97iSSHSL22oVyaf7XPwnU3ZG1ApzQ 8 | jVrhVcNQhrkpJsLj2aDxaQMoIIBFIi1WpztUlVYiWR8o3x8gPW2iNr4joLFutbEn 9 | PzlTCeqrauh0ssJlXI6/fMN4hM2eFvz1Lk8gKgifd/PFHsSaUmYeSF7jEAaPIpjh 10 | ZY4bXSNmO7ilMlHIhqqhqZ5/dpTCpmy3QfDVyAY45tQM4vM7TG1QjMSDJ8EThFk9 11 | nnV0ttgCXjqQesBCNnLsak3c78QA3xMYV18meMjWCnl3v/evt3a5pQuEF10Q6m/h 12 | q5URX208o1xNg1vysxmKgIsLhwIDAQABoyYwJDASBgNVHRMBAf8ECDAGAQH/AgED 13 | MA4GA1UdDwEB/wQEAwIBxjANBgkqhkiG9w0BAQUFAAOCAQEADkbVPK7ih9legYsC 14 | mEEIjEy82tvuJxuC52pF7BaLT4Wg87JwvVqWuspube5Gi27nKi6Wsxkz67SfqLI3 15 | 7piol7Yutmcn1KZJ/RyTZXaeQi/cImyaT/JaFTmxcdcrUehtHJjA2Sr0oYJ71clB 16 | oiMBdDhViw+5LmeiIAQ32pwL0xch4I+XeTRvhEgCIDMb5jREn5Fw9IBehEPCKdJs 17 | EhTkYY2sEJCehFC78JZvRZ+K88psT/oROhUVRsPNH4NbLUES7VBnQRM9IauUiqpO 18 | fMGx+6fWtScvl6tu4B3i0RwsH0Ti/L6RoZz71ilTc4afU9hDDl3WY4JxHYB0yvbi 19 | AmvZWg== 20 | -----END CERTIFICATE----- 21 | 22 | -------------------------------------------------------------------------------- /spec/fixtures/certificates/hongkong-post-root-ca.pem: -------------------------------------------------------------------------------- 1 | -----BEGIN CERTIFICATE----- 2 | MIIDKzCCAhOgAwIBAgIBATANBgkqhkiG9w0BAQUFADBFMQswCQYDVQQGEwJISzEW 3 | MBQGA1UEChMNSG9uZ2tvbmcgUG9zdDEeMBwGA1UEAxMVSG9uZ2tvbmcgUG9zdCBS 4 | b290IENBMB4XDTAwMDExNjA3NDIwMFoXDTEwMDExNjIzNTkwMFowRTELMAkGA1UE 5 | BhMCSEsxFjAUBgNVBAoTDUhvbmdrb25nIFBvc3QxHjAcBgNVBAMTFUhvbmdrb25n 6 | IFBvc3QgUm9vdCBDQTCCASIwDQYJKoZIhvcNAQEBBQADggEPADCCAQoCggEBAKWi 7 | kb485TY8Su80j4j9um1xpmJy7cIGvHYlu4pfFSUnON3pkZEBim+O1Vumvz4js2PD 8 | VOHxwwyWTPt6Dice8CMQgNIoaL0GlYt96Sn51Cf28ndfIqslqEWXl0VWYVichyQy 9 | nTgdPe24HHQFNWWgpZEPr2g9eg9cIfvnLBv+VoZScXNkgdTghIDQlVa9SdA9JDNd 10 | jnqCOFS2avS6dGkuu9FBcBF05jdoY69M6tyiIM32j0zm0IAIJFCZGHTlztYH0HgW 11 | Bl893IXLdD94fxnIDY5IVGlqBYCpbLJB/rQEBou6taJ+Kaaqr0HpHEkRtw1ICHJ/ 12 | S4OY8xMaeUvRThTOEjMCAwEAAaMmMCQwEgYDVR0TAQH/BAgwBgEB/wIBAzAOBgNV 13 | HQ8BAf8EBAMCAQYwDQYJKoZIhvcNAQEFBQADggEBAJUFGRYwVpy5mEtANI0RW08k 14 | 8SxowrWsteC0hKBMbeyKdmHB89V08WGffL4gkidS+jEkuNGe4PNtULqt4i2vKHVr 15 | 4Srv+CHNO8vIMh7TulnPOOjykHjdnghElV+GT2qb3KKL5ZymVouGZTUcbAzKHqSe 16 | TlwSk9J4yNhp6mmGJtSk04+tV0pY9OzGf2y7tPLHkvLOPPRCwyFnhm2lTaA/wvcY 17 | q1fW6MeS+oanNgUOGXK/BvCspERMrD7o730LSMXBGzPpyQHQzO/+Ye2M6DKazPXJ 18 | 2ck9eKYABR0p6yYBjkv+OazFkv++cJpXwvLPIbhmLt7g1P6727RHiZJ/GLwshrU= 19 | -----END CERTIFICATE----- 20 | 21 | -------------------------------------------------------------------------------- /spec/fixtures/certificates/www.geocerts.com.pem: -------------------------------------------------------------------------------- 1 | -----BEGIN CERTIFICATE----- 2 | MIIFaDCCBFCgAwIBAgICCokwDQYJKoZIhvcNAQEFBQAwgYUxCzAJBgNVBAYTAlVT 3 | MRUwEwYDVQQKEwxHZW9UcnVzdCBJbmMxMTAvBgNVBAsTKFNlZSB3d3cuZ2VvdHJ1 4 | c3QuY29tL3Jlc291cmNlcy9jcHMgKGMpMDYxLDAqBgNVBAMTI0dlb1RydXN0IEV4 5 | dGVuZGVkIFZhbGlkYXRpb24gU1NMIENBMB4XDTEwMDYxODIwNTUwNloXDTEyMDgx 6 | MjE0MTkwNVowgdUxGzAZBgNVBA8TElYxLjAsIENsYXVzZSA1LihiKTETMBEGCysG 7 | AQQBgjc8AgEDEwJVUzEYMBYGCysGAQQBgjc8AgECEwdHZW9yZ2lhMRAwDgYDVQQF 8 | EwcwNDUwNzcxMQswCQYDVQQGEwJVUzEQMA4GA1UECBMHR2VvcmdpYTEQMA4GA1UE 9 | BxMHQXRsYW50YTEVMBMGA1UEChMMR2VvQ2VydHMgSW5jMRIwEAYDVQQLEwlTU0wg 10 | U2FsZXMxGTAXBgNVBAMTEHd3dy5nZW9jZXJ0cy5jb20wggEiMA0GCSqGSIb3DQEB 11 | AQUAA4IBDwAwggEKAoIBAQCfSaBRuqglkqNaIRqdd2CzPOVdw14YPheWEOG28iFI 12 | Oi+Pzjk0XU+KFEJ3ID7aC+ntyb/CjXjOiv7k9Xrjp4+y4e/bXPr4Cz1SSQwYtY5Q 13 | 6xbUnRXkCn3SETsGeub8pKM/KCJB0Tbmmtqw7TgJbGSHTbWNkxTY9oUIMRYx44sE 14 | 2LLh2o08WMiYrFO2L9kRyR6rn4tLL7RGj4Q2ZZbWG4xzkwDL4GhZ9eUnOFz7vzWc 15 | CB+EAggMlM8pck1bJD/7z8qCMbV7h/NYJFDRb8Gd1skBd0b58tYlY8sn+P9qYRWc 16 | oWUWES8XSP/HUehuLKIzIy0JckAt88U8rRy4DLP9rD3BAgMBAAGjggGOMIIBijAf 17 | BgNVHSMEGDAWgBQoxOuP8V95kKMrVcNWTn1rU3IsGDBuBggrBgEFBQcBAQRiMGAw 18 | KgYIKwYBBQUHMAGGHmh0dHA6Ly9FVlNTTC1vY3NwLmdlb3RydXN0LmNvbTAyBggr 19 | BgEFBQcwAoYmaHR0cDovL0VWU1NMLWFpYS5nZW90cnVzdC5jb20vZXZjYS5jcnQw 20 | DgYDVR0PAQH/BAQDAgWgMB0GA1UdJQQWMBQGCCsGAQUFBwMBBggrBgEFBQcDAjAp 21 | BgNVHREEIjAgghB3d3cuZ2VvY2VydHMuY29tggxnZW9jZXJ0cy5jb20wQgYDVR0f 22 | BDswOTA3oDWgM4YxaHR0cDovL0VWU1NMLWNybC5nZW90cnVzdC5jb20vY3Jscy9n 23 | dGV4dHZhbGNhLmNybDAMBgNVHRMBAf8EAjAAMEsGA1UdIAREMEIwQAYJKwYBBAHw 24 | IgEGMDMwMQYIKwYBBQUHAgEWJWh0dHA6Ly93d3cuZ2VvdHJ1c3QuY29tL3Jlc291 25 | cmNlcy9jcHMwDQYJKoZIhvcNAQEFBQADggEBAIppmd9Lm9+cbSPrKKlIdunEbwTU 26 | kquqmCaJP7tP6ASb2NfJczfzpdlxidiVOp1wJxIHhuAQjhWt0nO7aOTjMD8WZa1d 27 | NIQMWHeFyhAuqJFXtJ6Ha9t1CB+V3ksNNKIhR5urZXlRc4G7Y2udyIYuqq4VzWsS 28 | TFCS6/lAuDob4h5+TEdm51CV6BFyJweYt4o1FKSDVKwQmRMmc4Tk2oyBlX4jKPdS 29 | WPKMKb7f934e69sZlne575+Ml4FJm3g2QK+AR/2rSuQsO2vV+stkhknLZsCIrrkh 30 | 9zClcbFt/pHG1LTI0KNs87Eix3avl2uLIzb9MSyQbKPbtDXlH+fqSAao/mY= 31 | -----END CERTIFICATE----- 32 | -------------------------------------------------------------------------------- /spec/fixtures/certificates/www.twongo.com.pem: -------------------------------------------------------------------------------- 1 | -----BEGIN CERTIFICATE----- 2 | MIIDYTCCAsqgAwIBAgIDDqS7MA0GCSqGSIb3DQEBBQUAME4xCzAJBgNVBAYTAlVT 3 | MRAwDgYDVQQKEwdFcXVpZmF4MS0wKwYDVQQLEyRFcXVpZmF4IFNlY3VyZSBDZXJ0 4 | aWZpY2F0ZSBBdXRob3JpdHkwHhcNMTAwMTA0MTU1ODMzWhcNMTEwMTA2MTAzNzAy 5 | WjCB6zEpMCcGA1UEBRMgVk9hT080MW1qbHdiTS8tbFNvZWlpTjRuQWxabUJuRFMx 6 | CzAJBgNVBAYTAlVTMRcwFQYDVQQKEw53d3cudHdvbmdvLmNvbTETMBEGA1UECxMK 7 | R1Q5OTYyNTQyMzExMC8GA1UECxMoU2VlIHd3dy5nZW90cnVzdC5jb20vcmVzb3Vy 8 | Y2VzL2NwcyAoYykxMDE3MDUGA1UECxMuRG9tYWluIENvbnRyb2wgVmFsaWRhdGVk 9 | IC0gUXVpY2tTU0wgUHJlbWl1bShSKTEXMBUGA1UEAxMOd3d3LnR3b25nby5jb20w 10 | gZ8wDQYJKoZIhvcNAQEBBQADgY0AMIGJAoGBAPQnD+rXT4qG8tbp9qDV4EflRm1d 11 | z4U0DqjcSZDPVLN3ae3E1dQQP7aSe4LlgYP0ZFgn+cTNFOwe950diE22zPQR/yRq 12 | ZhzNkXwvwxL1eirP6Evd3aX60xmQoV/g103KsVeCx+ZJ/6G1xA01x4Sw+5G1pH0j 13 | T+NnqYAODxD3uxDnAgMBAAGjga4wgaswDgYDVR0PAQH/BAQDAgTwMB0GA1UdDgQW 14 | BBRO6N23fnbWi7eK5OHvN05UYN+LijA6BgNVHR8EMzAxMC+gLaArhilodHRwOi8v 15 | Y3JsLmdlb3RydXN0LmNvbS9jcmxzL3NlY3VyZWNhLmNybDAfBgNVHSMEGDAWgBRI 16 | 5mj5K9KylddH2CMgEE8zmJCf1DAdBgNVHSUEFjAUBggrBgEFBQcDAQYIKwYBBQUH 17 | AwIwDQYJKoZIhvcNAQEFBQADgYEACmWFvfv3qUV3KO0j5R3eS35cl44EvVeq8FFX 18 | Ib46eOoMybZLt4tMK56zeTyE4mACOdjqLB4G8gLYTDMw4bGpHBLnOM+F97As7FD3 19 | VpzIfd8DBTkJqC/u9zpHz7VCWoufYIXRzH/kSh6V3TJlTWasgYsI+OLfPuXra4WY 20 | FgjRhhc= 21 | -----END CERTIFICATE----- 22 | 23 | -------------------------------------------------------------------------------- /spec/fixtures/keys/dsa.key: -------------------------------------------------------------------------------- 1 | -----BEGIN DSA PRIVATE KEY----- 2 | MIIDPQIBAAKCAQEAiVNJHKZ1id9fDSwG1Ze3Klp2prgLNK42YbkWz2Zf/fubDAU5 3 | VIdbDAVNJfUrA0ZcMDdFXUw7UbU5rY4rBQWt3OmUD6YTDZoiAZ8wgqU8G6RPf/jh 4 | erimlVirUWWXGvef5EtXWZ1TaCNHBb9eErSuLWabFjwBDhezgP5ERswBSGDLvB0b 5 | pabFN8wp/aMgUv315qCPo9meekhPjv0kxqoOai0Jpgl31W/FuJE80tEDBLoVdxvG 6 | rX7tb9WffBDlonQzEFj5kargRLElZHMo4iQq8l+NmQjVdFQCRrzxwSXpM5B4QYmw 7 | OKvbr3/6+RBTlymWiY1PoLo0PL70/DFZVT80SwIVAJcUyYEMiZCpIiDBgG7HgUIM 8 | vpX7AoIBAHtG/S/nEbvdS1rUkUsxJEH4C2wWHUqIpVjpKnwPKDrW4wVDchg4Bblu 9 | ckK3+6SnFGPyMxiChOtSux1AUwAd170OFp2a4kYHDnjM+Z/rGVlE8Ou5wS7NYEYr 10 | N707TFTUyQ2S/Gp/+UWmFyfMb2pZcB66KhLT5HMMA1EpHagxRzI9YXgRNIlh7al1 11 | GOVoG9orz80NJkyZF5AOIXwXLGXJN6Y03V4E9uyIdfnkDyjlcwHHLqy6Jit1r/Q1 12 | KXOf9vzFhXZ5zClR8WQNjrgd9YKRaMcZl5CjLg5cR+Q/zLfEPmrf2wbnA6plOWCr 13 | fpHjZke9EolwPBLGPtX+L3mblZAQguQCggEAS0ncfxyk8PPXxTRFTE0ztPijWUIb 14 | 0w8yFKMkV0Cn5XBCIQ7lB3Q9wrlBgxoOuMCJcILyUru8sOlynYTJEmDen3WtHUIs 15 | wbwd2DevrFM5c9/fWL0ZHghdefzrY47/urzqgFl9L7yCCBMM/WwTjgNMwcuG2NMM 16 | iXXpwjy6hD5DuXVHAmJ9i45ciDR+WVfFsVp3SvUWB6Pq0CraAE+S5Z8BRyiSmEHR 17 | 4n3NGAnDm7ztFKBIOa+mca1hTN7ggUQ+AkknOd90defpDcoGl2250sgwxZacS1A3 18 | XIZT4X7/cOrxlfpfxkFUNFW/yVGKhvhGxq6BcEBmrxIpKmDm7Fu7tfdHRgIUJgS4 19 | GiwEtZtv2O0Jh/DHgJ/2nc0= 20 | -----END DSA PRIVATE KEY----- 21 | -------------------------------------------------------------------------------- /spec/fixtures/keys/rsa.key: -------------------------------------------------------------------------------- 1 | -----BEGIN RSA PRIVATE KEY----- 2 | MIIEowIBAAKCAQEA3f3Bou3FMmtUGzCgmSsNAg+qZspDR1b/Ym9V+cfqHup4yzJf 3 | YGysS7ISiHfeny+AoMxeIycFMwfu28zKBo90wxNe9AMSFUEcwzUCSH7ZgErEZS+n 4 | siXIV3+ZfDOl3/dZ2Nu8pzBBtoRIga9QKlfzTbudiysxjAQF3dk37lZ+vAJt3sJh 5 | ThcGZy1yqYPvN6NQw65hUuqm+zvRzgk+ZIDXs6cZXzZX+LjWRKZivbeNQDiQF4Z2 6 | Qsf0GSFV4hb5ASLtdl8FFdV7wEda7XTsHrxOHYgNnlhtKgflGDwC0Q2Ll92b8xY6 7 | vWL4KmZFPm1drHlSr6Ssv+XjceZArz4cii5SsQIDAQABAoIBAA1vE87GUM6yTA6x 8 | LKbYQhtD024RA21HyICueCd7f6GLUvAJBliRewPBrPlxN5muiDwy4eeCrv/dlQjc 9 | JcWr3McJXw+pN+jtXc46YP7+Hi9zVttAvQc83hjdce5gOcOJ9nf3JD2B4BgpfTHL 10 | 6DAHzoN/vRL09DSi8a/xOz9DILxD1H2rz7U4932VpMfFJvGvg5Cnh2pXg6mBo4IB 11 | t5JHHWm8wPoQzhVqo4AXltc3J8oi5nWC5gCgnx2dOt9OBBtrRLvofo4SYOs5ZX4N 12 | ZjPm9zgrzL0JDpVWE1ZOGK6eC8Nd2k0hTN85XmzZR7bWvblq9/nIYbkPm88GkDvi 13 | YRW2jzkCgYEA+GmrmHQqua471KNk9qLFww5wfJHyBfoD5XVp0xnPb2+9gC2si8+W 14 | 6wLF01zU1fGB4rHXrlDLM0gjk93LBCLLK11tZuYDDAjAoWgBalcmVINiHowLKfUA 15 | goJb2styB3Oy+uVwPXw2fCa4m7jwB1mfghmOAI2NBgC4vW2c4LfoJy8CgYEA5MV/ 16 | Q69oDtlnM45zverQkCZjtu69oZWYrkX0Iloj78MxEn36XFkcLfQXG6fCopGKneED 17 | HOwCMR2nGifexzRcp21sXA1r/46VAhwM260ssDOUQN2DoxngOaMUgZHCm6zGuq4k 18 | YBv6atSTrWL9wLCH4Iu82lIE5UauRULAirGirB8CgYB3zcbDPOtKJ7yVPcQ4qqIE 19 | A5e+jEiUF1L/vBOwitcfIF3dTqnTt+dKKkksOa4wDEye/zRMzoxhjB4UdzgtI3am 20 | 0GUt5ip8CFCWx2k/Vw6WXdLZdqL/tjhruOEB8XJqI6OjGzp5fMluMOKBv7yLXu7t 21 | uNNicuIbjgeK3C13t1pQYwKBgF7ymR8+bbG8CVkchm0MSAOL61bBP3MQ441w+yJG 22 | xyN7n6JNJtutIETySaTwu5c9UIq2GNSoH0DDTddNNOj4knvqrk6GQ4yNYMq/WZbT 23 | CPkYby98wGWxF57UUG52UI7L5q8UF4TMF+K+14veyfJvUfWgCYGTCPUfoLSeP8tQ 24 | bnOBAoGBAO2uosK4fpL/aasgNdnQl6C0LxYdexWUkv5Qmhyqh24mPNIPmqaQeFVm 25 | OIzf/MfUBz1y7th/37hjICUVUuCk1siXltlflbw1q2AJMT3ZHxCpBwQyKjO2QWqM 26 | yWRMZH2Bjn0P7+pxVfKX5bfItH3rBFyQmkAi73KB3hf4amT0q3Ku 27 | -----END RSA PRIVATE KEY----- 28 | -------------------------------------------------------------------------------- /spec/models/openssl-extensions/pkey/pkey_spec.rb: -------------------------------------------------------------------------------- 1 | require 'spec_helper' 2 | 3 | describe OpenSSLExtensions::PKey::PKey do 4 | subject { extended_pkeys('rsa') } 5 | 6 | context '==' do 7 | it 'is true for matching keys' do 8 | subject.should == extended_pkeys('rsa') 9 | end 10 | 11 | it 'is false for mismatched keys' do 12 | subject.should_not == extended_pkeys('dsa') 13 | end 14 | end 15 | 16 | context 'eql?' do 17 | it 'is true for matching keys' do 18 | subject.should be_eql extended_pkeys('rsa') 19 | end 20 | 21 | it 'is false for mismatched keys' do 22 | subject.should_not be_eql extended_pkeys('dsa') 23 | end 24 | end 25 | 26 | context 'hash' do 27 | it 'returns the hash of the PEM string' do 28 | subject.stub!(:to_pem => mock('PEM', :hash => 'test')) 29 | subject.hash 30 | end 31 | end 32 | end 33 | -------------------------------------------------------------------------------- /spec/models/openssl-extensions/ssl/ssl_socket_spec.rb: -------------------------------------------------------------------------------- 1 | require 'spec_helper' 2 | 3 | describe OpenSSLExtensions::SSL::SSLSocket do 4 | context 'peer_cert_chain' do 5 | it 'delegates to OpenSSLExtensions::X509::CertificateChain' do 6 | pending 'Figure out how to stub the IO required for SSLSocket without using an actual File or TCPSocket.' 7 | OpenSSLExtensions::X509::CertificateChain. 8 | should_receive(:new). 9 | with(an_instance_of(OpenSSL::X509::Certificate), 10 | an_instance_of(Array)). 11 | once. 12 | and_return([]) 13 | subject.peer_cert_chain 14 | end 15 | end 16 | end 17 | -------------------------------------------------------------------------------- /spec/models/openssl-extensions/x509/authority_key_identifier_spec.rb: -------------------------------------------------------------------------------- 1 | require 'spec_helper' 2 | 3 | describe OpenSSLExtensions::X509::AuthorityKeyIdentifier do 4 | context 'using a V1 identifier' do 5 | subject { OpenSSLExtensions::X509::AuthorityKeyIdentifier.new('DirName:/C=HK/O=Hongkong Post/CN=Hongkong Post Root CA 1, serial:03:ED') } 6 | 7 | its(:issuer_name) { should == 'Hongkong Post Root CA 1' } 8 | its(:serial_number) { should == '03:ED' } 9 | its(:serial) { should == '03:ED' } 10 | end 11 | 12 | context 'using a V3 identifier' do 13 | subject { OpenSSLExtensions::X509::AuthorityKeyIdentifier.new("keyid:28:C4:EB:8F:F1:5F:79:90:A3:2B:55:C3:56:4E:7D:6B:53:72:2C:18\n") } 14 | 15 | its(:key_id) { should == '28:C4:EB:8F:F1:5F:79:90:A3:2B:55:C3:56:4E:7D:6B:53:72:2C:18' } 16 | end 17 | end 18 | -------------------------------------------------------------------------------- /spec/models/openssl-extensions/x509/certificate_chain_spec.rb: -------------------------------------------------------------------------------- 1 | require 'spec_helper' 2 | 3 | describe OpenSSLExtensions::X509::CertificateChain do 4 | context 'with SSL V3 certificates' do 5 | subject do 6 | OpenSSLExtensions::X509::CertificateChain. 7 | new(ssl_certificates('www.geocerts.com'), [ssl_certificates('www.geocerts.com'), 8 | ssl_certificates('GeoTrust Primary Certification Authority'), 9 | ssl_certificates('GeoTrust Extended Validation SSL CA') ]) 10 | end 11 | 12 | it 'is the correct size' do 13 | subject.size.should == 3 14 | end 15 | 16 | it 'reports itself as an Array' do 17 | subject.class.should == Array 18 | end 19 | 20 | it 'is in the correct order' do 21 | subject.should == [ssl_certificates('www.geocerts.com'), 22 | ssl_certificates('GeoTrust Extended Validation SSL CA'), 23 | ssl_certificates('GeoTrust Primary Certification Authority')] 24 | end 25 | end 26 | 27 | context 'with SSL V1 certificates' do 28 | subject do 29 | OpenSSLExtensions::X509::CertificateChain. 30 | new(ssl_certificates('app1.hongkongpost.com'), [ssl_certificates('app1.hongkongpost.com'), 31 | ssl_certificates('Hongkong Post e-Cert CA'), 32 | ssl_certificates('Hongkong Post Root CA'), 33 | ssl_certificates('Hongkong Post e-Cert CA 1'), 34 | ssl_certificates('Hongkong Post Root CA 1') ]) 35 | end 36 | 37 | it 'filters out unlinked certificates' do 38 | subject.should_not include(ssl_certificates('Hongkong Post e-Cert CA')) 39 | subject.should_not include(ssl_certificates('Hongkong Post Root CA')) 40 | end 41 | 42 | it 'includes chained certificates' do 43 | subject.should include(ssl_certificates('app1.hongkongpost.com')) 44 | subject.should include(ssl_certificates('Hongkong Post e-Cert CA 1')) 45 | subject.should include(ssl_certificates('Hongkong Post Root CA 1')) 46 | end 47 | 48 | it 'is in the correct order' do 49 | subject.should == [ssl_certificates('app1.hongkongpost.com'), 50 | ssl_certificates('Hongkong Post e-Cert CA 1'), 51 | ssl_certificates('Hongkong Post Root CA 1')] 52 | end 53 | end 54 | end 55 | -------------------------------------------------------------------------------- /spec/models/openssl-extensions/x509/certificate_spec.rb: -------------------------------------------------------------------------------- 1 | require 'spec_helper' 2 | 3 | describe OpenSSLExtensions::X509::Certificate do 4 | let(:certificate) { extended_ssl_certificates('www.geocerts.com') } 5 | subject { certificate } 6 | 7 | its(:subject_alternative_names) { should == %w(www.geocerts.com geocerts.com) } 8 | its(:subject_key_identifier) { should be_nil } 9 | its(:authority_key_identifier) { should be_kind_of(OpenSSLExtensions::X509::AuthorityKeyIdentifier) } 10 | its(:ssl_version) { should == 3 } 11 | 12 | context 'strength' do 13 | subject { certificate.strength } 14 | 15 | context 'for a 2048 bit certificate' do 16 | it { should == 2048 } 17 | end 18 | 19 | context 'for a 1024 bit RSA-signed certificate' do 20 | let(:certificate) { extended_ssl_certificates('www.twongo.com') } 21 | 22 | it { should == 1024 } 23 | end 24 | 25 | context 'for a 1024 bit DSA-signed certificate' do 26 | let(:certificate) { extended_ssl_certificates('bgthelpdesk.braxtongrant.com') } 27 | 28 | it { should == 1024 } 29 | end 30 | end 31 | 32 | context 'allows_certificate_signing?' do 33 | subject { certificate.allows_certificate_signing? } 34 | 35 | context 'for V3' do 36 | context 'for a root certificate' do 37 | let(:certificate) { extended_ssl_certificates('GeoTrust Primary Certification Authority') } 38 | it { should be_true } 39 | end 40 | 41 | context 'for a site certificate' do 42 | it { should be_false } 43 | end 44 | end 45 | 46 | context 'for V1' do 47 | context 'for a root certificate' do 48 | let(:certificate) { extended_ssl_certificates('HongKong Post Root CA 1') } 49 | it { should be_true } 50 | end 51 | 52 | context 'for a site certificate' do 53 | let(:certificate) { extended_ssl_certificates('app1.hongkongpost.com') } 54 | it { should be_false } 55 | end 56 | end 57 | end 58 | 59 | context 'issuing_certificate?' do 60 | context 'for V3' do 61 | it 'is true when passing the issuing certificate' do 62 | extended_ssl_certificates('www.geocerts.com'). 63 | issuing_certificate?(extended_ssl_certificates('GeoTrust Extended Validation SSL CA')).should be_true 64 | end 65 | 66 | it 'is false when passing the distant root certificate' do 67 | extended_ssl_certificates('www.geocerts.com'). 68 | issuing_certificate?(extended_ssl_certificates('GeoTrust Primary Certification Authority')).should be_false 69 | end 70 | 71 | it 'is false when passing a different site certificate' do 72 | extended_ssl_certificates('www.geocerts.com'). 73 | issuing_certificate?(extended_ssl_certificates('www.twongo.com')) 74 | end 75 | end 76 | end 77 | 78 | context 'equality (==)' do 79 | it 'is true with matching PEMs' do 80 | ssl_certificates('www.geocerts.com'). 81 | should == ssl_certificates('www.geocerts.com') 82 | end 83 | 84 | it 'is false with mismatched PEMs' do 85 | certificate = ssl_certificates('www.geocerts.com') 86 | certificate.should_receive(:to_pem).and_return('DIFFERENTPEM') 87 | ssl_certificates('www.geocerts.com').should_not == certificate 88 | end 89 | end 90 | 91 | context 'in a collection, uniq' do 92 | it 'removes duplicate certificates' do 93 | [ssl_certificates('www.geocerts.com'), 94 | ssl_certificates('www.geocerts.com')].uniq.should == 95 | [ssl_certificates('www.geocerts.com')] 96 | end 97 | 98 | it 'does not modify non-duplicates' do 99 | [ssl_certificates('www.geocerts.com'), 100 | ssl_certificates('GeoTrust Extended Validation SSL CA')].uniq.should == 101 | [ssl_certificates('www.geocerts.com'), 102 | ssl_certificates('GeoTrust Extended Validation SSL CA')] 103 | end 104 | end 105 | 106 | context 'subject_key_identifier' do 107 | subject { ssl_certificates('GeoTrust Extended Validation SSL CA').extend(OpenSSLExtensions::X509::Certificate).subject_key_identifier } 108 | 109 | it { should == '28:C4:EB:8F:F1:5F:79:90:A3:2B:55:C3:56:4E:7D:6B:53:72:2C:18' } 110 | end 111 | 112 | context 'root?' do 113 | it 'is false for a certificate with a separate issuer' do 114 | extended_ssl_certificates('www.geocerts.com').should_not be_root 115 | end 116 | 117 | it 'is true for a certificate which is its own issuer' do 118 | extended_ssl_certificates('equifax-secure-ca').should be_root 119 | end 120 | 121 | it 'is true for a certificate with a matching subject and issuer, subject identifier given, but no authority identifier provided' do 122 | extended_ssl_certificates('globalsign-root-ca').should be_root 123 | end 124 | end 125 | 126 | context 'serial' do 127 | context 'to_hex' do 128 | let(:certificate) { extended_ssl_certificates('GeoTrust Extended Validation SSL CA') } 129 | subject { certificate.serial.to_hex } 130 | 131 | it 'returns the base 16 (hex) format of the serial number' do 132 | should eq('6948A26B201AA421E898B1C492C7C58E') 133 | end 134 | end 135 | end 136 | 137 | context 'crl_distribution_points' do 138 | subject { certificate.crl_distribution_points } 139 | 140 | it { should be_a String } 141 | it { should include "URI:http://EVSSL-crl.geotrust.com/crls/gtextvalca.crl" } 142 | end 143 | 144 | context 'authority_info_access' do 145 | subject { certificate.authority_info_access } 146 | 147 | it { should be_a String } 148 | it { should include "OCSP - URI:http://EVSSL-ocsp.geotrust.com" } 149 | it { should include "CA Issuers - URI:http://EVSSL-aia.geotrust.com/evca.crt" } 150 | end 151 | end 152 | -------------------------------------------------------------------------------- /spec/models/openssl-extensions/x509/name_spec.rb: -------------------------------------------------------------------------------- 1 | require 'spec_helper' 2 | 3 | describe OpenSSLExtensions::X509::Name do 4 | subject { ssl_certificates('www.geocerts.com').subject.extend(OpenSSLExtensions::X509::Name) } 5 | 6 | its(:organization) { should == 'GeoCerts Inc' } 7 | its(:organizational_unit) { should == 'SSL Sales' } 8 | its(:common_name) { should == 'www.geocerts.com' } 9 | its(:country) { should == 'US' } 10 | its(:locality) { should == 'Atlanta' } 11 | its(:state) { should == 'Georgia' } 12 | its(:region) { should == 'Georgia' } 13 | 14 | context 'with an email address' do 15 | subject { certificate_request('envylabs').subject.extend(OpenSSLExtensions::X509::Name) } 16 | its(:email) { should == 'support@envylabs.com' } 17 | end 18 | 19 | its(:location) { should == 'Atlanta, Georgia, US' } 20 | end 21 | -------------------------------------------------------------------------------- /spec/models/openssl-extensions/x509/request_spec.rb: -------------------------------------------------------------------------------- 1 | require 'spec_helper' 2 | 3 | describe OpenSSLExtensions::X509::Request do 4 | subject { extended_certificate_request('geocerts') } 5 | 6 | context 'subject_alternative_names' do 7 | context 'on a CSR with SANs' do 8 | subject { extended_certificate_request('sans') } 9 | it 'returns a collection of the alternative names' do 10 | subject.subject_alternative_names.should == 11 | ['mail.sipchem.local', 12 | 'mail.sipchem.com', 13 | 'sipchem.com', 14 | 'autodiscover.sipchem.local', 15 | 'autodiscover.sipchem.com', 16 | 'sipc-cas01', 17 | 'sipc-cas02', 18 | 'sipchem.local' ] 19 | end 20 | end 21 | 22 | context 'on a CSR without SANs' do 23 | it 'returns an empty collection' do 24 | subject.subject_alternative_names.should == [] 25 | end 26 | end 27 | end 28 | 29 | context 'challenge_password?' do 30 | context 'on a CSR with a challenge password' do 31 | subject { extended_certificate_request('challenge') } 32 | its(:challenge_password?) { should be_true } 33 | end 34 | 35 | context 'on a CSR without a challenge password' do 36 | its(:challenge_password?) { should be_false } 37 | end 38 | end 39 | 40 | context 'strength' do 41 | it 'is 2048 bits' do 42 | subject.strength.should == 2048 43 | end 44 | 45 | it 'is 1024 bits' do 46 | extended_certificate_request('1024').strength.should == 1024 47 | end 48 | end 49 | 50 | context 'equality (==)' do 51 | it 'is true with matching PEMs' do 52 | extended_certificate_request('geocerts').should == 53 | extended_certificate_request('geocerts') 54 | end 55 | 56 | it 'is false with mismatched PEMs' do 57 | certificate = extended_certificate_request('geocerts') 58 | certificate.should_receive(:to_pem).and_return('DIFFERENTPEM') 59 | extended_certificate_request('geocerts').should_not == certificate 60 | end 61 | end 62 | 63 | context 'in a collection, uniq' do 64 | it 'removes duplicate certificates' do 65 | [extended_certificate_request('geocerts'), 66 | extended_certificate_request('geocerts')].uniq.should == 67 | [extended_certificate_request('geocerts')] 68 | end 69 | 70 | it 'does not modify non-duplicates' do 71 | [extended_certificate_request('geocerts'), 72 | extended_certificate_request('1024')].uniq.should == 73 | [extended_certificate_request('geocerts'), 74 | extended_certificate_request('1024')] 75 | end 76 | end 77 | end 78 | -------------------------------------------------------------------------------- /spec/models/openssl-extensions_spec.rb: -------------------------------------------------------------------------------- 1 | require 'spec_helper' 2 | 3 | describe OpenSSLExtensions do 4 | context 'check_dependencies!' do 5 | context 'with OpenSSL extensions installed' do 6 | before(:each) do 7 | OpenSSLExtensions.should_receive(:require).with('openssl').and_return(true) 8 | end 9 | 10 | it 'does not exit' do 11 | OpenSSLExtensions.should_receive(:exit).never 12 | OpenSSLExtensions.check_dependencies! 13 | end 14 | 15 | it 'does not write to STDERR' do 16 | $stderr.should_receive(:puts).never 17 | OpenSSLExtensions.check_dependencies! 18 | end 19 | end 20 | 21 | context 'without OpenSSL extensions installed' do 22 | before(:each) do 23 | OpenSSLExtensions.should_receive(:require).with('openssl').and_raise(LoadError) 24 | 25 | $stderr.stub!(:puts) 26 | OpenSSLExtensions.stub!(:exit) 27 | end 28 | 29 | it 'write a message on STDERR' do 30 | $stderr.should_receive(:puts).with("OpenSSLExtensions requires Ruby to be compiled with OpenSSL support.") 31 | OpenSSLExtensions.check_dependencies! 32 | end 33 | 34 | it 'exits with error' do 35 | OpenSSLExtensions.should_receive(:exit).with(1) 36 | OpenSSLExtensions.check_dependencies! 37 | end 38 | end 39 | end 40 | end 41 | -------------------------------------------------------------------------------- /spec/requests/openssl/bn_spec.rb: -------------------------------------------------------------------------------- 1 | require 'spec_helper' 2 | 3 | describe OpenSSL::BN do 4 | subject { ssl_certificates('www.geocerts.com').serial } 5 | 6 | it "includes the OpenSSLExtensions::BN extensions" do 7 | subject.should be_kind_of OpenSSLExtensions::BN 8 | end 9 | end 10 | -------------------------------------------------------------------------------- /spec/requests/openssl/pkey/pkey_spec.rb: -------------------------------------------------------------------------------- 1 | require 'spec_helper' 2 | 3 | describe OpenSSL::PKey::PKey do 4 | subject { pkeys('rsa') } 5 | 6 | it 'includes the OpenSSLExtensions::PKey::PKey extensions' do 7 | subject.should be_kind_of OpenSSLExtensions::PKey::PKey 8 | end 9 | end 10 | -------------------------------------------------------------------------------- /spec/requests/openssl/ssl/ssl_socket_spec.rb: -------------------------------------------------------------------------------- 1 | require 'spec_helper' 2 | 3 | describe OpenSSL::SSL::SSLSocket do 4 | it 'carries the OpenSSLExtensions::SSL::SSLSocket extensions' do 5 | OpenSSL::SSL::SSLSocket.ancestors.should include(OpenSSLExtensions::SSL::SSLSocket) 6 | end 7 | end 8 | -------------------------------------------------------------------------------- /spec/requests/openssl/x509/certificate_spec.rb: -------------------------------------------------------------------------------- 1 | require 'spec_helper' 2 | 3 | describe OpenSSL::X509::Certificate do 4 | subject { ssl_certificates('www.geocerts.com') } 5 | 6 | it "includes the OpenSSLExtensions::X509::Certificate extensions" do 7 | subject.should be_kind_of OpenSSLExtensions::X509::Certificate 8 | end 9 | end 10 | -------------------------------------------------------------------------------- /spec/requests/openssl/x509/name_spec.rb: -------------------------------------------------------------------------------- 1 | require 'spec_helper' 2 | 3 | describe OpenSSL::X509::Name do 4 | subject { ssl_certificates('www.geocerts.com').issuer } 5 | 6 | it "includes the OpenSSLExtensions::X509::Name extensions" do 7 | subject.should be_kind_of OpenSSLExtensions::X509::Name 8 | end 9 | end 10 | -------------------------------------------------------------------------------- /spec/requests/openssl/x509/request_spec.rb: -------------------------------------------------------------------------------- 1 | require 'spec_helper' 2 | 3 | describe OpenSSL::X509::Request do 4 | subject { certificate_request('geocerts') } 5 | 6 | it "includes the OpenSSLExtensions::X509::Request extensions" do 7 | subject.should be_kind_of OpenSSLExtensions::X509::Request 8 | end 9 | end 10 | 11 | -------------------------------------------------------------------------------- /spec/spec_helper.rb: -------------------------------------------------------------------------------- 1 | # encoding: utf-8 2 | lib = File.expand_path('../../lib', __FILE__) 3 | $:.unshift lib unless $:.include?(lib) 4 | 5 | begin 6 | require 'openssl' 7 | rescue LoadError 8 | $stderr.puts "OpenSSLExtensions requires Ruby to be compiled with OpenSSL support" 9 | exit(1) 10 | end 11 | 12 | require 'rubygems' 13 | require 'bundler' 14 | 15 | Bundler.setup 16 | Bundler.require :default, :test 17 | 18 | require 'openssl-extensions/all' 19 | 20 | Dir.glob(File.join(File.dirname(__FILE__), 'support/**/*.rb')).each do |f| 21 | require f 22 | end 23 | -------------------------------------------------------------------------------- /spec/support/certificate_request_fixtures.rb: -------------------------------------------------------------------------------- 1 | module CertificateRequestFixtures 2 | 3 | ## 4 | # Returns an OpenSSL::X509::Request without explicit extensions. 5 | # 6 | def certificate_request(name) 7 | name = name.to_s.downcase.gsub(/[^\w\.]/, '-') 8 | @_certificate_requests ||= {} 9 | return @_certificate_requests[name].dup if @_certificate_requests.has_key?(name) 10 | 11 | request_path = File.expand_path("../../fixtures/certificate_requests/#{name}.csr", __FILE__) 12 | @_certificate_requests[name] = File.exist?(request_path) ? 13 | OpenSSL::X509::Request.new(File.read(request_path)) : 14 | nil 15 | end 16 | 17 | ## 18 | # Returns an OpenSSL::X509::Request explicitly extended with OpenSSLExtensions::X509::Request. 19 | # 20 | def extended_certificate_request(name) 21 | certificate_request(name).extend(OpenSSLExtensions::X509::Request) 22 | end 23 | 24 | end 25 | 26 | RSpec.configure do |config| 27 | config.include CertificateRequestFixtures 28 | end 29 | 30 | -------------------------------------------------------------------------------- /spec/support/pkey_fixtures.rb: -------------------------------------------------------------------------------- 1 | module PKeyFixtures 2 | ## 3 | # Returns an OpenSSL::X509::Certificate without explicit extensions. 4 | # 5 | def pkeys(name) 6 | name = name.to_s.downcase.gsub(/[^\w\.]/, '-') 7 | pkey_path = File.expand_path("../../fixtures/keys/#{name}.key", __FILE__) 8 | key = nil 9 | 10 | if File.exist?(pkey_path) && File.readable?(pkey_path) 11 | data = File.read(pkey_path) 12 | key = [ 13 | OpenSSL::PKey::RSA, 14 | OpenSSL::PKey::DSA, 15 | OpenSSL::PKey::EC, 16 | OpenSSL::PKey::DH 17 | ].collect { |klass| instantiate_pkey(data, klass) }. 18 | detect { |key| !key.nil? } 19 | data = nil 20 | end 21 | 22 | key 23 | end 24 | 25 | ## 26 | # Returns an OpenSSL::PKey explicitly extended with 27 | # OpenSSLExtensions::PKey::PKey. 28 | # 29 | def extended_pkeys(name) 30 | pkeys(name).extend(OpenSSLExtensions::PKey::PKey) 31 | end 32 | 33 | 34 | protected 35 | 36 | 37 | def instantiate_pkey(data, klass) 38 | klass.new(data) 39 | rescue OpenSSL::PKey::PKeyError 40 | nil 41 | end 42 | end 43 | 44 | RSpec.configure do |config| 45 | config.include PKeyFixtures 46 | end 47 | -------------------------------------------------------------------------------- /spec/support/ssl_certificate_fixtures.rb: -------------------------------------------------------------------------------- 1 | module SslCertificateFixtures 2 | 3 | ## 4 | # Returns an OpenSSL::X509::Certificate without explicit extensions. 5 | # 6 | def ssl_certificates(name) 7 | name = name.to_s.downcase.gsub(/[^\w\.]/, '-') 8 | @_ssl_certificates ||= {} 9 | return @_ssl_certificates[name].dup if @_ssl_certificates.has_key?(name) 10 | 11 | certificate_path = File.expand_path("../../fixtures/certificates/#{name}.pem", __FILE__) 12 | @_ssl_certificates[name] = File.exist?(certificate_path) ? 13 | OpenSSL::X509::Certificate.new(File.read(certificate_path)) : 14 | nil 15 | end 16 | 17 | ## 18 | # Returns an OpenSSL::X509::Certificate explicitly extended with OpenSSLExtensions::X509::Certificate. 19 | # 20 | def extended_ssl_certificates(name) 21 | ssl_certificates(name).extend(OpenSSLExtensions::X509::Certificate) 22 | end 23 | 24 | end 25 | 26 | RSpec.configure do |config| 27 | config.include SslCertificateFixtures 28 | end 29 | --------------------------------------------------------------------------------