AES-GCM 上的 BCryptEncrypt returns STATUS_INVALID_PARAMETER
BCryptEncrypt returns STATUS_INVALID_PARAMETER on AES-GCM
我正在尝试使用 CNG Windows API 实施 AES GCM,但卡在了最后一步。
免责声明:不要害怕那么多代码,大部分只是 WinAPI 函数和结构声明,向下滚动到实际问题文本。谢谢
使用:
uses System.Classes, Winapi.Windows, System.SysUtils;
接口部分(NOT这里的一切都是正确的(参见) ,刚在如果有人会尝试重现):
type
BCRYPT_KEY_LENGTHS_STRUCT = packed record
dwMinLength, dwMaxLength, dwIncrement: ULONG;
end;
BCRYPT_AUTH_TAG_LENGTHS_STRUCT = BCRYPT_KEY_LENGTHS_STRUCT;
BCRYPT_KEY_DATA_BLOB_HEADER = packed record
dwMagic, dwVersion, cbKeyData: ULONG;
end;
BCRYPT_AUTHENTICATED_CIPHER_MODE_INFO = packed record
cbSize, dwInfoVersion: ULONG;
pbNonce: Pointer;
cbNonce: ULONG;
pbAuthData: Pointer;
cbAuthData: ULONG;
pbTag: Pointer;
cbTag: ULONG;
pbMacContext: Pointer;
cbMacContext, cbAAD: ULONG;
cbData: ULONGLONG;
dwFlags: ULONG;
end;
const
BCRYPT_CHAINING_MODE = 'ChainingMode';
BCRYPT_CHAIN_MODE_GCM = 'ChainingModeGCM';
BCRYPT_AUTH_TAG_LENGTH = 'AuthTagLength';
BCRYPT_KEY_DATA_BLOB = 'KeyDataBlob';
//
BCRYPT_AUTHENTICATED_CIPHER_MODE_INFO_VERSION = [=13=]000001;
//
BCrypt = 'Bcrypt.dll';
function BCryptOpenAlgorithmProvider(var phAlgorithm: Pointer;
pszAlgId: PWideChar; pszImplementation: PWideChar; dwFlags: ULONG): DWORD;
stdcall; external BCrypt;
function BCryptSetProperty(hObject: Pointer; pszProperty: PWideChar;
pbInput: Pointer; cbInput: ULONG; dwFlags: ULONG): DWORD; stdcall;
external BCrypt;
function BCryptGetProperty(hObject: Pointer; pszProperty: PWideChar;
pbOutput: Pointer; cbOutput: ULONG; var pcbResult: ULONG; dwFlagd: ULONG)
: DWORD; stdcall; external BCrypt;
function BCryptGenerateSymmetricKey(hAlgorithm: Pointer; var phKey: Pointer;
pbKeyObject: Pointer; cbKeyObject: ULONG; pbSecret: Pointer; cbSecret: ULONG;
dwFlags: ULONG): DWORD; stdcall; external BCrypt;
function BCryptGenRandom(phAlgorithm: Pointer; pbBuffer: Pointer;
cbBuffer: ULONG; dwFlags: ULONG): DWORD; stdcall; external BCrypt;
function BCryptExportKey(hKey: Pointer; hExportKey: Pointer;
pszBlobType: PWideChar; pbOutput: Pointer; cbOutput: ULONG;
var pcbResult: ULONG; dwFlags: ULONG): DWORD; stdcall; external BCrypt;
function BCryptEncrypt(hKey: Pointer; pbInput: Pointer; cbInput: ULONG;
pPaddingInfo: Pointer; pbIV: Pointer; cbIV: ULONG; pbOutput: Pointer;
cbOutput: ULONG; var pcbResult: ULONG; dwFlags: ULONG): DWORD; stdcall;
function BCryptDestroyKey(hKey: Pointer): DWORD; stdcall; external BCrypt;
function BCryptCloseAlgorithmProvider(hAlgorithm: Pointer; dwFlags: ULONG)
: DWORD; stdcall; external BCrypt;
实施:
function GetCryptoRandomBytes(var Buffer: TBytes; Size: DWORD): Boolean;
var
Status: DWORD;
hAlgorithm, hKey: Pointer;
begin
result := False;
Status := BCryptOpenAlgorithmProvider(hAlgorithm,
BCRYPT_RNG_ALGORITHM, nil, 0);
if Status = 0 then
begin
SetLength(Buffer, Size);
Status := BCryptGenRandom(hAlgorithm, Buffer, Size, 0);
if Status = 0 then
result := True;
end;
BCryptCloseAlgorithmProvider(hAlgorithm, 0)
end;
function AESGCMEncrypt(var Data, AAD, Key, IV, Tag, EncryptedData: TBytes;
KeyLength: DWORD = 32): Boolean;
const
AES_GCM_IV_LENGTH = 12; // nonce
var
Status, KeyLen, BytesDone, BlockLength: DWORD;
hAlgorithm, hKey: Pointer;
TagLength: BCRYPT_AUTH_TAG_LENGTHS_STRUCT;
KeyDataBlobHeader: BCRYPT_KEY_DATA_BLOB_HEADER;
AuthCiferModeInfo: BCRYPT_AUTHENTICATED_CIPHER_MODE_INFO;
KeyTemp: TBytes;
begin
result := False;
BytesDone := 0;
Status := BCryptOpenAlgorithmProvider(hAlgorithm,
BCRYPT_AES_ALGORITHM, nil, 0);
if Status = 0 then
begin
KeyLen := Length(BCRYPT_CHAIN_MODE_GCM);
Status := BCryptSetProperty(hAlgorithm, BCRYPT_CHAINING_MODE,
PChar(BCRYPT_CHAIN_MODE_GCM), BytesDone, 0);
if Status = 0 then
begin
KeyLen := SizeOf(TagLength);
Status := BCryptGetProperty(hAlgorithm, BCRYPT_AUTH_TAG_LENGTH,
@TagLength, KeyLen, BytesDone, 0);
if (Status = 0) and GetCryptoRandomBytes(KeyTemp, KeyLength) then
begin
Status := BCryptGenerateSymmetricKey(hAlgorithm, hKey, nil, 0, KeyTemp,
KeyLength, 0);
if Status = 0 then
begin
Status := BCryptExportKey(hKey, nil, BCRYPT_KEY_DATA_BLOB, nil, 0,
KeyLen, 0); // Get size
if Status = 0 then
begin
SetLength(KeyTemp, KeyLen);
Status := BCryptExportKey(hKey, nil, BCRYPT_KEY_DATA_BLOB, KeyTemp,
KeyLen, KeyLen, 0);
if Status = 0 then
begin
Move(KeyTemp[0], KeyDataBlobHeader, SizeOf(KeyDataBlobHeader));
SetLength(Key, KeyDataBlobHeader.cbKeyData);
Move(KeyTemp[SizeOf(KeyDataBlobHeader)], Key[0],
KeyDataBlobHeader.cbKeyData);
if GetCryptoRandomBytes(IV, AES_GCM_IV_LENGTH) then
begin
SetLength(Tag, TagLength.dwMaxLength);
SetLength(EncryptedData, Length(Data)); // same length as source
FillChar(AuthCiferModeInfo, SizeOf(AuthCiferModeInfo), #0);
with AuthCiferModeInfo do
begin
cbSize := SizeOf(AuthCiferModeInfo);
dwInfoVersion := BCRYPT_AUTHENTICATED_CIPHER_MODE_INFO_VERSION;
pbNonce := IV;
cbNonce := AES_GCM_IV_LENGTH;
pbAuthData := AAD;
cbAuthData := Length(AAD);
pbTag := Tag;
cbTag := TagLength.dwMaxLength;
end;
KeyLen := Length(Data);
Status := BCryptEncrypt(hKey, Data, KeyLen, @AuthCiferModeInfo,
nil, 0, EncryptedData, KeyLen, BytesDone, 0);
// Status = $C000000D = STATUS_INVALID_PARAMETER
if Status = 0 then
result := True
else // Free all buffers
begin
SetLength(Tag, 0);
SetLength(EncryptedData, 0);
SetLength(Key, 0);
SetLength(IV, 0);
end;
end
else // Free all buffers
begin
SetLength(Key, 0);
SetLength(IV, 0);
end;
end;
end;
BCryptDestroyKey(hKey);
end;
SetLength(KeyTemp, 0); // Free buffer
end;
end;
end;
BCryptCloseAlgorithmProvider(hAlgorithm, 0);
end;
这里正式提问
我知道好像有很多代码,但这不是重点。除了最后一个 BCryptEncrypt()
调用 returns STATUS_INVALID_PARAMETER
(0xC000000D
).
我试着按照文档进行操作,所以我不知道哪个参数不合适。我猜问题出在 BCRYPT_AUTHENTICATED_CIPHER_MODE_INFO
结构中,但找不到它。
那么,亲爱的 WinAPI 大神们,能否请您指出我做错了什么?如果有任何帮助,我将不胜感激:链接、调试想法等。
我是这样调用这个函数的:
var
Key, Tag, IV, AAD, Data, EncData: TBytes;
src, add_data: string;
begin
src := 'test_string_1234';
add_data := '12345678';
Data := TEncoding.UTF8.GetBytes(src);
AAD := TEncoding.UTF8.GetBytes(add_data);
AESGCMEncrypt(Data, AAD, Key, IV, Tag, EncData);
end;
P.S。我正在使用 Delphi 10.3 社区。
更新
我已经尝试检查这个 API 在 C++ 中是如何工作的,并且...看看吧。这是 BCRYPT_AUTHENTICATED_CIPHER_MODE_INFO
结构( 从 docs 复制):
typedef struct _BCRYPT_AUTHENTICATED_CIPHER_MODE_INFO {
ULONG cbSize; // 4 bytes
ULONG dwInfoVersion; // 4 bytes
PUCHAR pbNonce; // 8 bytes (on x64)
ULONG cbNonce; // 4 bytes
PUCHAR pbAuthData; // 8 bytes (on x64)
ULONG cbAuthData; // 4 bytes
PUCHAR pbTag; // 8 bytes (on x64)
ULONG cbTag; // 4 bytes
PUCHAR pbMacContext; // 8 bytes (on x64)
ULONG cbMacContext; // 4 bytes
ULONG cbAAD; // 4 bytes
ULONGLONG cbData; // 8 bytes
ULONG dwFlags; // 4 bytes
} BCRYPT_AUTHENTICATED_CIPHER_MODE_INFO, *PBCRYPT_AUTHENTICATED_CIPHER_MODE_INFO;
让我们计算总大小:4 + 4 + 8 + 4 + 8 + 4 + 8 + 4 + 8 + 4 + 4 + 8 + 4 = 72
。 但是 sizeof(BCRYPT_AUTHENTICATED_CIPHER_MODE_INFO)
returns 88
。我不知道 16 个额外的字节是从哪里来的。有人可以解释一下吗??
BCryptEncrypt() call which returns STATUS_INVALID_PARAMETER
(0xC000000D).
BCryptEncrypt(keyHandle, pt, sizeof(pt), &authInfo, NULL, 0, ct, sizeof(ct), &bytesDone, 0);
显示上面函数中传递的对我有用的值,然后你可以与你的进行比较。
更新:
经过比较,发现BCRYPT_AUTHENTICATED_CIPHER_MODE_INFO
结构的大小(cbSize
)计算错误。它期望 88
但传入 72
。它与“Padding and Alignment of Structure Members”有关。
The size, in bytes, of this structure. Do not set this field directly.
Use the BCRYPT_INIT_AUTH_MODE_INFO macro instead.
小更新
为了让问题中的代码正常工作,将 BCRYPT_AUTHENTICATED_CIPHER_MODE_INFO = packed record
更改为 BCRYPT_AUTHENTICATED_CIPHER_MODE_INFO = record
。
我正在尝试使用 CNG Windows API 实施 AES GCM,但卡在了最后一步。
免责声明:不要害怕那么多代码,大部分只是 WinAPI 函数和结构声明,向下滚动到实际问题文本。谢谢
使用:
uses System.Classes, Winapi.Windows, System.SysUtils;
接口部分(NOT这里的一切都是正确的(参见
type
BCRYPT_KEY_LENGTHS_STRUCT = packed record
dwMinLength, dwMaxLength, dwIncrement: ULONG;
end;
BCRYPT_AUTH_TAG_LENGTHS_STRUCT = BCRYPT_KEY_LENGTHS_STRUCT;
BCRYPT_KEY_DATA_BLOB_HEADER = packed record
dwMagic, dwVersion, cbKeyData: ULONG;
end;
BCRYPT_AUTHENTICATED_CIPHER_MODE_INFO = packed record
cbSize, dwInfoVersion: ULONG;
pbNonce: Pointer;
cbNonce: ULONG;
pbAuthData: Pointer;
cbAuthData: ULONG;
pbTag: Pointer;
cbTag: ULONG;
pbMacContext: Pointer;
cbMacContext, cbAAD: ULONG;
cbData: ULONGLONG;
dwFlags: ULONG;
end;
const
BCRYPT_CHAINING_MODE = 'ChainingMode';
BCRYPT_CHAIN_MODE_GCM = 'ChainingModeGCM';
BCRYPT_AUTH_TAG_LENGTH = 'AuthTagLength';
BCRYPT_KEY_DATA_BLOB = 'KeyDataBlob';
//
BCRYPT_AUTHENTICATED_CIPHER_MODE_INFO_VERSION = [=13=]000001;
//
BCrypt = 'Bcrypt.dll';
function BCryptOpenAlgorithmProvider(var phAlgorithm: Pointer;
pszAlgId: PWideChar; pszImplementation: PWideChar; dwFlags: ULONG): DWORD;
stdcall; external BCrypt;
function BCryptSetProperty(hObject: Pointer; pszProperty: PWideChar;
pbInput: Pointer; cbInput: ULONG; dwFlags: ULONG): DWORD; stdcall;
external BCrypt;
function BCryptGetProperty(hObject: Pointer; pszProperty: PWideChar;
pbOutput: Pointer; cbOutput: ULONG; var pcbResult: ULONG; dwFlagd: ULONG)
: DWORD; stdcall; external BCrypt;
function BCryptGenerateSymmetricKey(hAlgorithm: Pointer; var phKey: Pointer;
pbKeyObject: Pointer; cbKeyObject: ULONG; pbSecret: Pointer; cbSecret: ULONG;
dwFlags: ULONG): DWORD; stdcall; external BCrypt;
function BCryptGenRandom(phAlgorithm: Pointer; pbBuffer: Pointer;
cbBuffer: ULONG; dwFlags: ULONG): DWORD; stdcall; external BCrypt;
function BCryptExportKey(hKey: Pointer; hExportKey: Pointer;
pszBlobType: PWideChar; pbOutput: Pointer; cbOutput: ULONG;
var pcbResult: ULONG; dwFlags: ULONG): DWORD; stdcall; external BCrypt;
function BCryptEncrypt(hKey: Pointer; pbInput: Pointer; cbInput: ULONG;
pPaddingInfo: Pointer; pbIV: Pointer; cbIV: ULONG; pbOutput: Pointer;
cbOutput: ULONG; var pcbResult: ULONG; dwFlags: ULONG): DWORD; stdcall;
function BCryptDestroyKey(hKey: Pointer): DWORD; stdcall; external BCrypt;
function BCryptCloseAlgorithmProvider(hAlgorithm: Pointer; dwFlags: ULONG)
: DWORD; stdcall; external BCrypt;
实施:
function GetCryptoRandomBytes(var Buffer: TBytes; Size: DWORD): Boolean;
var
Status: DWORD;
hAlgorithm, hKey: Pointer;
begin
result := False;
Status := BCryptOpenAlgorithmProvider(hAlgorithm,
BCRYPT_RNG_ALGORITHM, nil, 0);
if Status = 0 then
begin
SetLength(Buffer, Size);
Status := BCryptGenRandom(hAlgorithm, Buffer, Size, 0);
if Status = 0 then
result := True;
end;
BCryptCloseAlgorithmProvider(hAlgorithm, 0)
end;
function AESGCMEncrypt(var Data, AAD, Key, IV, Tag, EncryptedData: TBytes;
KeyLength: DWORD = 32): Boolean;
const
AES_GCM_IV_LENGTH = 12; // nonce
var
Status, KeyLen, BytesDone, BlockLength: DWORD;
hAlgorithm, hKey: Pointer;
TagLength: BCRYPT_AUTH_TAG_LENGTHS_STRUCT;
KeyDataBlobHeader: BCRYPT_KEY_DATA_BLOB_HEADER;
AuthCiferModeInfo: BCRYPT_AUTHENTICATED_CIPHER_MODE_INFO;
KeyTemp: TBytes;
begin
result := False;
BytesDone := 0;
Status := BCryptOpenAlgorithmProvider(hAlgorithm,
BCRYPT_AES_ALGORITHM, nil, 0);
if Status = 0 then
begin
KeyLen := Length(BCRYPT_CHAIN_MODE_GCM);
Status := BCryptSetProperty(hAlgorithm, BCRYPT_CHAINING_MODE,
PChar(BCRYPT_CHAIN_MODE_GCM), BytesDone, 0);
if Status = 0 then
begin
KeyLen := SizeOf(TagLength);
Status := BCryptGetProperty(hAlgorithm, BCRYPT_AUTH_TAG_LENGTH,
@TagLength, KeyLen, BytesDone, 0);
if (Status = 0) and GetCryptoRandomBytes(KeyTemp, KeyLength) then
begin
Status := BCryptGenerateSymmetricKey(hAlgorithm, hKey, nil, 0, KeyTemp,
KeyLength, 0);
if Status = 0 then
begin
Status := BCryptExportKey(hKey, nil, BCRYPT_KEY_DATA_BLOB, nil, 0,
KeyLen, 0); // Get size
if Status = 0 then
begin
SetLength(KeyTemp, KeyLen);
Status := BCryptExportKey(hKey, nil, BCRYPT_KEY_DATA_BLOB, KeyTemp,
KeyLen, KeyLen, 0);
if Status = 0 then
begin
Move(KeyTemp[0], KeyDataBlobHeader, SizeOf(KeyDataBlobHeader));
SetLength(Key, KeyDataBlobHeader.cbKeyData);
Move(KeyTemp[SizeOf(KeyDataBlobHeader)], Key[0],
KeyDataBlobHeader.cbKeyData);
if GetCryptoRandomBytes(IV, AES_GCM_IV_LENGTH) then
begin
SetLength(Tag, TagLength.dwMaxLength);
SetLength(EncryptedData, Length(Data)); // same length as source
FillChar(AuthCiferModeInfo, SizeOf(AuthCiferModeInfo), #0);
with AuthCiferModeInfo do
begin
cbSize := SizeOf(AuthCiferModeInfo);
dwInfoVersion := BCRYPT_AUTHENTICATED_CIPHER_MODE_INFO_VERSION;
pbNonce := IV;
cbNonce := AES_GCM_IV_LENGTH;
pbAuthData := AAD;
cbAuthData := Length(AAD);
pbTag := Tag;
cbTag := TagLength.dwMaxLength;
end;
KeyLen := Length(Data);
Status := BCryptEncrypt(hKey, Data, KeyLen, @AuthCiferModeInfo,
nil, 0, EncryptedData, KeyLen, BytesDone, 0);
// Status = $C000000D = STATUS_INVALID_PARAMETER
if Status = 0 then
result := True
else // Free all buffers
begin
SetLength(Tag, 0);
SetLength(EncryptedData, 0);
SetLength(Key, 0);
SetLength(IV, 0);
end;
end
else // Free all buffers
begin
SetLength(Key, 0);
SetLength(IV, 0);
end;
end;
end;
BCryptDestroyKey(hKey);
end;
SetLength(KeyTemp, 0); // Free buffer
end;
end;
end;
BCryptCloseAlgorithmProvider(hAlgorithm, 0);
end;
这里正式提问
我知道好像有很多代码,但这不是重点。除了最后一个 BCryptEncrypt()
调用 returns STATUS_INVALID_PARAMETER
(0xC000000D
).
我试着按照文档进行操作,所以我不知道哪个参数不合适。我猜问题出在 BCRYPT_AUTHENTICATED_CIPHER_MODE_INFO
结构中,但找不到它。
那么,亲爱的 WinAPI 大神们,能否请您指出我做错了什么?如果有任何帮助,我将不胜感激:链接、调试想法等。
我是这样调用这个函数的:
var
Key, Tag, IV, AAD, Data, EncData: TBytes;
src, add_data: string;
begin
src := 'test_string_1234';
add_data := '12345678';
Data := TEncoding.UTF8.GetBytes(src);
AAD := TEncoding.UTF8.GetBytes(add_data);
AESGCMEncrypt(Data, AAD, Key, IV, Tag, EncData);
end;
P.S。我正在使用 Delphi 10.3 社区。
更新
我已经尝试检查这个 API 在 C++ 中是如何工作的,并且...看看吧。这是 BCRYPT_AUTHENTICATED_CIPHER_MODE_INFO
结构( 从 docs 复制):
typedef struct _BCRYPT_AUTHENTICATED_CIPHER_MODE_INFO {
ULONG cbSize; // 4 bytes
ULONG dwInfoVersion; // 4 bytes
PUCHAR pbNonce; // 8 bytes (on x64)
ULONG cbNonce; // 4 bytes
PUCHAR pbAuthData; // 8 bytes (on x64)
ULONG cbAuthData; // 4 bytes
PUCHAR pbTag; // 8 bytes (on x64)
ULONG cbTag; // 4 bytes
PUCHAR pbMacContext; // 8 bytes (on x64)
ULONG cbMacContext; // 4 bytes
ULONG cbAAD; // 4 bytes
ULONGLONG cbData; // 8 bytes
ULONG dwFlags; // 4 bytes
} BCRYPT_AUTHENTICATED_CIPHER_MODE_INFO, *PBCRYPT_AUTHENTICATED_CIPHER_MODE_INFO;
让我们计算总大小:4 + 4 + 8 + 4 + 8 + 4 + 8 + 4 + 8 + 4 + 4 + 8 + 4 = 72
。 但是 sizeof(BCRYPT_AUTHENTICATED_CIPHER_MODE_INFO)
returns 88
。我不知道 16 个额外的字节是从哪里来的。有人可以解释一下吗??
BCryptEncrypt() call which returns STATUS_INVALID_PARAMETER (0xC000000D).
BCryptEncrypt(keyHandle, pt, sizeof(pt), &authInfo, NULL, 0, ct, sizeof(ct), &bytesDone, 0);
显示上面函数中传递的对我有用的值,然后你可以与你的进行比较。
更新:
经过比较,发现BCRYPT_AUTHENTICATED_CIPHER_MODE_INFO
结构的大小(cbSize
)计算错误。它期望 88
但传入 72
。它与“Padding and Alignment of Structure Members”有关。
The size, in bytes, of this structure. Do not set this field directly. Use the BCRYPT_INIT_AUTH_MODE_INFO macro instead.
小更新
为了让问题中的代码正常工作,将 BCRYPT_AUTHENTICATED_CIPHER_MODE_INFO = packed record
更改为 BCRYPT_AUTHENTICATED_CIPHER_MODE_INFO = record
。