CryptoAPI and hashing
I will openly admit that I have little experience with the Microsoft Cryptography API. My previous hands-on experience with using certificates with Delphi was panic strikken to say the least. I had to support HTTPS via Indy, using OpenSSL to do the job; and I found that Delphi’s support for modern certificate standards (back then) were quite thin on the ground. Also, using openSSL to encrypt streams of data was likewise an undocumented black art. I was able to piece together a working unit from snippets I found online mixed with my own C/C++ port – but it was a terrible solution to what must be considered a modern, everyday programming task.
Anyways, I am putting together a library to solve “common use” of the Microsoft Cryptography API. A set of routines that can be dropped into a project and used to support the most widely used features. Rather than simply porting the headers (which has been done by several programmers before me) I want simplified wrapper classes that are easy to use.
My own needs included, the most common requirements asked for by Delphi developers seems to be (in no particular order):
- Generating a hash of a stream, string or memory buffer
- Creating keys on the fly
- Encrypting or decrypting a file based on a key
- Signing a file using a key
- Signing an XML document using a key
- Signing a PDF document using a key
- Signature verification
Note: The term “key” here is ambiguous, it may refer to a crypto-key or a crypto-hash. You dont encrypt using a password (old school) but rather by generating a unique hash of the password (if you are rolling your own security that is, certificates and keys have stricts guidelines for use.
My uncle’s hashed haggis
Right – on with the code! Generating a Hash from a pice of data is fairly straight forward, and it goes a little something like this:
TCryptoAPI = Class protected class function rsaMakeHash(const aHashType:ULONG; const HashBufLen:Integer; const aData:TStream;var aText:String):Boolean; public class function rsaMakeSHAHash(const aData:TStream;var aText:String):Boolean; class function rsaMakeMD5Hash(const aData:TStream;var aText:String):Boolean; End; 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;
Well, that wasnt to hard. Then of course comes the creation of keys, or a key-pair. You have one private key and one public key. That is fairly straightforward as well, although I need to get a full overview of the crypt flags (scavenger the C headers):
class function TCryptoAPI.rsaMakeKeys(var aPrivate, aPublic: TStream): Boolean; const RSA1024BIT_KEY = $04000000; 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, RSA1024BIT_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;
Now the fun really begins, namely to encrypt and decrypt something (!)
To be continued shortly …
Recent
The vatican vault
- March 2023
- February 2023
- December 2022
- October 2022
- January 2022
- October 2021
- March 2021
- November 2020
- September 2020
- July 2020
- June 2020
- April 2020
- March 2020
- February 2020
- January 2020
- November 2019
- October 2019
- September 2019
- August 2019
- July 2019
- June 2019
- May 2019
- April 2019
- March 2019
- February 2019
- January 2019
- December 2018
- November 2018
- October 2018
- September 2018
- August 2018
- July 2018
- June 2018
- May 2018
- April 2018
- March 2018
- February 2018
- January 2018
- December 2017
- November 2017
- October 2017
- August 2017
- July 2017
- June 2017
- May 2017
- April 2017
- March 2017
- February 2017
- January 2017
- December 2016
- November 2016
- October 2016
- September 2016
- August 2016
- July 2016
- June 2016
- May 2016
- April 2016
- March 2016
- January 2016
- December 2015
- November 2015
- October 2015
- September 2015
- August 2015
- June 2015
- May 2015
- April 2015
- March 2015
- February 2015
- January 2015
- December 2014
- November 2014
- October 2014
- September 2014
- August 2014
- July 2014
- June 2014
- May 2014
- April 2014
- March 2014
- February 2014
- January 2014
- December 2013
- November 2013
- October 2013
- September 2013
- August 2013
- July 2013
- June 2013
- May 2013
- February 2013
- August 2012
- June 2012
- May 2012
- April 2012