Class: R509::Cert

Inherits:
Object
  • Object
show all
Includes:
IOHelpers
Defined in:
lib/r509/cert.rb,
lib/r509/cert/extensions.rb

Overview

The primary certificate object.

Defined Under Namespace

Modules: Extensions

Instance Attribute Summary (collapse)

Class Method Summary (collapse)

Instance Method Summary (collapse)

Methods included from IOHelpers

#read_data, read_data, #write_data, write_data

Constructor Details

- (Cert) initialize(opts = {})

A new instance of Cert

Parameters:

  • opts (Hash) (defaults to: {})

    a customizable set of options

Options Hash (opts):

  • :cert (String, OpenSSL::X509::Certificate)

    a cert

  • :key (R509::PrivateKey, String)

    optional private key to supply. either an unencrypted PEM/DER string or an R509::PrivateKey object (use the latter if you need password/hardware support)

  • :pkcs12 (String)

    a PKCS12 object containing both key and cert

  • :password (String)

    password for PKCS12 or private key (if supplied)



17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
# File 'lib/r509/cert.rb', line 17

def initialize(opts={})
    if not opts.kind_of?(Hash)
        raise ArgumentError, 'Must provide a hash of options'
    end
    if opts.has_key?(:pkcs12) and ( opts.has_key?(:key) or opts.has_key?(:cert) )
        raise ArgumentError, "When providing pkcs12, do not pass cert or key"
    elsif opts.has_key?(:pkcs12)
        pkcs12 = OpenSSL::PKCS12.new( opts[:pkcs12], opts[:password] )
        parse_certificate(pkcs12.certificate)
        @key = R509::PrivateKey.new( :key => pkcs12.key )
    elsif not opts.has_key?(:cert)
        raise ArgumentError, 'Must provide :cert or :pkcs12'
    else
        csr_check(opts[:cert])
        parse_certificate(opts[:cert])
    end

    if opts.has_key?(:key)
        if opts[:key].kind_of?(R509::PrivateKey)
            @key = opts[:key]
        else
            @key = R509::PrivateKey.new( :key => opts[:key], :password => opts[:password] )
        end
    end
    if not @key.nil?
        if not @cert.public_key.to_s == @key.public_key.to_s then
            raise R509Error, 'Key does not match cert.'
        end
    end
end

Instance Attribute Details

- (Object) cert (readonly)

Returns the value of attribute cert



11
12
13
# File 'lib/r509/cert.rb', line 11

def cert
  @cert
end

- (Object) key (readonly)

Returns the value of attribute key



11
12
13
# File 'lib/r509/cert.rb', line 11

def key
  @key
end

Class Method Details

+ (R509::Cert) load_from_file(filename)

Helper method to quickly load a cert from the filesystem

Parameters:

  • filename (String)

    Path to file you want to load

Returns:



52
53
54
# File 'lib/r509/cert.rb', line 52

def self.load_from_file( filename )
    return R509::Cert.new(:cert => IOHelpers.read_data(filename) )
end

Instance Method Details

- (R509::Cert::Extensions::AuthorityInfoAccess) authority_info_access

Returns this object's AuthorityInfoAccess extension as an R509 extension

Returns:



385
386
387
# File 'lib/r509/cert.rb', line 385

def authority_info_access
    return r509_extensions[R509::Cert::Extensions::AuthorityInfoAccess]
end

- (R509::Cert::Extensions::AuthorityKeyIdentifier) authority_key_identifier

Returns this object's AuthorityKeyIdentifier extension as an R509 extension

Returns:



369
370
371
# File 'lib/r509/cert.rb', line 369

def authority_key_identifier
    return r509_extensions[R509::Cert::Extensions::AuthorityKeyIdentifier]
end

- (R509::Cert::Extensions::BasicConstraints) basic_constraints

Returns this object's BasicConstraints extension as an R509 extension

Returns:



337
338
339
# File 'lib/r509/cert.rb', line 337

def basic_constraints
    return r509_extensions[R509::Cert::Extensions::BasicConstraints]
end

- (Integer) bit_strength

Returns the bit strength of the key used to create the certificate

Returns:

  • (Integer)

    integer value of bit strength



