Use Microsoft Crypto APITag(s): WinAPI/Registry
With almost all Windows installation, the Microsoft Crypto API is available.
CryptoAPI 1.0 is provided through Microsoft Windows NT 4.0 and Microsoft Internet Explorer 3.0 and later. CryptoAPI 1.0 will also ship with the Windows 95 update.
Microsoft provides a separate COM object to make it easy to exploit this API from VBScript or Powerbuilder. But you need to installed the COM object before using it. This How-to will show you how to call directly the Crypto DLL.
The n_cst_crypto object can encrypt/decrypt a string based on a given key. This can be used to encrypt user/password entries in INI file for example.
Based on this Visual Basic example, the PB7 PBL containing the n_cst_crypto object can be download from here.
Many thanks to Martyn Bannister for VB to PB development.
To encrypt a string
n_cst_crypto lnv_crypt
string ls_encrypted
lnv_crypt = CREATE n_cst_crypto
ls_encrypted = lnv_crypt.EncryptData("my sensitive data" , "SecretKey")
DESTROY lnv_crypt
To decrypt a string
n_cst_crypto lnv_crypt string ls_decrypted lnv_crypt = CREATE n_cst_crypto ls_decrypted = lnv_crypt.DecryptData(is_crypted , "SecretKey") DESTROY lnv_crypt
Check this How-to for a powerscript-only.
J.C. de Souza Ribeiro wrote :
I tried to use your "PB7 PBL containing the n_cst_crypto object" to encrypt a string of 59 numeric digits, using the MD5 algorithm, but everytime I run it, using different values, I get strings of different lengths and I was expecting always a string of 32 bytes.
The original VB algorithm is making a great deal to make sure that there is no tab/cr/lf characters in the result string and do encryption (with a counter) again if there are present. I suspect this is why the length is varying.
/*
External Function Definitions
FUNCTION Boolean CryptAcquireContextA (ref ulong hProv, &
ref string pszContainer, &
ref string pszProvider, ulong dwProvType, &
ulong dwFlags) &
LIBRARY "advapi32.dll"
FUNCTION Boolean CryptReleaseContext (ulong hProv, ulong dwFlags) &
LIBRARY "advapi32.dll"
FUNCTION Boolean CryptCreateHash (ulong hProv, uint Algid, ulong hKey, &
ulong dwFlags, ref ulong phHash) &
LIBRARY "advapi32.dll"
FUNCTION Boolean CryptHashData (ulong hHash, ref string pbData, &
ulong dwDataLen, ulong dwFlags) &
LIBRARY "advapi32.dll"
FUNCTION Boolean CryptDestroyHash (ulong hHash) &
LIBRARY "advapi32.dll"
FUNCTION Boolean CryptGetHashParam (ulong hHash, ulong dwParam, &
ref blob pbData, &
ref ulong pdwDataLen, ulong dwFlags) &
LIBRARY "advapi32.dll"
FUNCTION Ulong GetLastError () Library "kernel32.dll"
*/
// Constants
CONSTANT ULONG PROV_RSA_FULL = 1
CONSTANT ULONG CRYPT_VERIFYCONTEXT = 4026531840 // 0xF0000000
CONSTANT ULONG CALG_MD5 = 32771 // 4<<13 | 0 | 3
CONSTANT ULONG HP_HASHVAL = 2 // 0x0002
public function string of_md5 (string as_text);
// Calculate the MD5 message digest hash of a string
// Using the Windows Crypto API
ulong MD5LEN = 16
ulong hProv // provider handle
ulong hHash // hash object handle
ulong err_number
String s_result, s_null
Integer i, l, r, b
Blob{16} bl_hash
Blob{1} bl_byte
SetNull (s_null)
ulong cbHash = 0
CHAR HexDigits[0 TO 15] = &
{'0','1','2','3','4','5','6','7','8','9','a','b','c','d','e','f'}
//Get handle to the crypto provider
IF NOT CryptAcquireContextA&
(hProv, s_null, s_null, PROV_RSA_FULL, CRYPT_VERIFYCONTEXT) &
THEN
err_number = GetLastError()
return 'acquire context failed ' + String (err_number)
END IF
// Create the hash object
IF NOT CryptCreateHash(hProv, CALG_MD5, 0, 0, hHash) THEN
err_number = GetLastError()
CryptReleaseContext(hProv, 0)
return 'create hash failed ' + String (err_number)
END IF
// Add the input to the hash
IF NOT CryptHashData(hHash, as_text, Len(as_text), 0) THEN
err_number = GetLastError()
CryptDestroyHash(hHash)
CryptReleaseContext(hProv, 0)
return 'hashdata failed ' + String (err_number)
END IF
// Get the hash value and convert it to readable characters
cbHash = MD5LEN
IF (CryptGetHashParam(hHash, HP_HASHVAL, bl_hash, cbHash, 0)) THEN
FOR i = 1 TO 16
bl_byte = BlobMid (bl_hash, i, 1)
b = Asc (String(bl_byte))
r = Mod (b, 16) // right 4 bits
l = b / 16 // left 4 bits
s_result += HexDigits [l] + HexDigits [r]
NEXT
ELSE
err_number = GetLastError()
return 'gethashparam failed ' + String (err_number)
END IF
// clean up and return
CryptDestroyHash(hHash)
CryptReleaseContext(hProv, 0)
return s_result
Since PB10 is Unicode, you need to specify to the String() function to use ANSI.
[PB7/8/9]
b = Asc (String(bl_byte))
[PB10]
b = AscA(String(bl_byte,EncodingANSI!))
Another fix for PB10.5 from Thomas Mathys.
I took your code to encode password by MD5 in a powerbuilder 10.5 application, but I saw the result was different than what I obtained with others tools (Oracle, Javascript example,...), if I encoded more than one character. I searched a lot, and finally found a solution: you must pass your text as a blob, and not as a string, to the CryptHashData function. You got to change the prototype and your powerscript must be changed like this:
... // Add the input to the hash Blob bl_text bl_text = Blob(as_text, EncodingANSI!) IF NOT CryptHashData(hHash, bl_text, Len(bl_text), 0) THEN ...
A complete Powerbuilder object is available here : http://www.topwizprogramming.com/freecode_crypto.html
If you want to generate SHA1 hash then your DBMS may help you.
For Oracle 10g, it's something like :
SELECT DBMS_CRYPTO.HASH (utl_raw.cast_to_raw('Tester12'), :sh1) FROM dual;
Send comment, question or suggestion to howto@rgagnon.com