Snippets

Алексей Корепанов Decrypt Capicom cipher with CryptoApi

Created by Алексей Корепанов last modified
unit uCryptoDecode;

interface

uses
  System.SysUtils;

function SBDecryptData(const AData: PByte; const ADataSize: Cardinal; const APassword: PByte; const APasswordSize: Cardinal): TBytes; overload;
function SBDecryptData(const AData, APassword: TBytes): TBytes; overload;
function SBDecryptData(const AData, APassword: String): String; overload;

implementation

uses
  System.Classes,
  JwaWinCrypt,
  JwaWinType,
  Winapi.Windows;

type
  TObjectValue<T: record> = record
  private
    FBuffer: TBytes;
    FStruct: ^T;
    function GetValue: T; inline;
  public
    procedure Read(const AStructType: PAnsiChar; const AData: PByte; const ADataSize: Cardinal;
      const AEncodingType: Cardinal = PKCS_7_ASN_ENCODING or X509_ASN_ENCODING; const AFlags: Cardinal = CRYPT_DECODE_NOCOPY_FLAG or CRYPT_DECODE_SHARE_OID_STRING_FLAG);
    property Value: T read GetValue;
    property Buffer: TBytes read FBuffer;
  end;

function Win32Check(RetVal: BOOL): BOOL; inline;
begin
  {$WARN SYMBOL_PLATFORM OFF}
  Result := System.SysUtils.Win32Check(RetVal);
  {$WARN SYMBOL_PLATFORM ON}
end;

function DecryptCipher(const ACipher, ASalt, APassword, AIV: PByte;
  const ACipherLength, ASaltLength, APasswordLength: Cardinal; const AHashAlg, AEncAlg: ALG_ID): TBytes;
var
  LProv: HCRYPTPROV;
  LHash: HCRYPTHASH;
  LDeriveKey: HCRYPTKEY;
  LBufferSize: Cardinal;
begin
  Win32Check(CryptAcquireContext(LProv, nil, nil, PROV_RSA_FULL, CRYPT_VERIFYCONTEXT));
  try
    Win32Check(CryptCreateHash(LProv, AHashAlg, 0, 0, LHash));
    try
      Win32Check(CryptHashData(LHash, APassword, APasswordLength, 0));
      Win32Check(CryptHashData(LHash, ASalt, ASaltLength, 0));

      Win32Check(CryptDeriveKey(LProv, AEncAlg, LHash, CRYPT_EXPORTABLE or CRYPT_NO_SALT, LDeriveKey));
    finally
      Win32Check(CryptDestroyHash(LHash));
    end;
    try
      Win32Check(CryptSetKeyParam(LDeriveKey, KP_IV, AIV, 0));
      LBufferSize := ACipherLength;
      Win32Check(CryptDecrypt(LDeriveKey, 0, True, 0, ACipher, LBufferSize));
      SetLength(Result, LBufferSize);
      CopyMemory(@Result[0], ACipher, LBufferSize);
    finally
      Win32Check(CryptDestroyKey(LDeriveKey));
    end;
  finally
    Win32Check(CryptReleaseContext(LProv, 0));
  end;
end;

function SBDecryptData(const AData: PByte; const ADataSize: Cardinal; const APassword: PByte; const APasswordSize: Cardinal): TBytes;
var
  LContentInfo: TObjectValue<CRYPT_CONTENT_INFO>;
  LContentInfoSequence: TObjectValue<CRYPT_CONTENT_INFO_SEQUENCE_OF_ANY>;
  LSequence: PCRYPT_DER_BLOB;
  LCapicomVersion: TObjectValue<Integer>;
  LAlgId: TObjectValue<Integer>;
  LKeyLength: TObjectValue<Integer>;
  LIV: TObjectValue<CRYPT_DATA_BLOB>;
  LSalt: TObjectValue<CRYPT_DATA_BLOB>;
  LCipher: TObjectValue<CRYPT_DATA_BLOB>;
begin
  LContentInfo.Read(PKCS_CONTENT_INFO, AData, ADataSize);
  LContentInfoSequence.Read(PKCS_CONTENT_INFO_SEQUENCE_OF_ANY, LContentInfo.Value.Content.pbData, LContentInfo.Value.Content.cbData);

  LSequence := LContentInfoSequence.Value.rgValue;
  LCapicomVersion.Read(X509_INTEGER, LSequence.pbData, LSequence.cbData);
  Inc(LSequence);
  LAlgId.Read(X509_INTEGER, LSequence.pbData, LSequence.cbData);
  Inc(LSequence);
  LKeyLength.Read(X509_INTEGER, LSequence.pbData, LSequence.cbData);

  Inc(LSequence);
  LIV.Read(X509_OCTET_STRING, LSequence.pbData, LSequence.cbData);
  Inc(LSequence);
  LSalt.Read(X509_OCTET_STRING, LSequence.pbData, LSequence.cbData);
  Inc(LSequence);
  LCipher.Read(X509_OCTET_STRING, LSequence.pbData, LSequence.cbData);

  Result := DecryptCipher(LCipher.Value.pbData, LSalt.Value.pbData, APassword, LIV.Value.pbData,
    LCipher.Value.cbData, LSalt.Value.cbData, APasswordSize, CALG_SHA, LAlgId.Value);
end;

function SBDecryptData(const AData: TBytes; const APassword: TBytes): TBytes;
begin
  Result := SBDecryptData(@AData[0], Length(AData), @APassword[0], Length(APassword));
end;

function CryptStringToBinary(pszString: LPCTSTR; cchString: DWORD;
  dwFlags: DWORD; ppBinary: PBYTE; var ppcbBinary: DWORD;
  ppdwSkip: PDWORD; ppdwFlags: PDWORD): BOOL; stdcall; external 'crypt32.dll' name 'CryptStringToBinaryW';

function SBDecryptData(const AData, APassword: String): String; overload;
var
  LData: TBytes;
  LDataSize: Cardinal;
begin
  Win32Check(CryptStringToBinary(PWideChar(AData), Length(AData) + 1, CRYPT_STRING_BASE64, nil, LDataSize, nil, nil));
  SetLength(LData, LDataSize);
  Win32Check(CryptStringToBinary(PWideChar(AData), Length(AData) + 1, CRYPT_STRING_BASE64, @LData[0], LDataSize, nil, nil));

  with TEncoding.Unicode do
    Result := GetString(SBDecryptData(LData, GetBytes(APassword)));
end;

{ TObjectValue<T> }

function TObjectValue<T>.GetValue: T;
begin
  Result := FStruct^;
end;

procedure TObjectValue<T>.Read(const AStructType: PAnsiChar; const AData: PByte; const ADataSize, AEncodingType,
  AFlags: Cardinal);
var
  LBufferSize: DWORD;
begin
  Win32Check(CryptDecodeObject(AEncodingType, AStructType, AData, ADataSize, AFlags, nil, LBufferSize));
  SetLength(FBuffer, LBufferSize);
  Win32Check(CryptDecodeObject(AEncodingType, AStructType, AData, ADataSize, AFlags, @FBuffer[0], LBufferSize));
  FStruct := @FBuffer[0];
end;

end.

Comments (0)

HTTPS SSH

You can clone a snippet to your computer for local editing. Learn more.