I have a string - a serial number of a mother board (only numbers and letters). How to encrypt/decrypt it and have a normal view: letters only from A to Z and numbers from 0
Simple Enc/Dec with support Unicode , Enc output is only hexadecimal characters :
const CKEY1 = 53761;
CKEY2 = 32618;
function EncryptStr(const S :WideString; Key: Word): String;
var i :Integer;
RStr :RawByteString;
RStrB :TBytes Absolute RStr;
begin
Result:= '';
RStr:= UTF8Encode(S);
for i := 0 to Length(RStr)-1 do begin
RStrB[i] := RStrB[i] xor (Key shr 8);
Key := (RStrB[i] + Key) * CKEY1 + CKEY2;
end;
for i := 0 to Length(RStr)-1 do begin
Result:= Result + IntToHex(RStrB[i], 2);
end;
end;
function DecryptStr(const S: String; Key: Word): String;
var i, tmpKey :Integer;
RStr :RawByteString;
RStrB :TBytes Absolute RStr;
tmpStr :string;
begin
tmpStr:= UpperCase(S);
SetLength(RStr, Length(tmpStr) div 2);
i:= 1;
try
while (i < Length(tmpStr)) do begin
RStrB[i div 2]:= StrToInt('$' + tmpStr[i] + tmpStr[i+1]);
Inc(i, 2);
end;
except
Result:= '';
Exit;
end;
for i := 0 to Length(RStr)-1 do begin
tmpKey:= RStrB[i];
RStrB[i] := RStrB[i] xor (Key shr 8);
Key := (tmpKey + Key) * CKEY1 + CKEY2;
end;
Result:= UTF8Decode(RStr);
end;
Example :
procedure TForm1.btn1Click(Sender: TObject);
begin
txt2.Text:= EncryptStr(txt1.Text, 223);
lbl1.Caption:= DecryptStr(txt2.Text, 223);
end;
I use this very simple trick found on https://edn.embarcadero.com/article/28325 that is good for its simplicity:
I use it to store non critical encrypted passwords in a database. My goal is to put a layer of obfuscation (and minimize the computation needed for decrypting), not to do serious encryption, for admin password for example i use AES:
// this function both encryptes and decryptes
// EnDecrypt(EnDeCrypt(astring)) = astring
function EnDeCrypt(const Value : String) : String;
var
CharIndex : integer;
begin
Result := Value;
for CharIndex := 1 to Length(Value) do
Result[CharIndex] := chr(not(ord(Value[CharIndex])));
end;
This is some kind of the well-known ROT13 crypt:
// will crypt A..Z, a..z, 0..9 characters by rotating
function Crypt(const s: string): string;
var i: integer;
begin
result := s;
for i := 1 to length(s) do
case ord(s[i]) of
ord('A')..ord('M'),ord('a')..ord('m'): result[i] := chr(ord(s[i])+13);
ord('N')..ord('Z'),ord('n')..ord('z'): result[i] := chr(ord(s[i])-13);
ord('0')..ord('4'): result[i] := chr(ord(s[i])+5);
ord('5')..ord('9'): result[i] := chr(ord(s[i])-5);
end;
end;
Any characters other than A..Z, a..z, 0..9 will stay unchanged.
The best way is encrypt and then encode the string.
Check this sample which uses the JWSCL library to encrypt a string and Indy to encode and decode in base64.
{$APPTYPE CONSOLE}
uses
ExceptionLog,
Classes,
JwaWinType,
JwaWinCrypt,
IdCoderMIME,
SysUtils;
function CryptString(Const Input: string; password : AnsiString; Encrypt: Boolean) : string;
const
BufferSize=1024*1024;
var
StreamSource : TStringStream;
StreamDest : TStringStream;
CRYPTPROV : HCRYPTPROV;
CRYPTHASH : HCRYPTHASH;
CRYPTKEY : HCRYPTKEY;
Buffer : LPBYTE;
BytesIn : DWORD;
Final : Boolean;
Encoder : TIdEncoderMIME;
Decoder : TIdDecoderMIME;
DestStream : TStringStream;
begin
CryptAcquireContext(CRYPTPROV, nil, nil, PROV_RSA_FULL, CRYPT_VERIFYCONTEXT);
try
//create a valid key based in the password
if not CryptCreateHash(CRYPTPROV, CALG_SHA1, 0, 0, CRYPTHASH) then RaiseLastOSError;
try
if not CryptHashData(CRYPTHASH, @Password[1], Length(Password), 0) then RaiseLastOSError;
if not CryptDeriveKey(CRYPTPROV, CALG_RC4, CRYPTHASH, 0, CRYPTKEY) then RaiseLastOSError;
finally
CryptDestroyHash(CRYPTHASH);
end;
StreamSource := TStringStream.Create(Input);
StreamSource.Position:=0;
StreamDest := TStringStream.Create;
try
GetMem(Buffer, BufferSize);
try
if not Encrypt then
begin
//decode the string using base64
Decoder := TIdDecoderMIME.Create(nil);
try
DestStream := TStringStream.Create;
try
StreamDest.Position:=0;
Decoder.DecodeBegin(DestStream);
Decoder.Decode(StreamSource);
Decoder.DecodeEnd;
StreamSource.Clear;
DestStream.Position:=0;
StreamSource.CopyFrom(DestStream,DestStream.Size);
StreamSource.Position:=0;
finally
DestStream.Free;
end;
finally
Decoder.Free;
end;
end;
repeat
BytesIn := StreamSource.Read(Buffer^, BufferSize);
Final := (StreamSource.Position >= StreamSource.Size);
if Encrypt then
begin
if not CryptEncrypt(CRYPTKEY, 0, Final, 0, Buffer, BytesIn, BytesIn) then RaiseLastOSError;
end
else
if not CryptDecrypt(CRYPTKEY, 0, Final, 0, Buffer, BytesIn) then RaiseLastOSError;
StreamDest.Write(Buffer^, BytesIn);
until Final;
//encode the string using base64
if Encrypt then
begin
Encoder := TIdEncoderMIME.Create(nil);
try
DestStream:=TStringStream.Create;
try
StreamDest.Position:=0;
Encoder.Encode(StreamDest,DestStream);
Result := DestStream.DataString;
finally
DestStream.Free;
end;
finally
Encoder.Free;
end;
end
else
Result:= StreamDest.DataString;
finally
FreeMem(Buffer, BufferSize);
end;
finally
StreamSource.Free;
StreamDest.Free;
end;
finally
CryptReleaseContext(CRYPTPROV, 0);
end;
end;
var
plaintext : string;
Encrypted : string;
begin
try
plaintext:='this is a plain text'; Writeln('Plain Text '+plaintext);
Encrypted:=CryptString(plaintext,'...ThiS Is A PaSsWord...',True);
Writeln('Encrypted/Encoded string '+Encrypted);
plaintext:=CryptString(Encrypted,'...ThiS Is A PaSsWord...',False);
Writeln('Original string '+plaintext);
except
on E: Exception do
Writeln(E.ClassName, ': ', E.Message);
end;
Readln;
end.