apple_pay 0.0.1 → 0.0.2
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +4 -4
- data/VERSION +1 -1
- data/apple_pay.gemspec +2 -2
- data/lib/apple_pay.rb +2 -1
- data/lib/apple_pay/merchant.rb +3 -1
- data/lib/apple_pay/payment_token.rb +35 -0
- data/lib/apple_pay/payment_token/AppleRootCA-G3.cer +0 -0
- data/lib/apple_pay/payment_token/certificate_chain.rb +32 -0
- data/lib/apple_pay/payment_token/encrypted_data.rb +77 -0
- data/lib/apple_pay/payment_token/signature.rb +31 -0
- metadata +10 -5
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA1:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: 872eee631d7a4283f5db260d42e8c8d7ec74374b
|
4
|
+
data.tar.gz: 37de100fe240ee997d2fbc651cde5556c6a1be14
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: 4d65c58543c27b2479c0ce62f2645306223a2702b19a7462c744df3be5b4427e446c1ffd368ba86cdea8a5675b6889d619c72cbd75833b5360dbaaabeea04f19
|
7
|
+
data.tar.gz: 2983387b72c306189db5407a1654006ba45547423ccb36cc4e7e1ecbaeb665c483e19cd3f903effe2138e9485bc2a63254770aebebe37b846f7ac8db933f8b85
|
data/VERSION
CHANGED
@@ -1 +1 @@
|
|
1
|
-
0.0.
|
1
|
+
0.0.2
|
data/apple_pay.gemspec
CHANGED
@@ -4,8 +4,8 @@ Gem::Specification.new do |spec|
|
|
4
4
|
spec.authors = ['Nov Matake']
|
5
5
|
spec.email = ['nov@matake.jp']
|
6
6
|
|
7
|
-
spec.summary = %q{
|
8
|
-
spec.description = %q{
|
7
|
+
spec.summary = %q{Apple Pay Merchant Backend}
|
8
|
+
spec.description = %q{Apple Pay Merchant Backend}
|
9
9
|
spec.homepage = 'https://github.com/nov/apple_pay.git'
|
10
10
|
spec.license = 'MIT'
|
11
11
|
spec.files = `git ls-files -z`.split("\x0").reject do |f|
|
data/lib/apple_pay.rb
CHANGED
@@ -7,7 +7,7 @@ module ApplePay
|
|
7
7
|
File.join(__dir__, '../VERSION')
|
8
8
|
).strip
|
9
9
|
|
10
|
-
class
|
10
|
+
class Error < StandardError; end
|
11
11
|
|
12
12
|
def self.logger
|
13
13
|
@@logger
|
@@ -38,4 +38,5 @@ module ApplePay
|
|
38
38
|
end
|
39
39
|
|
40
40
|
require 'apple_pay/merchant'
|
41
|
+
require 'apple_pay/payment_token'
|
41
42
|
require 'apple_pay/request_filter/debugger'
|
data/lib/apple_pay/merchant.rb
CHANGED
@@ -1,5 +1,7 @@
|
|
1
1
|
module ApplePay
|
2
2
|
class Merchant
|
3
|
+
class APIError < Error; end
|
4
|
+
|
3
5
|
attr_accessor :identifier, :domain, :display_name, :client_cert, :private_key
|
4
6
|
|
5
7
|
def initialize(identifier, domain:, display_name:)
|
@@ -9,7 +11,7 @@ module ApplePay
|
|
9
11
|
end
|
10
12
|
|
11
13
|
def authenticate(client_cert, private_key)
|
12
|
-
self.client_cert = client_cert
|
14
|
+
self.client_cert = client_cert # NOTE: Apple Pay Merchant Identity
|
13
15
|
self.private_key = private_key
|
14
16
|
self
|
15
17
|
end
|
@@ -0,0 +1,35 @@
|
|
1
|
+
module ApplePay
|
2
|
+
class PaymentToken
|
3
|
+
attr_accessor :token
|
4
|
+
|
5
|
+
def initialize(token)
|
6
|
+
self.token = token.with_indifferent_access
|
7
|
+
end
|
8
|
+
|
9
|
+
def verify!
|
10
|
+
Signature.new(
|
11
|
+
token[:paymentData][:signature],
|
12
|
+
data: token[:paymentData][:data],
|
13
|
+
ephemeral_public_key: token[:paymentData][:header][:ephemeralPublicKey],
|
14
|
+
transaction_id: token[:paymentData][:header][:transactionId],
|
15
|
+
application_data: token[:paymentData][:header][:applicationData]
|
16
|
+
).verify!
|
17
|
+
self
|
18
|
+
end
|
19
|
+
|
20
|
+
def decrypt!(client_cert, private_key)
|
21
|
+
decrypted = EncryptedData.new(
|
22
|
+
token[:paymentData][:data]
|
23
|
+
).decrypt!(
|
24
|
+
client_cert,
|
25
|
+
private_key,
|
26
|
+
token[:paymentData][:header][:ephemeralPublicKey]
|
27
|
+
)
|
28
|
+
JSON.parse decrypted
|
29
|
+
end
|
30
|
+
end
|
31
|
+
end
|
32
|
+
|
33
|
+
require 'apple_pay/payment_token/certificate_chain'
|
34
|
+
require 'apple_pay/payment_token/signature'
|
35
|
+
require 'apple_pay/payment_token/encrypted_data'
|
Binary file
|
@@ -0,0 +1,32 @@
|
|
1
|
+
module ApplePay
|
2
|
+
class PaymentToken
|
3
|
+
class CertificateChain
|
4
|
+
CHAIN_OIDS = {
|
5
|
+
leaf: '1.2.840.113635.100.6.29',
|
6
|
+
intermediate: '1.2.840.113635.100.6.2.14'
|
7
|
+
}
|
8
|
+
|
9
|
+
attr_accessor :pkcs7, :leaf, :intermediate, :root
|
10
|
+
|
11
|
+
def initialize(pkcs7_encoded)
|
12
|
+
self.pkcs7 = OpenSSL::PKCS7.new Base64.decode64(pkcs7_encoded)
|
13
|
+
[:leaf, :intermediate].each do |position|
|
14
|
+
detected = pkcs7.certificates.detect do |cert|
|
15
|
+
cert.extensions.collect(&:oid).include? CHAIN_OIDS[position]
|
16
|
+
end
|
17
|
+
self.send "#{position}=", detected
|
18
|
+
end
|
19
|
+
self.root = OpenSSL::X509::Certificate.new(
|
20
|
+
File.read File.join(__dir__, 'AppleRootCa-G3.cer')
|
21
|
+
)
|
22
|
+
end
|
23
|
+
|
24
|
+
def verify(signature_base_string)
|
25
|
+
trusted_store = OpenSSL::X509::Store.new
|
26
|
+
trusted_store.add_cert root
|
27
|
+
pkcs7.certificates = [leaf, intermediate].compact
|
28
|
+
pkcs7.verify nil, trusted_store, signature_base_string
|
29
|
+
end
|
30
|
+
end
|
31
|
+
end
|
32
|
+
end
|
@@ -0,0 +1,77 @@
|
|
1
|
+
module ApplePay
|
2
|
+
class PaymentToken
|
3
|
+
class EncryptedData
|
4
|
+
class DecryptionFailed < Error; end
|
5
|
+
|
6
|
+
MERCHANT_ID_OID = '1.2.840.113635.100.6.32'
|
7
|
+
|
8
|
+
attr_accessor :data
|
9
|
+
|
10
|
+
def initialize(encoded_data)
|
11
|
+
self.data = Base64.decode64 encoded_data
|
12
|
+
end
|
13
|
+
|
14
|
+
def decrypt!(client_cert, private_key, ephemeral_public_key_or_wrapped_key) # NOTE: Payment Processing Certificate
|
15
|
+
merchant_id = merchant_id_in client_cert
|
16
|
+
shared_secret = shared_secret_derived_from private_key, ephemeral_public_key_or_wrapped_key
|
17
|
+
symmetric_key = symmetric_key_derived_from merchant_id, shared_secret
|
18
|
+
cipher = OpenSSL::Cipher.new('aes-256-gcm')
|
19
|
+
cipher.decrypt
|
20
|
+
cipher.iv_len = 16 # NOTE: require ruby 2.4.0+ & openssl gem v2.0.0+
|
21
|
+
cipher.key = symmetric_key
|
22
|
+
cipher.auth_tag = data[-16..-1]
|
23
|
+
cipher.update(data[0..-17]) + cipher.final
|
24
|
+
end
|
25
|
+
|
26
|
+
private
|
27
|
+
|
28
|
+
def merchant_id_in(client_cert)
|
29
|
+
merchant_id_with_prefix = client_cert.extensions.detect do |ext|
|
30
|
+
ext.oid == MERCHANT_ID_OID
|
31
|
+
end
|
32
|
+
raise DecryptionFailed, 'Merchant ID missing' unless merchant_id_with_prefix
|
33
|
+
merchant_id_with_prefix.value[2..-1]
|
34
|
+
end
|
35
|
+
|
36
|
+
def shared_secret_derived_from(private_key, ephemeral_public_key_or_wrapped_key)
|
37
|
+
case private_key
|
38
|
+
when OpenSSL::PKey::RSA
|
39
|
+
shared_secret_derived_from_rsa(
|
40
|
+
private_key,
|
41
|
+
ephemeral_public_key_or_wrapped_key
|
42
|
+
)
|
43
|
+
when OpenSSL::PKey::EC
|
44
|
+
shared_secret_derived_from_ec(
|
45
|
+
private_key,
|
46
|
+
ephemeral_public_key_or_wrapped_key
|
47
|
+
)
|
48
|
+
else
|
49
|
+
raise DecryptionFailed, "Unknown algorithm (#{private_key.class})"
|
50
|
+
end
|
51
|
+
end
|
52
|
+
|
53
|
+
def shared_secret_derived_from_rsa(private_key, wrapped_key)
|
54
|
+
raise DecryptionFailed, 'RSA not supported yet'
|
55
|
+
end
|
56
|
+
|
57
|
+
def shared_secret_derived_from_ec(private_key, ephemeral_public_key)
|
58
|
+
public_key = OpenSSL::PKey::EC.new(
|
59
|
+
Base64.decode64 ephemeral_public_key
|
60
|
+
).public_key
|
61
|
+
point = OpenSSL::PKey::EC::Point.new(
|
62
|
+
private_key.group,
|
63
|
+
public_key.to_bn
|
64
|
+
)
|
65
|
+
private_key.dh_compute_key point
|
66
|
+
end
|
67
|
+
|
68
|
+
def symmetric_key_derived_from(merchant_id, shared_secret)
|
69
|
+
OpenSSL::Digest::SHA256.digest(
|
70
|
+
"\x00" * 3 + "\x01" +
|
71
|
+
shared_secret +
|
72
|
+
"\x0D" + 'id-aes256-GCM' + 'Apple' + [merchant_id].pack("H*")
|
73
|
+
)
|
74
|
+
end
|
75
|
+
end
|
76
|
+
end
|
77
|
+
end
|
@@ -0,0 +1,31 @@
|
|
1
|
+
module ApplePay
|
2
|
+
class PaymentToken
|
3
|
+
class Signature
|
4
|
+
class VerificationFailed < Error; end
|
5
|
+
|
6
|
+
attr_accessor :signature, :data, :ephemeral_public_key, :wrapped_key, :transaction_id, :application_data
|
7
|
+
|
8
|
+
def initialize(signature, data:, ephemeral_public_key: nil, wrapped_key: nil, transaction_id:, application_data: nil)
|
9
|
+
self.signature = signature
|
10
|
+
self.data = data
|
11
|
+
self.ephemeral_public_key = ephemeral_public_key
|
12
|
+
self.wrapped_key = wrapped_key
|
13
|
+
self.transaction_id = transaction_id
|
14
|
+
self.application_data = application_data
|
15
|
+
end
|
16
|
+
|
17
|
+
def verify!
|
18
|
+
chain = CertificateChain.new signature
|
19
|
+
signature_base_string = [
|
20
|
+
Base64.decode64(ephemeral_public_key || wrapped_key),
|
21
|
+
Base64.decode64(data),
|
22
|
+
[transaction_id].pack('H*'),
|
23
|
+
[application_data].pack('H*')
|
24
|
+
].join
|
25
|
+
chain.verify(
|
26
|
+
signature_base_string
|
27
|
+
) or raise VerificationFailed
|
28
|
+
end
|
29
|
+
end
|
30
|
+
end
|
31
|
+
end
|
metadata
CHANGED
@@ -1,14 +1,14 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: apple_pay
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 0.0.
|
4
|
+
version: 0.0.2
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- Nov Matake
|
8
8
|
autorequire:
|
9
9
|
bindir: exe
|
10
10
|
cert_chain: []
|
11
|
-
date: 2016-
|
11
|
+
date: 2016-10-13 00:00:00.000000000 Z
|
12
12
|
dependencies:
|
13
13
|
- !ruby/object:Gem::Dependency
|
14
14
|
name: activesupport
|
@@ -94,7 +94,7 @@ dependencies:
|
|
94
94
|
- - ">="
|
95
95
|
- !ruby/object:Gem::Version
|
96
96
|
version: '0'
|
97
|
-
description:
|
97
|
+
description: Apple Pay Merchant Backend
|
98
98
|
email:
|
99
99
|
- nov@matake.jp
|
100
100
|
executables: []
|
@@ -114,6 +114,11 @@ files:
|
|
114
114
|
- bin/setup
|
115
115
|
- lib/apple_pay.rb
|
116
116
|
- lib/apple_pay/merchant.rb
|
117
|
+
- lib/apple_pay/payment_token.rb
|
118
|
+
- lib/apple_pay/payment_token/AppleRootCA-G3.cer
|
119
|
+
- lib/apple_pay/payment_token/certificate_chain.rb
|
120
|
+
- lib/apple_pay/payment_token/encrypted_data.rb
|
121
|
+
- lib/apple_pay/payment_token/signature.rb
|
117
122
|
- lib/apple_pay/request_filter/debugger.rb
|
118
123
|
homepage: https://github.com/nov/apple_pay.git
|
119
124
|
licenses:
|
@@ -135,8 +140,8 @@ required_rubygems_version: !ruby/object:Gem::Requirement
|
|
135
140
|
version: '0'
|
136
141
|
requirements: []
|
137
142
|
rubyforge_project:
|
138
|
-
rubygems_version: 2.
|
143
|
+
rubygems_version: 2.6.6
|
139
144
|
signing_key:
|
140
145
|
specification_version: 4
|
141
|
-
summary:
|
146
|
+
summary: Apple Pay Merchant Backend
|
142
147
|
test_files: []
|