C# in a Nutshell says that plaintext much longer than 1/2 the key size will cause an error. It's likely the same for most languages for security purposes. For longer messages, use RSA to pass a key for symmetrical encryption such as AES.
Windows Crypto Api bad length
Question
I found a simple unit for generating a RSA KeyPair and encrypting / decrypting. It works fine on small strings but on larger text I get error : Bad Length.
What could be the cause , here is the unit, the unit wcrypt2 you can download here
Tested on Delphi 2010
unit Crypt_RSA;
interface
uses
Windows, Classes, SysUtils, WCrypt2;
function RSAGenerateKeys(var PrivateKey, PublicKey: String): Boolean;
function RSAEncrypt(Source, Key: String): String;
function RSADecrypt(Source, Key: String): String;
implementation
function RSAGenerateKeys(var PrivateKey, PublicKey: String): Boolean;
const
RSA1024BIT_KEY = $04000000;
var
RSA: HCRYPTPROV;
HKeyPair: HCRYPTKEY;
Pair: TStringStream;
buflen: DWORD;
function SetKey(BlobDef: Cardinal; var Key: String): Boolean;
begin
Result := Bool(CryptExportKey(HKeyPair, 0, BlobDef, 0, nil, @buflen));
if Result then
begin
Pair.SetSize(buflen);
Result := Bool(CryptExportKey(HKeyPair, 0, BlobDef, 0, PByte(Pair.Memory), @buflen));
end;
Key := Pair.ReadString(buflen);
Pair.Seek(0, soBeginning);
end;
begin
Pair := TStringStream.Create;
Result := Bool(CryptAcquireContext(@RSA, nil, nil, PROV_RSA_FULL, CRYPT_VERIFYCONTEXT));
if Result then
Result := Bool(CryptGenKey(RSA, AT_KEYEXCHANGE, RSA1024BIT_KEY or CRYPT_EXPORTABLE, @HKeyPair));
if Result then
Result := SetKey(PRIVATEKEYBLOB, PrivateKey);
if Result then
Result := SetKey(PUBLICKEYBLOB, PublicKey);
CryptDestroyKey(HKeyPair);
CryptReleaseContext(RSA, 0);
FreeAndNil(Pair);
end;
function RSAEncrypt(Source, Key: String): String;
var
KeyPair: TStringStream;
RSA: HCRYPTPROV;
HPair: HCRYPTKEY;
DDataSize, EDataSize: DWORD;
begin
Result := '';
KeyPair := TStringStream.Create(Key);
if CryptAcquireContext(@RSA, nil, nil, PROV_RSA_FULL, CRYPT_VERIFYCONTEXT) then
try
if CryptImportKey(RSA, PByte(KeyPair.Memory), KeyPair.Size, 0, 0, @HPair) then
try
EDataSize := SizeOf(Source);
if CryptEncrypt(HPair, 0, true, 0, nil, @EDataSize, 0) then
begin
Result := Source;
SetLength(Result, EDataSize);
DDataSize := Length(Source) * SizeOf(Char);
if not(CryptEncrypt(HPair, 0, True, 0, PByte(PChar(Result)), @DDataSize, EDataSize)) then
Result := '';
end;
finally
CryptDestroyKey(HPair);
end;
finally
CryptReleaseContext(RSA, 0);
end;
FreeAndNil(KeyPair);
end;
function RSADecrypt(Source, Key: String): String;
var
KeyPair: TStringStream;
RSA: HCRYPTPROV;
HPair: HCRYPTKEY;
EDataSize: DWORD;
begin
KeyPair := TStringStream.Create(Key);
Result := '';
if CryptAcquireContext(@RSA, nil, nil, PROV_RSA_FULL, CRYPT_VERIFYCONTEXT) then
try
if CryptImportKey(RSA, PByte(KeyPair.Memory), KeyPair.Size, 0, 0, @HPair) then
try
Result := Source;
EDataSize := Length(Result);
if not Bool(CryptDecrypt(HPair, 0, True, 0, PByte(PChar(Result)), @EDataSize)) then
EDataSize := 0;
SetLength(Result, EDataSize div SizeOf(Char));
finally
CryptDestroyKey(HPair);
end;
finally
CryptReleaseContext(RSA, 0);
end;
FreeAndNil(KeyPair);
end;
end.
Solution
OTHER TIPS
Asymmetric encryption limits the data to less than the key size, there is padding such as OAEP which is 42 bytes.
If you must have asymmetric encryption, that is need a key pair, use hybrid encryption which is essentially what https does. See Hybrid cryptosystem.
For Hybrid encryption basically you create a random symmetric encryption key, encrypt the symmetric key with an asymmetric encryption, encrypt the data with the symmetric key and package the two encryptions together.
Why don't you want symmetric encryption? It is much faster and handles essentially any data size? AES is the encryption workhorse.