Class: CertificateAuthority::Certificate

Inherits:
Object
  • Object
show all
Includes:
Revocable, Validations
Defined in:
lib/certificate_authority/certificate.rb

Instance Attribute Summary collapse

Attributes included from Revocable

#revoked_at

Class Method Summary collapse

Instance Method Summary collapse

Methods included from Revocable

#revoke!

Methods included from Validations

#errors, #valid?

Constructor Details

#initializeCertificate

Returns a new instance of Certificate.



31
32
33
34
35
36
37
38
39
40
41
42
# File 'lib/certificate_authority/certificate.rb', line 31

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

Instance Attribute Details

#distinguished_nameObject Also known as: subject

Returns the value of attribute distinguished_name.



6
7
8
# File 'lib/certificate_authority/certificate.rb', line 6

def distinguished_name
  @distinguished_name
end

#extensionsObject

Returns the value of attribute extensions.



11
12
13
# File 'lib/certificate_authority/certificate.rb', line 11

def extensions
  @extensions
end

#key_materialObject

Returns the value of attribute key_material.



8
9
10
# File 'lib/certificate_authority/certificate.rb', line 8

def key_material
  @key_material
end

#not_afterObject

Returns the value of attribute not_after.



10
11
12
# File 'lib/certificate_authority/certificate.rb', line 10

def not_after
  @not_after
end

#not_beforeObject

Returns the value of attribute not_before.



9
10
11
# File 'lib/certificate_authority/certificate.rb', line 9

def not_before
  @not_before
end

#openssl_bodyObject

Returns the value of attribute openssl_body.



12
13
14
# File 'lib/certificate_authority/certificate.rb', line 12

def openssl_body
  @openssl_body
end

#parentObject

Returns the value of attribute parent.



16
17
18
# File 'lib/certificate_authority/certificate.rb', line 16

def parent
  @parent
end

#serial_numberObject

Returns the value of attribute serial_number.



7
8
9
# File 'lib/certificate_authority/certificate.rb', line 7

def serial_number
  @serial_number
end

Class Method Details

.from_x509_cert(raw_cert) ⇒ Object



147
148
149
150
# File 'lib/certificate_authority/certificate.rb', line 147

def self.from_x509_cert(raw_cert)
  openssl_cert = OpenSSL::X509::Certificate.new(raw_cert)
  Certificate.from_openssl(openssl_cert)
end

Instance Method Details

#is_intermediate_entity?Boolean

Returns:

  • (Boolean)


156
157
158
# File 'lib/certificate_authority/certificate.rb', line 156

def is_intermediate_entity?
  (self.parent != self) && is_signing_entity?
end

#is_root_entity?Boolean

Returns:

  • (Boolean)


152
153
154
# File 'lib/certificate_authority/certificate.rb', line 152

def is_root_entity?
  self.parent == self && is_signing_entity?
end

#is_signing_entity?Boolean

Returns:

  • (Boolean)


107
108
109
# File 'lib/certificate_authority/certificate.rb', line 107

def is_signing_entity?
  self.extensions["basicConstraints"].ca
end

#revoked?Boolean

Returns:

  • (Boolean)


115
116
117
# File 'lib/certificate_authority/certificate.rb', line 115

def revoked?
  !self.revoked_at.nil?
end

#sign!(signing_profile = {}) ⇒ Object

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


63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
# File 'lib/certificate_authority/certificate.rb', line 63

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

#signing_entity=(signing) ⇒ Object



111
112
113
# File 'lib/certificate_authority/certificate.rb', line 111

def signing_entity=(signing)
  self.extensions["basicConstraints"].ca = signing
end

#to_csrObject



124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
# File 'lib/certificate_authority/certificate.rb', line 124

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

#to_pemObject



119
120
121
122
# File 'lib/certificate_authority/certificate.rb', line 119

def to_pem
  raise "Certificate has no signed body" if self.openssl_body.nil?
  self.openssl_body.to_pem
end

#validateObject



18
19
20
21
22
23
24
25
26
27
28
29
# File 'lib/certificate_authority/certificate.rb', line 18

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