Skip to content

Latest commit

 

History

History
442 lines (351 loc) · 12.7 KB

README.md

File metadata and controls

442 lines (351 loc) · 12.7 KB

#r509 Build Status r509 is a wrapper for various OpenSSL functions to allow easy creation of CSRs, signing of certificates, and revocation via CRL. Together with projects like r509-ocsp-responder and r509-ca-http it is intended to be a complete certificate authority for use in production environments.

##Requirements/Installation

r509 requires the Ruby OpenSSL bindings as well as yaml support (present by default in modern Ruby builds). To install the gem: gem install r509-(version).gem

##Running Tests/Building Gem If you want to run the tests for r509 you'll need rspec. Additionally, you may want to install rcov/simplecov (ruby 1.8/1.9 respectively) and yard for running the code coverage and documentation tasks in the Rakefile. rake -T for a complete list of rake tasks available.

##Continuous Integration We run continuous integration tests (using Travis-CI) against 1.8.7, 1.9.2, 1.9.3, ree, ruby-head, and rubinius(rbx) 2.0 in 1.9 mode.

##Executable

Inside the gem there is a bin directory that contains r509. You can use this in interactive mode to generate a CSR and (optionally) self-sign it.

##Usage ###CSR To generate a 2048-bit RSA CSR

csr = R509::Csr.new(
    :subject => [
        ['CN','somedomain.com'],
        ['O','My Org'],
        ['L','City'],
        ['ST','State'],
        ['C','US']
    ]
)

To load an existing CSR (without private key)

csr_pem = File.read("/path/to/csr")
csr = R509::Csr.new(:csr => csr_pem)

To create a new CSR from the subject of a certificate

cert_pem = File.read("/path/to/cert")
csr = R509::Csr.new(:cert => cert_pem)

To create a CSR with SAN names

csr = R509::Csr.new(
    :subject => [['CN','something.com']],
    :san_names => ["something2.com","something3.com"]
)

###Cert To load an existing certificate

cert_pem = File.read("/path/to/cert")
cert = R509::Cert.new(:cert => cert_pem)

Load a cert and key

cert_pem = File.read("/path/to/cert")
key_pem = File.read("/path/to/key")
cert = R509::Cert.new(
    :cert => cert_pem,
    :key => key_pem
)

Load an encrypted private key

cert_pem = File.read("/path/to/cert")
key_pem = File.read("/path/to/key")
cert = R509::Cert.new(
    :cert => cert_pem,
    :key => key_pem,
    :password => "private_key_password"
)

Load a PKCS12 file

pkcs12_der = File.read("/path/to/p12")
cert = R509::Cert.new(
    :pkcs12 => pkcs12_der,
    :password => "password"
)

###Self-Signed Certificate To create a self-signed certificate

not_before = Time.now.to_i
not_after = Time.now.to_i+3600*24*7300
csr = R509::Csr.new(
    :subject => [['C','US'],['O','r509 LLC'],['CN','r509 Self-Signed CA Test']]
)
ca = R509::CertificateAuthority::Signer.new
cert = ca.selfsign(
    :csr => csr,
    :not_before => not_before,
    :not_after => not_after
)

###Config

Create a basic CaConfig object

cert_pem = File.read("/path/to/cert")
key_pem = File.read("/path/to/key")
cert = R509::Cert.new(
    :cert => cert_pem,
    :key => key_pem
)
config = R509::Config::CaConfig.new(
    :ca_cert => cert
)

Add a signing profile named "server" (CaProfile) to a config object

profile = R509::Config::CaProfile.new(
    :basic_constraints => "CA:FALSE",
    :key_usage => ["digitalSignature","keyEncipherment"],
    :extended_key_usage => ["serverAuth"],
    :certificate_policies => [ ["policyIdentifier=2.16.840.1.999999999.1.2.3.4.1", "CPS.1=http://example.com/cps"] ],
    :subject_item_policy => nil
)
# config object from above assumed
config.set_profile("server",profile)

Set up a subject item policy (required/optional). The keys must match OpenSSL's shortnames!

profile = R509::Config::CaProfile.new(
    :basic_constraints => "CA:FALSE",
    :key_usage => ["digitalSignature","keyEncipherment"],
    :extended_key_usage => ["serverAuth"],
    :certificate_policies => [ ["policyIdentifier=2.16.840.1.999999999.1.2.3.4.1", "CPS.1=http://example.com/cps"] ],
    :subject_item_policy => {
        "CN" => "required",
        "O" => "optional"
    }
)
# config object from above assumed
config.set_profile("server",profile)

