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 …