Bezpečnost
TLS
Veškerá komunikace s bránou je povinně zabezpečena použitím Transport Layer Security se serverovým certifikátem.
Server brány požaduje použití TLS verze 1.2 nebo vyšší.
Server brány požaduje použití ciphersuites aktuálně (Q3/2023) považovaných za silné. To jsou:
Ve verzi 1.2:
TLS_ECDHE_RSA_WITH_AES_128_GCM_SHA256TLS_ECDHE_RSA_WITH_AES_256_GCM_SHA384TLS_ECDHE_RSA_WITH_CHACHA20_POLY1305_SHA256
Ve verzi 1.3:
TLS_AES_256_GCM_SHA384TLS_CHACHA20_POLY1305_SHA256TLS_AES_128_GCM_SHA256
Které ciphersuites jsou považované za silné se průběžně mění. Počítejte s tím, že tento výčet není pevný a server jej může změnit.
Autentizace e-shopu - authenticate()
Před zahájením platby se e-shop musí nejprve k bráně přihlásit operací POST: /auth/token. Operace slouží k ověření e-shopu a získání dočasného přístupového tokenu potřebného pro další komunikaci s bránou. Pro autentizaci této operace použijete schéma Basic podle RFC 7617 s přístupovými údaji eshopId a eshopPassword, které jste získali při registraci vašeho e-shopu.
Získaný token pak musíte poslat v návazných operacích s platbou.
Životnost tokenu je omezena do času vráceného ve validTo. V případě vypršení tokenu je potřeba požádat o nový.
Pokud brána e-shop neověří tak:
V testovacím prostředí brána požadavek zamítne s HTTP status 401.
V produkčním prostředí brána může požadavek ignorovat.
Ref: Platební brána | Sekvenční diagram platby #10, #11
Podepisování zpráv
Komunikace s bránou je dále zabezpečena elektronickým podepsáním jednotlivých vyměňovaných zpráv asymetrickou kryptografií.
Pokud je v požadavku operace specifikován podpis (pole signature) musíte:
Sestavit pole z požadavku oddělená oddělovačem “
|" (svislou čarou) do řetězce znaků k podepsáníclearTextString.Podepsat metodou RSASSA-PKCS1-v1_5 specifikovanou v kapitole 8.2 RFC 8017 (SHA-256 with RSA) s použitím privátního podpisového klíče obchodníka ustanoveného při registraci vašeho e-shopu. Pro testovací prostředí viz Generování testovacích klíčů.
Výsledný podpis zakódovat nejprve do Base64 a potom do URL encoded.
Výsledný řetězec vložit do parametru
signature, který nikdy není součástí podepisovaného řetězce.
Pole požadavku skládejte do řetězce k podepsání clearTextString vždy v pořadí, v jakém jsou uvedeny v tabulce níže.
Nezapomeňte, že pořadí záznamů v JSON není garantované.
Nepovinná pole, která nejsou v požadavku přítomná, do clearTextString nevkládejte. Nevkládejte ani jejich případné oddělovače.
Textová pole začínající a/nebo končící neviditelnými znaky budou bránou odmínuta jako nevalidní.
Při kontrole podpisu požadavku bránou budou textová pole s nulovou délkou považována za nepřítomná a do kontroly nebudou zahrnuta.
Skladba řetězce k podpisu v požadavcích jednotlivých operací
Operace | Seznam a pořadí polí ke vložení do řetězce k podepsání clearTextString - tučně jsou povinná |
|---|---|
initPayment() | x-correlation-id*, amount, productCode, orderReferenceCode, orderDescription, merchantData, returnUrl, language
|
getPaymentState() | paymentId |
cancelPayment() | paymentId |
Příklad sestavení řetězce k podpisu pro operaci initPayment()
Budou složeny hodnoty z polí: amount, productCode, orderReferenceCode, merchantData, returnUrl, language, protože pole x-correlation-id a orderDescription jako nepovinná obchodník nevyplnil.
clearTextString=1250|LEISURE|34324Q22A4|cMWZw61rYWQ7ZGF0O29iY2hvZG7DrWth|https:/partners.eshop.cz/returns|cs
Příklad vytvoření podpisu operace getPaymentState()
Do clearTextString bude vloženo jen paymentId. Příklad hodnoty paymentId je “EC42E6695FFADAE5D0017952F0CF7A69”.
clearTextString=EC42E6695FFADAE5D0017952F0CF7A69
https://pay.benefit-plus.cz/v3/payments/EC42E6695FFADAE5D0017952F0CF7A69/state?signature=URL-encoded-Base64-encoded-hodnota-podpisu-vypočteného-z-řetězce-EC42E6695FFADAE5D0017952F0CF7A69
Příklad podepsání řetězce v jazyku Java
Příklady jsou poskytované tak jak jsou, bez záruky jejich použitelnosti ve vašem konkrétním eshopu!
/*
merchantsPrivateKeyBytes is your private key in PKCS#8
clearTextString is compounded from selected request fields separated by the | (vertical bar) ordered according to this spec
*/
java.security.PrivateKey merchantsPrivateKey = java.security.KeyFactory.getInstance("RSA").generatePrivate(new java.security.spec.PKCS8EncodedKeySpec(merchantsPrivateKeyBytes));
java.security.Signature requestSignature = java.security.Signature.getInstance("SHA256withRSA");
requestSignature.initSign(merchantsPrivateKey);
requestSignature.update(clearTextString.getBytes(java.nio.charset.StandardCharsets.UTF_8));
byte[] signatureBytes = requestSignature.sign();
String signatureString = java.net.URLEncoder.encode(java.util.Base64.getEncoder().encodeToString(signatureBytes), java.nio.charset.StandardCharsets.UTF_8);
Příklad podepsání řetězce v jazyku PHP
Příklady jsou poskytované tak jak jsou, bez záruky jejich použitelnosti ve vašem konkrétním eshopu!
<?php
$dataToSign = "2500|LEISURE|ord3436232|https://eshop.partnerx.cz/returns|cs";
$privateKey = "-----BEGIN RSA PRIVATE KEY-----
... insert here your private key in ASN.1 encoded as DER according to PKCS#1 (traditional)
-----END RSA PRIVATE KEY-----";
try {
// Instatiates a private key
$pkeyid = openssl_get_privatekey($privateKey);
// Signs the data
openssl_sign($dataToSign, $signature, $pkeyid, OPENSSL_ALGO_SHA256);
// Releases the key resource
openssl_free_key($pkeyid);
// Otputs signature encoded in Base64
echo base64_encode($signature);
echo "<br>";
// Otputs signature encoded in Base64 and then URL encoded
echo urlencode(base64_encode($signature));
} catch (Exception $e) {
http_response_code(500);
echo 'Signing Error: ', $e->getMessage(), "\n";
}
?>Kontrola požadavků e-shopu
Pokud je v požadavku operace specifikován podpis (pole signature) brána jej vždy ověří. Pokud kontrola podpisu selže, tak:
V testovacím prostředí brána zamítne požadavek s HTTP status 403 a s označením chybného pole
signature.V produkčním prostředí brána může požadavek ignorovat, nebo ho zamítnout s HTTP 401.
Kontrola odpovědí brány
Doporučujeme vám kontrolovat podpisy odpovědí přijatých z brány pomocí veřejného klíče brány. Můžete tak ověřit, že zprávy nejsou podvržené.
Testovací klíč
v X.509 formátu.
-----BEGIN PUBLIC KEY-----
MIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEAzMvwEmT983FnrEXC8nfX
F+o+AKKvoGAOTUE+Ivo1EtsXBGAfavoj+xmWP+P8A+itquX7XWCX71Hh6VW/zeXJ
YHrK60PdGYICAhjcWH4s7Lb/E1V0hQzhXl2hgs+O5ZruJoSel+obd1JX05scPVFo
vhk4HPpWVcj2oOgTNEJ4wV4NlwUFbJuxNhWM0Wu5zfElqRAlgPhpS90u3bV1O1Xw
WHSYcgYrvNfb3AKs/9AdjOKaPw53QIZu5PR4jLIf/3b3xfNm2cP5uZUxpHuxRAtq
c6xjqaKnHwjhcOGiZWb+OE74t9SxOr8fpbu6Qug+5LTv0YfRDSITB4eAr/CbunaD
BQIDAQAB
-----END PUBLIC KEY-----Produkční klíč
V X.509 formátu.
-----BEGIN PUBLIC KEY-----
MIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEAyeGC0UUZl4sOKPvVFJ5Z
Mouy4R9E4PuXOqryxjk4NfUZElFMx5aLLLp1zWVDWKxjkR8Q9iNDpgs2fOUD24+M
OilT5qXS63xzyTEpoVjJBMjCQZ+roUOMg+J7JWbn0kiC2dRCanfCYbFPQTEimR0v
iH7jePiavl8MeEz4Xxcfa84eV0mcaph9dJhs1FADTCI2hkgpCbxcjDzfEGRmA+v9
KN6mLvl7cWMiFBSvBe4btBb7oa8ba/HOyxOQKOSNJt0Vnpr4njU2wc+Bfh+IdOue
Pn5ukv8CeyonOPgrb2xNVbVxPLS96EsGQldKGQnY5iNyqEvGtKpig1358B7X5/4K
LwIDAQAB
-----END PUBLIC KEY-----Brána používá stejnou metodu (RSASSA-PKCS1-v1_5) podepisování odpovědí, jakou používáte při podepsování požadavků.
V případě neověření podpisu byste takové odpovědi neměli důvěřovat.
Skladba řetězce clearTextString ke kontrole odpovědí z jednotlivých operací:
Operace | Seznam a pořadí polí ke vložení do kontrolního řetězce clearTextString - pokud jsou součástí odpovědi brány |
|---|---|
initPayment() response | x-correlation-id, paymentId, orderReferenceCode, beneficiaryId, amount, currency, gatewayUrl |
getPaymentState() reponse | x-correlation-id, paymentId, amountAuthorized remainingAmount (version 2), paymentState, paymentDateTime, responseCode, orderReferenceCode, responseMessage |
Příklad kontroly podpisu v jazyku Java
Příklady jsou poskytované tak jak jsou, bez záruky jejich použitelnosti ve vašem konkrétním eshopu!
*
gatewayPublicKeyString is a gateway's public key in X509 format encoded in Base64
clearTextString is compounded from selected response fields separated by the | (vertical bar) ordered according to this spec
receivedSignature is a value of signature field received in a gateway response
*/
java.security.PublicKey gatewayPublicKey = java.security.KeyFactory.getInstance("RSA").generatePublic(new java.security.spec.X509EncodedKeySpec(java.util.Base64.getDecoder().decode(gatewayPublicKeyString)));
java.security.Signature recalculatedSignature = java.security.Signature.getInstance("SHA256withRSA");
recalculatedSignature.initVerify(gatewayPublicKey);
recalculatedSignature.update(clearTextString.getBytes(java.nio.charset.StandardCharsets.UTF_8));
boolean canITrustTheResponse = recalculatedSignature.verify(java.util.Base64.getDecoder().decode(receivedSignature));Příklad kontroly podpisu v jazyku PHP
Příklady jsou poskytované tak jak jsou, bez záruky jejich použitelnosti ve vašem konkrétním eshopu!
<?php
// Public key of the gateway (use the right value for the environment)
$publicKey = "-----BEGIN PUBLIC KEY-----
MIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEAzMvwEmT983FnrEXC8nfX
F+o+AKKvoGAOTUE+Ivo1EtsXBGAfavoj+xmWP+P8A+itquX7XWCX71Hh6VW/zeXJ
YHrK60PdGYICAhjcWH4s7Lb/E1V0hQzhXl2hgs+O5ZruJoSel+obd1JX05scPVFo
vhk4HPpWVcj2oOgTNEJ4wV4NlwUFbJuxNhWM0Wu5zfElqRAlgPhpS90u3bV1O1Xw
WHSYcgYrvNfb3AKs/9AdjOKaPw53QIZu5PR4jLIf/3b3xfNm2cP5uZUxpHuxRAtq
c6xjqaKnHwjhcOGiZWb+OE74t9SxOr8fpbu6Qug+5LTv0YfRDSITB4eAr/CbunaD
BQIDAQAB
-----END PUBLIC KEY-----";
// Response data as received from the gateway
// NOTE: use all the value strings exactly as receieved!
$data = [
'x-correlation-id' => '52BEFD5C95B84DF39549C402C241F900',
'paymentId' => '2A69ABB4829C4A41A5D0F7613A493220',
'orderReferenceCode' => '140654',
'beneficiaryId' => 'AAEEAA00001000000000200000000032',
'amount' => '600',
'currency' => 'CZK',
'gatewayUrl' => 'https://gateway-ui-dot-benefit-plus-sandbox-398907.ey.r.appspot.com/cs/sNax7vVyrqKLskX%2FfM0PpbDraOmDeXz82SKn8IEdYSed%2FYFKseHZRFyZUhi8IBPL3bXTG7Edwn53SDo0kgfCW18p',
'signature' => 'D9vIf/nMRxTLJ7EG3dHsXUH6QnI+HnEuAKBFP/LpbegjNKSJHB3/9j+IEZHgocQdWMf7bdzfPkqpDBFXcuFe4mjhAveEY7x5QiPNZ25EapyqYarv7WRN0WOTGhe9Y7F+lq+1ynavgN/CjawrJ9iy0uSuGmnfNexcYGqd2TgQPUPn5kzDdfZ0085X8h+Xx478OpIotUWTh4cWGmqjH3oun3nFOBUAx0Jyddw4ifDXWBtE2jLgD9tSMrZZsV5N48blNTjRwdpE50pCaTzLs/l3AcVbp6CAtxU/5ENtFvBcC5NiO8Ya/ZSiRU+ZPfwKF/BUJHahkp/Z+0cEIjRqp6tS4Q=='
];
// InitPayment response fields order
$fields = [
'x-correlation-id',
'paymentId',
'orderReferenceCode',
'beneficiaryId',
'amount',
'currency',
'gatewayUrl'
];
// Concatenation of the string for validation
$values = [];
foreach ($fields as $field) {
if (isset($data[$field])) {
$values[] = $data[$field];
}
}
$clearTextString = implode('|', $values);
// Signature validation
$key = openssl_pkey_get_public($publicKey);
if ($key === false) {
die('Invalid public key');
}
$signature = base64_decode($data['signature']);
if ($signature === false) {
die('Invalid signature format (Base64)');
}
$result = openssl_verify($clearTextString, $signature, $key, OPENSSL_ALGO_SHA256);
openssl_free_key($key);
if ($result === 1) {
echo "Signature is valid\n";
} elseif ($result === 0) {
echo "Signature is invalid\n";
} else {
echo "Signature validation error: " . openssl_error_string() . "\n";
}
?>
Příklad kontroly podpisu pomocí OpenSSL (and Unix shell-u)
Příklady jsou poskytované tak jak jsou, bez záruky jejich použitelnosti ve vašem konkrétním eshopu!
# CORRELATION_ID is `x-correlation-id` header of response
# RESPONSE is response body of init payment request
RESPONSE_CLEAR_TEXT="${CORRELATION_ID}|$(jq -r .paymentId <<< $RESPONSE)|$(jq -r .orderReferenceCode <<< $RESPONSE)|$(jq -r .beneficiaryId <<< $RESPONSE)|$(jq -r .amount <<< $RESPONSE)|$(jq -r .currency <<< $RESPONSE)|$(jq -r .gatewayUrl <<< $RESPONSE)"
echo
echo "Response clear text:"
# be sure to not introduce any new lines to cleartext
printf "%s" "${RESPONSE_CLEAR_TEXT//[$'\n']}" > response.cleartext
cat -A response.cleartext
jq -r .signature <<< $RESPONSE | base64 --decode > response.signature
#public.pem is public key provided by MúzaPay
echo
echo "Response signature verification:"
openssl dgst -sha256 -verify public.pem -keyform PEM -signature response.signature response.cleartextOchrana přístupových dat
Pro bezpečnost použití platební brány je kritické, abyste udrželi v tajnosti:
vaše přístupové heslo
eshopPasswordváš privátní klíč z páru pro podepisování požadavků.
Nastavte proto vaše procesy tak, aby nedošlo k jejich vyzrazení. Pokud k němu přesto dojde, informujte neprodleně Benefit Management.
Informace k PCI DSS
Platební brána žádným způsobem nepracuje s daty bankovních (ani jiných) platebních karet a nepodléhá tedy PCI standardům.
Brána MúzaPay je postavena na principu scanování QR prezentovaného obchodníkem (MPQR/Scan & Pay/Scan to Pay). Platby jsou autorizované ověřením identity plátce v mobilní aplikaci MúzaPay, buď s pomocí biometrie nebo PINu. Jde o funkční obdobu 3D Secure v2 klíčů v současných internetových bankovnictvích.