Home > Delphi > CryptoAPI part 2 – Encrypt something

CryptoAPI part 2 – Encrypt something

February 13, 2014 Leave a comment Go to comments

In my last post I added Hashing methods to our little unit, this time we add the ability to encrypt and decrypt streams using a segment of data as the basis for a key (this must not be confused with encrypting using a crypto-key, that is coming later).

Well, here is the unit so far – Enjoy!

  unit crypto;

  interface

  uses
  Winapi.Windows, System.SysUtils, System.Classes,
  Soap.EncdDecd,
  wcrypt2;

  type

  TCryptoAPI = Class
  protected
    class function  rsaMakeHash(const aHashType:ULONG;
          const HashBufLen:Integer;
          const aData:TStream;var aText:String):Boolean;
  public
    class function  rsaMakeKeys(var aPrivate,aPublic:TStream):Boolean;
    class function  rsaKeyToBase64(const aKey:TStream;var aText:String):Boolean;
    class function  rsaBase64ToKey(const aText:String;var aKey:TStream):Boolean;
    class function  rsaMakeSHAHash(const aData:TStream;var aText:String):Boolean;
    class function  rsaMakeMD5Hash(const aData:TStream;var aText:String):Boolean;

    class function  rsaCryptStream(const aKey:TStream;const aSourceStream:TStream;
                    const aTargetStream:TStream):Boolean;

    class function  rsaDecryptStream(Const aKey:TStream;const aSourceStream:TStream;
                    const aTargetStream:TStream):Boolean;

  End;

  implementation

  Const
  CNT_RSA_1024BIT_KEY = $04000000;
  CNT_RSA_512BIT_KEY  = $02000000;

class function TCryptoAPI.rsaMakeHash(const aHashType:ULONG;
          const HashBufLen:Integer;
          const aData:TStream;var aText:String):Boolean;
var
  mProvider: HCRYPTPROV;
  mHash:  HCRYPTHASH;
  mTotal:   Int64;
  mRead:    Int64;
  mBuffer:  PByte;
  mHashBuffer: packed array[1..512] of byte;
  mHashByteLen:  Integer;
  x:  Integer;
Begin
  setLength(aText,0);
  result:=False;

  if aData<>NIL then
  begin
    if aData.Size>0 then
    begin
      aData.Position:=0;
      if CryptAcquireContext(@mProvider, nil, nil,
      PROV_RSA_FULL, CRYPT_VERIFYCONTEXT) then
      Begin
        try
          if CryptCreateHash(mProvider,aHashType,0,0,@mHash) then
          begin
            try
              mBuffer:=Allocmem(1024);
              try
                mTotal:=aData.Size;
                repeat
                  mRead:=aData.Read(mBuffer^,SizeOf(mBuffer));
                  if mRead>0 then
                  begin
                    if not CryptHashData(mHash,mBuffer,mRead, 0) then
                    break;
                  end;
                  mTotal:=mTotal - mRead;
                until mTotal<1;

                mHashByteLen:=HashBufLen;
                if CryptGetHashParam(mHash, HP_HASHVAL, @mHashBuffer,
                @mHashByteLen, 0) then
                Begin
                  for x:=1 to mHashByteLen do
                  aText:=aText + IntToHex(mHashBuffer[x],2);
                  result:=True;
                end;
              finally
                FreeMem(mbuffer);
              end;
            finally
              CryptDestroyHash(mHash);
            end;
          end;
        finally
          (* Release crypto provider context *)
          CryptReleaseContext(mProvider, 0);
        end;
      end;
    end;
  end;
end;

class function TCryptoAPI.rsaMakeMD5Hash(const aData: TStream;
  var aText: String): Boolean;
Begin
  result:=TCryptoAPI.rsaMakeHash(CALG_MD5,16,aData,atext);
end;

class function TCryptoAPI.rsaMakeSHAHash(const aData:TStream;
      var aText:String):Boolean;
Begin
  result:=TCryptoAPI.rsaMakeHash(CALG_SHA1,20,aData,atext);
end;

class function TCryptoAPI.rsaDecryptStream(Const aKey:TStream;
        const aSourceStream:TStream;
        const aTargetStream:TStream):Boolean;
var
  mProvider: HCRYPTPROV;
  key: HCRYPTKEY;
  mTotal:   Int64;
  mRead:    DWord;
  mBuffer:  PByte;
  mPrefetch:  Integer;
  mHash:  HCRYPTHASH;
  mHashBuffer:  PByte;