230
231
232
233
234
235
236
# File 'lib/r509/cert.rb', line 230

def bit_strength
    if self.rsa?
        return @cert.public_key.n.num_bits
    elsif self.dsa?
        return @cert.public_key.p.num_bits
    end
end

- (R509::Cert::Extensions::CrlDistributionPoints) crl_distribution_points

Returns this object's CrlDistributionPoints extension as an R509 extension

Returns:



393
394
395
# File 'lib/r509/cert.rb', line 393

def crl_distribution_points
    return r509_extensions[R509::Cert::Extensions::CrlDistributionPoints]
end

- (Boolean) dsa?

Returns whether the public key is DSA

Returns:

  • (Boolean)

    true if the public key is DSA, false otherwise



223
224
225
# File 'lib/r509/cert.rb', line 223

def dsa?
    @cert.public_key.kind_of?(OpenSSL::PKey::DSA)
end

- (R509::Cert::Extensions::ExtendedKeyUsage) extended_key_usage

Returns this object's ExtendedKeyUsage extension as an R509 extension

Returns:



353
354
355
# File 'lib/r509/cert.rb', line 353

def extended_key_usage
    return r509_extensions[R509::Cert::Extensions::ExtendedKeyUsage]
end

- (Array) extensions

Return the certificate extensions

Returns:

  • (Array)

    an array of hashes representing the extensions in the cert



295
296
297
298
299
300
301
302
303
304
# File 'lib/r509/cert.rb', line 295

def extensions
    if @extensions.nil?
        @extensions = Hash.new
        @cert.extensions.each { |extension|
            hash = {'value' => extension.value, 'critical' => extension.critical?}
            @extensions[extension.oid] = hash
        }
    end
    @extensions
end

- (String) fingerprint(algorithm = 'sha1')

Returns the certificate fingerprint with the specified algorithm (default sha1)

Parameters:

  • algorithm (String) (defaults to: 'sha1')

    Which algorithm to use for the fingerprint. See R509::MessageDigest for supported algorithm names

Returns:

  • (String)

    hex digest of the certificate



129
130
131
132
133
134
# File 'lib/r509/cert.rb', line 129

def fingerprint(algorithm='sha1')
    message_digest = R509::MessageDigest.new(algorithm)
    md = message_digest.digest
    md.update(@cert.to_der)
    md.to_s
end

- (Boolean) has_private_key?

Boolean of whether the object contains a private key

Returns:

  • (Boolean)

    Boolean of whether the object contains a private key



168
169
170
171
172
173
174
# File 'lib/r509/cert.rb', line 168

def has_private_key?
    if not @key.nil?
        true
    else
        false
    end
end

- (Boolean) is_revoked_by_crl?(r509_crl)

Checks the given CRL for this certificate's serial number. Note that this does NOT check to verify that the CRL you're checking is signed by the same CA as the cert so do that check yourself

Parameters:

  • r509_crl (R509::Crl)

    A CRL from the CA that issued this certificate.

Returns:

  • (Boolean)


288
289
290
# File 'lib/r509/cert.rb', line 288

def is_revoked_by_crl?( r509_crl )
    return r509_crl.revoked?( self.serial )
end

- (OpenSSL::X509::Name) issuer

Returns the issuer

Returns:

  • (OpenSSL::X509::Name)

    issuer object. Can be parsed as string easily



109
110
111
# File 'lib/r509/cert.rb', line 109

def issuer
    @cert.issuer
end

- (String) issuer_cn

The common name (CN) component of the issuer

Returns:

  • (String)

    The common name (CN) component of the issuer



114
115
116
117
118
119
120
121
122
123
# File 'lib/r509/cert.rb', line 114

def issuer_cn
    return nil if self.issuer.nil?

    self.issuer.to_a.each do |part, value, length|
        return value if part.upcase == 'CN'
    end

    # return nil if we didn't find a CN part
    return nil
end

- (String) key_algorithm

Returns key algorithm (RSA or DSA)

Returns:

  • (String)

    value of the key algorithm. RSA or DSA



248
249
250
251
252
253
254
# File 'lib/r509/cert.rb', line 248

