Satıcılar için ödeme verilerinin şifrelenmesi

Google Pay API, ödeme yöntemlerini imzalı ve şifrelenmiş bir PaymentMethodToken yükü içinde döndürür. Döndürülen ödeme yöntemleri, PAN'den oluşan kartlar veya cihaz PAN'si ve kriptogramlardan oluşan token'laştırılmış kartlardır.

Yük, yükün alıcısına hangi kriptografik temel öğelerin kullanıldığını ve beklenen biçimi bildiren protocolVersion adlı bir alan içerir.

Bu kılavuzda, Google tarafından imzalanmış ve şifrelenmiş bir ödeme yöntemi jetonu istemek için ortak anahtarın nasıl oluşturulacağı hakkında bilgi verilmekte ve jetonu doğrulamak ve şifresini çözmek için yapılması gereken adımlar ayrıntılı olarak açıklanmaktadır.

Bu kılavuz yalnızca protocolVersion = ECv2 için geçerlidir.

Ödeme kartı bilgilerini doğrudan aldığınız için devam etmeden önce uygulamanızın PCI DSS uyumlu olduğundan ve sunucularınızın, kullanıcının ödeme kimlik bilgilerini güvenli bir şekilde işlemek için gerekli altyapıya sahip olduğundan emin olun.

Aşağıdaki adımlarda, bir entegratörün Google Pay API ECv2 PaymentMethodToken yükünü kullanmak için yapması gerekenler açıklanmaktadır:

  1. Google kök imzalama anahtarlarını alın.
  2. Ara imzalama anahtarının imzasının, süresi dolmamış kök imzalama anahtarlarından herhangi biri tarafından geçerli olduğunu doğrulayın.
  3. Yükün ara imzalama anahtarının süresinin dolmadığını doğrulayın.
  4. Yükün imzasının ara imzalama anahtarı tarafından geçerli olduğunu doğrulayın.
  5. İmzayı doğruladıktan sonra yükün içeriğinin şifresini çözün.
  6. Mesajın süresinin dolmadığını doğrulayın. Bu, geçerli zamanın şifresi çözülmüş içeriklerdeki messageExpiration alanından daha az olduğunu kontrol etmenizi gerektirir.
  7. Şifresi çözülen içeriklerdeki ödeme yöntemini kullanın ve ödemeyi alın.

Tink kitaplığımızdaki örnek kod, 1-6 arasındaki adımları uygular.

Ödeme yöntemi jeton yapısı

Google tarafından PaymentData yanıtında döndürülen mesaj, aşağıdaki tabloda belirtilen anahtarları içeren, UTF-8 kodlu, serileştirilmiş bir JSON nesnesidir:

Ad Tür Açıklama
protocolVersion Dize İletinin oluşturulduğu şifreleme veya imzalama şemasını tanımlar. Bu, protokolün zaman içinde gelişmesine olanak tanır.
signature Dize İletinin Google'dan geldiğini doğrular. Base64 olarak kodlanmıştır ve ara imzalama anahtarı tarafından ECDSA ile oluşturulmuştur.
intermediateSigningKey Nesne Google'dan gelen ara imzalama anahtarını içeren bir JSON nesnesi. keyValue, keyExpiration ve signatures ile signedKey içerir. Ara imzalama anahtarı imza doğrulama sürecini basitleştirmek için serileştirilir.
signedMessage Dize encryptedMessage, ephemeralPublicKey ve tag içeren, HTML için güvenli bir dize olarak serileştirilmiş JSON nesnesi. İmza doğrulama sürecini basitleştirmek için serileştirilir.

Örnek

Aşağıda, JSON biçiminde bir ödeme yöntemi jetonu yanıtı verilmiştir:

{
  "protocolVersion":"ECv2",
  "signature":"MEQCIH6Q4OwQ0jAceFEkGF0JID6sJNXxOEi4r+mA7biRxqBQAiAondqoUpU/bdsrAOpZIsrHQS9nwiiNwOrr24RyPeHA0Q\u003d\u003d",
  "intermediateSigningKey":{
    "signedKey": "{\"keyExpiration\":\"1542323393147\",\"keyValue\":\"MFkwEwYHKoZIzj0CAQYIKoZIzj0DAQcDQgAE/1+3HBVSbdv+j7NaArdgMyoSAM43yRydzqdg1TxodSzA96Dj4Mc1EiKroxxunavVIvdxGnJeFViTzFvzFRxyCw\\u003d\\u003d\"}",
    "signatures": ["MEYCIQCO2EIi48s8VTH+ilMEpoXLFfkxAwHjfPSCVED/QDSHmQIhALLJmrUlNAY8hDQRV/y1iKZGsWpeNmIP+z+tCQHQxP0v"]
  },
  "signedMessage":"{\"tag\":\"jpGz1F1Bcoi/fCNxI9n7Qrsw7i7KHrGtTf3NrRclt+U\\u003d\",\"ephemeralPublicKey\":\"BJatyFvFPPD21l8/uLP46Ta1hsKHndf8Z+tAgk+DEPQgYTkhHy19cF3h/bXs0tWTmZtnNm+vlVrKbRU9K8+7cZs\\u003d\",\"encryptedMessage\":\"mKOoXwi8OavZ\"}"
}