begin
  result:=False;
  if aKey<>NIL then
  begin
    if aKey.Size>0 then
    begin
      aKey.Position:=0;
      if aSourceStream<>NIl then
      begin
        if atargetStream<>NIl then
        begin
          (* Get crypto-API context *)
          if CryptAcquireContext(@mProvider, nil, nil,
          PROV_RSA_FULL, CRYPT_VERIFYCONTEXT) then
          Begin
            try
              (* Create a Hash object *)
              if CryptCreateHash(mProvider, CALG_MD5, 0, 0, @mHash) then
              Begin

                try
                  (* Build the hash based on our key *)
                  mHashBuffer:=Allocmem(1024);
                  try
                    mTotal:=aKey.Size;
                    repeat
                      mRead:=aKey.Read(mHashBuffer^,1024);
                      if mRead>0 then
                      begin
                        if not CryptHashData(mHash,mHashBuffer,mRead,0) then
                        break;
                      end else
                      break;
                      mTotal:=mTotal - mRead;
                    until mTotal<1;

                    (* re-wind source stream *)
                    aSourceStream.Position:=0;

                    (* Derive an ecryption key from our hash *)
                    if CryptDeriveKey(mProvider, CALG_RC4, mHash, 0, @key) then
                    Begin
                      (* Query the prefetch-buffer for the context *)
                      mPrefetch:=1024;
                      if CryptEncrypt(key,0,true,0,NIL,@mPrefetch,mPrefetch) then
                      Begin
                        (* Allocate encryption cache *)
                        mBuffer:=Allocmem(mPrefetch);
                        try
                          mTotal:=aSourceStream.Size;
                          repeat
                            mRead:=aSourceStream.Read(mBuffer^,mPrefetch);
                            mTotal:=mTotal - mRead;
                            if mRead>0 then
                            begin
                              (* Encrypt read buffer *)
                              if not cryptDecrypt(key,0,
                              (mTotal<1),0,
                              mBuffer,@mRead) then
                              RaiseLastOSError;

                              (* Write encrypted buffer to target *)
                              atargetStream.Write(mBuffer^,mRead);
                            end else
                            break;
                          until mTotal<1;

                          (* Re-wind and return *)
                          aTargetStream.position:=0;
                          result:=aTargetStream.size>0;
                        finally
                          freeMem(mBuffer);
                        end;
                      end;
                    end else
                    RaiseLastOSError;
                  finally
                    FreeMem(mHashBuffer);
                  end;
                finally
                  (* Release the hash *)
                  CryptDestroyHash(mHash);
                end;
              end else
              RaiseLastOSError;
            finally
              (* Release crypto provider context *)
              CryptReleaseContext(mProvider, 0);
            end;
          end;
        end;
      end;
    end;
  end;
end;

(* NOTE: aKey here can be anything, not the same as a key-pair (!)
   What you can do is to generate a hash of a password, store that
   hash in a stream - and then use that as the key stream *)
class function TCryptoAPI.rsaCryptStream(const aKey, aSourceStream,
  aTargetStream: TStream): Boolean;
var
  mProvider: HCRYPTPROV;
  key: HCRYPTKEY;
  mTotal:   Int64;
  mRead:    DWord;
  mBuffer:  PByte;
  mPrefetch:  Integer;
  mHash:  HCRYPTHASH;
  mHashBuffer:  PByte;
begin
  result:=False;
  if aKey<>NIL then
  begin
    if aKey.Size>0 then
    begin
      aKey.Position:=0;
      if aSourceStream<>NIl then
      begin
        if atargetStream<>NIl then
        begin
          (* Get crypto-API context *)
          if CryptAcquireContext(@mProvider, nil, nil,
          PROV_RSA_FULL, CRYPT_VERIFYCONTEXT) then
          Begin

            try
              (* Create a Hash object *)
              if CryptCreateHash(mProvider, CALG_MD5, 0, 0, @mHash) then
              Begin

                try
                  (* Build the hash based on our key *)
                  mHashBuffer:=Allocmem(1024);
                  try
                    mTotal:=aKey.Size;
                    repeat
                      mRead:=aKey.Read(mHashBuffer^,1024);
                      if mRead>0 then
                      begin
                        if not CryptHashData(mHash,mHashBuffer,mRead,0) then
                        break;
                      end else
                      break;
                      mTotal:=mTotal - mRead;
                    until mTotal<1;

                    (* re-wind source stream *)
                    aSourceStream.Position:=0;

                    (* Derive an ecryption key from our hash *)
                    if CryptDeriveKey(mProvider, CALG_RC4, mHash, 0, @key) then
                    Begin
                      (* Query the prefetch-buffer for the context *)
                      mPrefetch:=1024;
                      if CryptEncrypt(key,0,true,0,NIL,@mPrefetch,mPrefetch) then
                      Begin
                        (* Allocate encryption cache *)
                        mBuffer:=Allocmem(mPrefetch);
                        try
                          mTotal:=aSourceStream.Size;
                          repeat
                            mRead:=aSourceStream.Read(mBuffer^,mPrefetch);
                            mTotal:=mTotal - mRead;
                            if mRead>0 then
                            begin
                              (* Encrypt read buffer *)
                              if not CryptEncrypt(key,0,
                              Bool(mTotal<1),0,
                              mBuffer,@mRead,mRead) then
                              RaiseLastOSError;

                              (* Write encrypted buffer to target *)
                              atargetStream.Write(mBuffer^,mRead);
                            end else
                            break;
                          until mTotal<1;

                          (* Re-wind and return *)
                          aTargetStream.position:=0;
                          result:=aTargetStream.size>0;
                        finally
                          freeMem(mBuffer);
                        end;
                      end;
                    end else
                    RaiseLastOSError;
                  finally
                    FreeMem(mHashBuffer);
                  end;
                finally
                  (* Release the hash *)
                  CryptDestroyHash(mHash);
                end;
              end else
              RaiseLastOSError;
            finally
              (* Release crypto provider context *)
              CryptReleaseContext(mProvider, 0);
            end;
          end;
        end;
      end;
    end;
  end;
