#r509 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.