def key_algorithm
    if self.rsa?
        "RSA"
    elsif self.dsa?
        "DSA"
    end
end

- (R509::Cert::Extensions::KeyUsage) key_usage

Returns this object's KeyUsage extension as an R509 extension

Returns:



345
346
347
# File 'lib/r509/cert.rb', line 345

def key_usage
    return r509_extensions[R509::Cert::Extensions::KeyUsage]
end

- (Time) not_after

Returns ending (notAfter) of certificate validity period

Returns:

  • (Time)

    time object



95
96
97
# File 'lib/r509/cert.rb', line 95

def not_after
    @cert.not_after
end

- (Time) not_before

Returns beginning (notBefore) of certificate validity period

Returns:

  • (Time)

    time object



81
82
83
# File 'lib/r509/cert.rb', line 81

def not_before
    @cert.not_before
end

- (OpenSSL::PKey::RSA) public_key

Returns the certificate public key

Returns:

  • (OpenSSL::PKey::RSA)

    public key object



102
103
104
# File 'lib/r509/cert.rb', line 102

def public_key
    @cert.public_key
end

- (Hash) r509_extensions

Returns the certificate extensions as a hash of R509::Cert::Extensions specific objects.

R509::Cert::Extensions module, each specific to the extension. The hash is keyed with the R509 extension class. Extensions without an R509 implementation are ignored (see #get_unknown_extensions).

Returns:

  • (Hash)

    A hash, in which the values are classes from the



313
314
315
316
317
318
319
# File 'lib/r509/cert.rb', line 313

def r509_extensions
    if @r509_extensions.nil?
        @r509_extensions = Extensions.wrap_openssl_extensions( self.cert.extensions )
    end

    return @r509_extensions
end

- (Boolean) rsa?

Returns whether the public key is RSA

Returns:

  • (Boolean)

    true if the public key is RSA, false otherwise



216
217
218
# File 'lib/r509/cert.rb', line 216

def rsa?
    @cert.public_key.kind_of?(OpenSSL::PKey::RSA)
end

- (Array) san_names

List of SAN DNS names

Returns:

  • (Array)

    list of SAN DNS names



177
178
179
180
181
182
183
# File 'lib/r509/cert.rb', line 177

def san_names
    if self.subject_alternative_name.nil?
        return []
    else
        return self.subject_alternative_name.dns_names
    end
end

- (Integer) serial

Returns the serial number of the certificate in decimal form

Returns:

  • (Integer)


88
89
90
# File 'lib/r509/cert.rb', line 88

def serial
    @cert.serial.to_i
end

- (String) signature_algorithm

Returns signature algorithm

Returns:

  • (String)

    value of the signature algorithm. E.g. sha1WithRSAEncryption, sha256WithRSAEncryption, md5WithRSAEncryption



241
242
243
# File 'lib/r509/cert.rb', line 241

def signature_algorithm
    @cert.signature_algorithm
end

- (OpenSSL::X509::Name) subject

Returns the subject

Returns:

  • (OpenSSL::X509::Name)

    subject object. Can be parsed as string easily



163
164
165
# File 'lib/r509/cert.rb', line 163

def subject
    @cert.subject
end

- (R509::Cert::Extensions::SubjectAlternativeName) subject_alternative_name

Returns this object's SubjectAlternativeName extension as an R509 extension

Returns:



377
378
379
# File 'lib/r509/cert.rb', line 377

def subject_alternative_name
    return r509_extensions[R509::Cert::Extensions::SubjectAlternativeName]
end

- (String) subject_cn

Returns the CN component, if any, of the subject

Returns:

  • (String)


188
189
190
# File 'lib/r509/cert.rb', line 188

def subject_cn()
    return self.subject_component('CN')
end

- (String) subject_component(short_name)

Returns subject component

Returns:

  • (String)

    value of the subject component requested



195
196
197
198
199
# File 'lib/r509/cert.rb', line 195

def subject_component short_name
    match = @cert.subject.to_a.find { |x| x[0] == short_name }
    return nil if match.nil?
    return match[1]
end

- (R509::Cert::Extensions::SubjectKeyIdentifier) subject_key_identifier

Returns this object's SubjectKeyIdentifier extension as an R509 extension

Returns:



361
362
363
# File 'lib/r509/cert.rb', line 361

def subject_key_identifier
    return r509_extensions[R509::Cert::Extensions::SubjectKeyIdentifier]
end

- (Array) subject_names

Return the CN, as well as all the subject alternative names (SANs).

Returns:

  • (Array)

    the array of names. Returns an empty array if there are no names, at all.



205
206
207
208
209
210
211
# File 'lib/r509/cert.rb', line 205

def subject_names
    ret = []
    ret << subject_cn unless subject_cn.nil?
    ret.concat( self.san_names )

    return ret.sort.uniq
end

- (String) to_der

Converts the Cert into the DER format

Returns:

  • (String)

    the Cert converted into DER format.



72
73
74
75
76
# File 'lib/r509/cert.rb', line 72

def to_der
    if @cert.kind_of?(OpenSSL::X509::Certificate)
        return @cert.to_der
    end
end

- (String) to_pem Also known as: to_s

Converts the Cert into the PEM format

Returns:

  • (String)

    the Cert converted into PEM format.



61
62
63
64
65
# File 'lib/r509/cert.rb', line 61

def to_pem
    if @cert.kind_of?(OpenSSL::X509::Certificate)
        return @cert.to_pem.chomp
    end
end

- (Array) unknown_extensions

Returns an array of OpenSSL::X509::Extension objects representing the extensions that do not have R509 implementations.

Returns:

  • (Array)

    An array of OpenSSL::X509::Extension objects.



325
326
327
# File 'lib/r509/cert.rb', line 325

def unknown_extensions
    return Extensions.get_unknown_extensions( self.cert.extensions )
end

- (Boolean) valid?

Returns whether the current time is between the notBefore and notAfter times in the certificate.

Returns:

  • (Boolean)


140
141
142
# File 'lib/r509/cert.rb', line 140

def valid?
    valid_at?(Time.now)
end

- (Boolean) valid_at?(time)

Returns whether the certificate was between its notBefore and notAfter at the time provided

Parameters:

  • time (Time, Integer)

    Time object or integer timestamp

Returns:

  • (Boolean)


148
149
150
151
152
153
154
155
156
157
158
# File 'lib/r509/cert.rb', line 148

def valid_at?(time)
    if time.kind_of?(Integer)
        time = Time.at(time)
    end

    if (self.not_after < time) or (self.not_before > time)
        false
    else
        true
    end
end

- (Object) write_der(filename_or_io)

Writes the Cert into the DER format

Parameters:

  • filename_or_io (String, #write)

    Either a string of the path for the file that you'd like to write, or an IO-like object.



266
267
268
# File 'lib/r509/cert.rb', line 266

def write_der(filename_or_io)
    write_data(filename_or_io, @cert.to_der)
end

- (Object) write_pem(filename_or_io)

Writes the Cert into the PEM format

Parameters:

  • filename_or_io (String, #write)

    Either a string of the path for the file that you'd like to write, or an IO-like object.



259
260
261
# File 'lib/r509/cert.rb', line 259

def write_pem(filename_or_io)
    write_data(filename_or_io, @cert.to_pem)
end

- (Object) write_pkcs12(filename_or_io, password, friendly_name = 'r509 pkcs12')

Writes cert and key into PKCS12 format using OpenSSL defaults for encryption (des3)

Parameters:

  • filename_or_io (String, #write)

    Either a string of the path for the file that you'd like to write, or an IO-like object.

  • password (String)

    password

  • friendly_name (String) (defaults to: 'r509 pkcs12')

    An optional string to encode in the PKCS12 for friendlyName. defaults to "r509 pkcs12"



275
276
277
278
279
280
281
# File 'lib/r509/cert.rb', line 275

def write_pkcs12(filename_or_io,password,friendly_name='r509 pkcs12')
    if @key.nil?
        raise R509::R509Error, "Writing a PKCS12 requires both key and cert"
    end
    pkcs12 = OpenSSL::PKCS12.create(password,friendly_name,@key.key,@cert)
    write_data(filename_or_io, pkcs12.to_der)
end