end;

class function TCryptoAPI.rsaBase64ToKey(const aText:String;
      var aKey:TStream):Boolean;
var
  mSrc: TStringStream;
  mdst: TmemoryStream;
begin
  result:=False;
  aKey:=NIL;
  if Length(aText)>0 then
  begin
    mSrc:=TStringStream.Create(aText);
    try
      mdst:=TmemoryStream.Create;
      try
        DecodeStream(mSrc,mDst);
      except
        on exception do
        mdst.Free;
      end;

      mDst.Position:=0;
      aKey:=mDSt;
      result:=True;

    finally
      mSrc.Free;
    end;
  end;
end;

class function TCryptoAPI.rsaKeyToBase64(const aKey: TStream;
  var aText: String): Boolean;
var
  mTemp:  TStringStream;
Begin
  setLength(aText,0);
  result:=False;
  if aKey<>NIL then
  begin
    mtemp:=TStringStream.Create;
    try
      aKey.Position:=0;
      encodeStream(aKey,mTemp);
      aText:=mTemp.DataString;
      result:=True;
    finally
      mtemp.Free;
    end;
  end;
end;

class function TCryptoAPI.rsaMakeKeys(var aPrivate,
  aPublic: TStream): Boolean;
var
  mProvider: HCRYPTPROV;
  mKeyPair: HCRYPTKEY;
  buflen: DWORD;
begin
  aPrivate:=NIL;
  aPublic:=NIL;
  result:=False;

  (* Get crypto-API context *)
  if CryptAcquireContext(@mProvider, nil, nil, PROV_RSA_FULL,CRYPT_VERIFYCONTEXT) then
  Begin
    try
      if CryptGenKey(mProvider, AT_KEYEXCHANGE,
      CNT_RSA_1024BIT_KEY or CRYPT_EXPORTABLE, @mKeyPair) then
      Begin
        try
          (* Query size of private buffer *)
          if CryptExportKey(mKeyPair, 0, PRIVATEKEYBLOB, 0, nil, @buflen) then
          Begin
            (* set private buffer to default size *)
            aPrivate:=TMemoryStream.Create;
            aPrivate.Size:=bufLen;

            (* export private key to buffer *)
            if CryptExportKey(mKeyPair, 0, PRIVATEKEYBLOB, 0,
            PByte(TMemoryStream(aPrivate).Memory), @buflen) then
            Begin

              (* Query size of pubic buffer *)
              if CryptExportKey(mKeyPair, 0, PUBLICKEYBLOB, 0, nil, @buflen) then
              Begin
                (* set public buffer to default size *)
                aPublic:=TMemoryStream.Create;
                aPublic.Size:=bufLen;

                (* export public key to buffer *)
                if CryptExportKey(mKeyPair, 0, PUBLICKEYBLOB, 0,
                PByte(TMemoryStream(aPublic).Memory), @buflen) then
                Begin
                  aPrivate.Position:=0;
                  aPublic.Position:=0;
                  result:=True;
                end else
                begin
                  FreeAndNIL(aPrivate);
                  FreeAndNIL(aPublic);
                  RaiseLastOSError;
                end;
              end;
            end else
            begin
              FreeAndNIL(aPrivate);
              RaiseLastOSError;
            end;
          end;
        finally
          (* Release key-pair *)
          CryptDestroyKey(mKeyPair);
        end;
      end;
    finally
      (* Release crypto provider context *)
      CryptReleaseContext(mProvider, 0);
    end;
  end;
end;

  end.
Advertisements
  1. No comments yet.
  1. No trackbacks yet.

Leave a Reply

Please log in using one of these methods to post your comment:

WordPress.com Logo

You are commenting using your WordPress.com account. Log Out / Change )

Twitter picture

You are commenting using your Twitter account. Log Out / Change )

Facebook photo

You are commenting using your Facebook account. Log Out / Change )

Google+ photo

You are commenting using your Google+ account. Log Out / Change )

Connecting to %s

%d bloggers like this: