Коллеги, день добрый. По мотивам темы пытаюс отослать сообщение в ФСС шифрованное. Получаю печальное
Если можно, просмотрите код, он основан на источнике верхних функций, типы данных все совпадают, за исключением некоторых доп кусков, комментирую которые. Может, сразу что-то в глаза бросится, вроде бы все выходит в соответствии с кодом выше.
Win32Check(CryptAcquireContext(@hProv, 'rnd-D-324E-E720-6256-5667-1F57-32A6-9907', 'Infotecs Cryptographic Service Provider', INFOTECS_PROV_TYPE, 0));
memStream := TMemoryStream.Create;
memStream.LoadFromFile('FSS_PROD_CERT_2018.cer'); //
Вроде как последний сертификат уполномоченного лица // получение сертификата
FCertContext := CertCreateCertificateContext(X509_ASN_ENCODING or PKCS_7_ASN_ENCODING, memStream.Memory, memStream.Size);
// импорт информации по открытому ключу
Win32Check(CryptImportPublicKeyInfoEx(
hProv,
X509_ASN_ENCODING or PKCS_7_ASN_ENCODING,
@FCertContext.pCertInfo.SubjectPublicKeyInfo,
CALG_GR3410EL,
0,
nil,
@FPublicKey));
// экспорт открытого ключа получателя в BLOB
// экспорт открытого ключа получателя в BLOB
Win32Check(CryptExportKey(FPublicKey, 0, PUBLICKEYBLOB, 0, nil, @FKeyLenNeed));
SetLength(FRecipientPublicKeyBlob, FKeyLenNeed);
Win32Check(CryptExportKey(FPublicKey, 0, PUBLICKEYBLOB, 0, @FRecipientPublicKeyBlob[1], @FKeyLenNeed));
// генерация эфемерной ключевой пары
Win32Check(CryptGenKey(hProv, CALG_DH_EL_EPHEM, CRYPT_EXPORTABLE, @FEphemeralKey));
// экспорт открытого ключа отправителя в BLOB
Win32Check(CryptExportKey(FEphemeralKey, 0, PUBLICKEYBLOB, 0, nil, @FKeyLenNeed));
SetLength(FSenderPublicKeyBlob, FKeyLenNeed);
Win32Check(CryptExportKey(FEphemeralKey, 0, PUBLICKEYBLOB, 0, @FSenderPublicKeyBlob[1], @FKeyLenNeed));
// получаем значение открытого ключа отправителя из PUBLICKEYBLOB
APublicKey := Copy(FSenderPublicKeyBlob, Length(FSenderPublicKeyBlob) - 64 + 1, 64);
// получение ключа согласования импортом открытого ключа получателя
// на эфемерном ключе
Win32Check(CryptImportKey(hProv, @FRecipientPublicKeyBlob[1], Length(FRecipientPublicKeyBlob), FEphemeralKey, 0, @FAgreeKey));
// установление PRO_EXPORT алгоритма ключа согласования
algProExport := CALG_PRO_EXPORT;
Win32Check(CryptSetKeyParam(FAgreeKey, KP_ALGID, @algProExport, 0));
// создание случайного сессионного ключа
Win32Check(CryptGenKey(hProv, CALG_G28147, CRYPT_EXPORTABLE, @FSessionKey));
// экспорт сессионного ключа в BLOB
Win32Check(CryptExportKey(FSessionKey, FAgreeKey, SIMPLEBLOB, 0, nil, @FKeyLenNeed));
SetLength(FSessionKeyBlob, FKeyLenNeed);
Win32Check(CryptExportKey(FSessionKey, FAgreeKey, SIMPLEBLOB, 0, @FSessionKeyBlob[1], @FKeyLenNeed));
(*
FSessionKeyBlob = CRYPT_SIMPLEBLOB
bType : offset 1, size 1
bVersion : offset 2, size 1
reserved : offset 3, size 2
aiKeyAlg : offset 5, size 4
Magic : offset 9, size 4
EncryptKeyAlgId : offset 13, size 4
bSV (bUKM) : offset 17, size 8
bEncryptedKey : offset 25, size 32
bMacKey : offset 57, size 4
bEncryptionParamSet : offset 61
end;
*)
ASessionSV := Copy(FSessionKeyBlob, 17, 8);
ASessionKey := Copy(FSessionKeyBlob, 25, 32);
ASessionMAC := Copy(FSessionKeyBlob, 57, 4);
//
Здесь я ручками формирую структуру GostR3410-KeyTransport, на основе шаблона abKeyTransport :=
[
$30, $81, $A4, $30, $28, $04, $20,
$5E, $70, $73, $5F, $36, $98, $B4, $35, $5B, $45, $03, $7F, $A7, $CE, $00, $97, // шифрованный (5,32)
$11, $5E, $45, $C6, $58, $59, $94, $72, $66, $42, $06, $3F, $72, $3A, $B4, $9E, // ключ
$04, $04,
$8C, $86, $08, $84, // MAC (39,4)
$A0, $78, $06, $07, $2A, $85, $03, $02, $02, $1F, $01, $A0, $63, $30, $1C, $06,
$06, $2A, $85, $03, $02, $02, $13, $30, $12, $06, $07, $2A, $85, $03, $02, $02,
$24, $00, $06, $07, $2A, $85, $03, $02, $02, $1E, $01, $03, $43, $00, $04, $40,
$2D, $C3, $FD, $F6, $9C, $91, $3D, $CC, $B6, $53, $26, $8E, $51, $2F, $5E, $DD, // (91, 64) публичный ключ
$E4, $1A, $5D, $B3, $58, $3C, $DF, $60, $68, $F2, $48, $A2, $B0, $B8, $DE, $7B,
$C9, $AA, $20, $E3, $CF, $63, $DF, $5F, $39, $55, $21, $E0, $A0, $DD, $85, $3E,
$0A, $AF, $44, $FA, $49, $3C, $D5, $4C, $A8, $04, $8D, $1D, $9C, $41, $85, $FB,
$04, $08,
$76, $EE, $B4, $6B, $1B, $10, $36, $EB // UKM (157,8)
];
for i := 1 to 32 do
abKeyTransport [i + 6] := Byte(ASessionKey[i]);
for i := 1 to 4 do
abKeyTransport [i + 40] := Byte(ASessionMAC[i]);
for i := 1 to 64 do
abKeyTransport [i + 92] := Byte(APublicKey[i]);
for i := 1 to 8 do
abKeyTransport [i + 158] := Byte(ASessionSV[i]);
//
На выходе получаем вот это // получение вектора инициализации
Win32Check(CryptGetKeyParam(FSessionKey, KP_IV, nil, @FKeyLenNeed, 0));
SetLength(AInitVector, FKeyLenNeed);
Win32Check(CryptGetKeyParam(FSessionKey, KP_IV, @AInitVector[1], @FKeyLenNeed, 0));
keyMode := CRYPT_MODE_CBC;
Win32Check(CryptSetKeyParam(FSessionKey, KP_MODE, @keyMode, 0));
// режим паддинга
keyMode := 1; // PKCS5_PADDING
Win32Check(CryptSetKeyParam(FSessionKey, KP_PADDING, @keyMode, 0));
// зашифрованные данные
AEncryptData := '';
ASourceData := MakePadding(ASourceData); //
Могу делать/не делать этот вызов с/без включением режима PKCS5_PADDING сверху, все равно не работает FSourceDataLen := Length(ASourceData);
FSourcePos := 1;
// шифрование блоками по 8 байт
while FSourcePos <= FSourceDataLen do
begin
// временный буфер с исходными данными для шифрования
FEncryptBuffer := Copy(ASourceData, FSourcePos, ENCRYPT_BLOCK_LENGTH);
FEncryptBufferLen := Length(FEncryptBuffer);
FEncryptDataLen := FEncryptBufferLen;
// последний блок данных
FEncryptFinal := (FSourcePos + ENCRYPT_BLOCK_LENGTH) > FSourceDataLen;
// получение размера буфера с учетом возможного паддинга
Win32Check(CryptEncrypt(FSessionKey, 0, FEncryptFinal, 0, nil, @FEncryptBufferLen, 0));
SetLength(FEncryptBuffer, FEncryptBufferLen);
Win32Check(CryptEncrypt(FSessionKey, 0, FEncryptFinal, 0, @FEncryptBuffer[1], @FEncryptDataLen, FEncryptBufferLen));
// накопление зашифрованных данных
AEncryptData := AEncryptData + FEncryptBuffer;
FSourcePos := FSourcePos + ENCRYPT_BLOCK_LENGTH;
end;
AEncryptData := TEncoding.UTF8.GetString(TEncoding.UTF8.GetBytes(TNetEncoding.Base64.Encode(AInitVector + AEncryptData))); //
Этот текст идет в EncryptedData->CipherData->CipherValue AEncryptData := StringReplace(AEncryptData, #$A, '', [rfReplaceAll]); //
Приходится форматировать от ctrl+entr, поскольку идет вставка автоматом этих символов. AEncryptData := StringReplace(AEncryptData, #$D, '', [rfReplaceAll]);
AEncryptKey := TEncoding.UTF8.GetString(TNetEncoding.Base64.Encode(abKeyTransport)); //
Этот текст идет в EncryptedData->KeyInfo->CipherData->CipherValue AEncryptKey := StringReplace(AEncryptKey, #$A, '', [rfReplaceAll]);
AEncryptKey := StringReplace(AEncryptKey, #$D, '', [rfReplaceAll]);
Собственно, все. Заранее благодарен.