apple_pay 0.0.1 → 0.0.2
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- 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: []
|