Ara imzalama anahtarı

intermediateSigningKey, aşağıdaki değerleri içeren UTF-8 kodlu, serileştirilmiş bir JSON nesnesidir:

Ad Tür Açıklama
signedKey Dize Anahtarın ödeme açıklamasını içeren base64 kodlu bir mesaj.
signatures Dize Ara imzalama anahtarının Google'dan geldiğini doğrular. Base64 olarak kodlanmıştır ve ECDSA ile oluşturulmuştur.

İmzalı anahtar

signedKey, aşağıdaki değerleri içeren UTF-8 kodlu, serileştirilmiş bir JSON nesnesidir:

Ad Tür Açıklama
keyValue Dize ASN.1 türünde kodlanmış anahtarın base64 sürümü. SubjectPublicKeyInfo, X.509 standardında tanımlanmıştır.
keyExpiration Dize Ara anahtarın süresinin dolduğu tarih ve saat (epoch'tan bu yana geçen UTC milisaniye cinsinden). Entegratörler, süresi dolmuş anahtarları reddeder.

İmzalı mesaj

signedMessage, aşağıdaki değerleri içeren UTF-8 kodlu, serileştirilmiş bir JSON nesnesidir:

Ad Tür Açıklama
encryptedMessage Dize Ödeme bilgilerini ve bazı ek güvenlik alanlarını içeren base64 ile kodlanmış şifreli mesaj.
ephemeralPublicKey Dize Mesajı sıkıştırılmamış nokta biçiminde şifrelemek için özel anahtarla ilişkili, base64 kodlu geçici bir ortak anahtar. Daha fazla bilgi için Şifreleme ortak anahtar biçimi başlıklı makaleyi inceleyin.
tag Dize encryptedMessage değerinin base64 kodlu MAC'i.

Şifrelenmiş mesaj

Şifresi çözülen encryptedMessage, UTF-8 olarak kodlanmış, serileştirilmiş bir JSON nesnesidir. JSON iki düzey içerir. Dış düzey, güvenlik için eklenen meta verileri ve alanları içerirken iç düzey, gerçek ödeme kimlik bilgilerini temsil eden başka bir JSON nesnesidir.

encryptedMessage hakkında daha fazla bilgi için aşağıdaki tablolara ve JSON nesnesi örneklerine bakın:

Ad Tür Açıklama
messageExpiration Dize Mesajın süresinin dolduğu tarih ve saat (sıfır zamandan itibaren UTC milisaniye cinsinden). Entegratörler, süresi dolmuş tüm mesajları reddetmelidir.
messageId Dize İptal edilmesi veya daha sonra bulunması gerektiğinde mesajı tanımlayan benzersiz bir kimlik.
paymentMethod Dize Ödeme kimlik bilgisinin türü. Şu anda yalnızca CARD desteklenmektedir.
paymentMethodDetails Nesne Ödeme kimlik bilgisinin kendisi. Bu nesnenin biçimi paymentMethod tarafından belirlenir ve aşağıdaki tablolarda açıklanır.

Kart

Aşağıdaki özellikler, CARD ödeme yönteminin ödeme kimlik bilgilerini oluşturur:

Ad Tür Açıklama
pan Dize Ücretlendirilen kişisel hesap numarası. Bu dize yalnızca rakam içerir.
expirationMonth Sayı Kartın son kullanma ayı. 1 Ocak'ı, 2 Şubat'ı vb. temsil eder.
expirationYear Sayı Kartın dört haneli son kullanma yılı (ör. 2020).
authMethod Dize Kart işleminin kimlik doğrulama yöntemi.
merchantTokenId Dize

Kart işleminin satıcı jeton kimliği. Bu isteğe bağlı alan yalnızca aşağıdaki koşulların tümü karşılandığında döndürülür:

  • Satıcı, istekte bir MIT *TransactionInfo nesnesi belirtir.
  • Satıcı, isteğe bir tokenUpdateUrl ekler.
  • Kullanıcı, jetonlu bir araç (ör. DPAN) seçer.

PAN_ONLY

Aşağıdaki JSON snippet'i, PAN_ONLY authMethod içeren bir CARD paymentMethod için tam encryptedMessage örneğidir.

{
  "paymentMethod": "CARD",
  "paymentMethodDetails": {
    "authMethod": "PAN_ONLY",
    "pan": "1111222233334444",
    "expirationMonth": 10,
    "expirationYear": 2025
  },
  "gatewayMerchantId": "some-merchant-id",
  "messageId": "some-message-id",
  "messageExpiration": "1759309000000"
}

CRYPTOGRAM_3DS

3-D Secure kriptogramı kullanılarak kimliği doğrulanmış bir CARD, CRYPTOGRAM_3DS authMethod. Aşağıdaki ek alanları içerir:

Ad Tür Açıklama
cryptogram Dize 3-D Secure kriptogramı.
eciIndicator Dize Bu dize her zaman mevcut değildir. Yalnızca Android'deki kimliği doğrulanmış cihaz jetonu işlemleri (CRYPTOGRAM_3DS) için döndürülür. Bu değer, ödeme işleme akışına iletilmelidir.

Aşağıdaki JSON snippet'i, CRYPTOGRAM_3DS authMethod içeren bir CARD paymentMethod için tam encryptedMessage örneğidir:

{
  "paymentMethod": "CARD",
  "paymentMethodDetails": {
    "authMethod": "CRYPTOGRAM_3DS",
    "pan": "1111222233334444",
    "expirationMonth": 10,
    "expirationYear": 2025,
    "cryptogram": "AAAAAA...",
    "eciIndicator": "eci indicator",
    "merchantTokenId": "merchant-token-id",
    
  },
  
  "messageId": "some-message-id",
  "messageExpiration": "1759309000000"
}

eciIndicator

Kart ağı, kimliği doğrulanmış cihaz jetonu işlemleri (CRYPTOGRAM_3DS) için eciIndicator sağlayabilir.

Yetkilendirme işleminde eciIndicator değerini değiştirmeden veya sabit kodlamadan iletmeniz gerekir. Aksi takdirde işlem başarısız olur. Aşağıdaki tabloda eciIndicator değerleri ayrıntılı olarak açıklanmıştır.

eciIndicator değeri Kart ağı Sorumlu Taraf authMethod
""(empty) Mastercard Satıcı/Ödeme Alıcı CRYPTOGRAM_3DS
02 Mastercard Kartı veren kuruluş CRYPTOGRAM_3DS
06 Mastercard Satıcı/Ödeme Alıcı CRYPTOGRAM_3DS
05 Visa Kartı veren kuruluş CRYPTOGRAM_3DS
07 Visa Satıcı/Ödeme Alıcı CRYPTOGRAM_3DS
""(empty) Diğer ağlar Satıcı/Ödeme Alıcı CRYPTOGRAM_3DS

VISA ve Mastercard için bu tabloda yer almayan diğer ECI değerleri döndürülmez.

İmza doğrulama

Ara anahtar ve mesaj imzalarını içeren imzaları doğrulamak için aşağıdaki öğeler gereklidir:

  • İmzayı oluşturmak için kullanılan algoritma
  • İmzayı oluşturmak için kullanılan bayt dizesi
  • İmzayı oluşturmak için kullanılan özel anahtara karşılık gelen ortak anahtar
  • İmzanın kendisi

İmza algoritması

Google, mesajları imzalamak için Eliptik Eğri Dijital İmza Algoritması'nı (ECDSA) kullanır. Bu algoritma, FIPS 186-4'te tanımlandığı şekilde karma işlevi olarak SHA-256 ile NIST P-256 üzerinden ECDSA parametrelerini kullanır.

İmza

İmza, iletinin en dış düzeyine eklenir. ASN.1 bayt biçiminde base64 ile kodlanır. ASN.1 hakkında daha fazla bilgi için IETF Araçları Ek A bölümüne bakın. İmza, ECDSA r ve s tam sayılarından oluşur. Daha fazla bilgi için İmza oluşturma algoritması başlıklı makaleyi inceleyin.

Aşağıda, Java Cryptography Extension (JCE) ECDSA uygulamaları tarafından üretilen standart biçim olan belirtilen ASN.1 bayt biçimine bir örnek verilmiştir.

ECDSA-Sig-Value :: = SEQUENCE {
 r INTEGER,
 s INTEGER
}

Ara imzalama anahtarı imzası için bayt dizesi oluşturma

Örnek ödeme yöntemi jetonundaki ara imzalama anahtarı imzasını doğrulamak için aşağıdaki formülle signedStringForIntermediateSigningKeySignature oluşturun:

signedStringForIntermediateSigningKeySignature =
length_of_sender_id || sender_id || length_of_protocol_version || protocol_version || length_of_signed_key || signed_key

"||" gösterimi, birleştirme anlamına gelir. Her bileşen (sender_id, protocolVersion, signedKey) UTF-8 kodlamalı olmalıdır. signedKey, intermediateSigningKey.signedKey dizesi olmalıdır. Her bileşenin bayt uzunluğu, little-endian biçiminde 4 bayttır.

Örnek

Bu örnekte aşağıdaki örnek ödeme yöntemi jetonu kullanılmaktadır:

{
  "protocolVersion":"ECv2",
  "signature":"MEQCIH6Q4OwQ0jAceFEkGF0JID6sJNXxOEi4r+mA7biRxqBQAiAondqoUpU/bdsrAOpZIsrHQS9nwiiNwOrr24RyPeHA0Q\u003d\u003d",
  "intermediateSigningKey":{
    "signedKey": "{\"keyExpiration\":\"1542323393147\",\"keyValue\":\"MFkwEwYHKoZIzj0CAQYIKoZIzj0DAQcDQgAE/1+3HBVSbdv+j7NaArdgMyoSAM43yRydzqdg1TxodSzA96Dj4Mc1EiKroxxunavVIvdxGnJeFViTzFvzFRxyCw\\u003d\\u003d\"}",
    "signatures": ["MEYCIQCO2EIi48s8VTH+ilMEpoXLFfkxAwHjfPSCVED/QDSHmQIhALLJmrUlNAY8hDQRV/y1iKZGsWpeNmIP+z+tCQHQxP0v"]
  },
  "signedMessage":"{\"tag\":\"jpGz1F1Bcoi/fCNxI9n7Qrsw7i7KHrGtTf3NrRclt+U\\u003d\",\"ephemeralPublicKey\":\"BJatyFvFPPD21l8/uLP46Ta1hsKHndf8Z+tAgk+DEPQgYTkhHy19cF3h/bXs0tWTmZtnNm+vlVrKbRU9K8+7cZs\\u003d\",\"encryptedMessage\":\"mKOoXwi8OavZ\"}"
}

sender_id her zaman Google, protocol_version ise ECv2 olur.

sender_id Google ise signedString aşağıdaki örnekte gösterildiği gibi görünür:

signedStringForIntermediateSigningKeySignature =
\x06\x00\x00\x00 || Google || | \x04\x00\x00\x00 || ECv2 || \xb5\x00\x00\x00 || {"keyExpiration":"1542323393147","keyValue":"MFkwEwYHKoZIzj0CAQYIKoZIzj0DAQcDQgAE/1+3HBVSbdv+j7NaArdgMyoSAM43yRydzqdg1TxodSzA96Dj4Mc1EiKroxxunavVIvdxGnJeFViTzFvzFRxyCw\u003d\u003d"}

signedStringForIntermediateSigningKeySignature üzerindeki imzayı doğrulama

Ara imzalama anahtarı imzası için imzalanmış dize birleştirilirken standart ECDSA doğrulama algoritması kullanılır. ECv2 protokolü için intermediateSigningKey.signatures içindeki tüm imzaları yinelemeniz ve her birini keys.json içindeki süresi dolmamış Google imzalama anahtarlarıyla doğrulamayı denemeniz gerekir. En az bir imza doğrulaması başarılı olursa doğrulama tamamlanmış sayılır. signedStringForMessageSignature doğrulamak için daha sonra intermediateSigningKey.signedKey.keyValue kullanın. Google, kendi doğrulama kodunuz yerine mevcut bir kriptografik kitaplığı kullanmanızı önemle tavsiye eder.

Mesaj imzası için bayt dizesi oluşturma

Örnek ödeme yöntemi jetonundaki imzayı doğrulamak için aşağıdaki formülle signedStringForMessageSignature oluşturun:

signedStringForMessageSignature =
length_of_sender_id || sender_id || length_of_recipient_id || recipient_id || length_of_protocolVersion || protocolVersion || length_of_signedMessage || signedMessage

"||" gösterimi, birleştirme anlamına gelir. Her bileşen (sender_id, recipient_id, protocolVersion, signedMessage) UTF-8 kodlamalı olmalıdır. Her bileşenin bayt uzunluğu, little-endian biçiminde 4 bayttır. Bayt dizesini oluştururken signedMessage değerini ayrıştırmayın veya değiştirmeyin. Örneğin, \u003d ifadesini = karakteriyle değiştirmeyin.

Örnek

Aşağıdaki örnek, bir ödeme yöntemi jetonu örneğidir:

{
  "protocolVersion":"ECv2",
  "signature":"MEQCIH6Q4OwQ0jAceFEkGF0JID6sJNXxOEi4r+mA7biRxqBQAiAondqoUpU/bdsrAOpZIsrHQS9nwiiNwOrr24RyPeHA0Q\u003d\u003d",
  "intermediateSigningKey":{
    "signedKey": "{\"keyExpiration\":\"1542323393147\",\"keyValue\":\"MFkwEwYHKoZIzj0CAQYIKoZIzj0DAQcDQgAE/1+3HBVSbdv+j7NaArdgMyoSAM43yRydzqdg1TxodSzA96Dj4Mc1EiKroxxunavVIvdxGnJeFViTzFvzFRxyCw\\u003d\\u003d\"}",
    "signatures": ["MEYCIQCO2EIi48s8VTH+ilMEpoXLFfkxAwHjfPSCVED/QDSHmQIhALLJmrUlNAY8hDQRV/y1iKZGsWpeNmIP+z+tCQHQxP0v"]
  },
  "signedMessage":"{\"tag\":\"jpGz1F1Bcoi/fCNxI9n7Qrsw7i7KHrGtTf3NrRclt+U\\u003d\",\"ephemeralPublicKey\":\"BJatyFvFPPD21l8/uLP46Ta1hsKHndf8Z+tAgk+DEPQgYTkhHy19cF3h/bXs0tWTmZtnNm+vlVrKbRU9K8+7cZs\\u003d\",\"encryptedMessage\":\"mKOoXwi8OavZ\"}"
}

sender_id her zaman Google, recipient_id ise merchant:merchantId olur. merchantId, üretim erişimi olan satıcılar için Google Pay ve Cüzdan Konsolu'nda bulunan değerle eşleşir.

sender_id Google ve recipient_id merchant:12345 ise signedString aşağıdaki örnekte olduğu gibi görünür:

signedStringForMessageSignature =
\x06\x00\x00\x00 || Google || \x0e\x00\x00\x00 || merchant:12345 || | \x04\x00\x00\x00 || ECv2 || \xd2\x00\x00\x00 || {"tag":"jpGz1F1Bcoi/fCNxI9n7Qrsw7i7KHrGtTf3NrRclt+U\u003d","ephemeralPublicKey":"BJatyFvFPPD21l8/uLP46Ta1hsKHndf8Z+tAgk+DEPQgYTkhHy19cF3h/bXs0tWTmZtnNm+vlVrKbRU9K8+7cZs\u003d","encryptedMessage":"mKOoXwi8OavZ"}

signedStringForMessageSignature'daki imza nasıl doğrulanır?

İmzalı dize oluşturulurken standart ECDSA doğrulama algoritması kullanılır. Önceki adımda doğrulanan intermediateSigningKey.signedKey.keyValue, signedMessage doğrulamak için kullanılır. Google, kendi doğrulama kodunuz yerine mevcut bir kriptografik kitaplığı kullanmanızı önemle tavsiye eder.

Şifreleme şeması spesifikasyonu

Google, Google Pay API yanıtında döndürülen ödeme yöntemi jetonunun güvenliğini sağlamak için Elips Biçimli Eğri Entegre Şifreleme Şeması'nı (ECIES) kullanır. Şifreleme şeması aşağıdaki parametreleri kullanır:

Parametre Tanım
Anahtar kapsülleme yöntemi

ISO 18033-2'de tanımlandığı şekilde ECIES-KEM.

  • Eliptik eğri: NIST P-256 (OpenSSL'de prime256v1 olarak da bilinir).
  • CheckMode, OldCofactorMode, SingleHashMode ve CofactorMode 0'dır.
  • Nokta biçimi sıkıştırılmamış.
Anahtar türetme işlevi

SHA-256 (HKDFwithSHA256) ile HMAC tabanlı.

  • Salt sağlanmamalıdır.
  • Bilgiler "Google" olmalı ve protokol sürümü ECv2 için ASCII olarak kodlanmalıdır.
  • AES256 anahtarı için 256 bit ve HMAC_SHA256 anahtarı için başka bir 256 bit türetilmelidir.
Simetrik şifreleme algoritması

ISO 18033-2'de tanımlandığı şekilde DEM2

Şifreleme algoritması: Sıfır IV'lü ve dolgusuz AES-256-CTR.

MAC algoritması Anahtar türetme işlevinden türetilen 256 bit anahtarla HMAC_SHA256.

Şifreleme ortak anahtarı biçimi

Şifreleme ortak anahtarı ve Google yüklerinde döndürülen ephemeralPublicKey, sıkıştırılmamış nokta biçiminde anahtarın base64 temsiliyle biçimlendirilir. Aşağıdaki iki unsurdan oluşur:

  • Biçimi belirten bir sihirli sayı (0x04).
  • Eliptik eğrideki X ve Y koordinatlarını temsil eden iki adet 32 baytlık büyük tam sayı.

Bu biçim, "Public Key Cryptography For The Financial Services Industry: The Elliptic Curve Digital Signature Algorithm (ECDSA)," ANSI X9.62, 1998'de daha ayrıntılı olarak açıklanmıştır.

Ortak anahtar oluşturmak için OpenSSL'yi kullanma

1. adım: Özel anahtar oluşturun

Aşağıdaki örnekte, NIST P-256 ile kullanıma uygun bir Eliptik Eğri özel anahtarı oluşturulur ve key.pem dosyasına yazılır:

openssl ecparam -name prime256v1 -genkey -noout -out key.pem

İsteğe bağlı: Özel ve ortak anahtarları görüntüleme

Hem özel hem de ortak anahtarı görüntülemek için aşağıdaki komutu kullanın:

openssl ec -in key.pem -pubout -text -noout

Komut, aşağıdakine benzer bir çıkış üretir:

read EC key
Private-Key: (256 bit)
priv:
    08:f4:ae:16:be:22:48:86:90:a6:b8:e3:72:11:cf:
    c8:3b:b6:35:71:5e:d2:f0:c1:a1:3a:4f:91:86:8a:
    f5:d7
pub:
    04:e7:68:5c:ff:bd:02:ae:3b:dd:29:c6:c2:0d:c9:
    53:56:a2:36:9b:1d:f6:f1:f6:a2:09:ea:e0:fb:43:
    b6:52:c6:6b:72:a3:f1:33:df:fa:36:90:34:fc:83:
    4a:48:77:25:48:62:4b:42:b2:ae:b9:56:84:08:0d:
    64:a1:d8:17:66
ASN1 OID: prime256v1

2. adım: Base64 kodlu bir ortak anahtar oluşturun

Önceki isteğe bağlı adım örneğinde oluşturulan özel ve ortak anahtar, onaltılık olarak kodlanmıştır. Sıkıştırılmamış nokta biçiminde Base64 olarak kodlanmış bir ortak anahtar almak için aşağıdaki komutu kullanın:

openssl ec -in key.pem -pubout -text -noout 2> /dev/null | grep "pub:" -A5 | sed 1d | xxd -r -p | base64 | paste -sd "\0" - | tr -d '\n\r ' > publicKey.txt

Komut, içeriği sıkıştırılmamış nokta biçimindeki anahtarın base64 sürümü olan ve aşağıdakine benzeyen bir publicKey.txt dosyası oluşturur:

BOdoXP+9Aq473SnGwg3JU1aiNpsd9vH2ognq4PtDtlLGa3Kj8TPf+jaQNPyDSkh3JUhiS0KyrrlWhAgNZKHYF2Y=

Dosya içeriğinde fazladan boşluk veya satır başı karakteri olmamalıdır. Bunu doğrulamak için Linux veya MacOS'te aşağıdaki komutu çalıştırın:

od -bc publicKey.txt

3. adım: PKCS #8 biçiminde base64 ile kodlanmış bir özel anahtar oluşturun

Tink kitaplığı, özel anahtarınızın PKCS #8 biçiminde base64 kodlu olmasını bekler. İlk adımda oluşturulan özel anahtardan bu biçimde özel anahtar oluşturmak için aşağıdaki komutu kullanın:

openssl pkcs8 -topk8 -inform PEM -outform DER -in key.pem -nocrypt | base64 | paste -sd "\0" -

Komut, aşağıdakine benzer bir çıkış üretir:

MIGHAgEAMBMGByqGSM49AgEGCCqGSM49AwEHBG0wawIBAQQgWV4oK8c/MZkCLk4qSCNjW0Zm6H0CBCtSYxkXkC9FBHehRANCAAQPldOnhO2/oXjdJD1dwlFPiNs6fcdoRgFu3/Z0iKj24SjTGyLRGAtYWLGXBZcDdPj3T2bJRHRVhE8Bc2AjkT7n

Ödeme yöntemi jetonunun şifresini çözme

Jetonun şifresini çözmek için aşağıdaki adımları uygulayın:

  1. ECIES-KEM kullanan 512 bit uzunluğunda bir paylaşılan anahtar türetmek için özel anahtarınızı ve verilen ephemeralPublicKey değerini kullanın. Aşağıdaki parametreleri kullanın:
    • Eliptik eğri: NIST P-256 (OpenSSL'de prime256v1 olarak da bilinir).
    • CheckMode, OldCofactorMode, SingleHashMode ve CofactorMode 0.
    • Kodlama işlevi: Sıkıştırılmamış nokta biçimi.
    • Anahtar türetme işlevi: RFC 5869'da açıklandığı şekilde, aşağıdaki parametreyle HKDFwithSHA256:
      • Salt sağlanmamalıdır. RFC uyarınca, bu değer 32 sıfırlanmış baytlık bir salt değerine eşdeğer olmalıdır.
  2. Oluşturulan anahtarı 256 bit uzunluğunda iki anahtara bölün: symmetricEncryptionKey ve macKey.
  3. tag alanının encryptedMessage için geçerli bir MAC olduğunu doğrulayın.

    Beklenen MAC'i oluşturmak için SHA256 karma işleviyle HMAC'yi (RFC 5869) ve 2. adımda elde edilen macKey değerini kullanın.

  4. AES-256-CTR modunu ve aşağıdakileri kullanarak encryptedMessage şifresini çözün:

    • Sıfır IV.
    • Dolgulu değil.
    • 2. adımda elde edilen symmetricEncryptionKey.

Anahtar yönetimi

Satıcı şifreleme anahtarları

Satıcılar, Şifreleme şeması spesifikasyonu bölümünde belirtilen spesifikasyonlara göre bir ortak anahtar oluşturur.

Google kök imzalama anahtarları

Google, herkese açık bir URL'den alınabilen, geçerli kök imzalama ortak anahtarlarını yayınlar. Anahtarlar, URL tarafından döndürülen HTTP önbellek üst bilgilerinde belirtilen süre boyunca geçerlidir. keyExpiration alanı tarafından belirlenen süre dolana kadar önbelleğe alınırlar. Bir getirme işleminin süresi dolduğunda, geçerli anahtarların güncel listesini almak için anahtarları herkese açık URL'den tekrar getirmenizi öneririz.

ECv2 protokolü için istisna: Çalışma zamanında Google'dan anahtarları getiremiyorsanız üretim URL'mizden keys.json anahtarını getirin, sisteminize kaydedin ve manuel olarak düzenli aralıklarla yenileyin. Normal koşullarda Google, en uzun geçerlilik bitiş tarihine sahip anahtarın süresi dolmadan beş yıl önce ECv2 için yeni bir kök imzalama anahtarı yayınlar. Anahtarların güvenliğinin ihlal edilmesi durumunda Google, keys.json anahtarının daha hızlı bir şekilde yeniden yüklenmesini talep etmek için self servis portalda sağlanan iletişim bilgileri aracılığıyla tüm satıcıları bilgilendirir. Düzenli rotasyonu kaçırmamanız için, Google anahtarlarını keys.json içeriğine kaydetmeyi seçen satıcıların, kendi yıllık anahtar rotasyonlarının bir parçası olarak her yıl yenileme yapmalarını öneririz.

Herkese açık URL aracılığıyla sağlanan anahtarlar aşağıdaki biçimde eşlenir:

{
  "keys": [
    {
      "keyValue": "encoded public key",
      "protocolVersion": "ECv2"
      "keyExpiration":"2000000000000"
    },
    {
      "keyValue": "encoded public key",
      "protocolVersion": "ECv2"
      "keyExpiration":"3000000000000"
    }
  ]
}

keyValue, X.509 standardında tanımlanan ASN.1 türü SubjectPublicKeyInfo ile kodlanmış anahtarın base64, sarmalanmamış veya doldurulmamış bir sürümüdür. Java'da, bahsedilen ASN.1 kodlaması X509EncodedKeySpec sınıfı ile temsil edilir. ECPublicKey.getEncoded() ile elde edilebilir.

Hem test hem de üretim ortamlarının URL'leri aşağıdaki bağlantılarda verilmiştir:

Anahtar rotasyonu

Doğrudan entegrasyonla sunucularınızda bir ödeme yöntemi jetonunun şifresini doğrudan çözerseniz anahtarları her yıl değiştirmeniz gerekir.

Şifreleme anahtarlarını rotasyona tabi tutmak için aşağıdaki adımları tamamlayın:

  1. Yeni bir anahtar çifti oluşturmak için OpenSSL'yi kullanın.
  2. Daha önce Google Pay'e geliştirici olarak kaydolmak için kullandığınız Google Hesabı ( ) ile oturum açarak Google Pay ve Cüzdan Konsolu'nu açın.
  3. Google Pay API sekmesindeki Doğrudan entegrasyon bölmesinde, mevcut ortak anahtarınızın yanındaki Yönet'i tıklayın. Başka bir anahtar ekle'yi tıklayın.
  4. Ortak şifreleme anahtarı metin giriş alanını seçin ve yeni oluşturduğunuz ortak anahtarı, sıkıştırılmamış nokta biçiminde base64 kodlu olarak ekleyin.
  5. Şifreleme anahtarlarını kaydet'i tıklayın.
  6. Anahtar rotasyonunun sorunsuz bir şekilde gerçekleşmesi için anahtarları taşırken hem yeni hem de eski özel anahtarların şifre çözme işlemini destekleyin.

    Jetonun şifresini çözmek için Tink kitaplığını kullanıyorsanız birden fazla özel anahtarı desteklemek için aşağıdaki Java kodunu kullanın:

    String decryptedMessage =
        new PaymentMethodTokenRecipient.Builder()
            .addRecipientPrivateKey(newPrivateKey)
            .addRecipientPrivateKey(oldPrivateKey);

    Şifre çözme kodunun üretime dağıtıldığından ve başarılı şifre çözme işlemlerini izlediğinizden emin olun.

  7. Kodunuzda kullanılan ortak anahtarı değiştirin.

    PaymentMethodTokenizationSpecification parameters özelliğindeki publicKey özelliğinin değerini değiştirin:

    const tokenizationSpecification = {
      "type": "DIRECT",
      "parameters": {
        "protocolVersion": "ECv2",
        "publicKey": "BOdoXP1aiNp.....kh3JUhiSZKHYF2Y="
      }
    }
  8. 4. adımdaki kodu üretime dağıtın. Kod dağıtıldıktan sonra şifreleme ve şifre çözme işlemleri yeni anahtar çiftlerini kullanır.
  9. Eski ortak anahtarın artık herhangi bir işlemi şifrelemek için kullanılmadığını doğrulayın.

  10. Eski özel anahtarı kaldırın.
  11. Daha önce Google Pay'e geliştirici olarak kaydolmak için kullandığınız Google Hesabı ile oturum açmışken Google Pay ve Cüzdan Konsolu'nu açın.
  12. Google Pay API sekmesindeki Doğrudan entegrasyon bölmesinde, mevcut ortak anahtarınızın yanındaki Yönet'i tıklayın. Eski ortak anahtarınızın yanındaki Sil'i ve ardından Şifreleme anahtarlarını kaydet'i tıklayın.

Google, aşağıdaki örnekte gösterildiği gibi PaymentMethodTokenizationSpecification parameters nesnesindeki publicKey özelliğinde belirtilen anahtarı kullanır:

{
  "protocolVersion": "ECv2",
  "publicKey": "BOdoXP+9Aq473SnGwg3JU1..."
}

Şifrelenmiş yanıtı yönetmek için Tink kitaplığını kullanın.

İmza doğrulaması ve mesaj şifre çözme işlemlerini gerçekleştirmek için Tink paymentmethodtoken kitaplığını kullanın. Bu kitaplık yalnızca Java'da kullanılabilir. Bu özelliği kullanmak için aşağıdaki adımları uygulayın:

  1. pom.xml dosyanızda Tink paymentmethodtoken uygulamasını bağımlılık olarak ekleyin:

    <dependencies>
      <!-- other dependencies ... -->
      <dependency>
        <groupId>com.google.crypto.tink</groupId>
        <artifactId>apps-paymentmethodtoken</artifactId>
        <version>1.9.1</version>  <!-- or latest version -->
      </dependency>
    </dependencies>
  2. Sunucu başlatılırken Google imzalama anahtarlarını önceden getirerek anahtarın bellekte kullanılabilir olmasını sağlayın. Bu sayede, şifre çözme işlemi anahtarları getirirken kullanıcının ağ gecikmesini görmesi engellenir.

    GooglePaymentsPublicKeysManager.INSTANCE_PRODUCTION.refreshInBackground();
  3. paymentMethodToken değerinin encryptedMessage değişkeninde depolandığını varsayan aşağıdaki kodla mesajın şifresini çözün ve kalın harflerle yazılmış bölümleri senaryonuza göre değiştirin.

    Üretim dışı testler için INSTANCE_PRODUCTION yerine INSTANCE_TEST kullanın. Entegrasyonunuz etkin değilse veya yapılandırılmış bir şifreleme anahtarı yoksa [YOUR MERCHANT ID] yerine 12345678901234567890 kullanın.

    • Etkin
    • DOĞRUDAN entegrasyon etkinleştirilmişse
    • Şifreleme anahtarı yapılandırılmış

    [YOUR MERCHANT ID] mülkünüzü değiştirmeyin.

    String decryptedMessage =
        new PaymentMethodTokenRecipient.Builder()
        .fetchSenderVerifyingKeysWith(
            GooglePaymentsPublicKeysManager.INSTANCE_PRODUCTION)
        .recipientId("merchant:[YOUR MERCHANT ID]")
        // This guide applies only to protocolVersion = ECv2
        .protocolVersion("ECv2")
        // Multiple private keys can be added to support graceful
        // key rotations.
        .addRecipientPrivateKey(PrivateKey1)
        .addRecipientPrivateKey(PrivateKey2)
        .build()
        .unseal(encryptedMessage);
  4. PrivateKey1 değerini, Anahtarlarınızı hazırlama ve Google'a kaydolma bölümünde Google'a kayıtlı ortak anahtar değeriyle ilişkilendirilen uygun özel anahtar değeriyle değiştirin. Google ile anahtarları rotasyona tabi tutmanız gerektiğinde daha sonra birden fazla özel anahtar değeri ekleyebilirsiniz. Değişkenler, base64 kodlu bir PKCS8 dizesi veya ECPrivateKey nesnesi olabilir. Base64 ile kodlanmış bir PKCS8 özel anahtarının nasıl oluşturulacağı hakkında daha fazla bilgi için Anahtarlarınızı hazırlama ve Google'a kaydolma başlıklı makaleyi inceleyin.

  5. Anahtarların şifresini her çözdüğünüzde Google sunucusunu arayamıyorsanız aşağıdaki kodla şifreyi çözün ve kalın harflerle yazılmış bölümleri senaryonuza göre değiştirin.

    String decryptedMessage =
        new PaymentMethodTokenRecipient.Builder()
        .addSenderVerifyingKey("ECv2 key fetched from test or production url")
        .recipientId("merchant:[YOUR MERCHANT ID]")
        // This guide applies only to protocolVersion = ECv2
        .protocolVersion("ECv2")
        // Multiple private keys can be added to support graceful
        // key rotations.
        .addRecipientPrivateKey(PrivateKey1)
        .addRecipientPrivateKey(PrivateKey2)
        .build()
        .unseal(encryptedMessage);

    Üretim ortamındaki mevcut anahtar, anahtarın güvenliğinin ihlal edilmesi dışında normal koşullarda 14.04.2038 tarihine kadar geçerlidir. Anahtarların güvenliğinin ihlal edilmesi durumunda Google, keys.json anahtarının daha hızlı bir şekilde yeniden yüklenmesini talep etmek için self servis portalda sağlanan iletişim bilgileri aracılığıyla tüm satıcıları bilgilendirir.

    Kod snippet'i, yükün kullanımına odaklanabilmeniz için aşağıdaki güvenlik ayrıntılarını işler:

    • Google imzalama anahtarları getirilir ve bellekte önbelleğe alınır.
    • İmza doğrulama
    • Şifre çözme