// Шифрование SOAP-запроса
class procedure TCrypto.EncryptSOAPRequest(FCryptoProvider: HCRYPTPROV; ARemoteCertPath: string; ASOAPRequest: IXMLDocument);
var
hEphemeralKey, hAgreeKey, hSessionKey: HCRYPTKEY;
size, keyParam,
BufLen, DataLen, sourceDataLen: DWORD;
pbPubCertData,
remotePublicKeyBlob, pbSenderPublicKeyBlob,
sessionKeyBlob,
sessionSV, sessionKey, sessionMAC,
SourseData,
CipherValue, CipherData,
publicKey,
initVector, encryptedData: TBytes;
keyParamAsBytes: TBytes;
provType: DWORD;
s: string;
sm: TStringStream;
var
FCertContext: PCCERT_CONTEXT;
remotePublicKey: HCRYPTKEY;
begin
// Получение дескриптора закрытого ключа отправителя. нам нужно для получения сертификата который вставим в запрос
pbPubCertData := GetProviderPublicCertificate(FCryptoProvider);
hEphemeralKey := 0; hAgreeKey := 0; hSessionKey := 0;
sm := TStringStream.Create;
CryptAcquireContext(@FCryptoProvider, nil, nil, PROV_GOST_2012_256, CRYPT_VERIFYCONTEXT);
try
size := SizeOf(provType);
CheckCryptoCall(CryptGetProvParam(FCryptoProvider, PP_PROVTYPE, @provType, @size, 0));
Assert(provType = PROV_GOST_2012_256);
// Загрузка PUBLICKEYBLOB из сертификата, открытие файла, в котором содержится открытый ключ получателя.
memStream := TMemoryStream.Create;
memStream.LoadFromFile(ARemoteCertPath);
FCertContext := CertCreateCertificateContext(PKCS_7_ASN_ENCODING or X509_ASN_ENCODING, memStream.Memory, memStream.Size);
// импорт информации по открытому ключу
CryptImportPublicKeyInfoEx(FCryptoProvider, PKCS_7_ASN_ENCODING or X509_ASN_ENCODING, @FCertContext.pCertInfo.SubjectPublicKeyInfo, 0, 0, nil, @remotePublicKey);
CryptExportKey(remotePublicKey, 0, PUBLICKEYBLOB, 0, nil, @size);
SetLength(remotePublicKeyBlob, size);
CryptExportKey(remotePublicKey, 0, PUBLICKEYBLOB, 0, @remotePublicKeyBlob [1], @size);
// генерация эфемерной ключевой пары
CheckCryptoCall(CryptGenKey(FCryptoProvider, CALG_DH_GR3410_12_256_EPHEM , CRYPT_EXPORTABLE, @hEphemeralKey));
// Проверяем что параметры эфемерного ключа также установлены в TK26Z
CheckCryptoCall(CryptGetKeyParam(hEphemeralKey, KP_CIPHEROID, nil, @size, 0));
SetLength(keyParamAsBytes, size);
CheckCryptoCall(CryptGetKeyParam(hEphemeralKey, KP_CIPHEROID, @keyParamAsBytes[0], @size, 0));
Assert(TEncoding.ANSI.GetString(keyParamAsBytes) = '1.2.643.7.1.2.5.1.1'#0); // szOID_Gost28147_89_TC26_Z_ParamSet + #0
// Экспорт открытого ключа в BLOB из локального закрытого.
CheckCryptoCall(CryptExportKey(hEphemeralKey, 0, PUBLICKEYBLOB, 0, nil, @size));
SetLength(pbSenderPublicKeyBlob, size);
CheckCryptoCall(CryptExportKey(hEphemeralKey, 0, PUBLICKEYBLOB, 0, @pbSenderPublicKeyBlob[1], @size));
// получаем значение открытого ключа отправителя из PUBLICKEYBLOB
publicKey := Copy(pbSenderPublicKeyBlob, Length(pbSenderPublicKeyBlob) - 64+1, 64);
// Получение ключа согласования импортом открытого ключа получателя зашифрованного сообщения (ФСС)
// на локальном закрытом ключе отправителя зашифрованного сообщения.
CheckCryptoCall(CryptImportKey(FCryptoProvider, @remotePublicKeyBlob[1], Length(remotePublicKeyBlob),
hEphemeralKey, 0, @hAgreeKey));
// Установка PRO_EXPORT алгоритма ключа согласования
keyParam := CALG_PRO_EXPORT;
CheckCryptoCall(CryptSetKeyParam(hAgreeKey, KP_ALGID, @keyParam, 0));
// Создание случайного сессионного ключа, которым будет зашифровано сообщение.
CheckCryptoCall(CryptGenKey(FCryptoProvider, CALG_G28147, CRYPT_EXPORTABLE, @hSessionKey));
// экспорт сессионного ключа в BLOB
CheckCryptoCall(CryptExportKey(hSessionKey, hAgreeKey, SIMPLEBLOB, 0, nil, @size));
SetLength(sessionKeyBlob, size);
CheckCryptoCall(CryptExportKey(hSessionKey, hAgreeKey, SIMPLEBLOB, 0, @sessionKeyBlob[1], @size));
// Выделение из BLOB-а контенты компонент UKM, сессионного ключа, MAC. Структуру BLOB-а см. в GetResponseKeysBlobs.
sessionSV := Copy(sessionKeyBlob, 16+1, 8);
sessionKey := Copy(sessionKeyBlob, 24+1, 32);
sessionMAC := Copy(sessionKeyBlob, 56+1, 4);
// Получение из сессионного ключа параметра вектора инициализации. Далее он прикрепляется
// к зашифрованному сообщению (см. ниже)
CheckCryptoCall(CryptGetKeyParam(hSessionKey, KP_IV, nil, @size, 0));
SetLength(initVector, size);
CheckCryptoCall(CryptGetKeyParam(hSessionKey, KP_IV, @initVector[1], @size, 0));
// Установка режима шифрования CBC
keyParam := CRYPT_MODE_CBC;
CheckCryptoCall(CryptSetKeyParam(hSessionKey, KP_MODE, @keyParam, 0));
// режим паддинга
keyParam := ISO10126_PADDING;
CheckCryptoCall(CryptSetKeyParam(hSessionKey, KP_PADDING, @keyParam, 0));
//--------------------------------------------------------------------
// Зашифрование сессионного ключа.
//--------------------------------------------------------------------
// Получение контента базового SOAP-запроса, который будет зашифрован перед отправкой получателю.
SourseData := TEncoding.UTF8.GetBytes(ASOAPRequest.DocumentElement.XML);
// Шифрование базового SOAP-запроса
encryptedData := SourseData;
BufLen:=Length(encryptedData);
DataLen:=Length(encryptedData);
// Вычисляем необходимый размер выходного буфера
CryptEncrypt(hSessionKey,0,true,0,nil,@BufLen,0);
// Выделяем память для буфера и шифруем
SetLength(SourseData,BufLen);
CryptEncrypt(hSessionKey,0,true,0, @encryptedData[1], @DataLen, BufLen);
CipherData := initVector+encryptedData;
// Формирование структуры GostR3410-KeyTransport, которая передается получателю зашифрованного сообщения
// в служебном заголовке этого сообщения. Далее получатель на основе этой структуры формирует сессионный ключ,
// которым дешифрует зашифрованное сообщение отправителя.
// transportBlob при необходимости можно сохранить в бинарный файл и открыть его редактором ASN1, например,
//
https://lapo.it/asn1js/ - онлайн
//
https://www.codeproject....Articles/4910/ASN-Editor - десктоп
CipherValue :=[$30, $81, $A9, $30, $28, 4, $20]+
SessionKey+
[4, 4]+
SessionMAC+
[$A0, $7D, 6, 9, $2A, $85, 3, 7, 1, 2, 5, 1, 1]+
[$A0, $66, $30, $1F, 6, 8, $2A, $85, 3, 7, 1, 1, 1, 1]+
[$30, $13, 6, 7, $2A, $85, 3, 2, 2, $24, 0]+
[6, 8, $2A, $85, 3, 7, 1, 1, 2, 2, 3]+
[$43, 0, 4, $40]+
PublicKey+
[$4, $8]+
SessionSV;
// Формирование структуры зашифрованного SOAP-запроса.
SetEncryptedContent(ASOAPRequest,
CipherData,
CipherValue,
pbPubCertData);
finally
if hSessionKey <> 0 then
CheckCryptoCall(CryptDestroyKey(hSessionKey));
if hAgreeKey <> 0 then
CheckCryptoCall(CryptDestroyKey(hAgreeKey));
if hEphemeralKey <> 0 then
CheckCryptoCall(CryptDestroyKey(hEphemeralKey));
SetLength(sessionKeyBlob, 0);
SetLength(pbSenderPublicKeyBlob, 0);
SetLength(CipherValue, 0);
SetLength(sessionSV, 0);
SetLength(sessionKey, 0);
SetLength(sessionMAC, 0);
SetLength(initVector, 0);
SetLength(encryptedData, 0);
end;
end;
В SetEncryptedContent собираю все в xml. Все переменные TBytes кодирую в Base64 строку (например TNetEncoding.Base64.EncodeBytesToString(CipherData))
Ошибка таже.