Load CaConfig + Profile from YAML

config = R509::Config::CaConfig.from_yaml("test_ca", "config_test.yaml")

Example YAML (more options are supported than this example)

test_ca: {
    ca_cert: {
        cert: '/path/to/test_ca.cer',
        key: '/path/to/test_ca.key'
    },
    crl_list: "crl_list_file.txt",
    crl_number: "crl_number_file.txt",
    cdp_location: 'URI:http://crl.domain.com/test_ca.crl',
    crl_validity_hours: 168, #7 days
    ocsp_location: 'URI:http://ocsp.domain.com',
    message_digest: 'SHA1', #SHA1, SHA256, SHA512 supported. MD5 too, but you really shouldn't use that unless you have a good reason
    profiles: {
        server: {
            basic_constraints: "CA:FALSE",
            key_usage: [digitalSignature,keyEncipherment],
            extended_key_usage: [serverAuth],
            certificate_policies: [ [ "policyIdentifier=2.16.840.1.9999999999.1.2.3.4.1", "CPS.1=http://example.com/cps"] ],
            subject_item_policy: {
                "CN" : "required",
                "O" : "optional",
                "ST" : "required",
                "C" : "required",
                "OU" : "optional" }
        }
    }
}

Load multiple CaConfigs using a CaConfigPool

pool = R509::Config::CaConfigPool.from_yaml("certificate_authorities", "config_pool.yaml")

Example (Minimal) Config Pool YAML

certificate_authorities: {
    test_ca: {
        ca_cert: {
            cert: 'test_ca.cer',
            key: 'test_ca.key'
        }
    },
    second_ca: {
        ca_cert: {
            cert: 'second_ca.cer',
            key: 'second_ca.key'
        }
    }
}

###CertificateAuthority

Sign a CSR

csr = R509::Csr.new(
    :subject => [
        ['CN','somedomain.com'],
        ['O','My Org'],
        ['L','City'],
        ['ST','State'],
        ['C','US']
    ]
)
# assume config from yaml load above
ca = R509::CertificateAuthority::Signer.new(config)
cert = ca.sign(
    :profile_name => "server",
    :csr => csr
)

Override a CSR's subject or SAN names when signing

csr = R509::Csr.new(
    :subject => [
        ['CN','somedomain.com'],
        ['O','My Org'],
        ['L','City'],
        ['ST','State'],
        ['C','US']
    ]
)
data_hash = csr.to_hash
data_hash[:san_names] = ["sannames.com","domain2.com"]
data_hash[:subject]["CN"] = "newdomain.com"
data_hash[:subject]["O"] = "Org 2.0"
# assume config from yaml load above
ca = R509::CertificateAuthority::Signer.new(config)
cert = ca.sign(
    :profile_name => "server",
    :csr => csr,
    :data_hash => data_hash
)

###Load Hardware Engines

The engine you want to load must already be available to OpenSSL. How to compile/install OpenSSL engines is outside the scope of this document.

OpenSSL::Engine.load("engine_name")
engine = OpenSSL::Engine.by_id("engine_name")
key = R509::PrivateKey(
    :engine => engine,
    :key_name => "my_key_name"
)

You can then use this key for signing.

###OID Mapping

Register one

R509::OidMapper.register("1.3.5.6.7.8.3.23.3","short_name","optional_long_name")

Register in batch

R509::OidMapper.batch_register([
    {:oid => "1.3.5.6.7.8.3.23.3", :short_name => "short_name", :long_name => "optional_long_name"},
    {:oid => "1.3.5.6.7.8.3.23.5", :short_name => "another_name"}
])

##Documentation

There is (relatively) complete documentation available for every method and class in r509 available via yardoc. If you installed via gem it should be pre-generated in the doc directory. If you cloned this repo, just type rake yard with the yard gem installed. You will also need the redcarpet and github-markup gems to properly parse the Readme.md.

##Thanks to...

##License See the LICENSE file. Licensed under the Apache 2.0 License.

#YAML Config Options r509 configs are nested hashes of key:values that define the behavior of each CA. See r509.yaml for a full example config.

