Виды ключей
Для полноценной работы необходимо иметь 2 вида ключей:
-
Ключ для подписи запросов от торговца к Flexo. Торговец должен сгенерировать пару публичный-приватный ключ (смотри ниже). Приватный ключ хранится в секрете, им подписываются исходящие запросы. Публичный ключ передается в Flexo, с помощью него запросы проверяются на подлинность.
-
Ключ для проверки уведомлений от Flexo к торговцу. Flexo подписывает уведомления своим приватным ключом, а торговец проверяет их публичным ключом. Публичный ключ можно взять со страницы описания уведомлений. Технически эту проверку можно опустить, но мы настоятельно не рекомендуем этого делать в целях безопасности.
ПРИМЕЧАНИЕ: Для тестовых операций ключ уже сгенерирован и на ходится на странице: Тестовый аккаунт.
Пример генерации RSA ключа для боевого аккаунта
openssl genrsa -out private_key.pem 2048
openssl rsa -in private_key.pem -pubout -outform PEM -out public_key.pem
Формирование подписи
-
Исходная строка для подписи получается путем конкатенации через \n следующих значений:
-
HTTP метода в верхнем регистре
-
URI (*)
-
тела запроса
-
-
Из строки формируется hash с помощью вашего приватного RSA ключа
(*) - URI берется без хоста, параметры должны быть UrlEncoded
Пример строки для подписи
var rawString = "POST\n/card/1-1/operations/purchase\n{}"
GET запрос: /card/1-1/operations/status?externalId=id#2&example=stub%stub
var rawString = "GET\n/card/1-1/operations/status?externalId=id%232&example=stub%25stub\n"
Пример на java
import java.security.KeyFactory;
import java.security.NoSuchAlgorithmException;
import java.security.NoSuchProviderException;
import java.security.PrivateKey;
import java.security.PublicKey;
import java.security.Security;
import java.security.Signature;
import java.security.spec.InvalidKeySpecException;
import java.security.spec.PKCS8EncodedKeySpec;
import java.security.spec.X509EncodedKeySpec;
import java.util.Base64;
import org.bouncycastle.jce.provider.BouncyCastleProvider;
public static PrivateKey loadPemPrivateKey(String privateKey)
throws NoSuchAlgorithmException, InvalidKeySpecException, NoSuchProviderException {
String privateKeyContent = privateKey
.replaceAll("(-+BEGIN RSA PRIVATE KEY-+\\r?\\n?|-+END RSA PRIVATE KEY-+\\r?\\n?)", "")
.replace("\n", "");
byte[] keyBytes = Base64.getDecoder().decode(privateKeyContent);
Security.addProvider(new BouncyCastleProvider());
KeyFactory keyFactory = KeyFactory.getInstance("SHA256withRSA", "BC");
PKCS8EncodedKeySpec privateKeySpec = new PKCS8EncodedKeySpec(keyBytes);
return keyFactory.generatePrivate(privateKeySpec);
}
public static String sign(String method, String uri, String body, String privateKeyString) {
try {
PrivateKey privateKey = loadPemPrivateKey(privateKeyString);
byte[] messageToSign = new StringBuilder()
.append(method)
.append("\n")
.append(uri)
.append("\n")
.append(body)
.toString()
.getBytes();
Signature signer = Signature.getInstance(signatureAlgorithmName);
signer.initSign(privateKey);
signer.update(messageToSign);
byte[] binarySignature = signer.sign();
String signature = Base64.getEncoder().encodeToString(binarySignature);
return signature;
} catch (Exception e) {
throw new RuntimeException(e);
}
}
Пример на С#
private static RSACryptoServiceProvider LoadPemPrivateKey(String pemPrivateKey)
{
using (var privateKeyTextReader = new StringReader(pemPrivateKey)) {
var readKeyPair = (AsymmetricCipherKeyPair) new PemReader(privateKeyTextReader).ReadObject();
var privateKeyParams = (RsaPrivateCrtKeyParameters) readKeyPair.Private;
var parms = new RSAParameters {
Modulus = privateKeyParams.Modulus.ToByteArrayUnsigned(),
P = privateKeyParams.P.ToByteArrayUnsigned(),
Q = privateKeyParams.Q.ToByteArrayUnsigned(),
DP = privateKeyParams.DP.ToByteArrayUnsigned(),
DQ = privateKeyParams.DQ.ToByteArrayUnsigned(),
InverseQ = privateKeyParams.QInv.ToByteArrayUnsigned(),
D = privateKeyParams.Exponent.ToByteArrayUnsigned(),
Exponent = privateKeyParams.PublicExponent.ToByteArrayUnsigned()
};
var cryptoServiceProvider = new RSACryptoServiceProvider();
cryptoServiceProvider.ImportParameters(parms);
return cryptoServiceProvider;
}
}
public static String sign(String messageToSign, String privateKey) {
var messageBytes = Encoding.UTF8.GetBytes(messageToSign);
var computedHash = new SHA256Managed().ComputeHash(messageBytes);
var rsa = LoadPemPrivateKey(privateKey);
var signature = rsa.SignHash(computedHash, "SHA256");
return Convert.ToBase64String(signature);
}
Пример на php
function sign(string $method, string $uri, string $data, string $private_key_pem): string
{
openssl_sign("{$method}\n{$uri}\n{$data}", $signature, $private_key_pem, OPENSSL_ALGO_SHA256);
return base64_encode($signature);
}