pax_global_header00006660000000000000000000000064142573545360014530gustar00rootroot0000000000000052 comment=2e9e607397fe960dd8e17f7d7200281d3b0346b0 certificate_authority-1.1.0/000077500000000000000000000000001425735453600161215ustar00rootroot00000000000000certificate_authority-1.1.0/.github/000077500000000000000000000000001425735453600174615ustar00rootroot00000000000000certificate_authority-1.1.0/.github/workflows/000077500000000000000000000000001425735453600215165ustar00rootroot00000000000000certificate_authority-1.1.0/.github/workflows/ci.yml000066400000000000000000000010031425735453600226260ustar00rootroot00000000000000name: Tests on: [push, pull_request] jobs: test: runs-on: ubuntu-latest strategy: matrix: ruby-version: - '3.1' - '3.0' - '2.7' - '2.6' - '2.5' steps: - uses: actions/checkout@v2 - name: Set up Ruby ${{ matrix.ruby-version }} uses: ruby/setup-ruby@v1 with: ruby-version: ${{ matrix.ruby-version }} bundler-cache: true # 'bundle install' and cache - name: Run tests run: bundle exec rake certificate_authority-1.1.0/.gitignore000066400000000000000000000000541425735453600201100ustar00rootroot00000000000000pkg development .bundle .rvmrc coverage doc certificate_authority-1.1.0/.rspec000066400000000000000000000000651425735453600172370ustar00rootroot00000000000000--require spec_helper --color --format documentation certificate_authority-1.1.0/Gemfile000066400000000000000000000000471425735453600174150ustar00rootroot00000000000000source 'https://rubygems.org' gemspec certificate_authority-1.1.0/Gemfile.lock000066400000000000000000000031531425735453600203450ustar00rootroot00000000000000PATH remote: . specs: certificate_authority (1.1.0) GEM remote: https://rubygems.org/ specs: ast (2.4.0) coderay (1.1.2) coveralls (0.8.23) json (>= 1.8, < 3) simplecov (~> 0.16.1) term-ansicolor (~> 1.3) thor (>= 0.19.4, < 2.0) tins (~> 1.6) diff-lcs (1.3) docile (1.3.2) json (2.3.0) method_source (1.0.0) parallel (1.19.1) parser (2.7.1.3) ast (~> 2.4.0) pry (0.13.1) coderay (~> 1.1) method_source (~> 1.0) rainbow (3.0.0) rake (13.0.1) rexml (3.2.5) rspec (3.9.0) rspec-core (~> 3.9.0) rspec-expectations (~> 3.9.0) rspec-mocks (~> 3.9.0) rspec-core (3.9.2) rspec-support (~> 3.9.3) rspec-expectations (3.9.2) diff-lcs (>= 1.2.0, < 2.0) rspec-support (~> 3.9.0) rspec-mocks (3.9.1) diff-lcs (>= 1.2.0, < 2.0) rspec-support (~> 3.9.0) rspec-support (3.9.3) rubocop (0.84.0) parallel (~> 1.10) parser (>= 2.7.0.1) rainbow (>= 2.2.2, < 4.0) rexml rubocop-ast (>= 0.0.3) ruby-progressbar (~> 1.7) unicode-display_width (>= 1.4.0, < 2.0) rubocop-ast (0.0.3) parser (>= 2.7.0.1) ruby-progressbar (1.10.1) simplecov (0.16.1) docile (~> 1.1) json (>= 1.8, < 3) simplecov-html (~> 0.10.0) simplecov-html (0.10.2) sync (0.5.0) term-ansicolor (1.7.1) tins (~> 1.0) thor (1.0.1) tins (1.25.0) sync unicode-display_width (1.7.0) PLATFORMS ruby DEPENDENCIES certificate_authority! coveralls pry rake rspec rubocop BUNDLED WITH 2.1.4 certificate_authority-1.1.0/README.rdoc000066400000000000000000000466451425735453600177460ustar00rootroot00000000000000= CertificateAuthority - Because it shouldn't be this damned complicated {Build Status}[https://travis-ci.org/cchandler/certificate_authority] {Code Climate}[https://codeclimate.com/github/cchandler/certificate_authority] {Code Coverage}[https://coveralls.io/r/cchandler/certificate_authority] This is meant to provide a (more) programmer-friendly implementation of all the basic functionality contained in RFC-3280 to implement your own certificate authority. You can generate root certificates, intermediate certificates, and terminal certificates. You can also generate/manage Certificate Revocation Lists (CRLs) and Online Certificate Status Protocol (OCSP) messages. Because this library is built using the native Ruby bindings for OpenSSL it also supports PKCS#11 cryptographic hardware for secure maintenance of private key materials. = So you want to maintain a certificate authority root Let's suppose hypothetically you want to be able to issue and manage your own certificates. This section is meant to outline the basic functions you'll need(optionally want) to support. Not everyone is out to be in total compliance with WebTrust[link:http://www.webtrust.org/] or {Mozilla's rules for CA inclusion}[link:https://wiki.mozilla.org/CA:How_to_apply]. The three primary elements to be aware of are: [Certificate Authority] These are the functions primarily related to the signing, issuance, and revocation of certificates. [Registration Authority] These are the functions primarily related to registering and requesting certificates and vetting of the entities requesting certification. [Validation Authority] These are the functions related to verifying the status of certificates out in the wild. Mostly CRLs and OCSP related functions. = Establishing a new root in software Let's look at a complete example for generating a new root certificate. Assuming that you don't have a PKCS#11 hardware token available (lists coming...) we'll have to store this safe. Generating a self-signed root certificate is fairly easy: require 'certificate_authority' root = CertificateAuthority::Certificate.new root.subject.common_name= "http://mydomain.com" root.serial_number.number=1 root.key_material.generate_key root.signing_entity = true signing_profile = {"extensions" => {"keyUsage" => {"usage" => ["critical", "keyCertSign"] }} } root.sign!(signing_profile) The required elements for the gem at this time are a common name for the subject and a serial number for the certificate. Since this is our self-signed root we're going to give it the first serial available of 1. Because certificate_authority is not designed to manage the issuance lifecycle you'll be expected to store serial numbers yourself. Next, after taking care of required fields, we will require key material for the new certificate. There's a convenience method made available on the key_material object for generating new keys. The private key will be available in: root.key_material.private_key and the public key: root.key_material.public_key Make sure to save the private key somewhere safe! Lastly, we declare that the certificate we're about to sign is itself a signing entity so we can continue on and sign other certificates. == Creating a new intermediate Maybe you don't want to actually sign certificates with your super-secret root certificate. This is actually how a good number of most public certificate authorities do it. Rather than sign with the primary root, they generate an intermediate root that is then responsible for signing the final certificates. If you wanted to create an intermediate root certificate you would do something like the following: intermediate = CertificateAuthority::Certificate.new intermediate.subject.common_name= "My snazzy intermediate!" intermediate.serial_number.number=2 intermediate.key_material.generate_key intermediate.signing_entity = true intermediate.parent = root signing_profile = {"extensions" => {"keyUsage" => {"usage" => ["critical", "keyCertSign"] }} } intermediate.sign!(signing_profile) All we have to do is create another certificate like we did with the root. In this example we gave it the next available serial number, which for us, was 2. We then generate (and save!) key material for this new entity. Even the +signing_entity+ is set to true so this certificate can sign other certificates. The difference here is that the +parent+ field is set to the root. Going forward, whatever entity you want to sign a certificate, you set that entity to be the parent. In this case, our root will be responsible for signing this intermediate when we call +sign!+. = Creating new certificates (in general) Now that we have a root certificate (and possibly an intermediate) we can sign end-user certificates. It is, perhaps unsurprisingly, similar to all the others: plain_cert = CertificateAuthority::Certificate.new plain_cert.subject.common_name= "http://mydomain.com" plain_cert.serial_number.number=4 plain_cert.key_material.generate_key plain_cert.parent = root # or intermediate plain_cert.sign! That's all there is to it! In this example we generate the key material ourselves, but it's possible for the end-user to generate certificate signing request (CSR) that we can then parse and consume automatically (coming soon). To get the PEM formatted certificate for the user you would need to call: plain_cert.to_pem to get the certificate body. = Signing Profiles Creating basic certificates is all well and good, but maybe you want _more_ signing control. +certificate_authority+ supports the idea of signing profiles. These are hashes containing values that +sign!+ will use to merge in additional control options for setting extensions on the certificate. Here's an example of a full signing profile for most of the common V3 extensions: signing_profile = { "extensions" => { "basicConstraints" => {"ca" => false}, "crlDistributionPoints" => {"uri" => "http://notme.com/other.crl" }, "subjectKeyIdentifier" => {}, "authorityKeyIdentifier" => {}, "authorityInfoAccess" => {"ocsp" => ["http://youFillThisOut/ocsp/"] }, "keyUsage" => {"usage" => ["digitalSignature","nonRepudiation"] }, "extendedKeyUsage" => {"usage" => [ "serverAuth","clientAuth"]}, "subjectAltName" => {"uris" => ["http://subdomains.youFillThisOut/"]}, "certificatePolicies" => { "policy_identifier" => "1.3.5.8", "cps_uris" => ["http://my.host.name/", "http://my.your.name/"], "user_notice" => { "explicit_text" => "Explicit Text Here", "organization" => "Organization name", "notice_numbers" => "1,2,3,4" } } } } Using a signing profile is done this way: certificate.sign!(signing_profile) At that point all the configuration options will be merged into the extensions. == Basic Constraints The basic constraints extension allows you to control whether or not a certificate can sign other certificates. [CA] If this value is true then this certificate has the authority to sign additional certificates. [pathlen] This is the maximum length of the chain-of-trust. For instance, if an intermediate certificate has a pathlen of 1 then it can sign additional certificates, but it cannot create another signing entity because the total chain-of-trust would have a length greater than 1. == CRL Distribution Points This extension controls where a conformant client can go to obtain a list of certificate revocation information. At this point +certificate_authority+ only supports a list of URIs. The formal RFC however provides for the ability to provide a URI and an issuer identifier that allows a different signing entity to generate/sign the CRL. [uri] The URI in subject alternative name format of the URI endpoint. Example: "http://ca.chrischandler.name/some_identifier.crl" == Subject Key Identifier This extension is required to be present, but doesn't offer any configurable parameters. Directly from the RFC: The subject key identifier extension provides a means of identifying certificates that contain a particular public key. To facilitate certification path construction, this extension MUST appear in all conforming CA certificates, that is, all certificates including the basic constraints extension (section 4.2.1.10) where the value of cA is TRUE. The value of the subject key identifier MUST be the value placed in the key identifier field of the Authority Key Identifier extension (section 4.2.1.1) of certificates issued by the subject of this certificate. == Authority Key Identifier Just like the subject key identifier, this is required under most circumstances and doesn't contain any meaningful configuration options. From the RFC: The keyIdentifier field of the authorityKeyIdentifier extension MUST be included in all certificates generated by conforming CAs to facilitate certification path construction. There is one exception; where a CA distributes its public key in the form of a "self-signed" certificate, the authority key identifier MAY be omitted. The signature on a self-signed certificate is generated with the private key associated with the certificate's subject public key. (This proves that the issuer possesses both the public and private keys.) In this case, the subject and authority key identifiers would be identical, but only the subject key identifier is needed for certification path building. == Authority Info Access The authority info access extension allows a CA to sign a certificate with information a client can use to get up-to-the-minute status information on a signed certificate. This takes the form of an OCSP[link:http://en.wikipedia.org/wiki/Online_Certificate_Status_Protocol] (Online Certificate Status Protocol) endpoints. [ocsp] This is an array of URIs specifying possible endpoints that will be able to provide a signed response. +certificate_authority+ has an OCSP message handler for parsing OCSP requests and generating OCSP signed responses. == Key Usage This extension contains a list of the functions this certificate is allowed to participate in. [usage] An array of OIDs in string format. The acceptable values are specified by OpenSSL and are: +digitalSignature+, +nonRepudiation+, +keyEncipherment+, +dataEncipherment+, +keyAgreement+, +keyCertSign+, +cRLSign+, +encipherOnly+ and +decipherOnly+. == Extended Key Usage This one is like key usage, but allows for certain application specific purposes. It's generally only present in end-user certificates. [usage] An array of OIDs in string format. The only ones with practical significance at this point are: +serverAuth+, +clientAuth+, and +codeSigning+. == Subject Alternative Name If the certificate needs to work for multiple domains then you can specify the others for which it is valid in the subject alternative name field. [uris] An array of full URIs for other common names this certificate should be valid for. For instance, if you want http://ca.chrischandler.name and http://www.ca.chrischandler.name to share the same cert you would place both in the +uris+ attribute of the subject alternative name. == Certificate Policies This is one of the most esoteric of the extensions. This allows a conformant certificate authority to embed signing policy information into the certificate body. Public certificate authorities are required to maintain a Certificate Practice Statement in accordance with {RFC 2527}[link:http://www.ietf.org/rfc/rfc2527.txt]. These CPSs define what vetting criteria and maintenance practices are required to issue, maintain, and revoke a certificate. While it might be overkill for private certificates, if you wanted to make an actual public CA you would need to put together a practice statement and embed it in certificates you issue. [policy_identifier] This is an arbitrary OID (that you make up!) that uniquely represents the policy you are enforcing for whatever kind of certificate this is meant to be. [cps_uris] This is an array of URIs where a client or human can go to get information related to your certification practice. [user_notice] This is a nested field containing explicit human readable text if you want to embed a notice in the certificate body related to certification practices. It contains nested attributes of +explicit_text+ for the notice, +organization+ and +notice_numbers+. Refer to the RFC for specific implications of how these are set, but whether or not browsers implement the correct specified behavior for their presence is another issue. = Certificate Signing Requests (CSRs) If you want certificate requestors to be able to request certificates without moving the private key you'll need to generate a CSR and submit it to the certificate authority. Here's an example of using +certificate_authority+ to generate a CSR. csr = CertificateAuthority::SigningRequest.new dn = CertificateAuthority::DistinguishedName.new dn.common_name = "localhost" csr.distinguished_name = dn k = CertificateAuthority::MemoryKeyMaterial.new k.generate_key(2048) csr.key_material = k csr.digest = "SHA256" csr.to_x509_csr.to_pem Similarly, reading a CSR in is as simple as providing the PEM formatted version to +SigningRequest.from_x509_csr+. csr = CertificateAuthority::SigningRequest.from_x509_csr(@pem_csr) Once you have the CSR in the form of a +SigningRequest+ you can transform it to a +Certificate+ with +to_cert+. At this point it works just like any other certificate. You'll have to provide a serial number to actually sign it. = Certificate Revocation Lists (CRLs) Revocation lists let clients know when a certificate in the wild should no longer be trusted. Like end-user certificates, CRLs have to be signed by a signing authority to be valid. Additionally, you will need to furnish a +nextUpdate+ value that indicates to the client when it should look for updates to the CRL and how long it should consider a cached value valid. Ideally you would place the result CRL somewhere generally accessible on the Internet and reference the URI in the +crlDistributionPoints+ extension on issued certificates. crl = CertificateAuthority::CertificateRevocationList.new crl << certificate # Some CertificateAuthority::Certificate crl << serial_number # Also works with plain CertificateAuthority::SerialNumber crl.parent = root_certificate # A valid root crl.next_update = (60 * 60 * 10) # 10 Hours crl.sign! crl.to_pem = OCSP Support OCSP is the Online Certificate Status Protocol. It provides a mechanism to query an authority to see if a certificate is still valid without downloading an entire CRL. To use this mechanism you provide a URI in the Authority Information Access extension. If a client wishes to check the validity of a certificate they can query this endpoint. This request will only contain serial numbers, so you'll need to uniquely identify your authority in the AIA path. If a client sends you a DER encoded OCSP request you can read it out via +OCSPRequestReader+ ocsp_request_reader = CertificateAuthority::OCSPRequestReader.from_der(@ocsp_request.to_der) ocsp_request_reader.serial_numbers Then, you can construct a response like this response_builder = CertificateAuthority::OCSPResponseBuilder.from_request_reader(ocsp_request_reader) response_builder.parent = root response = response_builder.build_response # Returns OpenSSL::OCSP::Response response.to_der The response builder will copy a (possible) nonce from the request. By default, the +OCSPResponseBuilder+ will say that every certificate is GOOD. You should definitely override this if you plan on revoking certificates. If you want to override this you'll need to supply a proc/lambda that takes a serial number and returns an array of status and reason. response_builder = CertificateAuthority::OCSPResponseBuilder.from_request_reader(ocsp_request_reader) response_builder.verification_mechanism = lambda {|certid| [CertificateAuthority::OCSPResponseBuilder::REVOKED,CertificateAuthority::OCSPResponseBuilder::UNSPECIFIED] } response_builder.parent = root response = response_builder.build_response # Response will say everything is revoked for unspecified reasons Lastly, you can configure a nextUpdate time in the response. This is the length of time for which a client may consider this response valid. The default is 15 minutes. response_builder.next_update = 30 * 60 # 30 minutes = PKCS#11 Support If you happen to have a PKCS#11 compliant hardware token you can use +certificate_authority+ to maintain private key materials in hardware security modules. At this point the scope of operating that hardware is out of scope of this README but it's there and it is supported. To configure a certificate to utilize PKCS#11 instead of in memory keys all you need to do is: root = CertificateAuthority::Certificate.new root.subject.common_name= "http://mydomain.com" root.serial_number.number=1 root.signing_entity = true key_material_in_hardware = CertificateAuthority::Pkcs11KeyMaterial.new key_material_in_hardware.token_id = "46" key_material_in_hardware.pkcs11_lib = "/usr/lib/libeTPkcs11.so" key_material_in_hardware.openssl_pkcs11_engine_lib = "/usr/lib/engines/engine_pkcs11.so" key_material_in_hardware.pin = "11111111" root.key_material = key_material_in_hardware root.sign! Your current version of OpenSSL _must_ include dynamic engine support and you will need to have OpenSSL PKCS#11 engine support. You will also require the actual PKCS#11 driver from the hardware manufacturer. As of today the only tokens I've gotten to work are: [eTokenPro] Released by Aladdin (now SafeNet Inc.). I have only had success with the version 4 and 5 (32 bit only) copy of the driver. The newer authentication client released by SafeNet appears to be completely broken for interacting with the tokens outside of SafeNet's own tools. If anyone has a different experience I'd like to hear from you. [ACS CryptoMate] Also a 32-bit only driver. You'll have to jump through some hoops to get the Linux PKCS#11 driver but it works surprisingly well. It also appears to support symmetric key operations in hardware. [Your company] Do you make a PKCS#11 device? I'd love to get it working but I probably can't afford your device. Get in touch with me and if you're willing to loan me one for a week I can get it listed. Also of note, I have gotten these to work with 32-bit copies of Ubuntu 10.10 and pre-Snow Leopard versions of OS X. If you are running Snow Leopard you're out of luck since none of the companies I've contacted make a 64 bit driver. = Hopefully in the future * More PKCS#11 hardware (I need driver support from the manufacturers) = Todone * Support for working with CSRs to request & issue certificates * OCSP support = Misc notes * Firefox will complain about root/intermediate certificates unless both digitalSignature and keyEncipherment are specified as keyUsage attributes. Thanks diogomonica = Special thanks and Contributions * Diogo Monica @diogo * Justin Cummins @sul3n3t * @databus23 * Colin Jones @trptcolin * Eric Monti @emonti * TJ Vanderpoel @bougyman == Meta Written by Chris Chandler(http://chrischandler.name) Released under the MIT License: http://www.opensource.org/licenses/mit-license.php Main page: http://github.com/cchandler/certificate_authority Issue tracking: https://github.com/cchandler/certificate_authority/issues certificate_authority-1.1.0/Rakefile000066400000000000000000000005411425735453600175660ustar00rootroot00000000000000require "bundler/gem_tasks" require "rspec/core/rake_task" require "rubocop/rake_task" desc "Default: run specs." task default: %i[spec] task :spec do Rake::Task["spec:units"].invoke end namespace :spec do desc "Run unit specs." RSpec::Core::RakeTask.new(:units) do |t| t.rspec_opts = ["--colour --format progress --tag ~pkcs11"] end end certificate_authority-1.1.0/certificate_authority.gemspec000066400000000000000000000020211425735453600240530ustar00rootroot00000000000000require File.expand_path("lib/certificate_authority/version", __dir__) Gem::Specification.new do |spec| spec.name = "certificate_authority" spec.version = CertificateAuthority::VERSION spec.authors = ["Chris Chandler"] spec.email = ["squanderingtime@gmail.com"] spec.summary = "Ruby gem for managing the core functions outlined in RFC-3280 for PKI" spec.homepage = "https://github.com/cchandler/certificate_authority" spec.license = "MIT" spec.metadata["homepage_uri"] = "https://github.com/cchandler/certificate_authority" spec.metadata["source_code_uri"] = "https://github.com/cchandler/certificate_authority" spec.files = Dir.chdir(__dir__) do `git ls-files -z`.split("\x0").reject { |f| f.match(%r{^(spec/)}) } end spec.require_paths = ["lib"] spec.required_ruby_version = ">= 2.4" spec.add_development_dependency "coveralls" spec.add_development_dependency "pry" spec.add_development_dependency "rake" spec.add_development_dependency "rspec" spec.add_development_dependency "rubocop" end certificate_authority-1.1.0/lib/000077500000000000000000000000001425735453600166675ustar00rootroot00000000000000certificate_authority-1.1.0/lib/certificate_authority.rb000066400000000000000000000013061425735453600236060ustar00rootroot00000000000000# Exterior requirements require 'openssl' # Internal modules require 'certificate_authority/core_extensions' require 'certificate_authority/signing_entity' require 'certificate_authority/revocable' require 'certificate_authority/validations' require 'certificate_authority/distinguished_name' require 'certificate_authority/serial_number' require 'certificate_authority/key_material' require 'certificate_authority/pkcs11_key_material' require 'certificate_authority/extensions' require 'certificate_authority/certificate' require 'certificate_authority/certificate_revocation_list' require 'certificate_authority/ocsp_handler' require 'certificate_authority/signing_request' module CertificateAuthority end certificate_authority-1.1.0/lib/certificate_authority/000077500000000000000000000000001425735453600232615ustar00rootroot00000000000000certificate_authority-1.1.0/lib/certificate_authority/certificate.rb000066400000000000000000000231041425735453600260700ustar00rootroot00000000000000module CertificateAuthority class Certificate include Validations include Revocable attr_accessor :distinguished_name attr_accessor :serial_number attr_accessor :key_material attr_accessor :not_before attr_accessor :not_after attr_accessor :extensions attr_accessor :openssl_body alias :subject :distinguished_name #Same thing as the DN attr_accessor :parent def validate errors.add :base, "Distinguished name must be valid" unless distinguished_name.valid? errors.add :base, "Key material must be valid" unless key_material.valid? errors.add :base, "Serial number must be valid" unless serial_number.valid? errors.add :base, "Extensions must be valid" unless extensions.each do |item| unless item.respond_to?(:valid?) true else item.valid? end end end def initialize self.distinguished_name = DistinguishedName.new self.serial_number = SerialNumber.new self.key_material = MemoryKeyMaterial.new self.not_before = Date.today.utc self.not_after = Date.today.advance(:years => 1).utc self.parent = self self.extensions = load_extensions() self.signing_entity = false end =begin def self.from_openssl openssl_cert unless openssl_cert.is_a? OpenSSL::X509::Certificate raise "Can only construct from an OpenSSL::X509::Certificate" end certificate = Certificate.new # Only subject, key_material, and body are used for signing certificate.distinguished_name = DistinguishedName.from_openssl openssl_cert.subject certificate.key_material.public_key = openssl_cert.public_key certificate.openssl_body = openssl_cert certificate.serial_number.number = openssl_cert.serial.to_i certificate.not_before = openssl_cert.not_before certificate.not_after = openssl_cert.not_after # TODO extensions certificate end =end def sign!(signing_profile={}) raise "Invalid certificate #{self.errors.full_messages}" unless valid? merge_profile_with_extensions(signing_profile) openssl_cert = OpenSSL::X509::Certificate.new openssl_cert.version = 2 openssl_cert.not_before = self.not_before openssl_cert.not_after = self.not_after openssl_cert.public_key = self.key_material.public_key openssl_cert.serial = self.serial_number.number openssl_cert.subject = self.distinguished_name.to_x509_name openssl_cert.issuer = parent.distinguished_name.to_x509_name factory = OpenSSL::X509::ExtensionFactory.new factory.subject_certificate = openssl_cert #NB: If the parent doesn't have an SSL body we're making this a self-signed cert if parent.openssl_body.nil? factory.issuer_certificate = openssl_cert else factory.issuer_certificate = parent.openssl_body end factory.config = build_openssl_config # Order matters: e.g. for self-signed, subjectKeyIdentifier must come before authorityKeyIdentifier self.extensions.keys.sort{|a,b| b<=>a}.each do |k| e = extensions[k] next if e.to_s.nil? or e.to_s == "" ## If the extension returns an empty string we won't include it ext = factory.create_ext(e.openssl_identifier, e.to_s, e.critical) openssl_cert.add_extension(ext) end if signing_profile["digest"].nil? digest = OpenSSL::Digest.new("SHA512") else digest = OpenSSL::Digest.new(signing_profile["digest"]) end self.openssl_body = openssl_cert.sign(parent.key_material.private_key, digest) end def is_signing_entity? self.extensions["basicConstraints"].ca end def signing_entity=(signing) self.extensions["basicConstraints"].ca = signing end def revoked? !self.revoked_at.nil? end def to_pem raise "Certificate has no signed body" if self.openssl_body.nil? self.openssl_body.to_pem end def to_csr csr = SigningRequest.new csr.distinguished_name = self.distinguished_name csr.key_material = self.key_material factory = OpenSSL::X509::ExtensionFactory.new exts = [] self.extensions.keys.each do |k| ## Don't copy over key identifiers for CSRs next if k == "subjectKeyIdentifier" || k == "authorityKeyIdentifier" e = extensions[k] ## If the extension returns an empty string we won't include it next if e.to_s.nil? or e.to_s == "" exts << factory.create_ext(e.openssl_identifier, e.to_s, e.critical) end attrval = OpenSSL::ASN1::Set([OpenSSL::ASN1::Sequence(exts)]) attrs = [ OpenSSL::X509::Attribute.new("extReq", attrval), OpenSSL::X509::Attribute.new("msExtReq", attrval) ] csr.attributes = attrs csr end def self.from_x509_cert(raw_cert) openssl_cert = OpenSSL::X509::Certificate.new(raw_cert) Certificate.from_openssl(openssl_cert) end def is_root_entity? self.parent == self && is_signing_entity? end def is_intermediate_entity? (self.parent != self) && is_signing_entity? end private def merge_profile_with_extensions(signing_profile={}) return self.extensions if signing_profile["extensions"].nil? signing_config = signing_profile["extensions"] signing_config.keys.each do |k| extension = self.extensions[k] items = signing_config[k] items.keys.each do |profile_item_key| if extension.respond_to?("#{profile_item_key}=".to_sym) if k == 'subjectAltName' && profile_item_key == 'emails' items[profile_item_key].map do |email| if email == 'email:copy' fail "no email address provided for subject: #{subject.to_x509_name}" unless subject.email_address "email:#{subject.email_address}" else email end end end extension.send("#{profile_item_key}=".to_sym, items[profile_item_key] ) else p "Tried applying '#{profile_item_key}' to #{extension.class} but it doesn't respond!" end end end end # Enumeration of the extensions. Not the worst option since # the likelihood of these needing to be updated is low at best. EXTENSIONS = [ CertificateAuthority::Extensions::BasicConstraints, CertificateAuthority::Extensions::CrlDistributionPoints, CertificateAuthority::Extensions::SubjectKeyIdentifier, CertificateAuthority::Extensions::AuthorityKeyIdentifier, CertificateAuthority::Extensions::AuthorityInfoAccess, CertificateAuthority::Extensions::KeyUsage, CertificateAuthority::Extensions::ExtendedKeyUsage, CertificateAuthority::Extensions::SubjectAlternativeName, CertificateAuthority::Extensions::CertificatePolicies ] def load_extensions extension_hash = {} EXTENSIONS.each do |klass| extension = klass.new extension_hash[extension.openssl_identifier] = extension end extension_hash end def build_openssl_config OpenSSL::Config.parse(openssl_config_string) end def openssl_config_string lines = openssl_config_without_multi_value + openssl_config_with_multi_value return '' if lines.empty? (["[extensions]" ]+ lines).join("\n") end def openssl_config_without_multi_value no_multi_value_keys = self.extensions.keys.select { |k| extensions[k].config_extensions.empty? } lines = no_multi_value_keys.map do |k| value = extensions[k].to_s value.empty? ? '' : "#{k} = #{value}" end.reject(&:empty?) lines end def openssl_config_with_multi_value multi_value_keys = self.extensions.keys.reject { |k| extensions[k].config_extensions.empty? } sections = {} entries = multi_value_keys.map do |k| sections.merge!(extensions[k].config_extensions) value = comma_terminate(extensions[k]) + section_ref_str(extensions[k].config_extensions.keys) "#{k} = #{value}" end.reject(&:empty?) section_lines = sections.keys.flat_map do |k| section_lines(k, sections[k]) end entries + [''] + section_lines end def comma_terminate(val) s = val.to_s s.empty? ? s : "#{s}," end def section_ref_str(section_names) section_names.map { |n| "@#{n}"}.join(',') end def section_lines(section_name, value_hash) ["[#{section_name}]"] + value_hash.keys.map { |k| "#{k} = #{value_hash[k]}"} + [''] end def merge_options(config,hash) hash.keys.each do |k| config[k] = hash[k] end config end def self.from_openssl openssl_cert unless openssl_cert.is_a? OpenSSL::X509::Certificate raise "Can only construct from an OpenSSL::X509::Certificate" end certificate = Certificate.new # Only subject, key_material, and body are used for signing certificate.distinguished_name = DistinguishedName.from_openssl openssl_cert.subject certificate.key_material.public_key = openssl_cert.public_key certificate.openssl_body = openssl_cert certificate.serial_number.number = openssl_cert.serial.to_i certificate.not_before = openssl_cert.not_before certificate.not_after = openssl_cert.not_after EXTENSIONS.each do |klass| _,v,c = (openssl_cert.extensions.detect { |e| e.to_a.first == klass::OPENSSL_IDENTIFIER } || []).to_a certificate.extensions[klass::OPENSSL_IDENTIFIER] = klass.parse(v, c) if v end certificate end end end certificate_authority-1.1.0/lib/certificate_authority/certificate_revocation_list.rb000066400000000000000000000045421425735453600313610ustar00rootroot00000000000000module CertificateAuthority class CertificateRevocationList include Validations attr_accessor :certificates attr_accessor :parent attr_accessor :crl_body attr_accessor :next_update attr_accessor :last_update_skew_seconds def validate errors.add :next_update, "Next update must be a positive value" if self.next_update < 0 errors.add :parent, "A parent entity must be set" if self.parent.nil? end def initialize self.certificates = [] self.next_update = 60 * 60 * 4 # 4 hour default self.last_update_skew_seconds = 0 end def <<(revocable) case revocable when Revocable raise "Only revoked entities can be added to a CRL" unless revocable.revoked? self.certificates << revocable when OpenSSL::X509::Certificate raise "Not implemented yet" else raise "#{revocable.class} cannot be included in a CRL" end end def sign!(signing_profile={}) raise "No parent entity has been set!" if self.parent.nil? raise "Invalid CRL" unless self.valid? revocations = self.certificates.collect do |revocable| revocation = OpenSSL::X509::Revoked.new ## We really just need a serial number, now we have to dig it out case revocable when Certificate x509_cert = OpenSSL::X509::Certificate.new(revocable.to_pem) revocation.serial = x509_cert.serial when SerialNumber revocation.serial = revocable.number end revocation.time = revocable.revoked_at revocation end crl = OpenSSL::X509::CRL.new revocations.each do |revocation| crl.add_revoked(revocation) end crl.version = 1 crl.last_update = Time.now - self.last_update_skew_seconds crl.next_update = Time.now + self.next_update signing_cert = OpenSSL::X509::Certificate.new(self.parent.to_pem) if signing_profile["digest"].nil? digest = OpenSSL::Digest.new("SHA512") else digest = OpenSSL::Digest.new(signing_profile["digest"]) end crl.issuer = signing_cert.subject self.crl_body = crl.sign(self.parent.key_material.private_key, digest) self.crl_body end def to_pem raise "No signed CRL body" if self.crl_body.nil? self.crl_body.to_pem end end#CertificateRevocationList end certificate_authority-1.1.0/lib/certificate_authority/core_extensions.rb000066400000000000000000000015221425735453600270150ustar00rootroot00000000000000# # ActiveSupport has these modifications. Now that we don't use ActiveSupport, # these are added here as a kindness. # require 'date' unless nil.respond_to?(:blank?) class NilClass def blank? true end end end unless String.respond_to?(:blank?) class String def blank? self.empty? end end end class Date def today t = Time.now.utc Date.new(t.year, t.month, t.day) end def utc self.to_datetime.to_time.utc end unless Date.respond_to?(:advance) def advance(options) options = options.dup d = self d = d >> options.delete(:years) * 12 if options[:years] d = d >> options.delete(:months) if options[:months] d = d + options.delete(:weeks) * 7 if options[:weeks] d = d + options.delete(:days) if options[:days] d end end end certificate_authority-1.1.0/lib/certificate_authority/distinguished_name.rb000066400000000000000000000053601425735453600274550ustar00rootroot00000000000000module CertificateAuthority class DistinguishedName include Validations def validate if self.common_name.nil? || self.common_name.empty? errors.add :common_name, 'cannot be blank' end end attr_accessor :common_name alias :cn :common_name alias :cn= :common_name= attr_accessor :locality alias :l :locality alias :l= :locality= attr_accessor :state alias :s :state alias :st= :state= attr_accessor :country alias :c :country alias :c= :country= attr_accessor :organization alias :o :organization alias :o= :organization= attr_accessor :organizational_unit alias :ou :organizational_unit alias :ou= :organizational_unit= attr_accessor :email_address alias :emailAddress :email_address alias :emailAddress= :email_address= attr_accessor :serial_number alias :serialNumber :serial_number alias :serialNumber= :serial_number= def to_x509_name raise "Invalid Distinguished Name" unless valid? # NB: the capitalization in the strings counts name = OpenSSL::X509::Name.new name.add_entry("serialNumber", serial_number) unless serial_number.blank? name.add_entry("C", country) unless country.blank? name.add_entry("ST", state) unless state.blank? name.add_entry("L", locality) unless locality.blank? name.add_entry("O", organization) unless organization.blank? name.add_entry("OU", organizational_unit) unless organizational_unit.blank? name.add_entry("CN", common_name) name.add_entry("emailAddress", email_address) unless email_address.blank? name end def ==(other) # Use the established OpenSSL comparison self.to_x509_name() == other.to_x509_name() end def self.from_openssl openssl_name unless openssl_name.is_a? OpenSSL::X509::Name raise "Argument must be a OpenSSL::X509::Name" end WrappedDistinguishedName.new(openssl_name) end end ## This is a significantly more complicated case. It's possible that ## generically handled certificates will include custom OIDs in the ## subject. class WrappedDistinguishedName < DistinguishedName attr_accessor :x509_name def initialize(x509_name) @x509_name = x509_name subject = @x509_name.to_a subject.each do |element| field = element[0].downcase value = element[1] #type = element[2] ## -not used method_sym = "#{field}=".to_sym if self.respond_to?(method_sym) self.send("#{field}=",value) else ## Custom OID @custom_oids = true end end end def to_x509_name @x509_name end def custom_oids? @custom_oids end end end certificate_authority-1.1.0/lib/certificate_authority/extensions.rb000066400000000000000000000360011425735453600260050ustar00rootroot00000000000000module CertificateAuthority module Extensions module ExtensionAPI def to_s raise "Implementation required" end def self.parse(value, critical) raise "Implementation required" end def config_extensions {} end def openssl_identifier raise "Implementation required" end def ==(value) raise "Implementation required" end end # Specifies whether an X.509v3 certificate can act as a CA, signing other # certificates to be verified. If set, a path length constraint can also be # specified. # Reference: Section 4.2.1.10 of RFC3280 # http://tools.ietf.org/html/rfc3280#section-4.2.1.10 class BasicConstraints OPENSSL_IDENTIFIER = "basicConstraints" include ExtensionAPI include Validations attr_accessor :critical attr_accessor :ca attr_accessor :path_len def validate unless [true, false].include? self.critical errors.add :critical, 'must be true or false' end unless [true, false].include? self.ca errors.add :ca, 'must be true or false' end end def initialize @critical = false @ca = false end def openssl_identifier OPENSSL_IDENTIFIER end def is_ca? @ca end def path_len=(value) fail(ArgumentError, "path_len must be a non-negative integer") if !value.is_a?(Integer) || value < 0 @path_len = value end def to_s res = [] res << "CA:#{@ca}" res << "pathlen:#{@path_len}" unless @path_len.nil? res.join(',') end def ==(o) o.class == self.class && o.state == state end def self.parse(value, critical) obj = self.new return obj if value.nil? obj.critical = critical value.split(/,\s*/).each do |v| c = v.split(':', 2) obj.ca = (c.last.upcase == "TRUE") if c.first == "CA" obj.path_len = c.last.to_i if c.first == "pathlen" end obj end protected def state [@critical,@ca,@path_len] end end # Specifies where CRL information be be retrieved. This extension isn't # critical, but is recommended for proper CAs. # Reference: Section 4.2.1.14 of RFC3280 # http://tools.ietf.org/html/rfc3280#section-4.2.1.14 class CrlDistributionPoints OPENSSL_IDENTIFIER = "crlDistributionPoints" include ExtensionAPI attr_accessor :critical attr_accessor :uris def initialize @critical = false @uris = [] end def openssl_identifier OPENSSL_IDENTIFIER end ## NB: At this time it seems OpenSSL's extension handlers don't support ## any of the config options the docs claim to support... everything comes back ## "missing value" on GENERAL NAME. Even if copied verbatim def config_extensions { # "custom_crl_fields" => {"fullname" => "URI:#{fullname}"}, # "issuer_sect" => {"CN" => "crlissuer.com", "C" => "US", "O" => "shudder"} } end # This is for legacy support. Technically it can (and probably should) # be an array. But if someone is calling the old accessor we shouldn't # necessarily break it. def uri=(value) @uris << value end def to_s res = [] @uris.each do |uri| res << "URI:#{uri}" end res.join(',') end def ==(o) o.class == self.class && o.state == state end def self.parse(value, critical) obj = self.new return obj if value.nil? obj.critical = critical value.split(/,\s*/).each do |v| c = v.split(':', 2) obj.uris << c.last if c.first == "URI" end obj end protected def state [@critical,@uri] end end # Identifies the public key associated with a given certificate. # Should be required for "CA" certificates. # Reference: Section 4.2.1.2 of RFC3280 # http://tools.ietf.org/html/rfc3280#section-4.2.1.2 class SubjectKeyIdentifier OPENSSL_IDENTIFIER = "subjectKeyIdentifier" include ExtensionAPI attr_accessor :critical attr_accessor :identifier def initialize @critical = false @identifier = "hash" end def openssl_identifier OPENSSL_IDENTIFIER end def to_s res = [] res << @identifier res.join(',') end def ==(o) o.class == self.class && o.state == state end def self.parse(value, critical) obj = self.new return obj if value.nil? obj.critical = critical obj.identifier = value obj end protected def state [@critical,@identifier] end end # Identifies the public key associated with a given private key. # Reference: Section 4.2.1.1 of RFC3280 # http://tools.ietf.org/html/rfc3280#section-4.2.1.1 class AuthorityKeyIdentifier OPENSSL_IDENTIFIER = "authorityKeyIdentifier" include ExtensionAPI attr_accessor :critical attr_accessor :identifier def initialize @critical = false @identifier = ["keyid", "issuer"] end def openssl_identifier OPENSSL_IDENTIFIER end def to_s res = [] res += @identifier res.join(',') end def ==(o) o.class == self.class && o.state == state end def self.parse(value, critical) obj = self.new return obj if value.nil? obj.critical = critical obj.identifier = value.split(/,\s*/).last.chomp obj end protected def state [@critical,@identifier] end end # Specifies how to access CA information and services for the CA that # issued this certificate. # Generally used to specify OCSP servers. # Reference: Section 4.2.2.1 of RFC3280 # http://tools.ietf.org/html/rfc3280#section-4.2.2.1 class AuthorityInfoAccess OPENSSL_IDENTIFIER = "authorityInfoAccess" include ExtensionAPI attr_accessor :critical attr_accessor :ocsp attr_accessor :ca_issuers def initialize @critical = false @ocsp = [] @ca_issuers = [] end def openssl_identifier OPENSSL_IDENTIFIER end def to_s res = [] res += @ocsp.map {|o| "OCSP;URI:#{o}" } res += @ca_issuers.map {|c| "caIssuers;URI:#{c}" } res.join(',') end def ==(o) o.class == self.class && o.state == state end def self.parse(value, critical) obj = self.new return obj if value.nil? obj.critical = critical value.split("\n").each do |v| if v =~ /^OCSP/ obj.ocsp << v.split.last end if v =~ /^CA Issuers/ obj.ca_issuers << v.split.last end end obj end protected def state [@critical,@ocsp,@ca_issuers] end end # Specifies the allowed usage purposes of the keypair specified in this certificate. # Reference: Section 4.2.1.3 of RFC3280 # http://tools.ietf.org/html/rfc3280#section-4.2.1.3 # # Note: OpenSSL when parsing an extension will return results in the form # 'Digital Signature', but on signing you have to set it to 'digitalSignature'. # So copying an extension from an imported cert isn't going to work yet. class KeyUsage OPENSSL_IDENTIFIER = "keyUsage" include ExtensionAPI attr_accessor :critical attr_accessor :usage def initialize @critical = false @usage = ["digitalSignature", "nonRepudiation"] end def openssl_identifier OPENSSL_IDENTIFIER end def to_s res = [] res += @usage res.join(',') end def ==(o) o.class == self.class && o.state == state end def self.parse(value, critical) obj = self.new return obj if value.nil? obj.critical = critical obj.usage = value.split(/,\s*/) obj end protected def state [@critical,@usage] end end # Specifies even more allowed usages in addition to what is specified in # the Key Usage extension. # Reference: Section 4.2.1.13 of RFC3280 # http://tools.ietf.org/html/rfc3280#section-4.2.1.13 class ExtendedKeyUsage OPENSSL_IDENTIFIER = "extendedKeyUsage" include ExtensionAPI attr_accessor :critical attr_accessor :usage def initialize @critical = false @usage = ["serverAuth"] end def openssl_identifier OPENSSL_IDENTIFIER end def to_s res = [] res += @usage res.join(',') end def ==(o) o.class == self.class && o.state == state end def self.parse(value, critical) obj = self.new return obj if value.nil? obj.critical = critical obj.usage = value.split(/,\s*/) obj end protected def state [@critical,@usage] end end # Specifies additional "names" for which this certificate is valid. # Reference: Section 4.2.1.7 of RFC3280 # http://tools.ietf.org/html/rfc3280#section-4.2.1.7 class SubjectAlternativeName OPENSSL_IDENTIFIER = "subjectAltName" include ExtensionAPI attr_accessor :critical attr_accessor :uris, :dns_names, :ips, :emails def initialize @critical = false @uris = [] @dns_names = [] @ips = [] @emails = [] end def openssl_identifier OPENSSL_IDENTIFIER end def uris=(value) raise "URIs must be an array" unless value.is_a?(Array) @uris = value end def dns_names=(value) raise "DNS names must be an array" unless value.is_a?(Array) @dns_names = value end def ips=(value) raise "IPs must be an array" unless value.is_a?(Array) @ips = value end def emails=(value) raise "Emails must be an array" unless value.is_a?(Array) @emails = value end def to_s res = [] res += @uris.map {|u| "URI:#{u}" } res += @dns_names.map {|d| "DNS:#{d}" } res += @ips.map {|i| "IP:#{i}" } res += @emails.map {|i| "email:#{i}" } res.join(',') end def ==(o) o.class == self.class && o.state == state end def self.parse(value, critical) obj = self.new return obj if value.nil? obj.critical = critical value.split(/,\s*/).each do |v| c = v.split(':', 2) obj.uris << c.last if c.first == "URI" obj.dns_names << c.last if c.first == "DNS" obj.ips << c.last if c.first == "IP" obj.emails << c.last if c.first == "EMAIL" end obj end protected def state [@critical,@uris,@dns_names,@ips,@emails] end end class CertificatePolicies OPENSSL_IDENTIFIER = "certificatePolicies" include ExtensionAPI attr_accessor :critical attr_accessor :policy_identifier attr_accessor :cps_uris ##User notice attr_accessor :explicit_text attr_accessor :organization attr_accessor :notice_numbers def initialize self.critical = false @contains_data = false end def openssl_identifier OPENSSL_IDENTIFIER end def user_notice=(value={}) value.keys.each do |key| self.send("#{key}=".to_sym, value[key]) end end def config_extensions config_extension = {} custom_policies = {} notice = {} unless self.policy_identifier.nil? custom_policies["policyIdentifier"] = self.policy_identifier end if !self.cps_uris.nil? and self.cps_uris.is_a?(Array) self.cps_uris.each_with_index do |cps_uri,i| custom_policies["CPS.#{i}"] = cps_uri end end unless self.explicit_text.nil? notice["explicitText"] = self.explicit_text end unless self.organization.nil? notice["organization"] = self.organization end unless self.notice_numbers.nil? notice["noticeNumbers"] = self.notice_numbers end if notice.keys.size > 0 custom_policies["userNotice.1"] = "@notice" config_extension["notice"] = notice end if custom_policies.keys.size > 0 config_extension["custom_policies"] = custom_policies @contains_data = true end config_extension end def to_s return "" unless @contains_data res = [] res << "ia5org" res += @config_extensions["custom_policies"] unless @config_extensions.nil? res.join(',') end def self.parse(value, critical) obj = self.new return obj if value.nil? obj.critical = critical value.split(/,\s*/).each do |v| c = v.split(':', 2) obj.policy_identifier = c.last if c.first == "policyIdentifier" obj.cps_uris << c.last if c.first =~ %r{CPS.\d+} # TODO: explicit_text, organization, notice_numbers end obj end end # DEPRECATED # Specifics the purposes for which a certificate can be used. # The basicConstraints, keyUsage, and extendedKeyUsage extensions are now used instead. # https://www.openssl.org/docs/apps/x509v3_config.html#Netscape_Certificate_Type class NetscapeCertificateType OPENSSL_IDENTIFIER = "nsCertType" include ExtensionAPI attr_accessor :critical attr_accessor :flags def initialize self.critical = false self.flags = [] end def openssl_identifier OPENSSL_IDENTIFIER end def to_s res = [] res += self.flags res.join(',') end def self.parse(value, critical) obj = self.new return obj if value.nil? obj.critical = critical obj.flags = value.split(/,\s*/) obj end end # DEPRECATED # Contains a comment which will be displayed when the certificate is viewed in some browsers. # https://www.openssl.org/docs/apps/x509v3_config.html#Netscape_String_extensions_ class NetscapeComment OPENSSL_IDENTIFIER = "nsComment" include ExtensionAPI attr_accessor :critical attr_accessor :comment def initialize self.critical = false end def openssl_identifier OPENSSL_IDENTIFIER end def to_s res = [] res << self.comment if self.comment res.join(',') end def self.parse(value, critical) obj = self.new return obj if value.nil? obj.critical = critical obj.comment = value obj end end end end certificate_authority-1.1.0/lib/certificate_authority/key_material.rb000066400000000000000000000043771425735453600262670ustar00rootroot00000000000000module CertificateAuthority module KeyMaterial def public_key raise "Required implementation" end def private_key raise "Required implementation" end def is_in_hardware? raise "Required implementation" end def is_in_memory? raise "Required implementation" end def self.from_x509_key_pair(pair,password=nil) if password.nil? key = OpenSSL::PKey::RSA.new(pair) else key = OpenSSL::PKey::RSA.new(pair,password) end mem_key = MemoryKeyMaterial.new mem_key.public_key = key.public_key mem_key.private_key = key mem_key end def self.from_x509_public_key(public_key_pem) key = OpenSSL::PKey::RSA.new(public_key_pem) signing_request_key = SigningRequestKeyMaterial.new signing_request_key.public_key = key.public_key signing_request_key end end class MemoryKeyMaterial include KeyMaterial include Validations attr_accessor :keypair attr_accessor :private_key attr_accessor :public_key def initialize end def validate if private_key.nil? errors.add :private_key, "cannot be blank" end if public_key.nil? errors.add :public_key, "cannot be blank" end end def is_in_hardware? false end def is_in_memory? true end def generate_key(modulus_bits=2048) self.keypair = OpenSSL::PKey::RSA.new(modulus_bits) self.private_key = keypair self.public_key = keypair.public_key self.keypair end def private_key @private_key end def public_key @public_key end end class SigningRequestKeyMaterial include KeyMaterial include Validations def validate errors.add :public_key, "cannot be blank" if public_key.nil? end attr_accessor :public_key def initialize(request=nil) if request.is_a? OpenSSL::X509::Request raise "Invalid certificate signing request" unless request.verify request.public_key self.public_key = request.public_key end end def is_in_hardware? false end def is_in_memory? true end def private_key nil end def public_key @public_key end end end certificate_authority-1.1.0/lib/certificate_authority/ocsp_handler.rb000066400000000000000000000105441425735453600262530ustar00rootroot00000000000000module CertificateAuthority class OCSPResponseBuilder attr_accessor :ocsp_response attr_accessor :verification_mechanism attr_accessor :ocsp_request_reader attr_accessor :parent attr_accessor :next_update GOOD = OpenSSL::OCSP::V_CERTSTATUS_GOOD REVOKED = OpenSSL::OCSP::V_CERTSTATUS_REVOKED NO_REASON=0 KEY_COMPROMISED=OpenSSL::OCSP::REVOKED_STATUS_KEYCOMPROMISE UNSPECIFIED=OpenSSL::OCSP::REVOKED_STATUS_UNSPECIFIED def build_response() raise "Requires a parent for signing" if @parent.nil? if @verification_mechanism.nil? ## If no verification callback is provided we're marking it GOOD @verification_mechanism = lambda {|cert_id| [GOOD,NO_REASON] } end @ocsp_request_reader.ocsp_request.certid.each do |cert_id| result,reason = verification_mechanism.call(cert_id.serial) ## cert_id, status, reason, rev_time, this update, next update, ext ## - unit of time is seconds ## - rev_time is currently set to "now" @ocsp_response.add_status(cert_id, result, reason, 0, 0, @next_update, nil) end @ocsp_response.sign(OpenSSL::X509::Certificate.new(@parent.to_pem), @parent.key_material.private_key, nil, nil) OpenSSL::OCSP::Response.create(OpenSSL::OCSP::RESPONSE_STATUS_SUCCESSFUL, @ocsp_response) end def self.from_request_reader(request_reader,verification_mechanism=nil) response_builder = OCSPResponseBuilder.new response_builder.ocsp_request_reader = request_reader ocsp_response = OpenSSL::OCSP::BasicResponse.new ocsp_response.copy_nonce(request_reader.ocsp_request) response_builder.ocsp_response = ocsp_response response_builder.next_update = 60*15 #Default of 15 minutes response_builder end end class OCSPRequestReader attr_accessor :raw_ocsp_request attr_accessor :ocsp_request def serial_numbers @ocsp_request.certid.collect do |cert_id| cert_id.serial end end def self.from_der(request_body) reader = OCSPRequestReader.new reader.raw_ocsp_request = request_body reader.ocsp_request = OpenSSL::OCSP::Request.new(request_body) reader end end ## DEPRECATED class OCSPHandler include Validations attr_accessor :ocsp_request attr_accessor :certificate_ids attr_accessor :certificates attr_accessor :parent attr_accessor :ocsp_response_body def validate errors.add :parent, "A parent entity must be set" if parent.nil? all_certificates_available end def initialize self.certificates = {} end def <<(cert) self.certificates[cert.serial_number.number.to_s] = cert end def extract_certificate_serials openssl_request = OpenSSL::OCSP::Request.new(@ocsp_request) if openssl_request.certid.nil? raise "Invalid openssl request" end self.certificate_ids = openssl_request.certid.collect do |cert_id| cert_id.serial end self.certificate_ids end def response raise "Invalid response" unless valid? openssl_ocsp_response = OpenSSL::OCSP::BasicResponse.new openssl_ocsp_request = OpenSSL::OCSP::Request.new(self.ocsp_request) openssl_ocsp_response.copy_nonce(openssl_ocsp_request) openssl_ocsp_request.certid.each do |cert_id| certificate = self.certificates[cert_id.serial.to_s] openssl_ocsp_response.add_status(cert_id, OpenSSL::OCSP::V_CERTSTATUS_GOOD, 0, 0, 0, 30, nil) end openssl_ocsp_response.sign(OpenSSL::X509::Certificate.new(self.parent.to_pem), self.parent.key_material.private_key, nil, nil) final_response = OpenSSL::OCSP::Response.create(OpenSSL::OCSP::RESPONSE_STATUS_SUCCESSFUL, openssl_ocsp_response) self.ocsp_response_body = final_response self.ocsp_response_body end def to_der raise "No signed OCSP response body available" if self.ocsp_response_body.nil? self.ocsp_response_body.to_der end private def all_certificates_available openssl_ocsp_request = OpenSSL::OCSP::Request.new(self.ocsp_request) openssl_ocsp_request.certid.each do |cert_id| certificate = self.certificates[cert_id.serial.to_s] errors.add(:base, "Certificate #{cert_id.serial} has not been added yet") if certificate.nil? end end end end certificate_authority-1.1.0/lib/certificate_authority/pkcs11_key_material.rb000066400000000000000000000030001425735453600274270ustar00rootroot00000000000000module CertificateAuthority class Pkcs11KeyMaterial include KeyMaterial attr_accessor :engine attr_accessor :token_id attr_accessor :pkcs11_lib attr_accessor :openssl_pkcs11_engine_lib attr_accessor :pin def initialize(attributes = {}) @attributes = attributes initialize_engine end def is_in_hardware? true end def is_in_memory? false end def generate_key(modulus_bits=1024) puts "Key generation is not currently supported in hardware" nil end def private_key initialize_engine self.engine.load_private_key(self.token_id) end def public_key initialize_engine self.engine.load_public_key(self.token_id) end private def initialize_engine ## We're going to return early and try again later if params weren't passed in ## at initialization. Any attempt at getting a public/private key will try ## again. return false if self.openssl_pkcs11_engine_lib.nil? or self.pkcs11_lib.nil? return self.engine unless self.engine.nil? OpenSSL::Engine.load pkcs11 = OpenSSL::Engine.by_id("dynamic") do |e| e.ctrl_cmd("SO_PATH",self.openssl_pkcs11_engine_lib) e.ctrl_cmd("ID","pkcs11") e.ctrl_cmd("LIST_ADD","1") e.ctrl_cmd("LOAD") e.ctrl_cmd("PIN",self.pin) unless self.pin.nil? or self.pin == "" e.ctrl_cmd("MODULE_PATH",self.pkcs11_lib) end self.engine = pkcs11 pkcs11 end end end certificate_authority-1.1.0/lib/certificate_authority/revocable.rb000066400000000000000000000003701425735453600255500ustar00rootroot00000000000000module CertificateAuthority module Revocable attr_accessor :revoked_at def revoke!(time=Time.now) @revoked_at = time end def revoked? # If we have a time, then we're revoked !@revoked_at.nil? end end end certificate_authority-1.1.0/lib/certificate_authority/serial_number.rb000066400000000000000000000006711425735453600264410ustar00rootroot00000000000000require 'securerandom' module CertificateAuthority class SerialNumber include Validations include Revocable attr_accessor :number def validate if self.number.nil? errors.add :number, "must not be empty" elsif self.number.to_i <= 0 errors.add :number, "must be greater than zero" end end def initialize self.number = SecureRandom.random_number(2**128-1) end end end certificate_authority-1.1.0/lib/certificate_authority/signing_entity.rb000066400000000000000000000004521425735453600266410ustar00rootroot00000000000000module CertificateAuthority module SigningEntity def self.included(mod) mod.class_eval do attr_accessor :signing_entity end end def signing_entity=(val) raise "invalid param" unless [true,false].include?(val) @signing_entity = val end end end certificate_authority-1.1.0/lib/certificate_authority/signing_request.rb000066400000000000000000000062571425735453600270260ustar00rootroot00000000000000module CertificateAuthority class SigningRequest attr_accessor :distinguished_name attr_accessor :key_material attr_accessor :raw_body attr_accessor :openssl_csr attr_accessor :digest attr_accessor :attributes def initialize() @attributes = [] end # Fake attribute for convenience because adding # alternative names on a CSR is remarkably non-trivial. def subject_alternative_names=(alt_names) raise "alt_names must be an Array" unless alt_names.is_a?(Array) factory = OpenSSL::X509::ExtensionFactory.new name_list = alt_names.map{|m| "DNS:#{m}"}.join(",") ext = factory.create_ext("subjectAltName",name_list,false) ext_set = OpenSSL::ASN1::Set([OpenSSL::ASN1::Sequence([ext])]) attr = OpenSSL::X509::Attribute.new("extReq", ext_set) @attributes << attr end def read_attributes_by_oid(*oids) attributes.detect { |a| oids.include?(a.oid) } end protected :read_attributes_by_oid def to_cert cert = Certificate.new if !@distinguished_name.nil? cert.distinguished_name = @distinguished_name end cert.key_material = @key_material if attribute = read_attributes_by_oid('extReq', 'msExtReq') set = OpenSSL::ASN1.decode(attribute.value) seq = set.value.first seq.value.collect { |asn1ext| OpenSSL::X509::Extension.new(asn1ext).to_a }.each do |o, v, c| Certificate::EXTENSIONS.each do |klass| cert.extensions[klass::OPENSSL_IDENTIFIER] = klass.parse(v, c) if v && klass::OPENSSL_IDENTIFIER == o end end end cert end def to_pem to_x509_csr.to_pem end def to_x509_csr raise "Must specify a DN/subject on csr" if @distinguished_name.nil? raise "Invalid DN in request" unless @distinguished_name.valid? raise "CSR must have key material" if @key_material.nil? raise "CSR must include a public key on key material" if @key_material.public_key.nil? raise "Need a private key on key material for CSR generation" if @key_material.private_key.nil? opensslcsr = OpenSSL::X509::Request.new opensslcsr.subject = @distinguished_name.to_x509_name opensslcsr.public_key = @key_material.public_key opensslcsr.attributes = @attributes unless @attributes.nil? opensslcsr.sign @key_material.private_key, OpenSSL::Digest.new(@digest || "SHA512") opensslcsr end def self.from_x509_csr(raw_csr) csr = SigningRequest.new openssl_csr = OpenSSL::X509::Request.new(raw_csr) csr.distinguished_name = DistinguishedName.from_openssl openssl_csr.subject csr.raw_body = raw_csr csr.openssl_csr = openssl_csr csr.attributes = openssl_csr.attributes key_material = SigningRequestKeyMaterial.new key_material.public_key = openssl_csr.public_key csr.key_material = key_material csr end def self.from_netscape_spkac(raw_spkac) openssl_spkac = OpenSSL::Netscape::SPKI.new raw_spkac csr = SigningRequest.new csr.raw_body = raw_spkac key_material = SigningRequestKeyMaterial.new key_material.public_key = openssl_spkac.public_key csr end end end certificate_authority-1.1.0/lib/certificate_authority/validations.rb000066400000000000000000000010251425735453600261210ustar00rootroot00000000000000# # This is a super simple replacement for ActiveSupport::Validations # module CertificateAuthority class Errors < Array def add(symbol, msg) self.push([symbol, msg]) end def full_messages self.map {|i| i[0].to_s + ": " + i[1]}.join("\n") end end module Validations def valid? @errors = Errors.new validate errors.empty? end # must be overridden def validate raise NotImplementedError end def errors @errors ||= Errors.new end end end certificate_authority-1.1.0/lib/certificate_authority/version.rb000066400000000000000000000000731425735453600252730ustar00rootroot00000000000000module CertificateAuthority VERSION = '1.1.0'.freeze end certificate_authority-1.1.0/lib/tasks/000077500000000000000000000000001425735453600200145ustar00rootroot00000000000000certificate_authority-1.1.0/lib/tasks/certificate_authority.rake000066400000000000000000000010371425735453600252530ustar00rootroot00000000000000require 'certificate_authority' namespace :certificate_authority do desc "Generate a quick self-signed cert" task :self_signed do cn = "http://localhost" cn = ENV['DOMAIN'] unless ENV['DOMAIN'].nil? root = CertificateAuthority::Certificate.new root.subject.common_name= cn root.key_material.generate_key root.signing_entity = true root.valid? root.sign! print "Your cert for #{cn}\n" print root.to_pem print "Your private key\n" print root.key_material.private_key.to_pem end end certificate_authority-1.1.0/spec/000077500000000000000000000000001425735453600170535ustar00rootroot00000000000000certificate_authority-1.1.0/spec/samples/000077500000000000000000000000001425735453600205175ustar00rootroot00000000000000certificate_authority-1.1.0/spec/samples/certs/000077500000000000000000000000001425735453600216375ustar00rootroot00000000000000certificate_authority-1.1.0/spec/samples/certs/DigiCertHighAssuranceEVCA-1.pem000066400000000000000000000146701425735453600273060ustar00rootroot00000000000000Certificate: Data: Version: 3 (0x2) Serial Number: 08:bb:b0:25:47:13:4b:c9:b1:10:d7:c1:a2:12:59:c5 Signature Algorithm: sha1WithRSAEncryption Issuer: C=US, O=DigiCert Inc, OU=www.digicert.com, CN=DigiCert High Assurance EV Root CA Validity Not Before: Nov 10 00:00:00 2006 GMT Not After : Nov 10 00:00:00 2021 GMT Subject: C=US, O=DigiCert Inc, OU=www.digicert.com, CN=DigiCert High Assurance EV CA-1 Subject Public Key Info: Public Key Algorithm: rsaEncryption RSA Public Key: (2048 bit) Modulus (2048 bit): 00:f3:96:62:d8:75:6e:19:ff:3f:34:7c:49:4f:31: 7e:0d:04:4e:99:81:e2:b3:85:55:91:30:b1:c0:af: 70:bb:2c:a8:e7:18:aa:3f:78:f7:90:68:52:86:01: 88:97:e2:3b:06:65:90:aa:bd:65:76:c2:ec:be:10: 5b:37:78:83:60:75:45:c6:bd:74:aa:b6:9f:a4:3a: 01:50:17:c4:39:69:b9:f1:4f:ef:82:c1:ca:f3:4a: db:cc:9e:50:4f:4d:40:a3:3a:90:e7:86:66:bc:f0: 3e:76:28:4c:d1:75:80:9e:6a:35:14:35:03:9e:db: 0c:8c:c2:28:ad:50:b2:ce:f6:91:a3:c3:a5:0a:58: 49:f6:75:44:6c:ba:f9:ce:e9:ab:3a:02:e0:4d:f3: ac:e2:7a:e0:60:22:05:3c:82:d3:52:e2:f3:9c:47: f8:3b:d8:b2:4b:93:56:4a:bf:70:ab:3e:e9:68:c8: 1d:8f:58:1d:2a:4d:5e:27:3d:ad:0a:59:2f:5a:11: 20:40:d9:68:04:68:2d:f4:c0:84:0b:0a:1b:78:df: ed:1a:58:dc:fb:41:5a:6d:6b:f2:ed:1c:ee:5c:32: b6:5c:ec:d7:a6:03:32:a6:e8:de:b7:28:27:59:88: 80:ff:7b:ad:89:58:d5:1e:14:a4:f2:b0:70:d4:a0: 3e:a7 Exponent: 65537 (0x10001) X509v3 extensions: X509v3 Key Usage: critical Digital Signature, Certificate Sign, CRL Sign X509v3 Extended Key Usage: TLS Web Server Authentication, TLS Web Client Authentication, Code Signing, E-mail Protection, Time Stamping X509v3 Certificate Policies: Policy: 2.16.840.1.114412.2.1 CPS: http://www.digicert.com/ssl-cps-repository.htm User Notice: Explicit Text: X509v3 Basic Constraints: critical CA:TRUE Authority Information Access: OCSP - URI:http://ocsp.digicert.com CA Issuers - URI:http://www.digicert.com/CACerts/DigiCertHighAssuranceEVRootCA.crt X509v3 CRL Distribution Points: URI:http://crl3.digicert.com/DigiCertHighAssuranceEVRootCA.crl URI:http://crl4.digicert.com/DigiCertHighAssuranceEVRootCA.crl X509v3 Subject Key Identifier: 4C:58:CB:25:F0:41:4F:52:F4:28:C8:81:43:9B:A6:A8:A0:E6:92:E5 X509v3 Authority Key Identifier: keyid:B1:3E:C3:69:03:F8:BF:47:01:D4:98:26:1A:08:02:EF:63:64:2B:C3 Signature Algorithm: sha1WithRSAEncryption 50:1e:43:b0:f7:4d:29:96:5b:bb:a7:d3:0a:b5:b5:d5:d0:27: aa:f9:af:c7:25:d1:95:d5:2f:5a:53:bd:42:07:7e:78:49:ca: 0b:eb:4c:55:e2:ea:2f:7f:49:ad:c7:ff:d1:2d:3e:9c:a0:64: 2b:51:9e:91:26:28:bb:87:bb:75:7c:bc:a1:fd:66:68:2e:4c: 4a:16:cc:fe:06:cf:31:ea:80:6e:e4:bd:e8:03:72:f6:25:b5: 41:83:61:d0:97:0a:27:1d:b3:f7:2b:32:84:8f:5b:e7:cc:3f: e2:2c:67:86:94:f4:b2:2b:6c:52:3b:67:2a:8d:58:95:00:14: 46:24:ac:0b:fa:c9:8e:c7:26:80:df:d1:e1:97:e3:f8:bb:68: c6:9c:bd:be:08:54:3b:10:32:7c:81:1f:2b:28:95:a8:41:0a: c6:d0:30:66:b4:e9:f2:a2:00:69:20:07:ca:82:4c:1e:cf:a7: 98:b8:0c:ee:cd:16:1c:be:1a:63:d4:c0:99:f6:67:b2:f0:8e: 17:2d:58:c2:80:aa:5d:96:c7:b3:28:ed:f0:da:8e:b6:47:1b: 8f:4e:15:f1:97:4c:0b:4b:af:81:d4:46:94:62:2c:43:a7:3c: 25:48:19:63:f2:5c:aa:15:89:76:84:85:73:91:7d:28:3c:09: 83:82:bc:f7 -----BEGIN CERTIFICATE----- MIIG4zCCBcugAwIBAgIQCLuwJUcTS8mxENfBohJZxTANBgkqhkiG9w0BAQUFADBs MQswCQYDVQQGEwJVUzEVMBMGA1UEChMMRGlnaUNlcnQgSW5jMRkwFwYDVQQLExB3 d3cuZGlnaWNlcnQuY29tMSswKQYDVQQDEyJEaWdpQ2VydCBIaWdoIEFzc3VyYW5j ZSBFViBSb290IENBMB4XDTA2MTExMDAwMDAwMFoXDTIxMTExMDAwMDAwMFowaTEL MAkGA1UEBhMCVVMxFTATBgNVBAoTDERpZ2lDZXJ0IEluYzEZMBcGA1UECxMQd3d3 LmRpZ2ljZXJ0LmNvbTEoMCYGA1UEAxMfRGlnaUNlcnQgSGlnaCBBc3N1cmFuY2Ug RVYgQ0EtMTCCASIwDQYJKoZIhvcNAQEBBQADggEPADCCAQoCggEBAPOWYth1bhn/ PzR8SU8xfg0ETpmB4rOFVZEwscCvcLssqOcYqj9495BoUoYBiJfiOwZlkKq9ZXbC 7L4QWzd4g2B1Rca9dKq2n6Q6AVAXxDlpufFP74LByvNK28yeUE9NQKM6kOeGZrzw PnYoTNF1gJ5qNRQ1A57bDIzCKK1Qss72kaPDpQpYSfZ1RGy6+c7pqzoC4E3zrOJ6 4GAiBTyC01Li85xH+DvYskuTVkq/cKs+6WjIHY9YHSpNXic9rQpZL1oRIEDZaARo LfTAhAsKG3jf7RpY3PtBWm1r8u0c7lwytlzs16YDMqbo3rcoJ1mIgP97rYlY1R4U pPKwcNSgPqcCAwEAAaOCA4IwggN+MA4GA1UdDwEB/wQEAwIBhjA7BgNVHSUENDAy BggrBgEFBQcDAQYIKwYBBQUHAwIGCCsGAQUFBwMDBggrBgEFBQcDBAYIKwYBBQUH AwgwggHEBgNVHSAEggG7MIIBtzCCAbMGCWCGSAGG/WwCATCCAaQwOgYIKwYBBQUH AgEWLmh0dHA6Ly93d3cuZGlnaWNlcnQuY29tL3NzbC1jcHMtcmVwb3NpdG9yeS5o dG0wggFkBggrBgEFBQcCAjCCAVYeggFSAEEAbgB5ACAAdQBzAGUAIABvAGYAIAB0 AGgAaQBzACAAQwBlAHIAdABpAGYAaQBjAGEAdABlACAAYwBvAG4AcwB0AGkAdAB1 AHQAZQBzACAAYQBjAGMAZQBwAHQAYQBuAGMAZQAgAG8AZgAgAHQAaABlACAARABp AGcAaQBDAGUAcgB0ACAARQBWACAAQwBQAFMAIABhAG4AZAAgAHQAaABlACAAUgBl AGwAeQBpAG4AZwAgAFAAYQByAHQAeQAgAEEAZwByAGUAZQBtAGUAbgB0ACAAdwBo AGkAYwBoACAAbABpAG0AaQB0ACAAbABpAGEAYgBpAGwAaQB0AHkAIABhAG4AZAAg AGEAcgBlACAAaQBuAGMAbwByAHAAbwByAGEAdABlAGQAIABoAGUAcgBlAGkAbgAg AGIAeQAgAHIAZQBmAGUAcgBlAG4AYwBlAC4wDwYDVR0TAQH/BAUwAwEB/zCBgwYI KwYBBQUHAQEEdzB1MCQGCCsGAQUFBzABhhhodHRwOi8vb2NzcC5kaWdpY2VydC5j b20wTQYIKwYBBQUHMAKGQWh0dHA6Ly93d3cuZGlnaWNlcnQuY29tL0NBQ2VydHMv RGlnaUNlcnRIaWdoQXNzdXJhbmNlRVZSb290Q0EuY3J0MIGPBgNVHR8EgYcwgYQw QKA+oDyGOmh0dHA6Ly9jcmwzLmRpZ2ljZXJ0LmNvbS9EaWdpQ2VydEhpZ2hBc3N1 cmFuY2VFVlJvb3RDQS5jcmwwQKA+oDyGOmh0dHA6Ly9jcmw0LmRpZ2ljZXJ0LmNv bS9EaWdpQ2VydEhpZ2hBc3N1cmFuY2VFVlJvb3RDQS5jcmwwHQYDVR0OBBYEFExY yyXwQU9S9CjIgUObpqig5pLlMB8GA1UdIwQYMBaAFLE+w2kD+L9HAdSYJhoIAu9j ZCvDMA0GCSqGSIb3DQEBBQUAA4IBAQBQHkOw900pllu7p9MKtbXV0Ceq+a/HJdGV 1S9aU71CB354ScoL60xV4uovf0mtx//RLT6coGQrUZ6RJii7h7t1fLyh/WZoLkxK Fsz+Bs8x6oBu5L3oA3L2JbVBg2HQlwonHbP3KzKEj1vnzD/iLGeGlPSyK2xSO2cq jViVABRGJKwL+smOxyaA39Hhl+P4u2jGnL2+CFQ7EDJ8gR8rKJWoQQrG0DBmtOny ogBpIAfKgkwez6eYuAzuzRYcvhpj1MCZ9mey8I4XLVjCgKpdlsezKO3w2o62RxuP ThXxl0wLS6+B1EaUYixDpzwlSBlj8lyqFYl2hIVzkX0oPAmDgrz3 -----END CERTIFICATE----- certificate_authority-1.1.0/spec/samples/certs/apple_wwdr_issued_cert.pem000066400000000000000000000141031425735453600270760ustar00rootroot00000000000000Certificate: Data: Version: 3 (0x2) Serial Number: 0f:50:11:d8:8f:07:09:bf Signature Algorithm: sha1WithRSAEncryption Issuer: C=US, O=Apple Inc., OU=Apple Worldwide Developer Relations, CN=Apple Worldwide Developer Relations Certification Authority Validity Not Before: Jun 26 21:18:40 2012 GMT Not After : Jun 26 21:18:40 2013 GMT Subject: UID=pass.void-star.com.meepedoo, CN=Pass Type ID: pass.void-star.com.meepedoo, OU=UL736KYQR9, O=Eric Monti, C=US Subject Public Key Info: Public Key Algorithm: rsaEncryption RSA Public Key: (2048 bit) Modulus (2048 bit): 00:af:f9:a6:9f:c0:8b:a5:56:f2:b6:97:0a:86:42: d0:f1:54:01:98:95:9f:d9:69:2b:9c:be:b0:b5:f4: a4:ad:9e:e6:ef:8e:a5:dc:50:d0:ce:2a:89:a9:41: ce:44:36:af:90:33:e7:56:76:9e:68:91:df:c6:e7: b8:21:f2:d5:75:d2:2a:17:3a:9d:4a:e0:cc:d2:94: 90:e7:f2:36:2f:1c:41:00:02:76:45:fe:c2:6a:fc: 36:96:e7:7e:59:00:f2:85:9e:31:ff:a3:9b:a0:b8: 6d:95:9e:e4:f1:c4:d0:e9:7c:70:61:52:03:39:5c: b8:8a:34:69:22:82:c5:44:f9:cd:a1:25:57:26:86: e4:31:d5:08:c9:9d:5f:73:44:10:21:6d:99:90:74: f6:69:fb:20:de:a9:46:49:a3:a9:96:ab:66:44:e6: bd:56:65:8e:7d:dd:07:7e:71:bd:13:0f:b1:50:07: af:eb:71:78:af:46:d5:71:39:da:ed:f2:d1:db:8d: 81:64:7f:56:c6:87:f1:7e:a5:a3:f4:9f:02:01:b9: d0:36:7f:87:f2:0d:d6:93:4e:cb:d8:32:9c:5a:ea: 44:07:64:6a:9f:1c:14:8e:9a:3a:0e:a3:86:2c:e8: 20:2a:f1:32:e0:11:2a:13:8a:d3:4c:73:5e:ab:70: fa:4d Exponent: 65537 (0x10001) X509v3 extensions: Authority Information Access: OCSP - URI:http://ocsp.apple.com/ocsp-wwdr03 X509v3 Subject Key Identifier: 02:87:24:00:CA:53:38:8F:C3:4D:5C:80:98:E1:65:95:38:D5:2B:69 X509v3 Basic Constraints: CA:FALSE X509v3 Authority Key Identifier: keyid:88:27:17:09:A9:B6:18:60:8B:EC:EB:BA:F6:47:59:C5:52:54:A3:B7 X509v3 Certificate Policies: Policy: 1.2.840.113635.100.5.1 CPS: http://www.apple.com/appleca/ User Notice: Explicit Text: Reliance on this certificate by any party assumes acceptance of the then applicable standard terms and conditions of use, certificate policy and certification practice statements. X509v3 CRL Distribution Points: URI:http://crl.apple.com/wwdrca.crl X509v3 Key Usage: Digital Signature X509v3 Extended Key Usage: TLS Web Client Authentication, 1.2.840.113635.100.4.14 1.2.840.113635.100.6.3.2: .. 1.2.840.113635.100.6.1.16: ..pass.void-star.com.meepedoo Signature Algorithm: sha1WithRSAEncryption 5d:e8:2d:5a:d7:65:9e:92:dd:bf:66:1c:04:99:08:a9:40:b7: 92:dc:fa:d4:c8:8c:cf:ad:3a:99:22:34:0c:0f:72:c9:4f:7f: c5:90:dc:8a:5d:47:c0:dd:ee:47:f7:01:81:6a:06:61:66:c3: 44:a1:0b:96:e5:70:a2:2f:c3:bb:98:d0:bf:07:0a:3d:56:d1: 95:01:08:16:6e:9a:5e:6b:45:ce:d9:b5:78:09:0f:eb:ff:11: a6:9a:eb:65:f3:b3:b1:14:a5:6f:97:a1:53:31:65:a6:e0:ea: e6:70:2f:df:5a:f9:b5:e4:59:2b:33:d4:a0:a3:4c:c6:61:c8: 56:5a:ca:be:4e:ac:12:c7:d3:1e:e5:b6:e3:de:04:c4:63:e5: 0e:33:4d:b9:33:92:7e:11:a4:ee:85:2b:65:00:7f:a5:dc:f6: 19:5b:69:37:fe:61:a7:e6:45:27:c5:1c:3a:b6:46:76:fb:f3: 56:93:00:2a:4f:b4:1e:d3:ed:75:8c:32:e5:09:c8:28:84:46: 29:4d:db:8f:e9:6e:1c:9e:bb:76:74:6a:8f:63:d5:04:1a:b3: 16:42:cc:70:4c:b1:88:e2:6d:58:bd:52:d2:3c:dc:52:9d:de: 37:94:20:00:07:6e:06:48:e7:17:86:44:a2:3a:23:07:c1:74: ef:6f:2a:a5 -----BEGIN CERTIFICATE----- MIIF8zCCBNugAwIBAgIID1AR2I8HCb8wDQYJKoZIhvcNAQEFBQAwgZYxCzAJBgNV BAYTAlVTMRMwEQYDVQQKDApBcHBsZSBJbmMuMSwwKgYDVQQLDCNBcHBsZSBXb3Js ZHdpZGUgRGV2ZWxvcGVyIFJlbGF0aW9uczFEMEIGA1UEAww7QXBwbGUgV29ybGR3 aWRlIERldmVsb3BlciBSZWxhdGlvbnMgQ2VydGlmaWNhdGlvbiBBdXRob3JpdHkw HhcNMTIwNjI2MjExODQwWhcNMTMwNjI2MjExODQwWjCBmDErMCkGCgmSJomT8ixk AQEMG3Bhc3Mudm9pZC1zdGFyLmNvbS5tZWVwZWRvbzEyMDAGA1UEAwwpUGFzcyBU eXBlIElEOiBwYXNzLnZvaWQtc3Rhci5jb20ubWVlcGVkb28xEzARBgNVBAsMClVM NzM2S1lRUjkxEzARBgNVBAoMCkVyaWMgTW9udGkxCzAJBgNVBAYTAlVTMIIBIjAN BgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEAr/mmn8CLpVbytpcKhkLQ8VQBmJWf 2WkrnL6wtfSkrZ7m746l3FDQziqJqUHORDavkDPnVnaeaJHfxue4IfLVddIqFzqd SuDM0pSQ5/I2LxxBAAJ2Rf7Cavw2lud+WQDyhZ4x/6OboLhtlZ7k8cTQ6XxwYVID OVy4ijRpIoLFRPnNoSVXJobkMdUIyZ1fc0QQIW2ZkHT2afsg3qlGSaOplqtmROa9 VmWOfd0HfnG9Ew+xUAev63F4r0bVcTna7fLR242BZH9WxofxfqWj9J8CAbnQNn+H 8g3Wk07L2DKcWupEB2RqnxwUjpo6DqOGLOggKvEy4BEqE4rTTHNeq3D6TQIDAQAB o4ICPzCCAjswPQYIKwYBBQUHAQEEMTAvMC0GCCsGAQUFBzABhiFodHRwOi8vb2Nz cC5hcHBsZS5jb20vb2NzcC13d2RyMDMwHQYDVR0OBBYEFAKHJADKUziPw01cgJjh ZZU41StpMAkGA1UdEwQCMAAwHwYDVR0jBBgwFoAUiCcXCam2GGCL7Ou69kdZxVJU o7cwggEPBgNVHSAEggEGMIIBAjCB/wYJKoZIhvdjZAUBMIHxMCkGCCsGAQUFBwIB Fh1odHRwOi8vd3d3LmFwcGxlLmNvbS9hcHBsZWNhLzCBwwYIKwYBBQUHAgIwgbYM gbNSZWxpYW5jZSBvbiB0aGlzIGNlcnRpZmljYXRlIGJ5IGFueSBwYXJ0eSBhc3N1 bWVzIGFjY2VwdGFuY2Ugb2YgdGhlIHRoZW4gYXBwbGljYWJsZSBzdGFuZGFyZCB0 ZXJtcyBhbmQgY29uZGl0aW9ucyBvZiB1c2UsIGNlcnRpZmljYXRlIHBvbGljeSBh bmQgY2VydGlmaWNhdGlvbiBwcmFjdGljZSBzdGF0ZW1lbnRzLjAwBgNVHR8EKTAn MCWgI6Ahhh9odHRwOi8vY3JsLmFwcGxlLmNvbS93d2RyY2EuY3JsMAsGA1UdDwQE AwIHgDAeBgNVHSUEFzAVBggrBgEFBQcDAgYJKoZIhvdjZAQOMBAGCiqGSIb3Y2QG AwIEAgUAMCsGCiqGSIb3Y2QGARAEHQwbcGFzcy52b2lkLXN0YXIuY29tLm1lZXBl ZG9vMA0GCSqGSIb3DQEBBQUAA4IBAQBd6C1a12Wekt2/ZhwEmQipQLeS3PrUyIzP rTqZIjQMD3LJT3/FkNyKXUfA3e5H9wGBagZhZsNEoQuW5XCiL8O7mNC/Bwo9VtGV AQgWbppea0XO2bV4CQ/r/xGmmutl87OxFKVvl6FTMWWm4OrmcC/fWvm15FkrM9Sg o0zGYchWWsq+TqwSx9Me5bbj3gTEY+UOM025M5J+EaTuhStlAH+l3PYZW2k3/mGn 5kUnxRw6tkZ2+/NWkwAqT7Qe0+11jDLlCcgohEYpTduP6W4cnrt2dGqPY9UEGrMW QsxwTLGI4m1YvVLSPNxSnd43lCAAB24GSOcXhkSiOiMHwXTvbyql -----END CERTIFICATE----- certificate_authority-1.1.0/spec/samples/certs/apple_wwdr_issuer.pem000066400000000000000000000114141425735453600261010ustar00rootroot00000000000000Certificate: Data: Version: 3 (0x2) Serial Number: 25 (0x19) Signature Algorithm: sha1WithRSAEncryption Issuer: C=US, O=Apple Inc., OU=Apple Certification Authority, CN=Apple Root CA Validity Not Before: Feb 14 18:56:35 2008 GMT Not After : Feb 14 18:56:35 2016 GMT Subject: C=US, O=Apple Inc., OU=Apple Worldwide Developer Relations, CN=Apple Worldwide Developer Relations Certification Authority Subject Public Key Info: Public Key Algorithm: rsaEncryption RSA Public Key: (2048 bit) Modulus (2048 bit): 00:ca:38:54:a6:cb:56:aa:c8:24:39:48:e9:8c:ee: ec:5f:b8:7f:26:91:bc:34:53:7a:ce:7c:63:80:61: 77:64:5e:a5:07:23:b6:39:fe:50:2d:15:56:58:70: 2d:7e:c4:6e:c1:4a:85:3e:2f:f0:de:84:1a:a1:57: c9:af:7b:18:ff:6a:fa:15:12:49:15:08:19:ac:aa: db:2a:32:ed:96:63:68:52:15:3d:8c:8a:ec:bf:6b: 18:95:e0:03:ac:01:7d:97:05:67:ce:0e:85:95:37: 6a:ed:09:b6:ae:67:cd:51:64:9f:c6:5c:d1:bc:57: 6e:67:35:80:76:36:a4:87:81:6e:38:8f:d8:2b:15: 4e:7b:25:d8:5a:bf:4e:83:c1:8d:d2:93:d5:1a:71: b5:60:9c:9d:33:4e:55:f9:12:58:0c:86:b8:16:0d: c1:e5:77:45:8d:50:48:ba:2b:2d:e4:94:85:e1:e8: c4:9d:c6:68:a5:b0:a3:fc:67:7e:70:ba:02:59:4b: 77:42:91:39:b9:f5:cd:e1:4c:ef:c0:3b:48:8c:a6: e5:21:5d:fd:6a:6a:bb:a7:16:35:60:d2:e6:ad:f3: 46:29:c9:e8:c3:8b:e9:79:c0:6a:61:67:15:b2:f0: fd:e5:68:bc:62:5f:6e:cf:99:dd:ef:1b:63:fe:92: 65:ab Exponent: 65537 (0x10001) X509v3 extensions: X509v3 Key Usage: critical Digital Signature, Certificate Sign, CRL Sign X509v3 Basic Constraints: critical CA:TRUE X509v3 Subject Key Identifier: 88:27:17:09:A9:B6:18:60:8B:EC:EB:BA:F6:47:59:C5:52:54:A3:B7 X509v3 Authority Key Identifier: keyid:2B:D0:69:47:94:76:09:FE:F4:6B:8D:2E:40:A6:F7:47:4D:7F:08:5E X509v3 CRL Distribution Points: URI:http://www.apple.com/appleca/root.crl 1.2.840.113635.100.6.2.1: .. Signature Algorithm: sha1WithRSAEncryption da:32:00:96:c5:54:94:d3:3b:82:37:66:7d:2e:68:d5:c3:c6: b8:cb:26:8c:48:90:cf:13:24:6a:46:8e:63:d4:f0:d0:13:06: dd:d8:c4:c1:37:15:f2:33:13:39:26:2d:ce:2e:55:40:e3:0b: 03:af:fa:12:c2:e7:0d:21:b8:d5:80:cf:ac:28:2f:ce:2d:b3: 4e:af:86:19:04:c6:e9:50:dd:4c:29:47:10:23:fc:6c:bb:1b: 98:6b:48:89:e1:5b:9d:de:46:db:35:85:35:ef:3e:d0:e2:58: 4b:38:f4:ed:75:5a:1f:5c:70:1d:56:39:12:e5:e1:0d:11:e4: 89:25:06:bd:d5:b4:15:8e:5e:d0:59:97:90:e9:4b:81:e2:df: 18:af:44:74:1e:19:a0:3a:47:cc:91:1d:3a:eb:23:5a:fe:a5: 2d:97:f7:7b:bb:d6:87:46:42:85:eb:52:3d:26:b2:63:a8:b4: b1:ca:8f:f4:cc:e2:b3:c8:47:e0:bf:9a:59:83:fa:da:98:53: 2a:82:f5:7c:65:2e:95:d9:33:5d:f5:ed:65:cc:31:37:c5:5a: 04:e8:6b:e1:e7:88:03:4a:75:9e:9b:28:cb:4a:40:88:65:43: 75:dd:cb:3a:25:23:c5:9e:57:f8:2e:ce:d2:a9:92:5e:73:2e: 2f:25:75:15 -----BEGIN CERTIFICATE----- MIIEIzCCAwugAwIBAgIBGTANBgkqhkiG9w0BAQUFADBiMQswCQYDVQQGEwJVUzET MBEGA1UEChMKQXBwbGUgSW5jLjEmMCQGA1UECxMdQXBwbGUgQ2VydGlmaWNhdGlv biBBdXRob3JpdHkxFjAUBgNVBAMTDUFwcGxlIFJvb3QgQ0EwHhcNMDgwMjE0MTg1 NjM1WhcNMTYwMjE0MTg1NjM1WjCBljELMAkGA1UEBhMCVVMxEzARBgNVBAoMCkFw cGxlIEluYy4xLDAqBgNVBAsMI0FwcGxlIFdvcmxkd2lkZSBEZXZlbG9wZXIgUmVs YXRpb25zMUQwQgYDVQQDDDtBcHBsZSBXb3JsZHdpZGUgRGV2ZWxvcGVyIFJlbGF0 aW9ucyBDZXJ0aWZpY2F0aW9uIEF1dGhvcml0eTCCASIwDQYJKoZIhvcNAQEBBQAD ggEPADCCAQoCggEBAMo4VKbLVqrIJDlI6Yzu7F+4fyaRvDRTes58Y4Bhd2RepQcj tjn+UC0VVlhwLX7EbsFKhT4v8N6EGqFXya97GP9q+hUSSRUIGayq2yoy7ZZjaFIV PYyK7L9rGJXgA6wBfZcFZ84OhZU3au0Jtq5nzVFkn8Zc0bxXbmc1gHY2pIeBbjiP 2CsVTnsl2Fq/ToPBjdKT1RpxtWCcnTNOVfkSWAyGuBYNweV3RY1QSLorLeSUheHo xJ3GaKWwo/xnfnC6AllLd0KRObn1zeFM78A7SIym5SFd/Wpqu6cWNWDS5q3zRinJ 6MOL6XnAamFnFbLw/eVovGJfbs+Z3e8bY/6SZasCAwEAAaOBrjCBqzAOBgNVHQ8B Af8EBAMCAYYwDwYDVR0TAQH/BAUwAwEB/zAdBgNVHQ4EFgQUiCcXCam2GGCL7Ou6 9kdZxVJUo7cwHwYDVR0jBBgwFoAUK9BpR5R2Cf70a40uQKb3R01/CF4wNgYDVR0f BC8wLTAroCmgJ4YlaHR0cDovL3d3dy5hcHBsZS5jb20vYXBwbGVjYS9yb290LmNy bDAQBgoqhkiG92NkBgIBBAIFADANBgkqhkiG9w0BAQUFAAOCAQEA2jIAlsVUlNM7 gjdmfS5o1cPGuMsmjEiQzxMkakaOY9Tw0BMG3djEwTcV8jMTOSYtzi5VQOMLA6/6 EsLnDSG41YDPrCgvzi2zTq+GGQTG6VDdTClHECP8bLsbmGtIieFbnd5G2zWFNe8+ 0OJYSzj07XVaH1xwHVY5EuXhDRHkiSUGvdW0FY5e0FmXkOlLgeLfGK9EdB4ZoDpH zJEdOusjWv6lLZf3e7vWh0ZChetSPSayY6i0scqP9Mzis8hH4L+aWYP62phTKoL1 fGUuldkzXfXtZcwxN8VaBOhr4eeIA0p1npsoy0pAiGVDdd3LOiUjxZ5X+C7O0qmS XnMuLyV1FQ== -----END CERTIFICATE----- certificate_authority-1.1.0/spec/samples/certs/ca.crt000066400000000000000000000171771425735453600227510ustar00rootroot00000000000000Certificate: Data: Version: 3 (0x2) Serial Number: e7:13:70:5d:01:cc:39:d2 Signature Algorithm: sha1WithRSAEncryption Issuer: C=AU, ST=Some-State, L=locality, O=Internet Widgits Pty Ltd, OU=section, CN=example ca/emailAddress=emailaddr@foo.com Validity Not Before: Sep 14 18:42:25 2012 GMT Not After : Sep 14 18:42:25 2013 GMT Subject: C=AU, ST=Some-State, L=locality, O=Internet Widgits Pty Ltd, OU=section, CN=example ca/emailAddress=emailaddr@foo.com Subject Public Key Info: Public Key Algorithm: rsaEncryption RSA Public Key: (4096 bit) Modulus (4096 bit): 00:c0:b4:be:5d:80:c2:19:5f:5c:f4:64:72:2f:3c: 0f:d1:6f:f2:69:74:de:ee:86:d2:a9:4e:02:85:d1: e2:b0:84:c8:73:1f:e0:0d:a9:9f:60:e0:53:7f:b3: 4e:ed:42:63:3d:19:09:bc:67:a0:13:32:dc:a0:25: ac:09:e1:7e:ae:72:73:4f:a1:9c:76:8c:80:35:e2: 75:2e:b2:4a:68:5f:03:83:bc:2e:63:1f:4a:7b:29: 80:2c:3e:fe:72:7f:43:ee:7b:59:6f:fc:51:8b:42: 0a:1b:b9:bb:5f:14:cd:9e:16:36:dd:05:ef:2c:21: 3e:de:39:bc:27:b1:0c:06:6a:49:f8:01:ff:3a:cc: 2c:5a:d9:56:0f:81:bc:6b:88:ee:83:72:d9:14:0c: 34:08:98:aa:1d:35:c2:34:fd:0c:3b:5d:5a:72:c1: 9b:28:ac:04:e1:30:3b:b8:ea:04:a2:48:34:a2:87: 3b:1c:b8:fc:89:f6:91:35:42:e9:6e:c7:af:ab:3b: f6:b7:0a:2e:e3:7f:31:45:1d:04:a5:a4:5a:f6:cd: bb:62:f9:49:e0:83:b3:77:df:ee:ba:84:bf:07:fa: 29:72:6d:2d:b8:e0:42:10:bd:f9:f3:e4:c6:7e:8a: e8:d7:11:b8:67:14:b4:8c:f5:c4:a9:fa:d9:04:bd: 17:7f:b4:d0:a3:b9:c7:df:44:2d:50:dd:ba:7c:01: 11:02:23:d9:9c:4c:9a:f0:92:2f:4a:b7:0a:72:3f: fc:61:6d:08:3b:cc:99:3c:65:78:92:76:63:a3:c4: cb:1f:b8:ca:4a:7a:b0:c4:22:b2:9a:13:b6:ae:19: 60:7a:bd:37:1b:93:0e:02:9f:22:e4:dc:21:24:11: 4d:9d:9e:30:7f:7b:92:48:2d:4d:b9:1a:2a:58:c6: 77:4b:70:cf:c1:a6:6d:06:f9:54:83:bf:27:73:8f: cc:8b:5c:7a:d2:79:56:75:41:d4:77:5f:9c:39:30: 02:0c:c5:0b:0c:32:6b:23:39:68:26:d5:f7:ef:b1: 7f:1e:c6:1d:a9:39:64:15:dd:b0:df:f5:d6:cf:c0: 73:b2:d0:f2:2a:f4:6e:9e:d8:9c:5b:9f:bf:ce:e7: 69:80:bf:69:d2:20:3b:aa:55:49:3e:fa:50:fc:63: 7e:8d:90:46:9a:7d:eb:9c:ca:17:bc:36:d7:88:e0: d9:c4:29:8c:2f:bc:ce:9d:e5:35:d5:ec:d7:a6:93: 49:2a:84:3b:b6:37:1e:0c:57:f1:04:bb:2f:ea:75: 75:6a:de:0c:71:42:e6:e9:14:5e:ee:55:1a:1f:a4: 87:21:2b:12:98:8b:17:63:c8:84:88:43:46:f4:8f: b2:b7:5b Exponent: 65537 (0x10001) X509v3 extensions: X509v3 Subject Key Identifier: 28:88:9A:FA:67:E6:16:C3:67:92:46:15:0B:46:12:08:54:45:AC:3A X509v3 Authority Key Identifier: keyid:28:88:9A:FA:67:E6:16:C3:67:92:46:15:0B:46:12:08:54:45:AC:3A DirName:/C=AU/ST=Some-State/L=locality/O=Internet Widgits Pty Ltd/OU=section/CN=example ca/emailAddress=emailaddr@foo.com serial:E7:13:70:5D:01:CC:39:D2 X509v3 Basic Constraints: CA:TRUE Signature Algorithm: sha1WithRSAEncryption 81:b5:c6:b6:4a:10:f5:8a:98:30:96:07:46:8e:bb:6b:b3:c1: 2e:fa:f3:59:ca:e1:c6:85:70:b6:bc:fe:9f:77:0a:70:1a:56: fb:46:71:d6:ee:90:cf:f5:a6:1d:4c:3c:24:76:32:fe:ec:55: d6:c4:cd:3a:ee:dd:2d:97:b6:3b:d3:3b:3f:02:21:1c:ba:03: 48:b8:8d:03:a7:b8:70:81:b6:43:82:82:5f:34:eb:b1:a7:01: cf:e4:f8:f7:d0:d8:5a:b6:c4:a6:0b:4e:e2:cc:4d:be:d4:99: f4:37:54:6f:a7:7d:63:7c:7f:25:34:bb:e0:f4:e2:cf:31:c3: 99:4e:9e:a0:d1:f0:3b:15:8e:95:2e:76:a7:14:f0:3b:0c:e7: 42:8d:12:53:f9:24:d7:f9:90:93:71:56:9a:5f:35:8e:f3:29: 1e:d8:d2:71:fe:d8:d0:eb:d9:61:d5:3e:dd:d8:8f:6a:10:0d: 7c:f8:2a:1d:ef:24:d0:c1:13:d7:d6:9e:f7:cc:20:62:ea:78: 71:d1:7a:5f:2e:20:0d:64:38:07:1b:c9:a0:0f:3c:c9:4e:3f: b1:84:8c:ae:86:ea:60:c7:84:2f:b6:d6:93:95:8d:36:9f:d2: 6a:bf:5e:98:1f:f9:da:fd:97:f3:e0:a7:0b:a7:52:30:e5:10: 89:c1:2c:85:75:07:a4:c7:d4:dc:b5:96:2a:c3:d5:eb:3b:7c: fb:a0:55:4c:c1:65:6c:f8:29:74:8b:78:54:7d:a0:d0:9c:32: 8a:87:48:90:ad:5c:41:fe:ab:16:5a:ad:44:06:1b:c6:4d:e4: 01:f0:d8:c7:b1:db:bf:ca:fd:31:75:0a:01:70:00:45:e1:2d: 98:42:d5:36:53:2c:f1:57:93:7d:cd:e6:24:f3:4d:c9:31:ce: a6:59:fe:2c:4a:61:56:9d:ad:55:7b:43:c5:48:ee:95:1e:b4: 0b:8d:4d:53:c7:a9:af:03:01:12:99:06:e8:fe:3a:63:8d:53: 30:8e:5a:95:64:d4:28:04:d8:bf:72:30:a1:fb:10:a7:84:15: 1b:13:a0:82:f3:38:5b:95:57:79:16:24:49:1a:1c:c5:43:90: 04:dd:5f:7b:d6:67:36:e1:41:fe:9d:20:a4:08:9c:14:9d:e3: 30:5c:1f:8e:46:f7:89:6b:f3:04:b9:75:b6:b6:33:49:54:99: 8e:9d:b0:f5:2b:51:61:3a:48:fc:f5:73:32:cd:a2:f6:9d:2a: 52:92:66:ae:a9:1f:06:b0:4d:63:29:83:e8:80:61:af:99:76: bc:fd:97:d4:98:0b:49:d5:94:24:b1:34:85:31:0a:4e:6c:c3: 0d:de:be:85:1d:49:41:6c -----BEGIN CERTIFICATE----- MIIG0zCCBLugAwIBAgIJAOcTcF0BzDnSMA0GCSqGSIb3DQEBBQUAMIGhMQswCQYD VQQGEwJBVTETMBEGA1UECBMKU29tZS1TdGF0ZTERMA8GA1UEBxMIbG9jYWxpdHkx ITAfBgNVBAoTGEludGVybmV0IFdpZGdpdHMgUHR5IEx0ZDEQMA4GA1UECxMHc2Vj dGlvbjETMBEGA1UEAxMKZXhhbXBsZSBjYTEgMB4GCSqGSIb3DQEJARYRZW1haWxh ZGRyQGZvby5jb20wHhcNMTIwOTE0MTg0MjI1WhcNMTMwOTE0MTg0MjI1WjCBoTEL MAkGA1UEBhMCQVUxEzARBgNVBAgTClNvbWUtU3RhdGUxETAPBgNVBAcTCGxvY2Fs aXR5MSEwHwYDVQQKExhJbnRlcm5ldCBXaWRnaXRzIFB0eSBMdGQxEDAOBgNVBAsT B3NlY3Rpb24xEzARBgNVBAMTCmV4YW1wbGUgY2ExIDAeBgkqhkiG9w0BCQEWEWVt YWlsYWRkckBmb28uY29tMIICIjANBgkqhkiG9w0BAQEFAAOCAg8AMIICCgKCAgEA wLS+XYDCGV9c9GRyLzwP0W/yaXTe7obSqU4ChdHisITIcx/gDamfYOBTf7NO7UJj PRkJvGegEzLcoCWsCeF+rnJzT6GcdoyANeJ1LrJKaF8Dg7wuYx9KeymALD7+cn9D 7ntZb/xRi0IKG7m7XxTNnhY23QXvLCE+3jm8J7EMBmpJ+AH/OswsWtlWD4G8a4ju g3LZFAw0CJiqHTXCNP0MO11acsGbKKwE4TA7uOoEokg0ooc7HLj8ifaRNULpbsev qzv2twou438xRR0EpaRa9s27YvlJ4IOzd9/uuoS/B/opcm0tuOBCEL358+TGforo 1xG4ZxS0jPXEqfrZBL0Xf7TQo7nH30QtUN26fAERAiPZnEya8JIvSrcKcj/8YW0I O8yZPGV4knZjo8TLH7jKSnqwxCKymhO2rhlger03G5MOAp8i5NwhJBFNnZ4wf3uS SC1NuRoqWMZ3S3DPwaZtBvlUg78nc4/Mi1x60nlWdUHUd1+cOTACDMULDDJrIzlo JtX377F/HsYdqTlkFd2w3/XWz8BzstDyKvRunticW5+/zudpgL9p0iA7qlVJPvpQ /GN+jZBGmn3rnMoXvDbXiODZxCmML7zOneU11ezXppNJKoQ7tjceDFfxBLsv6nV1 at4McULm6RRe7lUaH6SHISsSmIsXY8iEiENG9I+yt1sCAwEAAaOCAQowggEGMB0G A1UdDgQWBBQoiJr6Z+YWw2eSRhULRhIIVEWsOjCB1gYDVR0jBIHOMIHLgBQoiJr6 Z+YWw2eSRhULRhIIVEWsOqGBp6SBpDCBoTELMAkGA1UEBhMCQVUxEzARBgNVBAgT ClNvbWUtU3RhdGUxETAPBgNVBAcTCGxvY2FsaXR5MSEwHwYDVQQKExhJbnRlcm5l dCBXaWRnaXRzIFB0eSBMdGQxEDAOBgNVBAsTB3NlY3Rpb24xEzARBgNVBAMTCmV4 YW1wbGUgY2ExIDAeBgkqhkiG9w0BCQEWEWVtYWlsYWRkckBmb28uY29tggkA5xNw XQHMOdIwDAYDVR0TBAUwAwEB/zANBgkqhkiG9w0BAQUFAAOCAgEAgbXGtkoQ9YqY MJYHRo67a7PBLvrzWcrhxoVwtrz+n3cKcBpW+0Zx1u6Qz/WmHUw8JHYy/uxV1sTN Ou7dLZe2O9M7PwIhHLoDSLiNA6e4cIG2Q4KCXzTrsacBz+T499DYWrbEpgtO4sxN vtSZ9DdUb6d9Y3x/JTS74PTizzHDmU6eoNHwOxWOlS52pxTwOwznQo0SU/kk1/mQ k3FWml81jvMpHtjScf7Y0OvZYdU+3diPahANfPgqHe8k0MET19ae98wgYup4cdF6 Xy4gDWQ4BxvJoA88yU4/sYSMrobqYMeEL7bWk5WNNp/Sar9emB/52v2X8+CnC6dS MOUQicEshXUHpMfU3LWWKsPV6zt8+6BVTMFlbPgpdIt4VH2g0JwyiodIkK1cQf6r FlqtRAYbxk3kAfDYx7Hbv8r9MXUKAXAAReEtmELVNlMs8VeTfc3mJPNNyTHOpln+ LEphVp2tVXtDxUjulR60C41NU8eprwMBEpkG6P46Y41TMI5alWTUKATYv3IwofsQ p4QVGxOggvM4W5VXeRYkSRocxUOQBN1fe9ZnNuFB/p0gpAicFJ3jMFwfjkb3iWvz BLl1trYzSVSZjp2w9StRYTpI/PVzMs2i9p0qUpJmrqkfBrBNYymD6IBhr5l2vP2X 1JgLSdWUJLE0hTEKTmzDDd6+hR1JQWw= -----END CERTIFICATE----- certificate_authority-1.1.0/spec/samples/certs/ca.key000066400000000000000000000062531425735453600227420ustar00rootroot00000000000000-----BEGIN RSA PRIVATE KEY----- MIIJKQIBAAKCAgEAwLS+XYDCGV9c9GRyLzwP0W/yaXTe7obSqU4ChdHisITIcx/g DamfYOBTf7NO7UJjPRkJvGegEzLcoCWsCeF+rnJzT6GcdoyANeJ1LrJKaF8Dg7wu Yx9KeymALD7+cn9D7ntZb/xRi0IKG7m7XxTNnhY23QXvLCE+3jm8J7EMBmpJ+AH/ OswsWtlWD4G8a4jug3LZFAw0CJiqHTXCNP0MO11acsGbKKwE4TA7uOoEokg0ooc7 HLj8ifaRNULpbsevqzv2twou438xRR0EpaRa9s27YvlJ4IOzd9/uuoS/B/opcm0t uOBCEL358+TGforo1xG4ZxS0jPXEqfrZBL0Xf7TQo7nH30QtUN26fAERAiPZnEya 8JIvSrcKcj/8YW0IO8yZPGV4knZjo8TLH7jKSnqwxCKymhO2rhlger03G5MOAp8i 5NwhJBFNnZ4wf3uSSC1NuRoqWMZ3S3DPwaZtBvlUg78nc4/Mi1x60nlWdUHUd1+c OTACDMULDDJrIzloJtX377F/HsYdqTlkFd2w3/XWz8BzstDyKvRunticW5+/zudp gL9p0iA7qlVJPvpQ/GN+jZBGmn3rnMoXvDbXiODZxCmML7zOneU11ezXppNJKoQ7 tjceDFfxBLsv6nV1at4McULm6RRe7lUaH6SHISsSmIsXY8iEiENG9I+yt1sCAwEA AQKCAgAo+AQkwtQBKuoLNzOjYSSHxUIHM4aVtWoh/mjA5H9KQeCPwS4UGYS9xtNZ qdhUzrFkcudD+8/nZP/MuFWcACm7kq97NYObHIHBcvSwyczR5alMn3xJLITcLFWI kpfr5ayejfDUwxLfBVo6zMDOFREl09k0IifX/PVtr16WHajN0FkLdfk6GeAwLFaE k3NodUMoBQmrnnCNh09bSGuScl3gXRd2oDyJaBDdgzCfPnlfuvQdvZxOnfFqr1Xt ud0A5UkuoV/xSCPxz7+8zs+HG6sPH2wAPbl8FuPXz27kjoZCfufC4P1AecTx5EG4 nWGp04Ru/OB0Yc2EzldSP/dVb5IIAEeIzeqOC1dKh+m5BL6+KCMW4RA2VRmtVcSO Xu8/uLNw3coWj8OdUg4JJLWIuY17puKyhmMuZqXSwF5qcSCD2kGMtgdIFevHGLDy NCXIxAhnp/mWGJJvkRPxe7NVZSKGh7nfGGFbr2GAsTX5XZetk6ye/A+oNrUM4UP+ WQNtD5m2R1IPIp7f8NdTiSpAsKrgzDVEKSdXW/DObRm64DdRINuxGkrjDTOR6B+M HmcKQdMZPVyzWeFHF+JyR4+GVx2OZ/5lzEPTHO8thiA1nn1ov/RYZwuwrl8EKDt6 jgU26C+1TD3lL9smBdekOB8EtQT2EcuE7hwS6ca4Sryj5RYCmQKCAQEA/DTBSczZ HtPcHs75IiHfUXGoJB943LIR4cqrWKO2xy16MpamNQy8yBzet7pgXmrxixtar13B SpPozQNwlKD43rLSG77soY9eZcn2SQCOlcuekdRczR2jp4cnIo3bTXOm5OTz55EK WTLGkTirlcGCPFHv866qrmVQF+cIo3qtszrwuJGkOWTfU8X9zSN+AU+KwAqam5C5 ZbkcXFNwI2FOud5nQsOrjJ9Aivrw1CbHvHqAiZzZXjZmMNurH/cLX9jregxoiTAv wXsS1Apvvq0kvWJ9QrRBNyRsZ6qEt0ABgf2tl6+4t+S9bCmS2L6RzqjrxbkVPoM2 ChHX+8XkwuVUHwKCAQEAw5rao4MU4itSeaWLrny5oo57pICyY3zF5c5HySpaWyA8 JksxYCvNDR1aFZjCdOG9zOf5vLieniyTK+Tl3FF5P6OxSJOcQ9UOXaoTKny1Kw9W BMTbR0COLXsJSNvjNyZSyoLgOOLTZj11S+X/W1vb+Xi0kkx4SS6Tg3i5N+0Fl2Wa Mgucy/pgq+JBTlgKZ1Rjpna1T5ibUn0pT6fxgZ2U+FAD0cYNyaCjVmzMbiIkLUId jJEDGYXenyoIFI0rrJvF/0nl98BZjsT9+GNs0ZlejS0rfn2+DWlrLur3aqG2i3Yr L9nWZ51bSB7UhHALN2t2qd9etKWmz4YbuFHPvHOVRQKCAQEAgmdMoboXcYcdw5hE 3M8ixtu7kqHrPkGcWWEPY4+SzD3JdyrJ2ZgybE3xIpJtjaRCLCkCpLYXYVZFAuwK Y+8vfwZ6+PmpJIgayQq6G1j8YJud680gBraSjeal54ntoIhx/Nwc+NjXvvMwFJp2 rcIWctXy+c6QVgfwd8tvfgfKlGefW++COGLdzlULO+xkFI1qMo3JDzKviddCwMIr sz93E0fZoH3Hz7fwCWxi5W7/y4aTu5OsGLiL8itCug5khTSF3N9ZlcWii6n1PEoB KVghLQMlvT2ykq50ls1mPdIMdYgTH+Et43eUMb71PLicb3yMG/ns8Bur71z2jinu dI9bBQKCAQBk30XjTuUFIbw9mX6oNA/zYbEni2rzXVQdB70DY8EG/1+li99hrhTn r3xWaNnXNtcPhY3HohnCjlAzMa7MaIOzqvHw8JaEcKog6WVK4tb25sjAWtiOLR9l Gu8V0LejKDNH1ihVjbvhHM6RnoGKlpuhUnskeyUI8GkIQsiZq7TXd4EGT/DDTFJw MZTmFwb+dImTPeKQsq1e48bbGku0QRSi3XiqxI01ro6tMhxWq2qmoFLmu52ymtPM lvtlxcuBDzATUAO1OU+2Daa/Yl6q1IHrIiEs8SGCfxvULT38knq1/vGUkq077+00 CxojVjiiktu2DMglNswIdytyaVZM4/pVAoIBAQDmXzA7dptH9tOrp5/pYO4C8Fk0 4NB7OgKrhtE8YQtfghQn1O0FjvNT/o1+Gf9mf40Np7OtVulhZ56fQupmkk56TAJu P1YDVDRPLOvMkYeIsqqv6HnXGQaS6Bv91wrqwWPjLDD3pH+AdjP27SnObzARjRUq /s5LmI+u87rCldkGLG4qO4sjptWRlU8pIsLWQ3NckXINT7i8wwXzEfOcu7IzKWg6 sdzMWxc7Vt/4WE5m+9DLkpk3sD9dqELHry5lGWbpLHhfoVXoRdoFNVmyi2ckdr0J FJ6hkWkgQ4ee+yHyL3FfYRbGIolqaVSCd8/CvvDZ/24eS3/7TpCzf8SUqtL6 -----END RSA PRIVATE KEY----- certificate_authority-1.1.0/spec/samples/certs/client.crt000066400000000000000000000106431425735453600236330ustar00rootroot00000000000000Certificate: Data: Version: 1 (0x0) Serial Number: 2 (0x2) Signature Algorithm: sha1WithRSAEncryption Issuer: C=AU, ST=Some-State, L=locality, O=Internet Widgits Pty Ltd, OU=section, CN=example ca/emailAddress=emailaddr@foo.com Validity Not Before: Sep 14 18:43:15 2012 GMT Not After : Sep 14 18:43:15 2013 GMT Subject: C=AU, ST=Some-State, O=Internet Widgits Pty Ltd, CN=client.example.com Subject Public Key Info: Public Key Algorithm: rsaEncryption RSA Public Key: (1024 bit) Modulus (1024 bit): 00:dd:81:af:cb:67:4f:9d:2b:37:9f:bb:52:6a:17: f3:25:ba:ca:6d:23:45:94:dc:08:9b:cd:82:56:79: da:0b:47:c9:d0:3f:a8:16:bb:56:77:3a:85:a9:ab: 20:f3:1e:2d:5c:fa:b2:23:90:86:81:bf:a9:f6:10: 85:55:3f:c6:aa:7e:fa:43:96:08:c0:fa:e8:2f:cb: e5:2d:01:d1:12:22:a2:3f:7a:c5:75:4e:6f:47:d3: 2e:7a:fe:17:6e:4f:a3:20:60:5b:46:57:51:69:ce: 8f:33:87:05:df:1e:cb:fb:32:43:2a:71:6c:44:d1: a6:2d:98:41:e4:88:36:9a:f1 Exponent: 65537 (0x10001) Signature Algorithm: sha1WithRSAEncryption 9e:7f:e1:05:a7:9a:86:6c:0d:6e:37:76:ae:2e:5b:78:e5:22: 6f:52:40:36:e8:7e:cf:20:77:4f:3b:b4:7b:7c:72:46:68:df: fe:04:87:16:9f:cd:6e:0f:79:01:25:cf:ca:5e:b8:47:31:9e: f1:20:44:26:78:79:40:00:57:cc:a6:a8:39:67:ef:01:d7:65: 1c:dd:5d:8f:6e:48:43:f4:03:48:46:8f:08:95:cf:27:3f:30: da:82:a7:33:82:5f:82:cb:e6:2c:f0:25:e8:87:3b:e5:bf:82: 79:72:a9:10:45:69:3c:7f:f0:dd:c9:50:6e:02:c9:05:16:cf: c3:58:15:3f:a6:32:ec:80:4f:88:b4:72:d2:5f:70:62:24:98: e5:99:c8:a7:d9:dd:0c:b0:cb:9c:70:d1:6a:44:21:d9:d7:65: a6:71:6f:60:64:7e:28:de:5d:98:42:6d:aa:fc:32:f9:1c:d6: 5c:d7:b6:15:18:79:09:80:7e:d7:9e:74:16:a5:80:39:6d:93: 8e:8e:4e:c5:8c:f5:4c:ea:d3:fd:12:bc:fa:fc:b8:e2:2c:30: 52:f4:eb:ad:d9:56:e9:84:e9:a8:df:a3:16:fa:d2:1e:74:49: 5d:d1:24:10:f2:2e:c4:b9:4a:a9:2d:3d:a4:70:6d:24:00:26: 46:bf:e2:98:16:4d:c8:55:40:a7:ab:76:b3:c6:a7:72:46:2d: 9b:fd:a2:ca:b8:62:9c:59:53:cc:64:ef:60:76:10:c8:c9:e7: 51:11:82:d4:81:04:73:e9:af:df:2d:c4:c7:2d:e4:17:d4:e2: 10:82:68:56:ae:7a:f2:3c:60:b7:59:29:39:6a:56:86:94:fc: 93:2b:5b:f0:ac:80:1d:c7:c5:b7:27:36:94:1c:ad:e9:1c:6b: f3:8a:2a:6c:c8:ce:69:52:b2:42:d9:b9:e7:8e:a3:d4:18:07: a1:db:bf:54:3c:ec:2e:68:7f:cf:d6:71:8f:3e:99:88:e4:ea: 7f:98:22:3a:31:68:24:a5:47:23:e2:d6:21:8f:1f:5f:a7:9a: 12:10:ba:6d:ac:22:e7:97:95:93:a2:b5:1c:f8:c8:86:1a:ad: 32:ff:64:4f:25:8d:d5:25:29:46:85:30:bc:c4:86:41:1b:6b: 24:7e:04:b6:eb:46:39:55:9c:4d:84:86:2f:bf:11:26:a9:40: 3d:2d:f4:90:22:05:7f:27:3b:13:d1:86:17:70:05:e1:68:be: 12:ce:c5:30:7b:0a:1b:7b:8a:89:e6:e7:9a:9c:b0:8b:c1:f8: c3:b6:0a:4c:41:da:fe:cd:ea:ce:89:3b:a5:8d:30:90:93:93: 9b:85:b6:61:46:8d:69:f2 -----BEGIN CERTIFICATE----- MIID9DCCAdwCAQIwDQYJKoZIhvcNAQEFBQAwgaExCzAJBgNVBAYTAkFVMRMwEQYD VQQIEwpTb21lLVN0YXRlMREwDwYDVQQHEwhsb2NhbGl0eTEhMB8GA1UEChMYSW50 ZXJuZXQgV2lkZ2l0cyBQdHkgTHRkMRAwDgYDVQQLEwdzZWN0aW9uMRMwEQYDVQQD EwpleGFtcGxlIGNhMSAwHgYJKoZIhvcNAQkBFhFlbWFpbGFkZHJAZm9vLmNvbTAe Fw0xMjA5MTQxODQzMTVaFw0xMzA5MTQxODQzMTVaMGIxCzAJBgNVBAYTAkFVMRMw EQYDVQQIEwpTb21lLVN0YXRlMSEwHwYDVQQKExhJbnRlcm5ldCBXaWRnaXRzIFB0 eSBMdGQxGzAZBgNVBAMTEmNsaWVudC5leGFtcGxlLmNvbTCBnzANBgkqhkiG9w0B AQEFAAOBjQAwgYkCgYEA3YGvy2dPnSs3n7tSahfzJbrKbSNFlNwIm82CVnnaC0fJ 0D+oFrtWdzqFqasg8x4tXPqyI5CGgb+p9hCFVT/Gqn76Q5YIwProL8vlLQHREiKi P3rFdU5vR9Muev4Xbk+jIGBbRldRac6PM4cF3x7L+zJDKnFsRNGmLZhB5Ig2mvEC AwEAATANBgkqhkiG9w0BAQUFAAOCAgEAnn/hBaeahmwNbjd2ri5beOUib1JANuh+ zyB3Tzu0e3xyRmjf/gSHFp/Nbg95ASXPyl64RzGe8SBEJnh5QABXzKaoOWfvAddl HN1dj25IQ/QDSEaPCJXPJz8w2oKnM4JfgsvmLPAl6Ic75b+CeXKpEEVpPH/w3clQ bgLJBRbPw1gVP6Yy7IBPiLRy0l9wYiSY5ZnIp9ndDLDLnHDRakQh2ddlpnFvYGR+ KN5dmEJtqvwy+RzWXNe2FRh5CYB+1550FqWAOW2Tjo5OxYz1TOrT/RK8+vy44iww UvTrrdlW6YTpqN+jFvrSHnRJXdEkEPIuxLlKqS09pHBtJAAmRr/imBZNyFVAp6t2 s8anckYtm/2iyrhinFlTzGTvYHYQyMnnURGC1IEEc+mv3y3Exy3kF9TiEIJoVq56 8jxgt1kpOWpWhpT8kytb8KyAHcfFtyc2lByt6Rxr84oqbMjOaVKyQtm5546j1BgH odu/VDzsLmh/z9Zxjz6ZiOTqf5giOjFoJKVHI+LWIY8fX6eaEhC6bawi55eVk6K1 HPjIhhqtMv9kTyWN1SUpRoUwvMSGQRtrJH4EtutGOVWcTYSGL78RJqlAPS30kCIF fyc7E9GGF3AF4Wi+Es7FMHsKG3uKiebnmpywi8H4w7YKTEHa/s3qzok7pY0wkJOT m4W2YUaNafI= -----END CERTIFICATE----- certificate_authority-1.1.0/spec/samples/certs/client.csr000066400000000000000000000012031425735453600236220ustar00rootroot00000000000000-----BEGIN CERTIFICATE REQUEST----- MIIBojCCAQsCAQAwYjELMAkGA1UEBhMCQVUxEzARBgNVBAgTClNvbWUtU3RhdGUx ITAfBgNVBAoTGEludGVybmV0IFdpZGdpdHMgUHR5IEx0ZDEbMBkGA1UEAxMSY2xp ZW50LmV4YW1wbGUuY29tMIGfMA0GCSqGSIb3DQEBAQUAA4GNADCBiQKBgQDdga/L Z0+dKzefu1JqF/MlusptI0WU3AibzYJWedoLR8nQP6gWu1Z3OoWpqyDzHi1c+rIj kIaBv6n2EIVVP8aqfvpDlgjA+ugvy+UtAdESIqI/esV1Tm9H0y56/hduT6MgYFtG V1Fpzo8zhwXfHsv7MkMqcWxE0aYtmEHkiDaa8QIDAQABoAAwDQYJKoZIhvcNAQEF BQADgYEARSbIBSvvhvqX7zMBap+RcQfMdXbSQTI3iNVSEOoUtfuGEJOmkHrWwsz0 ZfKv/qC9LBeWD+yqDeKbuRNJEla2oIInUfs3FINYLsm3jufsuBpVPY1OOglq91VD v1zBodLbTvbHIHKStsMfNaS9lKYZ/PycWNXxhQhpZZAeGBqk3mY= -----END CERTIFICATE REQUEST----- certificate_authority-1.1.0/spec/samples/certs/client.key000066400000000000000000000015731425735453600236350ustar00rootroot00000000000000-----BEGIN RSA PRIVATE KEY----- MIICXgIBAAKBgQDdga/LZ0+dKzefu1JqF/MlusptI0WU3AibzYJWedoLR8nQP6gW u1Z3OoWpqyDzHi1c+rIjkIaBv6n2EIVVP8aqfvpDlgjA+ugvy+UtAdESIqI/esV1 Tm9H0y56/hduT6MgYFtGV1Fpzo8zhwXfHsv7MkMqcWxE0aYtmEHkiDaa8QIDAQAB AoGBAIdkD4mqWhVdJyCxJMzIWsyDAdv3pT45x+FDmhk1XbtrY8WwQxOx6kXyNWTh vsAbf+rHKT9nxW9lMYO/0V+sHcdRtG0NEXPfB8pX7LEsaHpRPHkVoiWpRlCL/maM ci85RPsATlDkiOn1luysfk2PHy5aSKG0RkLS7lSkahTQOn9JAkEA8goUnDdh5hVm 7o2npeZG66Zb2mnm1l8aO9LJ76u6L/jmTx5aSRXP0aBiDb1PXJZedqhd/MdPcYEI QsFCIoT/iwJBAOpIbAK15eWsubUme/UoCbfNpM4H3jQXbSODgJ83nwmqr2slrX9m soz/+2nZl6/TL344xxTmDChFGivHdh7JXvMCQBoHi3/hVN3xn0g4Y7crtKTTFz29 9d1IDQIyARWNWlCea+ZGVV9WwSrCHMlteoNyiGYqZTEyHhEO11yWfA5KT1ECQQCY bFHJWaqeyMdxsf4Hu+rGqIZGfRv17B/XcSDndXqFAYVrQnIkZx5XWduqPCTSAaXu iuYLFLhoIr0qKnURBpY9AkEAy9JOf5tqc1jFndLIgVtXzM5KptIFHr1ZA7VSfrdY ozt9adbCTXfNTTLqAK0N7A2F7T3APEbpPuR2a7TpzawRWA== -----END RSA PRIVATE KEY----- certificate_authority-1.1.0/spec/samples/certs/github.com.pem000066400000000000000000000151321425735453600244030ustar00rootroot00000000000000Certificate: Data: Version: 3 (0x2) Serial Number: 0e:77:76:8a:5d:07:f0:e5:79:59:ca:2a:9d:50:82:b5 Signature Algorithm: sha1WithRSAEncryption Issuer: C=US, O=DigiCert Inc, OU=www.digicert.com, CN=DigiCert High Assurance EV CA-1 Validity Not Before: May 27 00:00:00 2011 GMT Not After : Jul 29 12:00:00 2013 GMT Subject: businessCategory=Private Organization/1.3.6.1.4.1.311.60.2.1.3=US/1.3.6.1.4.1.311.60.2.1.2=California/serialNumber=C3268102, C=US, ST=California, L=San Francisco, O=GitHub, Inc., CN=github.com Subject Public Key Info: Public Key Algorithm: rsaEncryption RSA Public Key: (2048 bit) Modulus (2048 bit): 00:ed:d3:89:c3:5d:70:72:09:f3:33:4f:1a:72:74: d9:b6:5a:95:50:bb:68:61:9f:f7:fb:1f:19:e1:da: 04:31:af:15:7c:1a:7f:f9:73:af:1d:e5:43:2b:56: 09:00:45:69:4a:e8:c4:5b:df:c2:77:52:51:19:5b: d1:2b:d9:39:65:36:a0:32:19:1c:41:73:fb:32:b2: 3d:9f:98:ec:82:5b:0b:37:64:39:2c:b7:10:83:72: cd:f0:ea:24:4b:fa:d9:94:2e:c3:85:15:39:a9:3a: f6:88:da:f4:27:89:a6:95:4f:84:a2:37:4e:7c:25: 78:3a:c9:83:6d:02:17:95:78:7d:47:a8:55:83:ee: 13:c8:19:1a:b3:3c:f1:5f:fe:3b:02:e1:85:fb:11: 66:ab:09:5d:9f:4c:43:f0:c7:24:5e:29:72:28:ce: d4:75:68:4f:24:72:29:ae:39:28:fc:df:8d:4f:4d: 83:73:74:0c:6f:11:9b:a7:dd:62:de:ff:e2:eb:17: e6:ff:0c:bf:c0:2d:31:3b:d6:59:a2:f2:dd:87:4a: 48:7b:6d:33:11:14:4d:34:9f:32:38:f6:c8:19:9d: f1:b6:3d:c5:46:ef:51:0b:8a:c6:33:ed:48:61:c4: 1d:17:1b:bd:7c:b6:67:e9:39:cf:a5:52:80:0a:f4: ea:cd Exponent: 65537 (0x10001) X509v3 extensions: X509v3 Authority Key Identifier: keyid:4C:58:CB:25:F0:41:4F:52:F4:28:C8:81:43:9B:A6:A8:A0:E6:92:E5 X509v3 Subject Key Identifier: 87:D1:8F:19:6E:E4:87:6F:53:8C:77:91:07:50:DF:A3:BF:55:47:20 X509v3 Subject Alternative Name: DNS:github.com, DNS:www.github.com Authority Information Access: OCSP - URI:http://ocsp.digicert.com CA Issuers - URI:http://www.digicert.com/CACerts/DigiCertHighAssuranceEVCA-1.crt X509v3 Basic Constraints: critical CA:FALSE X509v3 CRL Distribution Points: URI:http://crl3.digicert.com/ev2009a.crl URI:http://crl4.digicert.com/ev2009a.crl X509v3 Certificate Policies: Policy: 2.16.840.1.114412.2.1 CPS: http://www.digicert.com/ssl-cps-repository.htm User Notice: Explicit Text: X509v3 Extended Key Usage: TLS Web Server Authentication, TLS Web Client Authentication Netscape Cert Type: SSL Client, SSL Server X509v3 Key Usage: critical Digital Signature, Key Encipherment Signature Algorithm: sha1WithRSAEncryption 14:52:71:1f:86:9d:6d:35:3e:86:bb:66:1a:8b:85:98:b9:00: 4c:cb:42:b5:46:fc:06:e7:44:39:c8:e8:52:d8:11:14:23:b3: 72:96:e9:14:94:9e:2f:00:28:f7:d5:04:45:40:00:c6:f4:57: 42:42:de:09:89:97:11:0d:14:5c:6b:bd:0b:f7:18:a3:5f:67: 02:f3:09:38:63:bf:c1:12:9d:30:ba:8e:a5:54:74:59:53:67: a1:1b:50:5b:26:da:fd:13:7e:59:17:bf:49:ef:94:7e:45:a4: fd:3a:49:32:f0:6a:ff:89:8d:a9:61:a9:aa:9b:96:46:c8:1c: e0:18:1c:e6:fb:82:f4:0a:ab:52:a6:ca:e8:54:22:d9:db:2a: 3d:5a:22:7b:80:ea:07:05:d4:f9:c7:f0:53:59:5f:bb:77:7e: de:93:70:41:4e:23:cb:78:79:79:c4:2e:ea:d7:66:2a:18:f7: d1:c5:7c:e2:12:78:82:8d:1d:ec:82:9e:01:a2:e5:02:be:78: a1:b9:59:58:c5:4c:6f:4f:a5:31:b4:49:5b:5e:98:1e:2e:38: f6:19:c4:39:a2:4a:fb:79:05:b8:f2:59:e5:26:12:70:ad:c0: e8:75:23:1f:18:d1:0b:e0:9f:65:e4:c3:d7:49:87:5b:72:6c: b1:2f:ac:6f -----BEGIN CERTIFICATE----- MIIHKjCCBhKgAwIBAgIQDnd2il0H8OV5WcoqnVCCtTANBgkqhkiG9w0BAQUFADBp MQswCQYDVQQGEwJVUzEVMBMGA1UEChMMRGlnaUNlcnQgSW5jMRkwFwYDVQQLExB3 d3cuZGlnaWNlcnQuY29tMSgwJgYDVQQDEx9EaWdpQ2VydCBIaWdoIEFzc3VyYW5j ZSBFViBDQS0xMB4XDTExMDUyNzAwMDAwMFoXDTEzMDcyOTEyMDAwMFowgcoxHTAb BgNVBA8MFFByaXZhdGUgT3JnYW5pemF0aW9uMRMwEQYLKwYBBAGCNzwCAQMTAlVT MRswGQYLKwYBBAGCNzwCAQITCkNhbGlmb3JuaWExETAPBgNVBAUTCEMzMjY4MTAy MQswCQYDVQQGEwJVUzETMBEGA1UECBMKQ2FsaWZvcm5pYTEWMBQGA1UEBxMNU2Fu IEZyYW5jaXNjbzEVMBMGA1UEChMMR2l0SHViLCBJbmMuMRMwEQYDVQQDEwpnaXRo dWIuY29tMIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEA7dOJw11wcgnz M08acnTZtlqVULtoYZ/3+x8Z4doEMa8VfBp/+XOvHeVDK1YJAEVpSujEW9/Cd1JR GVvRK9k5ZTagMhkcQXP7MrI9n5jsglsLN2Q5LLcQg3LN8OokS/rZlC7DhRU5qTr2 iNr0J4mmlU+EojdOfCV4OsmDbQIXlXh9R6hVg+4TyBkaszzxX/47AuGF+xFmqwld n0xD8MckXilyKM7UdWhPJHIprjko/N+NT02Dc3QMbxGbp91i3v/i6xfm/wy/wC0x O9ZZovLdh0pIe20zERRNNJ8yOPbIGZ3xtj3FRu9RC4rGM+1IYcQdFxu9fLZn6TnP pVKACvTqzQIDAQABo4IDajCCA2YwHwYDVR0jBBgwFoAUTFjLJfBBT1L0KMiBQ5um qKDmkuUwHQYDVR0OBBYEFIfRjxlu5IdvU4x3kQdQ36O/VUcgMCUGA1UdEQQeMByC CmdpdGh1Yi5jb22CDnd3dy5naXRodWIuY29tMIGBBggrBgEFBQcBAQR1MHMwJAYI KwYBBQUHMAGGGGh0dHA6Ly9vY3NwLmRpZ2ljZXJ0LmNvbTBLBggrBgEFBQcwAoY/ aHR0cDovL3d3dy5kaWdpY2VydC5jb20vQ0FDZXJ0cy9EaWdpQ2VydEhpZ2hBc3N1 cmFuY2VFVkNBLTEuY3J0MAwGA1UdEwEB/wQCMAAwYQYDVR0fBFowWDAqoCigJoYk aHR0cDovL2NybDMuZGlnaWNlcnQuY29tL2V2MjAwOWEuY3JsMCqgKKAmhiRodHRw Oi8vY3JsNC5kaWdpY2VydC5jb20vZXYyMDA5YS5jcmwwggHEBgNVHSAEggG7MIIB tzCCAbMGCWCGSAGG/WwCATCCAaQwOgYIKwYBBQUHAgEWLmh0dHA6Ly93d3cuZGln aWNlcnQuY29tL3NzbC1jcHMtcmVwb3NpdG9yeS5odG0wggFkBggrBgEFBQcCAjCC AVYeggFSAEEAbgB5ACAAdQBzAGUAIABvAGYAIAB0AGgAaQBzACAAQwBlAHIAdABp AGYAaQBjAGEAdABlACAAYwBvAG4AcwB0AGkAdAB1AHQAZQBzACAAYQBjAGMAZQBw AHQAYQBuAGMAZQAgAG8AZgAgAHQAaABlACAARABpAGcAaQBDAGUAcgB0ACAAQwBQ AC8AQwBQAFMAIABhAG4AZAAgAHQAaABlACAAUgBlAGwAeQBpAG4AZwAgAFAAYQBy AHQAeQAgAEEAZwByAGUAZQBtAGUAbgB0ACAAdwBoAGkAYwBoACAAbABpAG0AaQB0 ACAAbABpAGEAYgBpAGwAaQB0AHkAIABhAG4AZAAgAGEAcgBlACAAaQBuAGMAbwBy AHAAbwByAGEAdABlAGQAIABoAGUAcgBlAGkAbgAgAGIAeQAgAHIAZQBmAGUAcgBl AG4AYwBlAC4wHQYDVR0lBBYwFAYIKwYBBQUHAwEGCCsGAQUFBwMCMBEGCWCGSAGG +EIBAQQEAwIGwDAOBgNVHQ8BAf8EBAMCBaAwDQYJKoZIhvcNAQEFBQADggEBABRS cR+GnW01Poa7ZhqLhZi5AEzLQrVG/AbnRDnI6FLYERQjs3KW6RSUni8AKPfVBEVA AMb0V0JC3gmJlxENFFxrvQv3GKNfZwLzCThjv8ESnTC6jqVUdFlTZ6EbUFsm2v0T flkXv0nvlH5FpP06STLwav+JjalhqaqblkbIHOAYHOb7gvQKq1KmyuhUItnbKj1a InuA6gcF1PnH8FNZX7t3ft6TcEFOI8t4eXnELurXZioY99HFfOISeIKNHeyCngGi 5QK+eKG5WVjFTG9PpTG0SVtemB4uOPYZxDmiSvt5BbjyWeUmEnCtwOh1Ix8Y0Qvg n2Xkw9dJh1tybLEvrG8= -----END CERTIFICATE----- certificate_authority-1.1.0/spec/samples/certs/server.crt000066400000000000000000000110721425735453600236600ustar00rootroot00000000000000Certificate: Data: Version: 1 (0x0) Serial Number: 1 (0x1) Signature Algorithm: sha1WithRSAEncryption Issuer: C=AU, ST=Some-State, L=locality, O=Internet Widgits Pty Ltd, OU=section, CN=example ca/emailAddress=emailaddr@foo.com Validity Not Before: Sep 14 18:43:03 2012 GMT Not After : Sep 14 18:43:03 2013 GMT Subject: C=AU, ST=Some-State, L=locality, O=Internet Widgits Pty Ltd, OU=ouname, CN=server.example.com/emailAddress=email@example.com Subject Public Key Info: Public Key Algorithm: rsaEncryption RSA Public Key: (1024 bit) Modulus (1024 bit): 00:c2:6b:ec:81:d7:3f:94:09:f0:a6:f1:78:65:79: a7:28:26:5c:b7:96:86:5b:77:9a:be:67:7a:16:e0: a2:c8:8d:9c:ec:5d:9a:56:0e:c7:f4:8b:11:01:df: d2:94:80:ba:87:2b:cf:d4:33:4a:06:21:3b:c6:31: b5:fc:b4:30:f6:fd:28:58:5f:18:11:46:08:fa:12: 75:82:c2:e9:e6:65:9a:e4:fe:8d:f0:36:63:91:06: bb:43:f6:72:50:5c:8b:00:e3:53:2a:09:34:06:e6: 05:16:30:d0:f8:96:3b:b0:7d:b6:8d:ef:5f:b0:c1: 31:bc:77:d4:a9:93:31:d7:f1 Exponent: 65537 (0x10001) Signature Algorithm: sha1WithRSAEncryption aa:c7:e4:d5:c5:33:e8:75:43:01:23:9f:ae:91:c3:17:dc:54: d5:34:65:e3:76:ec:00:e4:71:06:70:84:10:2d:ae:ea:21:90: 70:27:ec:86:15:97:e5:e4:10:62:19:08:b5:56:86:4f:84:ef: 43:c7:86:06:33:8a:61:bb:71:ac:f9:e1:d2:f0:08:83:32:bb: 73:a0:fd:39:66:8f:a3:d9:bb:59:bb:c2:cc:5e:8f:56:fc:72: b2:42:da:d2:31:1b:98:be:0d:0c:8d:1a:8e:12:fe:7f:ef:5b: cf:93:b7:e1:ba:c8:a0:c4:de:60:4f:74:ea:12:9d:3a:ea:81: 28:b3:ed:14:6e:22:00:23:56:b0:ef:d4:7e:6b:7d:4a:fb:7e: 3b:c2:a8:9e:84:42:43:ad:6d:b1:41:78:75:a6:32:46:5c:98: c2:e5:3b:d0:dd:cc:17:35:7b:f9:54:25:ef:38:07:82:dc:a3: 32:69:bb:15:28:71:a9:c7:a3:8e:55:29:61:04:eb:ee:05:e9: 9f:4b:f6:c7:6e:9b:02:19:e9:0c:5e:66:a1:65:fe:ae:6e:25: a4:a4:31:3c:40:0d:b9:f4:c2:44:40:23:65:85:58:33:5a:0d: 84:3d:24:71:43:0f:65:69:28:75:de:ae:b3:b7:82:a4:09:f1: b7:21:8f:5d:76:66:4c:d7:08:19:80:68:a0:9f:33:df:46:a7: ab:7f:45:4d:1f:d1:45:54:8f:53:b9:da:77:86:b3:e2:b2:7e: 72:a2:6e:ad:08:01:2a:05:79:d7:ba:a2:17:c7:82:72:58:7c: 4d:fb:b9:0e:09:54:24:1e:34:e0:ae:32:d7:0a:00:1b:23:e4: 95:1b:8e:28:6c:7b:31:55:ad:6b:bb:e0:76:d3:2e:d2:14:0c: 02:9a:b5:65:ce:54:c7:28:08:7b:85:3c:43:00:09:c3:90:4e: b0:9c:57:f0:66:d2:18:95:ce:4e:18:f0:81:f1:16:a1:b0:ca: a8:85:33:c4:8c:b5:06:9c:eb:e6:5b:b9:13:31:14:53:83:3c: 8f:0e:02:56:f9:b7:07:d5:a0:66:8a:8a:06:ee:c7:46:a2:e6: b2:f5:ef:be:f5:ac:e0:a3:fa:9e:1d:03:b1:40:0f:b8:8c:bb: ab:f0:13:55:db:05:65:eb:8a:f7:03:86:e0:a5:bb:f2:ae:ab: f4:32:7b:2c:28:56:fb:7d:30:b3:c1:71:c7:52:3c:a9:ee:a0: 82:6f:b6:90:54:b7:5b:f2:74:11:4d:40:89:e4:cb:c3:50:b8: f7:e6:98:bd:95:7e:1c:8f:b9:e8:9f:ef:d6:ca:3b:77:40:6a: dc:1e:51:74:c0:32:53:61 -----BEGIN CERTIFICATE----- MIIEOzCCAiMCAQEwDQYJKoZIhvcNAQEFBQAwgaExCzAJBgNVBAYTAkFVMRMwEQYD VQQIEwpTb21lLVN0YXRlMREwDwYDVQQHEwhsb2NhbGl0eTEhMB8GA1UEChMYSW50 ZXJuZXQgV2lkZ2l0cyBQdHkgTHRkMRAwDgYDVQQLEwdzZWN0aW9uMRMwEQYDVQQD EwpleGFtcGxlIGNhMSAwHgYJKoZIhvcNAQkBFhFlbWFpbGFkZHJAZm9vLmNvbTAe Fw0xMjA5MTQxODQzMDNaFw0xMzA5MTQxODQzMDNaMIGoMQswCQYDVQQGEwJBVTET MBEGA1UECBMKU29tZS1TdGF0ZTERMA8GA1UEBxMIbG9jYWxpdHkxITAfBgNVBAoT GEludGVybmV0IFdpZGdpdHMgUHR5IEx0ZDEPMA0GA1UECxMGb3VuYW1lMRswGQYD VQQDExJzZXJ2ZXIuZXhhbXBsZS5jb20xIDAeBgkqhkiG9w0BCQEWEWVtYWlsQGV4 YW1wbGUuY29tMIGfMA0GCSqGSIb3DQEBAQUAA4GNADCBiQKBgQDCa+yB1z+UCfCm 8XhleacoJly3loZbd5q+Z3oW4KLIjZzsXZpWDsf0ixEB39KUgLqHK8/UM0oGITvG MbX8tDD2/ShYXxgRRgj6EnWCwunmZZrk/o3wNmORBrtD9nJQXIsA41MqCTQG5gUW MND4ljuwfbaN71+wwTG8d9SpkzHX8QIDAQABMA0GCSqGSIb3DQEBBQUAA4ICAQCq x+TVxTPodUMBI5+ukcMX3FTVNGXjduwA5HEGcIQQLa7qIZBwJ+yGFZfl5BBiGQi1 VoZPhO9Dx4YGM4phu3Gs+eHS8AiDMrtzoP05Zo+j2btZu8LMXo9W/HKyQtrSMRuY vg0MjRqOEv5/71vPk7fhusigxN5gT3TqEp066oEos+0UbiIAI1aw79R+a31K+347 wqiehEJDrW2xQXh1pjJGXJjC5TvQ3cwXNXv5VCXvOAeC3KMyabsVKHGpx6OOVSlh BOvuBemfS/bHbpsCGekMXmahZf6ubiWkpDE8QA259MJEQCNlhVgzWg2EPSRxQw9l aSh13q6zt4KkCfG3IY9ddmZM1wgZgGignzPfRqerf0VNH9FFVI9Tudp3hrPisn5y om6tCAEqBXnXuqIXx4JyWHxN+7kOCVQkHjTgrjLXCgAbI+SVG44obHsxVa1ru+B2 0y7SFAwCmrVlzlTHKAh7hTxDAAnDkE6wnFfwZtIYlc5OGPCB8RahsMqohTPEjLUG nOvmW7kTMRRTgzyPDgJW+bcH1aBmiooG7sdGouay9e++9azgo/qeHQOxQA+4jLur 8BNV2wVl64r3A4bgpbvyrqv0MnssKFb7fTCzwXHHUjyp7qCCb7aQVLdb8nQRTUCJ 5MvDULj35pi9lX4cj7non+/Wyjt3QGrcHlF0wDJTYQ== -----END CERTIFICATE----- certificate_authority-1.1.0/spec/samples/certs/server.csr000066400000000000000000000013451425735453600236610ustar00rootroot00000000000000-----BEGIN CERTIFICATE REQUEST----- MIIB6TCCAVICAQAwgagxCzAJBgNVBAYTAkFVMRMwEQYDVQQIEwpTb21lLVN0YXRl MREwDwYDVQQHEwhsb2NhbGl0eTEhMB8GA1UEChMYSW50ZXJuZXQgV2lkZ2l0cyBQ dHkgTHRkMQ8wDQYDVQQLEwZvdW5hbWUxGzAZBgNVBAMTEnNlcnZlci5leGFtcGxl LmNvbTEgMB4GCSqGSIb3DQEJARYRZW1haWxAZXhhbXBsZS5jb20wgZ8wDQYJKoZI hvcNAQEBBQADgY0AMIGJAoGBAMJr7IHXP5QJ8KbxeGV5pygmXLeWhlt3mr5nehbg osiNnOxdmlYOx/SLEQHf0pSAuocrz9QzSgYhO8Yxtfy0MPb9KFhfGBFGCPoSdYLC 6eZlmuT+jfA2Y5EGu0P2clBciwDjUyoJNAbmBRYw0PiWO7B9to3vX7DBMbx31KmT MdfxAgMBAAGgADANBgkqhkiG9w0BAQUFAAOBgQAyoSojahrYbGK+6QbxOO7i5Ufm VHlhVBbPFfmYDrpWjoRlKVwk1iRNi/3ijQi3oPONk19wRh/A0gD0DOiKi3fz2m5K gaFLIRcBy25EYVeBic39A6b69SiXQoHv00f5CBHNSHLk4hc30vGIWifexU8ehwJJ TlHmdHkECni6w0eDmg== -----END CERTIFICATE REQUEST----- certificate_authority-1.1.0/spec/samples/certs/server.key000066400000000000000000000015671425735453600236700ustar00rootroot00000000000000-----BEGIN RSA PRIVATE KEY----- MIICXAIBAAKBgQDCa+yB1z+UCfCm8XhleacoJly3loZbd5q+Z3oW4KLIjZzsXZpW Dsf0ixEB39KUgLqHK8/UM0oGITvGMbX8tDD2/ShYXxgRRgj6EnWCwunmZZrk/o3w NmORBrtD9nJQXIsA41MqCTQG5gUWMND4ljuwfbaN71+wwTG8d9SpkzHX8QIDAQAB AoGBALX9FBvOAsNuMofyjEJgh6m7jxqCmi3QXVdRwSTdDUMx2+wdCjT4DQ/JhRf+ DT3Y6cFRr27wu5/VSACT08hCW3mVgE5WcsmxvxPeaWMQCAUoHW4I9bmpvf/1AksZ x36N4GwdEvFjDGlM8B1ndW3qLUp6e5iDFUFB7veQGoA6WR/hAkEA39yivn0ThKH/ 9a8glJMBDVmnzNXQveBX+Y0aiJSNTT+rBfDRgWxR8A60z1Xl1iHtGG1j1tRXvDL3 2u2w4d1lnQJBAN5VTMFkf7ojTvPh0+QHkc7b3d0IRruojEIG3A1ZdUj4WaOqnOBs xk6EWsF5YbCBTWH42qWAq/EBXaJcPbyIdWUCQF25882LcpOSfCcyJpLuJX+gbPf/ AYGuH0dVg6lxgOO553H6TM1CO+AlWCCC11LbK3iRvD5i80TRliJsaCV426UCQEAD RS8lNVUtV00GhxBPUZ7CVPWPrXXYSFG2UeMSD5+ryXtC4xoGl24B03OC9CpygAom MSWXj2m7X+8gKbI/g7UCQEc0Ne6+4T0NsCz9Dw2TWqIvi+WbK65veyFC212OJ5Wh qEF4SY3WXkxJk4Y0ElQARMz6DojpwHI5PtAaYswe8wI= -----END RSA PRIVATE KEY----- certificate_authority-1.1.0/spec/spec_helper.rb000066400000000000000000000004021425735453600216650ustar00rootroot00000000000000require 'rubygems' require 'bundler/setup' require 'certificate_authority' require 'pathname' require 'pry' require 'coveralls' Coveralls.wear! SAMPLES_DIR = Pathname.new(__dir__).join('samples').freeze def sample_file(name) SAMPLES_DIR.join(name) end certificate_authority-1.1.0/spec/units/000077500000000000000000000000001425735453600202155ustar00rootroot00000000000000certificate_authority-1.1.0/spec/units/certificate_authority_spec.rb000066400000000000000000000000451425735453600261450ustar00rootroot00000000000000describe CertificateAuthority do end certificate_authority-1.1.0/spec/units/certificate_revocation_list_spec.rb000066400000000000000000000100631425735453600273220ustar00rootroot00000000000000describe CertificateAuthority::CertificateRevocationList do before(:each) do @crl = CertificateAuthority::CertificateRevocationList.new @root_certificate = CertificateAuthority::Certificate.new @root_certificate.signing_entity = true @root_certificate.subject.common_name = "CRL Root" @root_certificate.key_material.generate_key(768) @root_certificate.serial_number.number = 1 @root_certificate.sign! @certificate = CertificateAuthority::Certificate.new @certificate.key_material.generate_key(768) @certificate.subject.common_name = "http://bogusSite.com" @certificate.parent = @root_certificate @certificate.serial_number.number = 2 @certificate.sign! @serial_number = CertificateAuthority::SerialNumber.new @serial_number.revoked_at = Time.now @serial_number.number = 5 @crl.parent = @root_certificate @certificate.revoked_at = Time.now end it "should accept a list of certificates" do @crl << @certificate end it "should complain if you add a certificate without a revocation time" do @certificate.revoked_at = nil expect{ @crl << @certificate}.to raise_error(RuntimeError) end it "should have a 'parent' that will be responsible for signing" do @crl.parent = @root_certificate expect(@crl.parent).not_to be_nil end it "should raise an error if you try and sign a CRL without attaching a parent" do @crl.parent = nil expect { @crl.sign! }.to raise_error(RuntimeError) end it "should be able to generate a proper CRL" do @crl << @certificate expect {@crl.to_pem}.to raise_error(RuntimeError) @crl.parent = @root_certificate @crl.sign! expect(@crl.to_pem).not_to be_nil expect(OpenSSL::X509::CRL.new(@crl.to_pem)).not_to be_nil end it "should be able to mix Certificates and SerialNumbers for convenience" do @crl << @certificate @crl << @serial_number @crl.parent = @root_certificate @crl.sign! openssl_csr = OpenSSL::X509::CRL.new(@crl.to_pem) expect(openssl_csr.revoked.size).to eq(2) end it "should have the correct number of entities" do @crl << @certificate @crl.parent = @root_certificate @crl.sign! openssl_clr = OpenSSL::X509::CRL.new(@crl.to_pem) expect(openssl_clr.revoked).to be_a(Array) expect(openssl_clr.revoked.size).to eq(1) end it "should have the serial numbers of revoked entities" do @crl << @certificate @crl << @serial_number @crl.parent = @root_certificate @crl.sign! openssl_clr = OpenSSL::X509::CRL.new(@crl.to_pem) expect(openssl_clr.revoked).to be_a(Array) expect(openssl_clr.revoked.first.serial).to eq(@certificate.serial_number.number) expect(openssl_clr.revoked.last.serial).to eq(@serial_number.number) end it "should be valid according to OpenSSL and signer" do @crl << @certificate @crl.parent = @root_certificate @crl.sign! openssl_clr = OpenSSL::X509::CRL.new(@crl.to_pem) openssl_root = OpenSSL::X509::Certificate.new(@root_certificate.to_pem) expect(openssl_clr.verify(openssl_root.public_key)).to be_truthy end describe "Digests" do it "should use SHA512 by default" do @crl << @certificate @crl.parent = @root_certificate @crl.sign! openssl_clr = OpenSSL::X509::CRL.new(@crl.to_pem) expect(openssl_clr.signature_algorithm).to eq("sha512WithRSAEncryption") end it "should support alternate digests supported by OpenSSL" do @crl << @certificate @crl.parent = @root_certificate @crl.sign!({"digest" => "SHA1"}) openssl_clr = OpenSSL::X509::CRL.new(@crl.to_pem) expect(openssl_clr.signature_algorithm).to eq("sha1WithRSAEncryption") end end describe "Next update" do it "should be able to set a 'next_update' value" do @crl.next_update = (60 * 60 * 10) # 10 Hours expect(@crl.next_update).not_to be_nil end it "should throw an error if we try and sign up with a negative next_update" do @crl.sign! @crl.next_update = - (60 * 60 * 10) expect{@crl.sign!}.to raise_error(RuntimeError) end end end certificate_authority-1.1.0/spec/units/certificate_spec.rb000066400000000000000000000477021425735453600240500ustar00rootroot00000000000000describe CertificateAuthority::Certificate do before(:each) do @certificate = CertificateAuthority::Certificate.new end describe CertificateAuthority::SigningEntity do it "should behave as a signing entity" do expect(@certificate.respond_to?(:is_signing_entity?)).to be_truthy end it "should only be a signing entity if it's identified as a CA", :rfc3280 => true do expect(@certificate.is_signing_entity?).to be_falsey @certificate.signing_entity = true expect(@certificate.is_signing_entity?).to be_truthy end describe "Root certificates" do before(:each) do @certificate.signing_entity = true end it "should be able to be identified as a root certificate" do expect(@certificate.is_root_entity?).to be_truthy end it "should only be a root certificate if the parent entity is itself", :rfc3280 => true do expect(@certificate.parent).to eq(@certificate) end it "should be a root certificate by default" do expect(@certificate.is_root_entity?).to be_truthy end it "should be able to self-sign" do @certificate.serial_number.number = 1 @certificate.subject.common_name = "chrischandler.name" @certificate.key_material.generate_key(768) @certificate.sign! cert = OpenSSL::X509::Certificate.new(@certificate.to_pem) expect(cert.subject.to_s).to eq(cert.issuer.to_s) end it "should have the basicContraint CA:TRUE" do @certificate.serial_number.number = 1 @certificate.subject.common_name = "chrischandler.name" @certificate.key_material.generate_key(768) @certificate.sign! cert = OpenSSL::X509::Certificate.new(@certificate.to_pem) expect(cert.extensions.map{|i| [i.oid,i.value] }.select{|i| i.first == "basicConstraints"}.first[1]).to eq("CA:TRUE") end end describe "Intermediate certificates" do before(:each) do @different_cert = CertificateAuthority::Certificate.new @different_cert.signing_entity = true @different_cert.subject.common_name = "chrischandler.name root" @different_cert.key_material.generate_key(768) @different_cert.serial_number.number = 2 @different_cert.sign! #self-signed @certificate.parent = @different_cert @certificate.signing_entity = true end it "should be able to be identified as an intermediate certificate" do expect(@certificate.is_intermediate_entity?).to be_truthy end it "should not be identified as a root" do expect(@certificate.is_root_entity?).to be_falsey end it "should only be an intermediate certificate if the parent is a different entity" do expect(@certificate.parent).not_to eq(@certificate) expect(@certificate.parent).not_to be_nil end it "should correctly be signed by a parent certificate" do @certificate.subject.common_name = "chrischandler.name" @certificate.key_material.generate_key(768) @certificate.signing_entity = true @certificate.serial_number.number = 1 @certificate.sign! cert = OpenSSL::X509::Certificate.new(@certificate.to_pem) expect(cert.subject.to_s).not_to eq(cert.issuer.to_s) end it "should have the basicContraint CA:TRUE" do @certificate.subject.common_name = "chrischandler.name" @certificate.key_material.generate_key(768) @certificate.signing_entity = true @certificate.serial_number.number = 3 @certificate.sign! cert = OpenSSL::X509::Certificate.new(@certificate.to_pem) expect(cert.extensions.map{|i| [i.oid,i.value] }.select{|i| i.first == "basicConstraints"}.first[1]).to eq("CA:TRUE") end end describe "Terminal certificates" do before(:each) do @different_cert = CertificateAuthority::Certificate.new @different_cert.signing_entity = true @different_cert.subject.common_name = "chrischandler.name root" @different_cert.key_material.generate_key(768) @different_cert.serial_number.number = 1 @different_cert.sign! #self-signed @certificate.parent = @different_cert end it "should not be identified as an intermediate certificate" do expect(@certificate.is_intermediate_entity?).to be_falsey end it "should not be identified as a root" do expect(@certificate.is_root_entity?).to be_falsey end it "should have the basicContraint CA:FALSE" do @certificate.subject.common_name = "chrischandler.name" @certificate.key_material.generate_key(768) @certificate.signing_entity = false @certificate.serial_number.number = 1 @certificate.sign! cert = OpenSSL::X509::Certificate.new(@certificate.to_pem) expect(cert.extensions.map{|i| [i.oid,i.value] }.select{|i| i.first == "basicConstraints"}.first[1]).to eq("CA:FALSE") end end it "should be able to be identified as a root certificate" do expect(@certificate.respond_to?(:is_root_entity?)).to be_truthy end end #End of SigningEntity describe "Signed certificates" do before(:each) do @certificate = CertificateAuthority::Certificate.new @certificate.subject.common_name = "chrischandler.name" @certificate.key_material.generate_key(768) @certificate.serial_number.number = 1 @certificate.sign! end it "should have a PEM encoded certificate body available" do expect(@certificate.to_pem).not_to be_nil expect(OpenSSL::X509::Certificate.new(@certificate.to_pem)).not_to be_nil end end describe "X.509 V3 Extensions on Signed Certificates" do before(:each) do @certificate = CertificateAuthority::Certificate.new @certificate.subject.common_name = "chrischandler.name" @certificate.key_material.generate_key(768) @certificate.serial_number.number = 1 @signing_profile = { "extensions" => { "subjectAltName" => {"uris" => ["www.chrischandler.name"]}, "certificatePolicies" => { "policy_identifier" => "1.3.5.7", "cps_uris" => ["http://my.host.name/", "http://my.your.name/"], "user_notice" => { "explicit_text" => "Testing!", "organization" => "RSpec Test organization name", "notice_numbers" => "1,2,3,4" } } } } @certificate.sign!(@signing_profile) end describe "SubjectAltName" do before(:each) do @certificate = CertificateAuthority::Certificate.new @certificate.subject.common_name = "chrischandler.name" @certificate.key_material.generate_key(768) @certificate.serial_number.number = 1 end it "should have a subjectAltName if specified" do @certificate.sign!({"extensions" => {"subjectAltName" => {"uris" => ["www.chrischandler.name"]}}}) cert = OpenSSL::X509::Certificate.new(@certificate.to_pem) expect(cert.extensions.map(&:oid).include?("subjectAltName")).to be_truthy end it "should NOT have a subjectAltName if one was not specified" do @certificate.sign! cert = OpenSSL::X509::Certificate.new(@certificate.to_pem) expect(cert.extensions.map(&:oid).include?("subjectAltName")).to be_falsey end it 'should replace email:copy with email address' do @certificate.subject.email_address = 'foo@bar.com' @certificate.sign!( { "extensions" => { "subjectAltName" => { 'emails' => %w[copy fubar@bar.com] } } } ) cert = OpenSSL::X509::Certificate.new(@certificate.to_pem) alt = cert.extensions.select { |e| e.oid == 'subjectAltName' }.first expect(alt.value).to eq('email:foo@bar.com, email:fubar@bar.com') end end describe "AuthorityInfoAccess" do before(:each) do @certificate = CertificateAuthority::Certificate.new @certificate.subject.common_name = "chrischandler.name" @certificate.key_material.generate_key(768) @certificate.serial_number.number = 1 end it "should have an authority info access if specified" do @certificate.sign!({"extensions" => {"authorityInfoAccess" => {"ocsp" => ["www.chrischandler.name"]}}}) cert = OpenSSL::X509::Certificate.new(@certificate.to_pem) expect(cert.extensions.map(&:oid).include?("authorityInfoAccess")).to be_truthy end end describe "CrlDistributionPoints" do before(:each) do @certificate = CertificateAuthority::Certificate.new @certificate.subject.common_name = "chrischandler.name" @certificate.key_material.generate_key(768) @certificate.serial_number.number = 1 end it "should have a crlDistributionPoint if specified" do @certificate.sign!({"extensions" => {"crlDistributionPoints" => {"uris" => ["http://crlThingy.com"]}}}) cert = OpenSSL::X509::Certificate.new(@certificate.to_pem) expect(cert.extensions.map(&:oid).include?("crlDistributionPoints")).to be_truthy end it "should NOT have a crlDistributionPoint if one was not specified" do @certificate.sign! cert = OpenSSL::X509::Certificate.new(@certificate.to_pem) expect(cert.extensions.map(&:oid).include?("crlDistributionPoints")).to be_falsey end end describe "CertificatePolicies" do before(:each) do @certificate = CertificateAuthority::Certificate.new @certificate.subject.common_name = "chrischandler.name" @certificate.key_material.generate_key(768) @certificate.serial_number.number = 1 end it "should have a certificatePolicy if specified" do @certificate.sign!({ "extensions" => { "certificatePolicies" => { "policy_identifier" => "1.3.5.7", "cps_uris" => ["http://my.host.name/", "http://my.your.name/"] } } }) cert = OpenSSL::X509::Certificate.new(@certificate.to_pem) expect(cert.extensions.map(&:oid).include?("certificatePolicies")).to be_truthy end pending "should contain a nested userNotice if specified" do #pending @certificate.sign!({ "extensions" => { "certificatePolicies" => { "policy_identifier" => "1.3.5.7", "cps_uris" => ["http://my.host.name/", "http://my.your.name/"], "user_notice" => { "explicit_text" => "Testing explicit text!", "organization" => "RSpec Test organization name", "notice_numbers" => "1,2,3,4" } } } }) cert = OpenSSL::X509::Certificate.new(@certificate.to_pem) expect(cert.extensions.map(&:oid).include?("certificatePolicies")).to be_truthy ## Checking OIDs after they've run through OpenSSL is a pain... ## The nicely structured data will be flattened to a single String cert.extensions.each do |ext| if ext.oid == "certificatePolicies" expect(ext.to_a[1]).to include("Testing explicit text!") end end end it "should NOT include a certificatePolicy if not specified" do @certificate.sign! cert = OpenSSL::X509::Certificate.new(@certificate.to_pem) expect(cert.extensions.map(&:oid).include?("certificatePolicies")).to be_falsey end end it "should support BasicConstraints" do cert = OpenSSL::X509::Certificate.new(@certificate.to_pem) expect(cert.extensions.map(&:oid).include?("basicConstraints")).to be_truthy end it "should support subjectKeyIdentifier" do cert = OpenSSL::X509::Certificate.new(@certificate.to_pem) expect(cert.extensions.map(&:oid).include?("subjectKeyIdentifier")).to be_truthy end it "should support authorityKeyIdentifier" do cert = OpenSSL::X509::Certificate.new(@certificate.to_pem) expect(cert.extensions.map(&:oid).include?("authorityKeyIdentifier")).to be_truthy end it "should order subjectKeyIdentifier before authorityKeyIdentifier" do cert = OpenSSL::X509::Certificate.new(@certificate.to_pem) expect(cert.extensions.map(&:oid).select do |oid| ["subjectKeyIdentifier", "authorityKeyIdentifier"].include?(oid) end).to eq(["subjectKeyIdentifier", "authorityKeyIdentifier"]) end it "should support keyUsage" do cert = OpenSSL::X509::Certificate.new(@certificate.to_pem) expect(cert.extensions.map(&:oid).include?("keyUsage")).to be_truthy end it "should support extendedKeyUsage" do cert = OpenSSL::X509::Certificate.new(@certificate.to_pem) expect(cert.extensions.map(&:oid).include?("extendedKeyUsage")).to be_truthy end end describe "Signing profile" do before(:each) do @certificate = CertificateAuthority::Certificate.new @certificate.subject.common_name = "chrischandler.name" @certificate.key_material.generate_key(768) @certificate.serial_number.number = 1 @signing_profile = { "extensions" => { "basicConstraints" => {"ca" => false}, "crlDistributionPoints" => {"uri" => "http://notme.com/other.crl" }, "subjectKeyIdentifier" => {}, "authorityKeyIdentifier" => {}, "authorityInfoAccess" => {"ocsp" => ["http://youFillThisOut/ocsp/"], "ca_issuers" => ["http://me.com/other.crt"] }, "keyUsage" => {"usage" => ["digitalSignature","nonRepudiation"] }, "extendedKeyUsage" => {"usage" => [ "serverAuth","clientAuth"]}, "subjectAltName" => {"uris" => ["http://subdomains.youFillThisOut/"]}, "certificatePolicies" => { "policy_identifier" => "1.3.5.8", "cps_uris" => ["http://my.host.name/", "http://my.your.name/"], "user_notice" => { "explicit_text" => "Explicit Text Here", "organization" => "Organization name", "notice_numbers" => "1,2,3,4" } } } } end it "should be able to sign with an optional policy hash" do @certificate.sign!(@signing_profile) end it "should support a default signing digest of SHA512" do @certificate.sign!(@signing_profile) cert = OpenSSL::X509::Certificate.new(@certificate.to_pem) expect(cert.signature_algorithm).to eq("sha512WithRSAEncryption") end it "should support a configurable digest algorithm" do @signing_profile.merge!({"digest" => "SHA1"}) @certificate.sign!(@signing_profile) cert = OpenSSL::X509::Certificate.new(@certificate.to_pem) expect(cert.signature_algorithm).to eq("sha1WithRSAEncryption") end end describe "from_openssl" do before(:each) do @pem_cert=< Time.now + year - day end it "should be able to have a revoked at time" do expect(@certificate.revoked?).to be_falsey @certificate.revoked_at = Time.now.utc expect(@certificate.revoked?).to be_truthy end end certificate_authority-1.1.0/spec/units/distinguished_name_spec.rb000066400000000000000000000062001425735453600254150ustar00rootroot00000000000000describe CertificateAuthority::DistinguishedName do before(:each) do @distinguished_name = CertificateAuthority::DistinguishedName.new end it "should provide the standard x.509 distinguished name common attributes" do expect(@distinguished_name.respond_to?(:cn)).to be_truthy expect(@distinguished_name.respond_to?(:l)).to be_truthy expect(@distinguished_name.respond_to?(:s)).to be_truthy expect(@distinguished_name.respond_to?(:o)).to be_truthy expect(@distinguished_name.respond_to?(:ou)).to be_truthy expect(@distinguished_name.respond_to?(:c)).to be_truthy expect(@distinguished_name.respond_to?(:emailAddress)).to be_truthy expect(@distinguished_name.respond_to?(:serialNumber)).to be_truthy end it "should provide human-readable equivalents to the distinguished name common attributes" do expect(@distinguished_name.respond_to?(:common_name)).to be_truthy expect(@distinguished_name.respond_to?(:locality)).to be_truthy expect(@distinguished_name.respond_to?(:state)).to be_truthy expect(@distinguished_name.respond_to?(:organization)).to be_truthy expect(@distinguished_name.respond_to?(:organizational_unit)).to be_truthy expect(@distinguished_name.respond_to?(:country)).to be_truthy expect(@distinguished_name.respond_to?(:email_address)).to be_truthy expect(@distinguished_name.respond_to?(:serial_number)).to be_truthy end it "should require a common name" do expect(@distinguished_name.valid?).to be_falsey expect(@distinguished_name.errors.size).to eq(1) @distinguished_name.common_name = "chrischandler.name" expect(@distinguished_name.valid?).to be_truthy end it "should be convertible to an OpenSSL::X509::Name" do @distinguished_name.common_name = "chrischandler.name" @distinguished_name.to_x509_name end describe "from_openssl" do before do subject = "/CN=justincummins.name/L=on my laptop/ST=relaxed/C=as/O=programmer/OU=using this code" @name = OpenSSL::X509::Name.parse subject @dn = CertificateAuthority::DistinguishedName.from_openssl @name end it "should reject non Name objects" do expect { CertificateAuthority::DistinguishedName.from_openssl "Not a OpenSSL::X509::Name" }.to raise_error(RuntimeError) end [:common_name, :locality, :state, :country, :organization, :organizational_unit].each do |field| it "should set the #{field} attribute" do expect(@dn.send(field)).not_to be_nil end end it "should create an equivalent object" do expect(@dn.to_x509_name.to_s.split('/')).to match_array(@name.to_s.split('/')) end end describe CertificateAuthority::WrappedDistinguishedName do it "should mark the DN as having custom OIDs if there's an unknown subject element" do OpenSSL::ASN1::ObjectId.register("2.3.4.5","testing","testingCustomOIDs") subject = "/testingCustomOIDs=custom/CN=justincummins.name/L=on my laptop/ST=relaxed/C=as/O=programmer/OU=using this code" @name = OpenSSL::X509::Name.parse subject @dn = CertificateAuthority::DistinguishedName.from_openssl @name expect(@dn.custom_oids?).to be_truthy end end end certificate_authority-1.1.0/spec/units/extensions_spec.rb000066400000000000000000000230341425735453600237550ustar00rootroot00000000000000describe CertificateAuthority::Extensions do describe CertificateAuthority::Extensions::BasicConstraints do it "should only allow true/false" do basic_constraints = CertificateAuthority::Extensions::BasicConstraints.new expect(basic_constraints.valid?).to be_truthy basic_constraints.ca = "moo" expect(basic_constraints.valid?).to be_falsey end it "should respond to :path_len" do basic_constraints = CertificateAuthority::Extensions::BasicConstraints.new expect(basic_constraints.respond_to?(:path_len)).to be_truthy end it "should raise an error if :path_len isn't a non-negative integer" do basic_constraints = CertificateAuthority::Extensions::BasicConstraints.new expect {basic_constraints.path_len = "moo"}.to raise_error(ArgumentError) expect {basic_constraints.path_len = -1}.to raise_error(ArgumentError) expect {basic_constraints.path_len = 1.5}.to raise_error(ArgumentError) end it "should generate a proper OpenSSL extension string" do basic_constraints = CertificateAuthority::Extensions::BasicConstraints.new basic_constraints.ca = true basic_constraints.path_len = 2 expect(basic_constraints.to_s).to eq("CA:true,pathlen:2") end it "should parse values from a proper OpenSSL extension string" do basic_constraints = CertificateAuthority::Extensions::BasicConstraints.parse("CA:true,pathlen:2", true) expect(basic_constraints.critical).to be_truthy expect(basic_constraints.ca).to be_truthy expect(basic_constraints.path_len).to eq(2) end end describe CertificateAuthority::Extensions::SubjectAlternativeName do it "should respond to :uris" do subjectAltName = CertificateAuthority::Extensions::SubjectAlternativeName.new expect(subjectAltName.respond_to?(:uris)).to be_truthy end it "should require 'uris' to be an Array" do subjectAltName = CertificateAuthority::Extensions::SubjectAlternativeName.new expect {subjectAltName.uris = "not an array"}.to raise_error(RuntimeError) end it "should generate a proper OpenSSL extension string for URIs" do subjectAltName = CertificateAuthority::Extensions::SubjectAlternativeName.new subjectAltName.uris = ["http://localhost.altname.example.com"] expect(subjectAltName.to_s).to eq("URI:http://localhost.altname.example.com") subjectAltName.uris = ["http://localhost.altname.example.com", "http://other.example.com"] expect(subjectAltName.to_s).to eq("URI:http://localhost.altname.example.com,URI:http://other.example.com") end it "should parse URIs from a proper OpenSSL extension string" do subjectAltName = CertificateAuthority::Extensions::SubjectAlternativeName.parse("URI:http://localhost.altname.example.com", false) expect(subjectAltName.uris).to eq(["http://localhost.altname.example.com"]) subjectAltName = CertificateAuthority::Extensions::SubjectAlternativeName.parse("URI:http://localhost.altname.example.com,URI:http://other.example.com", false) expect(subjectAltName.uris).to eq(["http://localhost.altname.example.com", "http://other.example.com"]) end it "should respond to :dns_names" do subjectAltName = CertificateAuthority::Extensions::SubjectAlternativeName.new expect(subjectAltName.respond_to?(:dns_names)).to be_truthy end it "should require 'dns_names' to be an Array" do subjectAltName = CertificateAuthority::Extensions::SubjectAlternativeName.new expect {subjectAltName.dns_names = "not an array"}.to raise_error(RuntimeError) end it "should generate a proper OpenSSL extension string for DNS names" do subjectAltName = CertificateAuthority::Extensions::SubjectAlternativeName.new subjectAltName.dns_names = ["localhost.altname.example.com"] expect(subjectAltName.to_s).to eq("DNS:localhost.altname.example.com") subjectAltName.dns_names = ["localhost.altname.example.com", "other.example.com"] expect(subjectAltName.to_s).to eq("DNS:localhost.altname.example.com,DNS:other.example.com") end it "should parse DNS names from a proper OpenSSL extension string" do subjectAltName = CertificateAuthority::Extensions::SubjectAlternativeName.parse("DNS:localhost.altname.example.com", false) expect(subjectAltName.dns_names).to eq(["localhost.altname.example.com"]) subjectAltName = CertificateAuthority::Extensions::SubjectAlternativeName.parse("DNS:localhost.altname.example.com,DNS:other.example.com", false) expect(subjectAltName.dns_names).to eq(["localhost.altname.example.com", "other.example.com"]) end it "should respond to :ips" do subjectAltName = CertificateAuthority::Extensions::SubjectAlternativeName.new expect(subjectAltName.respond_to?(:ips)).to be_truthy end it "should require 'ips' to be an Array" do subjectAltName = CertificateAuthority::Extensions::SubjectAlternativeName.new expect {subjectAltName.ips = "not an array"}.to raise_error(RuntimeError) end it "should generate a proper OpenSSL extension string for IPs" do subjectAltName = CertificateAuthority::Extensions::SubjectAlternativeName.new subjectAltName.ips = ["1.2.3.4"] expect(subjectAltName.to_s).to eq("IP:1.2.3.4") subjectAltName.ips = ["1.2.3.4", "5.6.7.8"] expect(subjectAltName.to_s).to eq("IP:1.2.3.4,IP:5.6.7.8") end it "should parse IPs from a proper OpenSSL extension string" do subjectAltName = CertificateAuthority::Extensions::SubjectAlternativeName.parse("IP:1.2.3.4", false) expect(subjectAltName.ips).to eq(["1.2.3.4"]) subjectAltName = CertificateAuthority::Extensions::SubjectAlternativeName.parse("IP:1.2.3.4,IP:5.6.7.8", false) expect(subjectAltName.ips).to eq(["1.2.3.4", "5.6.7.8"]) end describe 'emails' do let(:subject) { CertificateAuthority::Extensions::SubjectAlternativeName.new } it "should require 'emails' to be an Array" do expect { subject.emails = "not an array" }.to raise_error "Emails must be an array" end it "should generate a proper OpenSSL extension string for emails" do subject.emails = ["copy"] expect(subject.to_s).to eq("email:copy") subject.emails = ["copy", "foo@bar.com"] expect(subject.to_s).to eq("email:copy,email:foo@bar.com") end end it "should generate a proper OpenSSL extension string for URIs IPs and DNS names together" do subjectAltName = CertificateAuthority::Extensions::SubjectAlternativeName.new subjectAltName.ips = ["1.2.3.4"] expect(subjectAltName.to_s).to eq("IP:1.2.3.4") subjectAltName.dns_names = ["localhost.altname.example.com"] expect(subjectAltName.to_s).to eq("DNS:localhost.altname.example.com,IP:1.2.3.4") subjectAltName.dns_names = ["localhost.altname.example.com", "other.example.com"] expect(subjectAltName.to_s).to eq("DNS:localhost.altname.example.com,DNS:other.example.com,IP:1.2.3.4") subjectAltName.ips = ["1.2.3.4", "5.6.7.8"] expect(subjectAltName.to_s).to eq("DNS:localhost.altname.example.com,DNS:other.example.com,IP:1.2.3.4,IP:5.6.7.8") subjectAltName.uris = ["http://localhost.altname.example.com"] expect(subjectAltName.to_s).to eq("URI:http://localhost.altname.example.com,DNS:localhost.altname.example.com,DNS:other.example.com,IP:1.2.3.4,IP:5.6.7.8") subjectAltName.uris = ["http://localhost.altname.example.com", "http://other.altname.example.com"] expect(subjectAltName.to_s).to eq("URI:http://localhost.altname.example.com,URI:http://other.altname.example.com,DNS:localhost.altname.example.com,DNS:other.example.com,IP:1.2.3.4,IP:5.6.7.8") end it "should parse URIs IPs and DNS names together from a proper OpenSSL extension string" do subjectAltName = CertificateAuthority::Extensions::SubjectAlternativeName.parse("IP:1.2.3.4", false) expect(subjectAltName.ips).to eq(["1.2.3.4"]) subjectAltName = CertificateAuthority::Extensions::SubjectAlternativeName.parse("DNS:localhost.altname.example.com,IP:1.2.3.4", false) expect(subjectAltName.dns_names).to eq(["localhost.altname.example.com"]) subjectAltName = CertificateAuthority::Extensions::SubjectAlternativeName.parse("DNS:localhost.altname.example.com,DNS:other.example.com,IP:1.2.3.4", false) expect(subjectAltName.dns_names).to eq(["localhost.altname.example.com", "other.example.com"]) subjectAltName = CertificateAuthority::Extensions::SubjectAlternativeName.parse("DNS:localhost.altname.example.com,DNS:other.example.com,IP:1.2.3.4,IP:5.6.7.8", false) expect(subjectAltName.ips).to eq(["1.2.3.4", "5.6.7.8"]) subjectAltName = CertificateAuthority::Extensions::SubjectAlternativeName.parse("URI:http://localhost.altname.example.com,DNS:localhost.altname.example.com,DNS:other.example.com,IP:1.2.3.4,IP:5.6.7.8", false) expect(subjectAltName.uris).to eq(["http://localhost.altname.example.com"]) subjectAltName = CertificateAuthority::Extensions::SubjectAlternativeName.parse("URI:http://localhost.altname.example.com,URI:http://other.altname.example.com,DNS:localhost.altname.example.com,DNS:other.example.com,IP:1.2.3.4,IP:5.6.7.8", false) expect(subjectAltName.uris).to eq(["http://localhost.altname.example.com", "http://other.altname.example.com"]) subjectAltName.emails= ["copy", "foo@bar.com"] expect(subjectAltName.to_s).to eq("URI:http://localhost.altname.example.com,URI:http://other.altname.example.com,DNS:localhost.altname.example.com,DNS:other.example.com,IP:1.2.3.4,IP:5.6.7.8,email:copy,email:foo@bar.com") end end end certificate_authority-1.1.0/spec/units/key_material_spec.rb000066400000000000000000000153361425735453600242320ustar00rootroot00000000000000describe CertificateAuthority::KeyMaterial do [CertificateAuthority::MemoryKeyMaterial, CertificateAuthority::SigningRequestKeyMaterial].each do |key_material_class| before do @key_material = key_material_class.new end it "#{key_material_class} should know if a key is in memory or hardware" do expect(@key_material.is_in_hardware?).not_to be_nil expect(@key_material.is_in_memory?).not_to be_nil end it "should use memory by default" do expect(@key_material.is_in_memory?).to be_truthy end end describe "reading keys from PEM" do before(:each) do @key_pair=< {"keyUsage" => {"usage" => ["critical", "keyCertSign"] }} }) @certificate = CertificateAuthority::Certificate.new @certificate.key_material.generate_key(768) @certificate.subject.common_name = "http://questionablesite.com" @certificate.parent = @root_certificate @certificate.serial_number.number = 2 @certificate.sign! @ocsp_request = OpenSSL::OCSP::Request.new @ocsp_request.add_nonce openssl_cert_issuer = OpenSSL::X509::Certificate.new(@root_certificate.to_pem) openssl_cert_subject = OpenSSL::X509::Certificate.new(@certificate.to_pem) cert_id = OpenSSL::OCSP::CertificateId.new(openssl_cert_subject, openssl_cert_issuer) @ocsp_request.add_certid(cert_id) @ocsp_request_reader = CertificateAuthority::OCSPRequestReader.from_der(@ocsp_request.to_der) @response_builder = CertificateAuthority::OCSPResponseBuilder.from_request_reader(@ocsp_request_reader) @response_builder.parent = @root_certificate end it "should build from a OCSPRequestReader" do expect(@response_builder).not_to be_nil expect(@response_builder).to be_a(CertificateAuthority::OCSPResponseBuilder) end it "should build a response" do response = @response_builder.build_response expect(response).to be_a(OpenSSL::OCSP::Response) end it "should verify against the root" do response = @response_builder.build_response root_cert = OpenSSL::X509::Certificate.new(@root_certificate.to_pem) store = OpenSSL::X509::Store.new store.add_cert(root_cert) expect(response.basic.verify([root_cert],store)).to be_truthy end it "should have a configurable nextUpdate" do time = 30 * 60 # 30 minutes @response_builder.next_update=time response = @response_builder.build_response response.basic.status.each do |status| ## 3 seconds of wabble is OK expect(status[5]).to be_within(3).of(status[4] + time) end end describe "verification mechanisms" do it "should support an everything's OK default (though somewhat useless)" do response = @response_builder.build_response response.basic.status.each do |status| expect(status[1]).to eq(OpenSSL::OCSP::V_CERTSTATUS_GOOD) end end it "should support an overridable verification mechanism callback" do verification = lambda {|serial_number| [CertificateAuthority::OCSPResponseBuilder::REVOKED,CertificateAuthority::OCSPResponseBuilder::UNSPECIFIED] } @response_builder.verification_mechanism = verification response = @response_builder.build_response response.basic.status.each do |status| expect(status[1]).to eq(OpenSSL::OCSP::V_CERTSTATUS_REVOKED) end end end end ## DEPRECATED describe CertificateAuthority::OCSPHandler do before(:each) do @ocsp_handler = CertificateAuthority::OCSPHandler.new @root_certificate = CertificateAuthority::Certificate.new @root_certificate.signing_entity = true @root_certificate.subject.common_name = "OCSP Root" @root_certificate.key_material.generate_key(768) @root_certificate.serial_number.number = 1 @root_certificate.sign! @certificate = CertificateAuthority::Certificate.new @certificate.key_material.generate_key(768) @certificate.subject.common_name = "http://questionablesite.com" @certificate.parent = @root_certificate @certificate.serial_number.number = 2 @certificate.sign! @ocsp_request = OpenSSL::OCSP::Request.new openssl_cert_issuer = OpenSSL::X509::Certificate.new(@root_certificate.to_pem) openssl_cert_subject = OpenSSL::X509::Certificate.new(@certificate.to_pem) cert_id = OpenSSL::OCSP::CertificateId.new(openssl_cert_subject, openssl_cert_issuer) @ocsp_request.add_certid(cert_id) @ocsp_handler.ocsp_request = @ocsp_request.to_der end it "should be able to accept an OCSP Request" do @ocsp_handler.ocsp_request = @ocsp_request expect(@ocsp_handler.ocsp_request).not_to be_nil end it "should raise an error if you try and extract certificates without a raw request" do @ocsp_handler.extract_certificate_serials @ocsp_handler.ocsp_request = nil expect {@ocsp_handler.extract_certificate_serials}.to raise_error(RuntimeError) end it "should return a hash of extracted certificates from OCSP requests" do result = @ocsp_handler.extract_certificate_serials expect(result.size).to eq(1) end it "should be able to generate an OCSP response" do @ocsp_handler.extract_certificate_serials @ocsp_handler << @certificate @ocsp_handler.parent = @root_certificate @ocsp_handler.response end it "should require a 'parent' entity for signing" do @ocsp_handler.parent = @root_certificate expect(@ocsp_handler.parent).not_to be_nil end it "should raise an error if you ask for the signed OCSP response without generating it" do @ocsp_handler.extract_certificate_serials @ocsp_handler << @certificate @ocsp_handler.parent = @root_certificate expect { @ocsp_handler.to_der }.to raise_error(RuntimeError) @ocsp_handler.response expect(@ocsp_handler.to_der).not_to be_nil end it "should raise an error if you generate a response without adding all certificates in request" do @ocsp_handler.extract_certificate_serials @ocsp_handler.parent = @root_certificate expect { @ocsp_handler.response }.to raise_error(RuntimeError) end it "should raise an error if you generate a response without adding a parent signing entity" do @ocsp_handler.extract_certificate_serials @ocsp_handler << @certificate expect { @ocsp_handler.response }.to raise_error(RuntimeError) end describe "Response" do before(:each) do @ocsp_handler.extract_certificate_serials @ocsp_handler << @certificate @ocsp_handler.parent = @root_certificate @ocsp_handler.response @openssl_ocsp_response = OpenSSL::OCSP::Response.new(@ocsp_handler.to_der) end it "should have a correct status/status string" do expect(@openssl_ocsp_response.status_string).to eq("successful") expect(@openssl_ocsp_response.status).to eq(0) end it "should have an embedded BasicResponse with certificate statuses" do # [#, 0, 1, nil, 2011-04-15 23:29:47 UTC, 2011-04-15 23:30:17 UTC, []] expect(@openssl_ocsp_response.basic.status.first[1]).to eq(0) # Everything is OK end it "should have a next_update time" do expect(@openssl_ocsp_response.basic.status.first[5]).not_to be_nil expect(@openssl_ocsp_response.basic.status.first[5].class).to eq(Time) end end end certificate_authority-1.1.0/spec/units/pkcs11_key_material_spec.rb000066400000000000000000000032271425735453600254100ustar00rootroot00000000000000## Anything that requires crypto hardware needs to be tagged as 'pkcs11' describe CertificateAuthority::Pkcs11KeyMaterial, :pkcs11 => true do before(:each) do @key_material_in_hardware = CertificateAuthority::Pkcs11KeyMaterial.new @key_material_in_hardware.token_id = "46" @key_material_in_hardware.pkcs11_lib = "/usr/lib/libeTPkcs11.so" @key_material_in_hardware.openssl_pkcs11_engine_lib = "/usr/lib/engines/engine_pkcs11.so" @key_material_in_hardware.pin = "11111111" end it "should identify as being in hardware", :pkcs11 => true do expect(@key_material_in_hardware.is_in_hardware?).to be_truthy end it "should return a Pkey ref if the private key is requested", :pkcs11 => true do expect(@key_material_in_hardware.private_key.class).to eq(OpenSSL::PKey::RSA) end it "should return a Pkey ref if the public key is requested", :pkcs11 => true do expect(@key_material_in_hardware.public_key.class).to eq(OpenSSL::PKey::RSA) end it "should accept an ID for on-token objects", :pkcs11 => true do expect(@key_material_in_hardware.respond_to?(:token_id)).to be_truthy end it "should accept a path to a shared library for a PKCS11 driver", :pkcs11 => true do expect(@key_material_in_hardware.respond_to?(:pkcs11_lib)).to be_truthy end it "should accept a path to OpenSSL's dynamic PKCS11 engine (provided by libengine-pkcs11-openssl)", :pkcs11 => true do expect(@key_material_in_hardware.respond_to?(:openssl_pkcs11_engine_lib)).to be_truthy end it "should accept an optional PIN to authenticate to the token", :pkcs11 => true do expect(@key_material_in_hardware.respond_to?(:pin)).to be_truthy end end certificate_authority-1.1.0/spec/units/serial_number_spec.rb000066400000000000000000000010011425735453600243730ustar00rootroot00000000000000describe CertificateAuthority::SerialNumber do before(:each) do @serial_number = CertificateAuthority::SerialNumber.new end it "should support basic integer serial numbers", :rfc3280 => true do @serial_number.number = 25 expect(@serial_number).to be_valid @serial_number.number = "abc" expect(@serial_number).not_to be_valid end it "should not allow negative serial numbers", :rfc3280 => true do @serial_number.number = -5 expect(@serial_number).not_to be_valid end end certificate_authority-1.1.0/spec/units/signing_entity_spec.rb000066400000000000000000000000641425735453600246060ustar00rootroot00000000000000describe CertificateAuthority::SigningEntity do end certificate_authority-1.1.0/spec/units/signing_request_spec.rb000066400000000000000000000163731425735453600247740ustar00rootroot00000000000000describe CertificateAuthority::SigningRequest do before(:each) do @pem_csr =<= '2.5.0' expected =<