Page 1 of 2

Windows: Encryption with password using the Windows API

Posted: Sat Jan 22, 2005 1:40 pm
by El_Choni
Hi,

I've seen other encryption snippets around, but I think none uses teh Windows API to do it. The API allows you to use several encryption algorithm. This example uses RC4.

Code: Select all

Procedure Error(message$)
  wError = GetLastError_()
  If wError
    *ErrorBuffer = AllocateMemory(1024)
    FormatMessage_(#FORMAT_MESSAGE_FROM_SYSTEM, 0, wError, 0, *ErrorBuffer, 1024, 0)
    message$+Chr(10)+PeekS(*ErrorBuffer)
    FreeMemory(*ErrorBuffer)
  EndIf
  MessageRequester("Error", message$)
EndProcedure

#PROV_RSA_FULL = 1
#ALG_SID_MD5 = 3
#ALG_SID_RC4 = 1
#ALG_CLASS_DATA_ENCRYPT = 3<<13
#ALG_CLASS_HASH = 4<<13
#ALG_TYPE_ANY = 0
#ALG_TYPE_STREAM = 4<<9
#CALG_MD5 = #ALG_CLASS_HASH|#ALG_TYPE_ANY|#ALG_SID_MD5
;
; Valid hashing algorithms:
;
; #ALG_SID_HMAC = 9
; #ALG_SID_MAC = 5
; #ALG_SID_MD2 = 1
; #ALG_SID_SHA = 4
; #ALG_SID_SHA1 = 4
; #ALG_SID_SSL3SHAMD5 = 8
; #CALG_HMAC = #ALG_CLASS_HASH|#ALG_TYPE_ANY|#ALG_SID_HMAC
; #CALG_MAC = #ALG_CLASS_HASH|#ALG_TYPE_ANY|#ALG_SID_MAC
; #CALG_MD2 = #ALG_CLASS_HASH|#ALG_TYPE_ANY|#ALG_SID_MD2
; #CALG_SHA = #ALG_CLASS_HASH|#ALG_TYPE_ANY|#ALG_SID_SHA
; #CALG_SHA1 = #ALG_CLASS_HASH|#ALG_TYPE_ANY|#ALG_SID_SHA1
; #CALG_SSL3_SHAMD5 = #ALG_CLASS_HASH|#ALG_TYPE_ANY|#ALG_SID_SSL3SHAMD5
#CALG_RC4 = #ALG_CLASS_DATA_ENCRYPT|#ALG_TYPE_STREAM|#ALG_SID_RC4
#CRYPT_EXPORTABLE = 1
#CRYPT_NEWKEYSET = 8

Procedure Encrypt(*lpData, DataLength, password$)
  If CryptAcquireContext_(@hProv, #NULL, #NULL, #PROV_RSA_FULL, 0)=0
    CryptAcquireContext_(@hProv, #NULL, #NULL, #PROV_RSA_FULL, #CRYPT_NEWKEYSET)
  EndIf
  If hProv
    ; Hashing algorithms defined in the Windows API (constants commented above):
    ;
    ; #CALG_HMAC HMAC, a keyed hash algorithm 
    ; #CALG_MAC Message Authentication Code 
    ; #CALG_MD2 MD2 
    ; #CALG_MD5 MD5 
    ; #CALG_SHA US DSA Secure Hash Algorithm 
    ; #CALG_SHA1 Same as CALG_SHA 
    ; #CALG_SSL3_SHAMD5 SSL3 client authentication 
    ;
    CryptCreateHash_(hProv, #CALG_MD5, 0, 0, @hHash)
    If hHash
      CryptHashData_(hHash, password$, Len(password$), 0)
      ;
      ; For a list of valid encryption algorithms, check:
      ;
      ; http://msdn.microsoft.com/library/en-us/seccrypto/security/alg_id.asp
      ; 
      ; The constant values can be found in the Platform SDK include file: WinCrypt.h
      ;
      ; Here we're using RC4
      ;
      CryptDeriveKey_(hProv, #CALG_RC4, hHash, #CRYPT_EXPORTABLE, @hKey)
      If hKey
        If CryptEncrypt_(hKey, 0, #TRUE, #NULL, *lpData, @DataLength, DataLength)
          result = #TRUE
        Else
          Error("CryptEncrypt_() failed")
        EndIf
        CryptDestroyKey_(hKey)
      Else
        Error("CryptDeriveKey_() failed")
      EndIf
      CryptDestroyHash_(hHash)
    Else
      Error("CryptCreateHash_() failed")
    EndIf
    CryptReleaseContext_(hProv, 0)
  Else
    Error("CryptAcquireContext_() failed")
  EndIf
  ProcedureReturn result
EndProcedure

Procedure Decrypt(*lpData, DataLength, password$)
  If CryptAcquireContext_(@hProv, #NULL, #NULL, #PROV_RSA_FULL, 0)=0
    CryptAcquireContext_(@hProv, #NULL, #NULL, #PROV_RSA_FULL, #CRYPT_NEWKEYSET)
  EndIf
  If hProv
    CryptCreateHash_(hProv, #CALG_MD5, 0, 0, @hHash)
    If hHash
      CryptHashData_(hHash, password$, Len(password$), 0)
      CryptDeriveKey_(hProv, #CALG_RC4, hHash, #CRYPT_EXPORTABLE, @hKey)
      If hKey
        If CryptDecrypt_(hKey, 0, #TRUE, 0, *lpData, @DataLength)
          result = #TRUE
        Else
          Error("CryptDecrypt_() failed")
        EndIf
        CryptDestroyKey_(hKey)
      Else
        Error("CryptDeriveKey_() failed")
      EndIf
      CryptDestroyHash_(hHash)
    Else
      Error("CryptCreateHash_() failed")
    EndIf
    CryptReleaseContext_(hProv, 0)
  Else
    Error("CryptAcquireContext_() failed")
  EndIf
  ProcedureReturn result
EndProcedure

mydata$ = "This a string to test the encryption/decryption code"
pwd$ = "my password"
If Encrypt(@mydata$, Len(mydata$), pwd$)
  Debug "Encrypted data:"
  Debug mydata$
  If Decrypt(@mydata$, Len(mydata$), pwd$)
    Debug ""
    Debug "Decrypted data:"
    Debug mydata$
  EndIf
EndIf
Regards,

Posted: Sat Jan 22, 2005 2:10 pm
by newbie
Very interesting, thx ! :D

Cool

Posted: Thu May 26, 2005 10:35 pm
by holyfieldstudios
Seems to work

Posted: Fri May 27, 2005 6:02 am
by Droopy
very good :wink:

Posted: Sun Jun 26, 2005 2:28 am
by HeX0R
I really love this function, but sometimes the results aren't o.k.

Example ?
o.k, try this with your function:

Code: Select all

mydata$ = "aol30ebpm4"
pwd$    = "uhDZOn"
If Encrypt(@mydata$, Len(mydata$), pwd$)
  Debug "Encrypted data:"
  Debug mydata$
  If Decrypt(@mydata$, Len(mydata$), pwd$)
    Debug "IS:" + mydata$ + " <->ShouldBe:aol30ebpm4"
  EndIf
EndIf
You will see, that the original and the decrypted(encrypted) aren't the same!
Problem is, when encryption contains a zero-byte, then the string will be cut! (i had a real long fight now with this thing, cause it happenes not so often...)

Here is my little change... well o.k. i needed a function which returns the String directly, so i changed this... and i removed the error-function for my needs

Code: Select all

Procedure.s Encrypt(string$, key$)
  ;Crypt-Constants
  #PROV_RSA_FULL = 1 
  #ALG_SID_MD5 = 3 
  #ALG_SID_RC4 = 1 
  #ALG_CLASS_DATA_ENCRYPT = 3<<13 
  #ALG_CLASS_HASH = 4<<13 
  #ALG_TYPE_ANY = 0 
  #ALG_TYPE_STREAM = 4<<9 
  #CALG_MD5 = #ALG_CLASS_HASH|#ALG_TYPE_ANY|#ALG_SID_MD5 
  #CALG_RC4 = #ALG_CLASS_DATA_ENCRYPT|#ALG_TYPE_STREAM|#ALG_SID_RC4 
  #CRYPT_EXPORTABLE = 1 
  #CRYPT_NEWKEYSET = 8
  ;Uses the RC4 User lib
  Protected *Buffer12
  *Buffer12 = AllocateMemory(1024)
  DataLength.l = Len(string$)
  PokeS(*Buffer12, string$)
  If CryptAcquireContext_(@hProv, #NULL, #NULL, #PROV_RSA_FULL, 0) = 0 
    CryptAcquireContext_(@hProv, #NULL, #NULL, #PROV_RSA_FULL, #CRYPT_NEWKEYSET)
  EndIf 
  If hProv 
    CryptCreateHash_(hProv, #CALG_MD5, 0, 0, @hHash) 
    If hHash 
      CryptHashData_(hHash, key$, Len(key$), 0) 
      CryptDeriveKey_(hProv, #CALG_RC4, hHash, #CRYPT_EXPORTABLE, @hKey) 
      If hKey 
        If CryptEncrypt_(hKey, 0, #TRUE, #NULL, *Buffer12, @DataLength, DataLength)
        EndIf 
        CryptDestroyKey_(hKey) 
      EndIf 
      CryptDestroyHash_(hHash) 
    EndIf 
    CryptReleaseContext_(hProv, 0) 
  EndIf
  Result.s = ""
  For i = 0 To DataLength - 1
    Result + RSet(Hex(PeekB(*Buffer12 + i) & $FF), 2, "0")
  Next i
  FreeMemory(*Buffer12)
  ProcedureReturn Result
EndProcedure

Procedure.s Decrypt(string$, key$)
  ;Uses the RC4 User lib
  Protected *Buffer12
  
  *Buffer12 = AllocateMemory(1024)
  
  hexa.s = "0123456789ABCDEF"
  Result.s = ""
  o.l = 0
  For i = 1 To Len(string$) Step 2
    hi.l = FindString(hexa, Mid(UCase(string$), i, 1), 1) - 1
    lo.l = FindString(hexa, Mid(UCase(string$), i + 1, 1), 1) - 1
    PokeB(*Buffer12 + o, (hi * 16) + lo)
    o + 1
  Next i
  DataLength.l = Len(string$) / 2
  
  If CryptAcquireContext_(@hProv, #NULL, #NULL, #PROV_RSA_FULL, 0)=0 
    CryptAcquireContext_(@hProv, #NULL, #NULL, #PROV_RSA_FULL, #CRYPT_NEWKEYSET) 
  EndIf 
  If hProv 
    CryptCreateHash_(hProv, #CALG_MD5, 0, 0, @hHash)
    If hHash 
      CryptHashData_(hHash, key$, Len(key$), 0) 
      CryptDeriveKey_(hProv, #CALG_RC4, hHash, #CRYPT_EXPORTABLE, @hKey) 
      If hKey 
        If CryptDecrypt_(hKey, 0, #TRUE, 0, *Buffer12, @DataLength) 
          Result = PeekS(*Buffer12)
        EndIf 
        CryptDestroyKey_(hKey) 
      EndIf 
      CryptDestroyHash_(hHash) 
    EndIf 
    CryptReleaseContext_(hProv, 0) 
  EndIf
  FreeMemory(*Buffer12)
  ProcedureReturn Result
EndProcedure

mydata$ = "aol30ebpm4"
pwd$    = "uhDZOn"
mydata$ = Encrypt(mydata$, pwd$)
Debug "Encrypted data:"
Debug mydata$
mydata$ = Decrypt(mydata$, pwd$)
Debug "IS:" + mydata$ + " <->ShouldBe:aol30ebpm4"

Posted: Sun Jun 26, 2005 1:07 pm
by MikeB
I think that El_Choni and HeXOR have done a great job and I have saved the resulting code for future use. My only question is, how secure is RC4 encryption?

MikeB

Posted: Sun Jun 26, 2005 1:15 pm
by PB
> how secure is RC4 encryption?

It's supposed to be VERY secure... here's one such quote:

http://www.rsasecurity.com/rsalabs/node.asp?id=2250

I've read about it before and would have no hesitation in using it.

Posted: Mon Aug 15, 2005 7:02 pm
by HeX0R
Sorry, but i have to reanimate this thread cause otherwise someone else will step into the same trap then me:

I recognized, that your generated encrypted strings didn't return the original string, when decrypting on another os.
This could be irritating for users, if you use it to encrypt passes into inis which the users wants to take with them to another pc for example.

So i found the solution:
Use one of M$'s Cryptographic Service Provider as name instead of a default one.
The function will then look like this:

Code: Select all

;Crypt-Constants
#PROV_RSA_FULL = 1 
#ALG_SID_MD5 = 3 
#ALG_SID_RC4 = 1
#ALG_SID_RC5 = 13
#ALG_CLASS_DATA_ENCRYPT = 3<<13 ;$6000
#ALG_CLASS_HASH = 4<<13 ;$8000
#ALG_TYPE_ANY = 0
#ALG_TYPE_STREAM = 4<<9 
#CALG_MD5 = #ALG_CLASS_HASH|#ALG_TYPE_ANY|#ALG_SID_MD5
#ALG_TYPE_BLOCK = 1536
#CRYPT_OAEP = $40
; Valid hashing algorithms: 
; 
; #ALG_SID_HMAC = 9 
; #ALG_SID_MAC = 5 
; #ALG_SID_MD2 = 1 
; #ALG_SID_SHA = 4 
; #ALG_SID_SHA1 = 4 
; #ALG_SID_SSL3SHAMD5 = 8 
; #CALG_HMAC = #ALG_CLASS_HASH|#ALG_TYPE_ANY|#ALG_SID_HMAC 
; #CALG_MAC = #ALG_CLASS_HASH|#ALG_TYPE_ANY|#ALG_SID_MAC 
; #CALG_MD2 = #ALG_CLASS_HASH|#ALG_TYPE_ANY|#ALG_SID_MD2 
; #CALG_SHA = #ALG_CLASS_HASH|#ALG_TYPE_ANY|#ALG_SID_SHA 
; #CALG_SHA1 = #ALG_CLASS_HASH|#ALG_TYPE_ANY|#ALG_SID_SHA1 
; #CALG_SSL3_SHAMD5 = #ALG_CLASS_HASH|#ALG_TYPE_ANY|#ALG_SID_SSL3SHAMD5 
#CALG_RC4 = #ALG_CLASS_DATA_ENCRYPT|#ALG_TYPE_STREAM|#ALG_SID_RC4
#CALG_RC5 = #ALG_CLASS_DATA_ENCRYPT|#ALG_TYPE_BLOCK|#ALG_SID_RC5
#CRYPT_CREATE_SALT = 4
#CRYPT_EXPORTABLE = 1 
#CRYPT_NEWKEYSET = 8
#MS_DEF_PROV = "Microsoft Base Cryptographic Provider v1.0"

Procedure.s EnCrypt(String$, Key$)
  Protected *Buffer
  *Buffer = AllocateMemory(1024)
  DataLength.l = Len(String$)
  PokeS(*Buffer, String$)
  If CryptAcquireContext_(@hProv, #NULL, #MS_DEF_PROV, #PROV_RSA_FULL, 0) = 0
    CryptAcquireContext_(@hProv, #NULL, #MS_DEF_PROV, #PROV_RSA_FULL, #CRYPT_NEWKEYSET)
  EndIf
  If hProv
    CryptCreateHash_(hProv, #CALG_MD5, 0, 0, @hHash)
    If hHash
      CryptHashData_(hHash, @Key$, Len(Key$), 0)
      CryptDeriveKey_(hProv, #CALG_RC4, hHash, #CRYPT_EXPORTABLE, @hKey)
      If hKey
        If CryptEncrypt_(hKey, 0, #TRUE, #NULL, *Buffer, @DataLength, 1024)
        EndIf
        CryptDestroyKey_(hKey)
      EndIf
      CryptDestroyHash_(hHash)
    EndIf
    CryptReleaseContext_(hProv, 0)
  EndIf

  Result.s = ""
  For i = 0 To DataLength - 1
    Result + RSet(Hex(PeekB(*Buffer + i) & $FF), 2, "0")
  Next i
  FreeMemory(*Buffer)
  ProcedureReturn Result
EndProcedure

Procedure.s Decrypt(String$, Key$)
 
  Protected *Buffer
 
  *Buffer = AllocateMemory(1024)
 
  Result.s = ""
  o.l = 0
  For i = 1 To Len(String$) Step 2
    hi.l = Asc(Mid(String$, i, 1)) - 48
    If hi > 9
      hi - 7
    EndIf
    lo.l = Asc(Mid(String$, i + 1, 1)) - 48
    If lo > 9
      lo - 7
    EndIf
    PokeB(*Buffer + o, (hi * 16) + lo)
    o + 1
  Next i
  DataLength.l = Len(String$) / 2
 
  If CryptAcquireContext_(@hProv, #NULL, #MS_DEF_PROV, #PROV_RSA_FULL, 0) = 0
    CryptAcquireContext_(@hProv, #NULL, #MS_DEF_PROV, #PROV_RSA_FULL, #CRYPT_NEWKEYSET)
  EndIf
  If hProv
    CryptCreateHash_(hProv, #CALG_MD5, 0, 0, @hHash)
    If hHash
      CryptHashData_(hHash, @Key$, Len(Key$), 0)
      CryptDeriveKey_(hProv, #CALG_RC4, hHash, #CRYPT_EXPORTABLE, @hKey)
      If hKey
        If CryptDecrypt_(hKey, 0, #TRUE, 0, *Buffer, @DataLength)
          Result = PeekS(*Buffer)
        EndIf
        CryptDestroyKey_(hKey)
      EndIf
      CryptDestroyHash_(hHash)
    EndIf
    CryptReleaseContext_(hProv, 0)
  EndIf
  FreeMemory(*Buffer)
  ProcedureReturn Result
EndProcedure
Just in case someone really is using this function ;)

Posted: Mon May 01, 2006 7:30 pm
by rsts
HeX0R wrote:
-snip

Just in case someone really is using this function ;)
stumbled across this thread while looking for an easy method to encrypt data for output.

seems to be a handy solution - so yes, there is someone using this function.

Thanks for all the effort "perfecting" it.

cheers

Posted: Mon May 01, 2006 7:50 pm
by srod
This turned up bang on note! Excellent, I was just about to search for just such an encryption routine.

Thanks.

Posted: Mon May 01, 2006 7:57 pm
by srod
Uhm, does this algorithm always produce encrypted strings which are twice as long as the original?

That's not so good for my purposes! :(

Posted: Mon May 01, 2006 9:57 pm
by rsts
srod wrote:Uhm, does this algorithm always produce encrypted strings which are twice as long as the original?

That's not so good for my purposes! :(
From my limited testing, yes - it transforms the bytes to an encrypted hex - thus 2 hex codes per byte.

probably a much better technical explanation will be forthcoming :)

cheers

Posted: Mon May 01, 2006 10:21 pm
by Phoenix
srod wrote:Uhm, does this algorithm always produce encrypted strings which are twice as long as the original?

That's not so good for my purposes! :(
From what I can see it needs to do that because a string encrypted with RC4 can contain the 0-byte, and you can't store those in a string, so it needs to store it as 2-byte hex strings to work around it. It's not a fault of this code but just the way it is. Even Paul's RC4_Lib does it that way too.

Posted: Mon May 01, 2006 10:33 pm
by srod
Got you. I didn't think of that.

Thanks.

Posted: Wed May 03, 2006 12:46 pm
by HeX0R
Here is another possibility Base64encoded :
(output gets smaller)

Code: Select all

;Crypt-Constants
#PROV_RSA_FULL = 1
#ALG_SID_MD5 = 3
#ALG_SID_RC4 = 1
#ALG_SID_RC5 = 13
#ALG_CLASS_DATA_ENCRYPT = 3<<13 ;$6000
#ALG_CLASS_HASH = 4<<13 ;$8000
#ALG_TYPE_ANY = 0
#ALG_TYPE_STREAM = 4<<9
#CALG_MD5 = #ALG_CLASS_HASH|#ALG_TYPE_ANY|#ALG_SID_MD5
#ALG_TYPE_BLOCK = 1536
#CRYPT_OAEP = $40
; Valid hashing algorithms:
;
; #ALG_SID_HMAC = 9
; #ALG_SID_MAC = 5
; #ALG_SID_MD2 = 1
; #ALG_SID_SHA = 4
; #ALG_SID_SHA1 = 4
; #ALG_SID_SSL3SHAMD5 = 8
; #CALG_HMAC = #ALG_CLASS_HASH|#ALG_TYPE_ANY|#ALG_SID_HMAC
; #CALG_MAC = #ALG_CLASS_HASH|#ALG_TYPE_ANY|#ALG_SID_MAC
; #CALG_MD2 = #ALG_CLASS_HASH|#ALG_TYPE_ANY|#ALG_SID_MD2
; #CALG_SHA = #ALG_CLASS_HASH|#ALG_TYPE_ANY|#ALG_SID_SHA
; #CALG_SHA1 = #ALG_CLASS_HASH|#ALG_TYPE_ANY|#ALG_SID_SHA1
; #CALG_SSL3_SHAMD5 = #ALG_CLASS_HASH|#ALG_TYPE_ANY|#ALG_SID_SSL3SHAMD5
#CALG_RC4 = #ALG_CLASS_DATA_ENCRYPT|#ALG_TYPE_STREAM|#ALG_SID_RC4
#CALG_RC5 = #ALG_CLASS_DATA_ENCRYPT|#ALG_TYPE_BLOCK|#ALG_SID_RC5
#CRYPT_CREATE_SALT = 4
#CRYPT_EXPORTABLE = 1
#CRYPT_NEWKEYSET = 8
#MS_DEF_PROV = "Microsoft Base Cryptographic Provider v1.0"

Procedure.s EnCrypt(String$, Key$)
  Protected *Buffer
  *Buffer = AllocateMemory(Len(String$) * 2 + 64)
  DataLength.l = Len(String$)
  PokeS(*Buffer, String$)
  If CryptAcquireContext_(@hProv, #Null, #MS_DEF_PROV, #PROV_RSA_FULL, 0) = 0
    CryptAcquireContext_(@hProv, #Null, #MS_DEF_PROV, #PROV_RSA_FULL, #CRYPT_NEWKEYSET)
  EndIf
  If hProv
    CryptCreateHash_(hProv, #CALG_MD5, 0, 0, @hHash)
    If hHash
      CryptHashData_(hHash, @Key$, Len(Key$), 0)
      CryptDeriveKey_(hProv, #CALG_RC4, hHash, #CRYPT_EXPORTABLE, @hKey)
      If hKey
        If CryptEncrypt_(hKey, 0, #True, #Null, *Buffer, @DataLength, Len(String$) * 2 + 64)
        EndIf
        CryptDestroyKey_(hKey)
      EndIf
      CryptDestroyHash_(hHash)
    EndIf
    CryptReleaseContext_(hProv, 0)
  EndIf
	
	Result.s = Space(DataLength * 2)
  Base64Encoder(*Buffer, DataLength, @Result, DataLength * 2)
  FreeMemory(*Buffer)
  ProcedureReturn Result
EndProcedure

Procedure.s Decrypt(String$, Key$)
 
  Protected *Buffer
 
  *Buffer = AllocateMemory(Len(String$) * 2 + 64)
 	DataLength.l = Base64Decoder(@String$, Len(String$), *Buffer, Len(String$) * 2 + 64)
  
  If CryptAcquireContext_(@hProv, #Null, #MS_DEF_PROV, #PROV_RSA_FULL, 0) = 0
    CryptAcquireContext_(@hProv, #Null, #MS_DEF_PROV, #PROV_RSA_FULL, #CRYPT_NEWKEYSET)
  EndIf
  If hProv
    CryptCreateHash_(hProv, #CALG_MD5, 0, 0, @hHash)
    If hHash
      CryptHashData_(hHash, @Key$, Len(Key$), 0)
      CryptDeriveKey_(hProv, #CALG_RC4, hHash, #CRYPT_EXPORTABLE, @hKey)
      If hKey
        If CryptDecrypt_(hKey, 0, #True, 0, *Buffer, @DataLength)
          Result.s = PeekS(*Buffer)
        EndIf
        CryptDestroyKey_(hKey)
      EndIf
      CryptDestroyHash_(hHash)
    EndIf
    CryptReleaseContext_(hProv, 0)
  EndIf
  FreeMemory(*Buffer)
  ProcedureReturn Result
EndProcedure

test$ = "Now we will check if this all works as expected..."
Debug Len(test$)
a$ = EnCrypt(test$, "081652")
Debug a$
Debug Len(a$)
Debug DeCrypt(a$, "081652")