##ca_name ###ca_cert This hash defines the certificate + key that will be used to sign for the ca_name. Depending on desired configuration various elements are optional. You can even supply just cert (for example, if you are using an ocsp_cert hash and only using the configured CA for OCSP responses)

  • cert (cannot use with pkcs12)
  • key (cannot use with key)
  • engine (optional, cannot be used with key or pkcs12)
  • key_name (required when using engine)
  • pkcs12 (optional, cannot be used with key or cert)
  • password (optional, used for pkcs12 or passworded private key)

###ocsp_cert This hash defines the certificate + key that will be used to sign for OCSP responses. OCSP responses cannot be directly created with r509, but require the ancillary gem r509-ocsp-responder. This hash is optional and if not provided r509 will automatically use the ca_cert as the OCSP certificate.

  • cert (cannot use with pkcs12)
  • key (cannot use with key)
  • engine (optional, cannot be used with key or pkcs12)
  • key_name (required when using engine)
  • pkcs12 (optional, cannot be used with key or cert)
  • password (optional, used for pkcs12 or passworded private key)

###cdp_location The CRL distribution point for certificates issued from this CA.

Example: 'URI:http://crl.r509.org/myca.crl'

###crl_list The path on the filesystem of the list of revoked certificates for this CA.

Example: '/path/to/my_ca_crl_list.txt'

###crl_number The path on the filesystem of the current CRL number for this CA.

Example: '/path/to/my_ca_crl_number.txt'

###crl_validity_hours Integer hours for CRL validity.

###ocsp_location The OCSP AIA extension value for certificates issued from this CA.

Example: 'URI:http://ocsp.r509.org'

###ocsp_chain An optional path to a concatenated text file of PEMs that should be attached to OCSP responses

###ocsp_validity_hours Integer hours for OCSP response validity.

###ocsp_start_skew_seconds Integer seconds to skew back the "thisUpdate" field. This prevents issues where the OCSP responder signs a response and the client rejects it because the response is "not yet valid" due to slight clock synchronization problems.

###message_digest String value of the message digest to use for signing (both CRL and certificates). Allowed values are:

  • SHA1 (default)
  • SHA256
  • SHA512
  • MD5 (Don't use this unless you have a really, really good reason. Even then, you shouldn't)

###profiles Each CA can have an arbitrary number of issuance profiles (with arbitrary names). For example, a CA named test_ca might have 3 issuance profiles: server, email, clientserver. Each of these profiles then has a set of options that define the encoded data in the certificate for that profile. If no profiles are defined the root cannot issue certs, but can still issue CRLs.

####basic_constraints All basic constraints are encoded with the critical bit set to true. In general you should only pass "CA:TRUE" (for an issuing CA) or "CA:FALSE" for everything else with this flag.

####key_usage An array of strings that conform to the OpenSSL naming scheme for available key usage OIDs. TODO: Document whether arbitrary OIDs can be passed here.

  • digitalSignature
  • nonRepudiation
  • keyEncipherment
  • dataEncipherment
  • keyAgreement
  • keyCertSign
  • cRLSign
  • encipherOnly
  • decipherOnly

####extended_key_usage An array of strings that conform to the OpenSSL naming scheme for available EKU OIDs. The following list of allowed shortnames is taken from the OpenSSL docs.

  • serverAuth
  • clientAuth
  • codeSigning
  • emailProtection
  • timeStamping
  • msCodeInd
  • msCodeCom
  • msCTLSign
  • msSGC
  • msEFS
  • nsSGC

####certificate_policies An array of arrays containing policy identifiers and CPS URIs. For example:

[ [ "policyIdentifier=2.16.840.1.9999999.1.2.3.4.2","CPS.1=http://r509.org/cps" ] ]

or

[ ["policyIdentifier=2.16.840.1.999999.0"], [ "policyIdentifier=2.16.840.1.9999999.1.2.3.4.2","CPS.1=http://r509.org/cps" ] ]

####subject_item_policy Hash of required/optional subject items. These must be in OpenSSL shortname format. If subject_item_policy is excluded from the profile then all subject items will be used. If it is included, only items listed in the policy will be copied to the certificate. Example:

CN : "required",
O: "required",
OU: "optional",
ST: "required",
C: "required",
L: "required",
emailAddress: "optional"

If you use the R509::OidMapper you can create new shortnames that are allowed within this directive.