Skip to content

Commit

Permalink
Merge pull request #15 from Voog/13_sha256_support
Browse files Browse the repository at this point in the history
Support signing and verification algorithms provider based configuration (#15)
  • Loading branch information
tanelj authored Sep 1, 2023
2 parents 1ab9a25 + d8d7d09 commit c40b08c
Show file tree
Hide file tree
Showing 7 changed files with 66 additions and 12 deletions.
25 changes: 22 additions & 3 deletions lib/ipizza/provider/base.rb
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,20 @@ class Base
SUPPORTED_ENCODINGS = %w(UTF-8 ISO-8859-1 WINDOWS-1257)

class << self
attr_accessor :service_url, :return_url, :cancel_url, :file_key, :key_secret, :file_cert, :snd_id, :rec_id, :rec_acc, :rec_name, :encoding, :lang
attr_accessor :service_url,
:return_url,
:cancel_url,
:file_key,
:key_secret,
:file_cert,
:sign_algorithm,
:verification_algorithm,
:snd_id,
:rec_id,
:rec_acc,
:rec_name,
:encoding,
:lang
end

def payment_request(payment, service_no = 1012)
Expand Down Expand Up @@ -40,7 +53,10 @@ def payment_request(payment, service_no = 1012)

def payment_response(params)
response = Ipizza::PaymentResponse.new(params)
response.verify(self.class.file_cert)
response.verify(
self.class.file_cert,
self.class.verification_algorithm || Ipizza::Util::DEFAULT_HASH_ALGORITHM
)
response
end

Expand Down Expand Up @@ -77,7 +93,10 @@ def authentication_request(service_no = 4011, param = {})

def authentication_response(params)
response = Ipizza::AuthenticationResponse.new(params)
response.verify(self.class.file_cert)
response.verify(
self.class.file_cert,
self.class.verification_algorithm || Ipizza::Util::DEFAULT_HASH_ALGORITHM
)
response
end

Expand Down
4 changes: 2 additions & 2 deletions lib/ipizza/response.rb
Original file line number Diff line number Diff line change
Expand Up @@ -14,9 +14,9 @@ def initialize(params)
@params = params
end

def verify(certificate_path)
def verify(certificate_path, hash_algorithm = Ipizza::Util::DEFAULT_HASH_ALGORITHM)
mac_string = Ipizza::Util.mac_data_string(@params, PARAM_ORDER[@params['VK_SERVICE']])

@valid = Ipizza::Util.verify_signature(certificate_path, @params['VK_MAC'], mac_string)
@valid = Ipizza::Util.verify_signature(certificate_path, @params['VK_MAC'], mac_string, hash_algorithm)
end
end
29 changes: 24 additions & 5 deletions lib/ipizza/util.rb
Original file line number Diff line number Diff line change
Expand Up @@ -4,23 +4,32 @@
module Ipizza
class Util

DEFAULT_HASH_ALGORITHM = 'sha1'

class << self

def verify_signature(certificate_path, signature, data)
def verify_signature(certificate_path, signature, data, hash_algorithm = DEFAULT_HASH_ALGORITHM)
if !certificate_path.to_s.empty? && !signature.to_s.empty? && File.file?(certificate_path)
certificate = OpenSSL::X509::Certificate.new(File.read(certificate_path).gsub(/ /, '')).public_key
certificate.verify(OpenSSL::Digest::SHA1.new, Base64.decode64(signature), data)
certificate.verify(
digest_class(hash_algorithm).new,
Base64.decode64(signature),
data
)
else
false
end
end

def sign(privkey_path, privkey_secret, data)
def sign(privkey_path, privkey_secret, data, hash_algorithm = DEFAULT_HASH_ALGORITHM)
privkey = File.open(privkey_path, 'r') { |f| f.read }
privkey = OpenSSL::PKey::RSA.new(privkey.gsub(/ /, ''), privkey_secret)

signature = privkey.sign(OpenSSL::Digest::SHA1.new, data)
signature = Base64.encode64(signature).gsub(/\n/, '')
signature = privkey.sign(
digest_class(hash_algorithm).new,
data
)
Base64.encode64(signature).gsub(/\n/, '')
end

# Calculates and adds control number using 7-3-1 algoritm for Estonian banking account and reference numbers.
Expand Down Expand Up @@ -74,6 +83,16 @@ def func_p(val)
sprintf('%03i', val.bytesize)
end
end

def digest_class(hash_algorithm)
algorithm = (hash_algorithm || '').upcase

if OpenSSL::Digest.const_defined?(algorithm)
OpenSSL::Digest.const_get(algorithm)
else
raise ArgumentError, "Unknown hash algorithm OpenSSL::Digest::#{algorithm}"
end
end
end
end
end
2 changes: 2 additions & 0 deletions spec/config/config.yml
Original file line number Diff line number Diff line change
Expand Up @@ -41,6 +41,8 @@ seb:
key_secret: foobar
encoding: UTF-8
snd_id: sender
sign_algorithm: 'sha256'
verification_algorithm: 'sha256'

luminor:
service_url: https://banklink.luminor.ee/test
Expand Down
2 changes: 2 additions & 0 deletions spec/config/plain_config.yml
Original file line number Diff line number Diff line change
Expand Up @@ -11,3 +11,5 @@ swedbank:

seb:
service_url: https://www.seb.ee/banklink
sign_algorithm: 'sha256'
verification_algorithm: 'sha1'
2 changes: 2 additions & 0 deletions spec/ipizza/config_spec.rb
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,8 @@
Ipizza::Provider::Swedbank.encoding.should == 'UTF-8'

Ipizza::Provider::Seb.service_url.should == 'https://www.seb.ee/banklink'
Ipizza::Provider::Seb.sign_algorithm.should == 'sha256'
Ipizza::Provider::Seb.verification_algorithm.should == 'sha1'
end

it 'should load certificates from path relative to configuration file' do
Expand Down
14 changes: 12 additions & 2 deletions spec/ipizza/provider/seb_spec.rb
Original file line number Diff line number Diff line change
Expand Up @@ -45,7 +45,12 @@
}

it 'should parse and verify the payment response from bank' do
signature = Ipizza::Util.sign(bank_key, nil, Ipizza::Util.mac_data_string(params, Ipizza::Response::PARAM_ORDER['1111']))
signature = Ipizza::Util.sign(
bank_key,
nil,
Ipizza::Util.mac_data_string(params, Ipizza::Response::PARAM_ORDER['1111']),
Ipizza::Provider::Seb.sign_algorithm
)
Ipizza::Provider::Seb.new.payment_response(params.merge('VK_MAC' => signature)).should be_valid
end
end
Expand Down Expand Up @@ -82,7 +87,12 @@
}

it 'should parse and verify the authentication response from bank' do
signature = Ipizza::Util.sign(bank_key, nil, Ipizza::Util.mac_data_string(params, Ipizza::Response::PARAM_ORDER['3012']))
signature = Ipizza::Util.sign(
bank_key,
nil,
Ipizza::Util.mac_data_string(params, Ipizza::Response::PARAM_ORDER['3012']),
Ipizza::Provider::Seb.sign_algorithm
)
Ipizza::Provider::Seb.new.authentication_response(params.merge('VK_MAC' => signature)).should be_valid
end
end
Expand Down

0 comments on commit c40b08c

Please sign in to comment.