class PedicelPay::Backend
Constants
- CertificateError
- Error
- KeyError
Attributes
ca_certificate[RW]
ca_key[RW]
intermediate_certificate[RW]
intermediate_key[RW]
leaf_certificate[RW]
leaf_key[RW]
Public Class Methods
generate(config: PedicelPay.config)
click to toggle source
# File lib/pedicel-pay/backend.rb, line 137 def self.generate(config: PedicelPay.config) ck, cc = generate_ca(config: config) ik, ic = generate_intermediate(ca_key: ck, ca_certificate: cc, config: config) lk, lc = generate_leaf(intermediate_key: ik, intermediate_certificate: ic, config: config) new(ca_key: ck, ca_certificate: cc, intermediate_key: ik, intermediate_certificate: ic, leaf_key: lk, leaf_certificate: lc) end
generate_ca(config: PedicelPay.config)
click to toggle source
# File lib/pedicel-pay/backend.rb, line 149 def self.generate_ca(config: PedicelPay.config) key = OpenSSL::PKey::EC.new(PedicelPay::EC_CURVE) key.generate_key cert = OpenSSL::X509::Certificate.new cert.version = 2 # https://www.ietf.org/rfc/rfc5280.txt -> Section 4.1, search for "v3(2)". cert.serial = 1 cert.subject = config[:subject][:ca] cert.issuer = cert.subject # Self-signed cert.public_key = PedicelPay::Helper.ec_key_to_pkey_public_key(key) cert.not_before = config[:valid].min cert.not_after = config[:valid].max ef = OpenSSL::X509::ExtensionFactory.new ef.subject_certificate = cert ef.issuer_certificate = cert cert.add_extension(ef.create_extension('basicConstraints','CA:TRUE',true)) cert.add_extension(ef.create_extension('keyUsage','keyCertSign, cRLSign', true)) cert.add_extension(ef.create_extension('subjectKeyIdentifier','hash',false)) cert.add_extension(ef.create_extension('authorityKeyIdentifier','keyid:always',false)) cert.sign(key, OpenSSL::Digest::SHA256.new) [key, cert] end
generate_intermediate(ca_key:, ca_certificate:, config: PedicelPay.config)
click to toggle source
# File lib/pedicel-pay/backend.rb, line 174 def self.generate_intermediate(ca_key:, ca_certificate:, config: PedicelPay.config) key = OpenSSL::PKey::EC.new(PedicelPay::EC_CURVE) key.generate_key cert = OpenSSL::X509::Certificate.new # https://www.ietf.org/rfc/rfc5280.txt -> Section 4.1, search for "v3(2)". cert.version = 2 cert.serial = 1 cert.subject = config[:subject][:intermediate] cert.issuer = ca_certificate.subject cert.public_key = PedicelPay::Helper.ec_key_to_pkey_public_key(key) cert.not_before = config[:valid].min cert.not_after = config[:valid].max ef = OpenSSL::X509::ExtensionFactory.new ef.subject_certificate = cert ef.issuer_certificate = ca_certificate # According to https://tools.ietf.org/html/rfc5280#section-4.2.1.9, # CA:TRUE must be set in order to allow signing using this intermediate # certificate. cert.add_extension(ef.create_extension('basicConstraints', 'CA:TRUE', true)) cert.add_extension(ef.create_extension('keyUsage', 'keyCertSign, cRLSign', true)) cert.add_extension(ef.create_extension('subjectKeyIdentifier', 'hash', false)) cert.add_extension(OpenSSL::X509::Extension.new(config[:oid][:intermediate_certificate], '')) cert.sign(ca_key, OpenSSL::Digest::SHA256.new) [key, cert] end
generate_leaf(intermediate_key:, intermediate_certificate:, config: PedicelPay.config)
click to toggle source
# File lib/pedicel-pay/backend.rb, line 207 def self.generate_leaf(intermediate_key:, intermediate_certificate:, config: PedicelPay.config) key = OpenSSL::PKey::EC.new(PedicelPay::EC_CURVE) key.generate_key cert = OpenSSL::X509::Certificate.new cert.version = 2 # https://www.ietf.org/rfc/rfc5280.txt -> Section 4.1, search for "v3(2)". cert.serial = 1 cert.subject = config[:subject][:leaf] cert.issuer = intermediate_certificate.subject cert.public_key = PedicelPay::Helper.ec_key_to_pkey_public_key(key) cert.not_before = config[:valid].min cert.not_after = config[:valid].max ef = OpenSSL::X509::ExtensionFactory.new ef.subject_certificate = cert ef.issuer_certificate = intermediate_certificate cert.add_extension(ef.create_extension('keyUsage','digitalSignature', true)) cert.add_extension(ef.create_extension('subjectKeyIdentifier','hash',false)) cert.add_extension(OpenSSL::X509::Extension.new(config[:oid][:leaf_certificate], '')) cert.sign(intermediate_key, OpenSSL::Digest::SHA256.new) [key, cert] end
new(ca_key: nil, ca_certificate: nil, intermediate_key: nil, intermediate_certificate: nil, leaf_key: nil, leaf_certificate: nil)
click to toggle source
# File lib/pedicel-pay/backend.rb, line 16 def initialize(ca_key: nil, ca_certificate: nil, intermediate_key: nil, intermediate_certificate: nil, leaf_key: nil, leaf_certificate: nil) @ca_key = ca_key @ca_certificate = ca_certificate @intermediate_key = intermediate_key @intermediate_certificate = intermediate_certificate @leaf_key = leaf_key @leaf_certificate = leaf_certificate end
Public Instance Methods
encrypt(token, recipient:, shared_secret: nil, ephemeral_pubkey: nil)
click to toggle source
# File lib/pedicel-pay/backend.rb, line 64 def encrypt(token, recipient:, shared_secret: nil, ephemeral_pubkey: nil) raise ArgumentError, 'invalid token' unless token.is_a?(Token) if shared_secret && ephemeral_pubkey # Use them. No check that they come from the same ephemeral secret key. elsif shared_secret.nil? ^ ephemeral_pubkey.nil? raise ArgumentError, "'shared_secret' and 'ephemeral_pubkey' must be supplied together" else # None of shared_secret or ephemeral_pubkey is supplied. shared_secret, ephemeral_pubkey = self.class.generate_shared_secret_and_ephemeral_pubkey(recipient: recipient) end symmetric_key = Pedicel::EC.symmetric_key(shared_secret: shared_secret, merchant_id: Helper.merchant_id(recipient)) token.encrypted_data = Helper.encrypt( data: token.unencrypted_data.to_json, key: symmetric_key ) token.header.ephemeral_pubkey = ephemeral_pubkey token.update_pubkey_hash(recipient: recipient) token end
encrypt_and_sign(token, recipient:, shared_secret: nil, ephemeral_pubkey: nil)
click to toggle source
# File lib/pedicel-pay/backend.rb, line 57 def encrypt_and_sign(token, recipient:, shared_secret: nil, ephemeral_pubkey: nil) encrypt(token, recipient: recipient, shared_secret: shared_secret, ephemeral_pubkey: ephemeral_pubkey) sign(token) token end
generate_client(valid: PedicelPay.config[:valid])
click to toggle source
# File lib/pedicel-pay/backend.rb, line 29 def generate_client(valid: PedicelPay.config[:valid]) client = PedicelPay::Client.new(ca_certificate_pem: ca_certificate.to_pem) client.generate_key client.ca_certificate_pem = ca_certificate.to_pem client.certificate = sign_csr(client.generate_csr, valid: valid) client end
sign(token, certificate: leaf_certificate, key: leaf_key, replace: true)
click to toggle source
# File lib/pedicel-pay/backend.rb, line 88 def sign(token, certificate: leaf_certificate, key: leaf_key, replace: true) raise ArgumentError, 'token has no encrypted_data' unless token.encrypted_data raise ArgumentError, 'token has no ephemeral_pubkey' unless token.header.ephemeral_pubkey message = [ Helper.ec_key_to_pkey_public_key(token.header.ephemeral_pubkey).to_der, token.encrypted_data, token.header.transaction_id, token.header.data_hash ].compact.join signature = OpenSSL::PKCS7.sign( certificate, key, message, [intermediate_certificate, ca_certificate], # Chain. OpenSSL::PKCS7::BINARY # Handle 0x00 correctly. ) if replace # Just replace token.signature. else if token.signature # Already signed. existing_signature = OpenSSL::PKCS7.new(Base64.strict_decode64(token.signature)) signature = existing_signature.add_signer(signature.signers.first) end end token.signature = Base64.strict_encode64(signature.to_der) token end
sign_csr(csr, valid: PedicelPay.config[:valid])
click to toggle source
# File lib/pedicel-pay/backend.rb, line 39 def sign_csr(csr, valid: PedicelPay.config[:valid]) cert = OpenSSL::X509::Certificate.new cert.version = 2 cert.serial = 1 cert.not_before = valid.min cert.not_after = valid.max cert.subject = PedicelPay.config[:subject][:client] cert.public_key = csr.public_key cert.issuer = intermediate_certificate.issuer cert.sign(intermediate_key, OpenSSL::Digest::SHA256.new) merchant_id_hex = Helper.bytestring_to_hex(PedicelPay.config[:random].bytes(32)) cert.add_extension(OpenSSL::X509::Extension.new(PedicelPay.config[:oid][:merchant_identifier_field], merchant_id_hex)) cert end
validate()
click to toggle source
# File lib/pedicel-pay/backend.rb, line 233 def validate validate_ca validate_intermediate validate_leaf true end
validate_ca()
click to toggle source
# File lib/pedicel-pay/backend.rb, line 241 def validate_ca raise KeyError, 'ca private key not valid for ca certificate' unless ca_certificate.check_private_key(ca_key) raise CertificateError, 'ca certificate is not self-signed' unless ca_certificate.verify(ca_key) true end
validate_intermediate()
click to toggle source
# File lib/pedicel-pay/backend.rb, line 249 def validate_intermediate raise KeyError, 'intermediate private key not valid for intermediate certificate' unless intermediate_certificate.check_private_key(intermediate_key) raise CertificateError, 'intermediate certificate not signed by ca' unless intermediate_certificate.verify(ca_key) true end
validate_leaf()
click to toggle source
# File lib/pedicel-pay/backend.rb, line 257 def validate_leaf raise KeyError, 'leaf private key not valid for leaf certificate' unless leaf_certificate.check_private_key(leaf_key) raise CertificateError, 'leaf certificate not signed by intermediate' unless leaf_certificate.verify(intermediate_key) true end