Commit fda1bd8
Changed files (3)
lib
xml
spec
xml
lib/xml/kit/certificate.rb
@@ -2,11 +2,15 @@ module Xml
module Kit
# {include:file:spec/xml/certificate_spec.rb}
class Certificate
+ BASE64_FORMAT = %r(\A([A-Za-z0-9+/]{4})*([A-Za-z0-9+/]{2}==|[A-Za-z0-9+/]{3}=)?\Z)
BEGIN_CERT=/-----BEGIN CERTIFICATE-----/
END_CERT=/-----END CERTIFICATE-----/
# The use can be `:signing` or `:encryption`. Use `nil` for both.
attr_reader :use
+ # The raw certificate value. This can be a Base64 encoded PEM or just a PEM format.
+ attr_reader :value
+
def initialize(value, use: nil)
@value = value
@use = use.nil? ? use : use.downcase.to_sym
@@ -44,7 +48,7 @@ module Xml
#
# return [OpenSSL::X509::Certificate] the OpenSSL equivalent.
def x509
- self.class.to_x509(value)
+ @x509 ||= self.class.to_x509(value)
end
# Returns the public key.
@@ -79,18 +83,31 @@ module Xml
end
def stripped
- value.to_s.gsub(BEGIN_CERT, '').gsub(END_CERT, '').gsub(/\n/, '')
+ self.class.strip(x509.to_pem)
+ end
+
+ def to_key_pair(private_key, passphrase: nil, use: nil)
+ KeyPair.new(x509.to_pem, private_key.to_s, passphrase, use)
end
def self.to_x509(value)
- OpenSSL::X509::Certificate.new(Base64.decode64(value))
- rescue OpenSSL::X509::CertificateError
+ value = Base64.decode64(strip(value)) if base64?(value)
OpenSSL::X509::Certificate.new(value)
end
- private
+ def self.base64?(value)
+ return unless value.is_a?(String)
- attr_reader :value
+ sanitized_value = strip(value)
+ !!sanitized_value.match(BASE64_FORMAT)
+ end
+
+ def self.strip(value)
+ value.
+ gsub(BEGIN_CERT, '').
+ gsub(END_CERT, '').
+ gsub(/[\r\n]|\\r|\\n|\s/, "")
+ end
end
end
end
lib/xml/kit/document.rb
@@ -59,16 +59,23 @@ module Xml
x509_certificates.each do |certificate|
inactive = now < certificate.not_before
- errors.add(:certificate, "Not valid before #{certificate.not_before}") if inactive
+ if inactive
+ error_message = "Not valid before #{certificate.not_before}"
+ errors.add(:certificate, error_message)
+ end
expired = now > certificate.not_after
- errors.add(:certificate, "Not valid after #{certificate.not_after}") if expired
+ if expired
+ error_message = "Not valid after #{certificate.not_after}"
+ errors.add(:certificate, error_message)
+ end
end
end
def x509_certificates
- xpath = "//ds:KeyInfo/ds:X509Data/ds:X509Certificate"
- find_all(xpath).map { |item| Certificate.to_x509(item.text) }
+ find_all("//ds:KeyInfo/ds:X509Data/ds:X509Certificate").map do |item|
+ Certificate.to_x509(item.text)
+ end
end
end
end
spec/xml/document_spec.rb
@@ -59,14 +59,15 @@ RSpec.describe Xml::Kit::Document do
end
let(:private_key) { OpenSSL::PKey::RSA.new(2048) }
let(:digest_algorithm) { OpenSSL::Digest::SHA256.new }
+ let(:item) { Item.new }
before :each do
expired_certificate.sign(private_key, digest_algorithm)
end
it 'is invalid' do
- item = Item.new
- item.sign_with(::Xml::Kit::KeyPair.new(expired_certificate.to_pem, private_key.to_s, nil, :signing))
+ certificate = ::Xml::Kit::Certificate.new(expired_certificate)
+ item.sign_with(certificate.to_key_pair(private_key))
subject = described_class.new(item.to_xml)
expect(subject).to be_invalid
expect(subject.errors[:certificate]).to be_present