CF 8 Decrypt function returning value enclosed with <pre> </pre> - encryption

I am using the following code to encrypt user password.
<cfset "EncryptedNewPass" = Encrypt(#HTMLCodeFormat(NewPass)#, Request.PasswordKey)>
Then compare it to the value stored in the database and it works fine. However, if i get the value from the database and use decrypt as follows
<cfset DecryptedPass = Decrypt(#getOrigPassFP.pass#, Request.PasswordKey)>
and do cfout, the value is wrapped with <pre> </pre> tag. The issue that I am having is since we also use the password to open a PDF document, it is throwing an error. I tried using replace to strip the tag but when it is displayed or passed to a variable, it still contains the <pre> tag.

You are putting the <pre> tags in there yourself!! What do you think HTMLCodeFormat() does??!

From the comments: I understand what you are saying but my issue is how do I remove the pre tags once it is decrypted before encrypting it back?
The following code is one example of how to remove the <PRE> and </PRE> tags from your decrypted values. (I am outputting all of the values so you can follow along when you execute this code.)
<cfset NewPass = "this_is_the_password">
<cfset PasswordKey = "this_is_the_really_weak_key">
<cfset EncryptedNewPass = Encrypt(HTMLCodeFormat(NewPass), PasswordKey)>
<cfset DecryptedPass = Decrypt(EncryptedNewPass, PasswordKey)>
<cfoutput>
<p>NewPass = #NewPass#</p>
<p>PasswordKey = #PasswordKey#</p>
<p>EncryptedNewPass = #EncryptedNewPass#</p>
<p>DecryptedPass = #HTMLEditFormat(DecryptedPass)#</p>
<cfif Left(DecryptedPass,5) EQ "<PRE>">
<cfset DecryptedPass = Right(DecryptedPass,(Len(DecryptedPass)-5))>
<p>Found and removed <PRE> tag = #HTMLEditFormat(DecryptedPass)#</p>
</cfif>
<cfif Right(DecryptedPass,6) EQ "</PRE>">
<cfset DecryptedPass = Left(DecryptedPass,(Len(DecryptedPass)-6))>
<p>Found and removed </PRE> tag = #HTMLEditFormat(DecryptedPass)#</p>
</cfif>
</cfoutput>
Looking at that code, this block will remove the <PRE> from the beginning of the string:
<cfif Left(DecryptedPass,5) EQ "<PRE>">
<cfset DecryptedPass = Right(DecryptedPass,(Len(DecryptedPass)-5))>
<p>Found and removed <PRE> tag = #HTMLEditFormat(DecryptedPass)#</p>
</cfif>
And this block will remove the </PRE> from the end of the string:
<cfif Right(DecryptedPass,6) EQ "</PRE>">
<cfset DecryptedPass = Left(DecryptedPass,(Len(DecryptedPass)-6))>
<p>Found and removed </PRE> tag = #HTMLEditFormat(DecryptedPass)#</p>
</cfif>
I only used HTMLEditFormat() to output the values for you to see in the browser. Do NOT use these functions when encrypting or decrypting your values.
You also need to be aware that the HTMLCodeFormat() function does more than just wrap the given string in <PRE> tags. That function also changes the characters: <, >, &, and " to their HTML character entity equivalent. If any of your users used those characters in their password then your decryption of them will fail (will not be equal to their actual password).
This function converts the following characters to HTML character entities:
Text character Encoding
< <
> >
& &
" "

Related

ColdFusion decrypt encodings are not the same

I am trying to decrypt a string using ColdFusion's Decrypt() function, but am getting an "encodings are not the same..." error.
These are the steps I was instructed to take from the string's source:
Create an MD5 hash of the shared key
Using TripeDES with the cipher of ECB, decrypt the encrypted string with the MD5 hash of the shared key
Code:
<cfset qKey = hash('shared_key','MD5') />
<cfset dc = Decrypt('string_to_decrypt', qkey, 'DESEDE/ECB/PKCS5Padding', 'Base64') />
<cfdump var="#dc#">
Error:
An error occurred while trying to encrypt or decrypt your input string: The input and output encodings are not same.
Update:
Also tried the following, but it throws the same error:
<cfset finalText = "WVJrOdkntkQ%3d">
<cfset theKey = "S3C016" />
<cfset theKeyInBase64 = toBase64(theKey)>
<cfset hashedKey = hash( theKeyInBase64, "md5" ) />
<cfset padBytes = left( hashedKey, 16 ) />
<cfset keyBytes = binaryDecode( hashedKey & padBytes , "hex" ) />
<cfset finalKey = binaryEncode( keyBytes, "base64" ) />
<cfset decrypted = decrypt( finalText, finalKey, "DESede/ECB/PKCS5Padding", "base64" ) />
Decrypted String: <cfdump var="#decrypted#">
These are the steps I was instructed to take
Honestly, I always find it a little disturbing when I come across a recommendation for using TripleDES these days. As Zach pointed out it is no longer considered secure and was replaced by stronger algorithms like AES. Also, you can clearly see from wiki article images an illustration of why ECB mode is discouraged: "it does not hide data patterns well". (Side note, in the area of hashing MD5 was also surpassed by stronger algorithms a while ago). Bottom line, I strongly suggest discussing the chosen algorithms with whomever gave you the instructions, because they are not very secure.
However, to answer your question, there are a couple of issues:
Your code says the encrypted value is base64 encoded. However, it includes a percent % symbol which is not a valid character for base64. I am guessing that value was url encoded and must be decoded first:
<cfset encryptedText = urlDecode("WVJrOdkntkQ%3d")>
Hash() returns a hexadecimal string. The encrypt() function requires that keys be base64 encoded.
An MD5 hash returns sixteen (16) bytes. While that is a valid key size for TripleDES, the CF/Java implementation only supports 24 byte keys. You need to pad the key with the first eight (8) bytes to make it the proper size.
<cfset originalKey = hash("S3C016","MD5")>
<cfset padChars = left(originalKey, 16)>
<cfset keyBytes = binaryDecode(originalKey & padChars, "hex")>
<cfset newKeyString = binaryEncode(keyBytes, "base64")>
Making the changes above will resolve the error and allow you to decrypt the string successfully. The result should be "20".
<cfset dc = Decrypt(encryptedText, newKeyString, "DESEDE/ECB/PKCS5Padding", "Base64") />
<cfdump var="#dc#">
3DES uses a 24-byte key, MD5 produces a 16-bytes. Many times the first 8-bytes are also used for the final 8-bytes. Some implementations do tis automatically, others zero pad, others---who knows.
Try creating a 24-byte key by duplication the first 8-bytes to the 16-bytes produced by MD5.
Encoding the 16-bytes produced by MD5 really does not make sense in a cryptographic sense eventhough it will prodice 24-bytes.

Struct being populated with duplicate data over and over

Consider the following code:
<cfset result.enrollments = {} />
<cfset result.enrollments = getCorrectionList(SESSION.id,SESSION.term) />
<cfdump var="#result#" /><Cfabort/>
<cffunction name="getCorrectionList">
<cfargument name="id" required="true" type="string" />
<cfargument name="term" required="true" type="numeric" default="#getCurrentSemester().code#" />
<cfset result = {} />
<cfset result.status = 500 />
<cfset result.message = 'Unknown Error' />
<cfhttp url="THERE IS A REAL URL HERE" />
<cfif cfhttp.statusCode EQ '200 OK'>
<cfset courses = deserializeJson(cfhttp.fileContent,false) />
<cfif courses.recordCount EQ 0>
<cfset result.message = 'You are not currently enrolled in any courses for #ARGUMENTS.term#' />
<cfdump var="#result#" />
<cfreturn result />
</cfif>
<!--- MORE STUFF --->
Now when this runs I get an single struct output with two keys message and status. This is from the dump inside of the getCorrectionList function.
I then get a second struct output with the keys enrollments, message, and status. Inside the enrollments key is another struct with enrollments, message, and status. Inside that enrollments key is another struct with the same keys and so on 50 times with the last struct being empty.
Seems like some recursive actions is going on but where/how?
I have no idea what is going on. As you can see from my code there are no loops. I know the URL resolves correctly and it returns a query and has a recordcount. I can see the data dump at the right spots. But how can the #result# in the function show a single struct but the #result# outside the function show a 50 deep struct repeating itself. It doesn't make any sense.
I dunno why neither Leigh or Scott actually made their comments answers, but they're both right.
You have this reference to result outside your function:
<cfset result.enrollments = getCorrectionList(SESSION.id,SESSION.term) />
And at the end of your function you do this:
<cfreturn result />
Which in effect means you're doing this:
<cfset result.enrollments = result />
Meaning result.enrollments is a reference back to its own parent result.
And <cfdump> is duly displaying the circular reference.
As they both said, you need to localise your variables inside a function, either by varing them:
<cfset var result = {} />
Or explicitly putting them in the local scope:
<cfset local.result = {} />
That will make the function's result be a discrete variable, not simply another reference to the calling code' result variable.
You should always localise your function variables, unless you specifically mean to be referencing a calling-code variable, in which event it makes you code clearer if you explicitly scope it to make it obvious what you mean, eg:
<cfset variables.result = {} />
But this is not what you are meaning to do here. Localise your function's variables.

SagePay V3 Form Integration using CFML - AES Encryption

I've got through the entire FORM integration successfully except for the encryption.
Version 3 requires AES encryption and I don't understand how to implement this stage.
Previously, the CFML script constructed the crypt field at the end using this:
//** call to include file to encrypt
crypt = base64Encode (SimpleXor(stuff,EncryptionPassword));
This called the functions file which did the actual work.
The help I need is in creating a new script in the function.cfm file and also what the call should be.
Can anyone help, please?
Cheers
Here's a snippet of the code:
<cfscript>
ThisVendorTxCode = "#sfo_id#";
ThisVendorName = "pivotell";
stuff = "VendorTxCode=" & ThisVendorTxCode & "&";
stuff = stuff & "VendorName=" & ThisVendorName & "&";
</cfscript>
<cfset encryptionKey = generateSecretKey( "AES" ) />
//** call to include file to encrypt
<cfset crypt = encrypt(stuff,encryptionKey,"AES","hex") />
<cfoutput>
<form action="https://test.sagepay.com/gateway/service/vspform-register.vsp" method="post" id="form1" name="form1">
<input type="hidden" name="VPSProtocol" value="3.00">
<input type="hidden" name="TxType" value="PAYMENT">
<input type="hidden" name="Crypt" value="#crypt#">
</form>
</cfoutput>
I've truncated the full thing for simplicity.
It took me 3 days and 3 nights to figure this out.
Please read page 37 of the Sage Pay Form Integration and Protocol Guidelines 3.00.
A1.1 The Crypt Field
The Crypt field should contain all the other transaction information in plain text as Name=Value fields separated by ‘&’
characters. Ensure that all mandatory fields are present and that
there are no spaces after the ‘&’ character.
This string should then be encrypted using AES (block size 128-bit) in CBC mode with PKCS#5 padding using the provided password as both
the key and initialisation vector and encode the result in hex
(making sure the letters are in upper case).
Prepend the ‘#’ sign to the beginning of the encoded result.
Together with the last answer on this post. It should sort it.
... Since your password string is not base64 encoded, the resulting key
length is too small, ie (12) instead of (16) bytes. ...The solution is
to base64 encode it first... Also, the iv parameter should be binary
<cfset keyIVBytes = charsetDecode(yourKeyString, "utf-8")>
<cfset base64Key = binaryEncode(keyIVBytes, "base64")>
<cfset result = encrypt(plainString, base64Key,"AES/CBC/PKCS5Padding", "hex", keyIVBytes)>

Coldfusion Encrypt and Decrypt

I am having issues using the cf9 encrypt and decrypt function. Specifically,the code I use needs to be able to encrypt with AES/ECB/PKCS5Padding 256. I am trying to post data to a third party bank with the following code:
<cfparam name="theKey" default="someStaticKey">
<cfset strName = leaddetail.leadlast&','&leaddetail.leadfirst />
<cfset stFields = {requesttype = "eftaddonetimecompletetransaction"
,clientid = "XXXXXX"
,urltoredirect = "#RedirectURl#"
,customerid = "#leaddetail.leadid#"
,isdebitcardonly = "No"
,customername = "#strName#"
,customeraddress1 = "#form.billingaddress#"
,customercity = "#form.billingcity#"
,customerstate = "#form.billingstate#"
,customerzip = "#form.billingzip#"
,cardbillingaddr1 = "#form.billingaddress#"
,cardbillingcity = "#form.billingcity#"
,cardbillingstate = "#form.billingstate#"
,cardbillingzip = "#form.billingzip#"
,accounttype = "CC"
,name_on_card = "#form.leadname#"
,accountnumber = "#form.ccacctnum#"
,expmonth = "#Left(form.ccexpdate,2)#"
,expyear = "#Right(form.ccexpdate,2)#"
,cvvcode = "#form.ccv2#"
,amount = "#NumberFormat(esigninfo.esignpayamt,'9999.99')#"
,startdate = "#DateFormat(Now(),'YYYY-MM-DD')#"
,transactiontypecode = "WEB"}/>
<cfset theEncryptedStr = ToBase64(encrypt(serializeJson(stFields),theKey))>
<!--- shake hands and login to api --->
<cfhttp url="https://www.somebank.com/cgi-bin/wsnvptest.vps" method="post" charset="ISO-8859-1" throwonerror="yes" result="httpResponse">
<!--- login Variables --->
<cfhttpparam type="Header" name="User-Agent" value="Mozilla/5.0 (Macintosh; U; Intel Mac OS X 10_5_8; en-US) AppleWebKit/534.7 (KHTML, like Gecko) Chrome/7.0.517.41">
<cfhttpparam type="header" name="Content-Type" value="application/x-www-form-urlencoded" >
<cfhttpparam type="header" name="Accept" value="application/json" >
<!--- Login Credentials --->
<cfhttpparam type="formfield"name="nvpvar"value="''"/>
<cfhttpparam type="formfield"name="requesttype"value="login"/>
<cfhttpparam type="formfield"name="userid"value="Dauserid"/>
<cfhttpparam type="formfield"name="password"value="password"/>
<cfhttpparam type="formfield"name="requestid"value="#uniquerequestid#"/>
<cfhttpparam type="formfield"name="PostData" value="#theEncryptedStr#"/>
<cfif isDefined('CheckSession.sessionID') AND CheckSession.sessionID NEQ ''>
<cfhttpparam type="formfield"name="sessionid" value="#checkSession.sessionID#"/>
</cfif>
</cfhttp>
for some reason I can encrypt but cant decrypt the response. Is there anyone out there that has more experience with this that can put on a path to success?
(Update from rejected edit)
I am using the following script to decrypt my cfhttp response string:
<cfset content = httpResponse.filecontent>
<cfset authdata = structNew()>
<cfloop index="line" list="#content#">
<cfset dtype = listFirst(line, "=")/>
<cfset EncodedString = listRest(line, "=")/>
<cfset authdata[dtype] = EncodedString />
</cfloop>
<cfscript>
keyInBase64 = "Some64baseKey;
// Now decrypt the base64 encoded encryption string
decryptedValue = decrypt(EncodedString , keyInBase64 , "AES/ECB/PKCS5Padding" , "base64" );
WriteDump(variables);
</cfscript>
The string I am trying to decrypt looks similar to the following:
nvpvar=fKsJGJ-Fe-2zoqUROAeE8N8a87USuQpE7dVd5IvRa67cHsVkPwx9taKLuXKwsCKFC_-NW3j6IOQAvCnRNc4cRNfq2fqyRHBUrFbFDCCmwpk=
encrypt(serializeJson(stFields),theKey)
First, that is NOT AES encryption. You omitted the algorithm parameter, so encrypt() defaults to the legacy CFMX_COMPAT algorithm (weakest). CFMX_COMPAT it is not a "real" encryption algorithm at all, so do not use it - for anything!
Second, with AES you are limited to 128 bit keys out of the box. In order to use 256 bit keys you must first install the (JCE) Unlimited
Strength Jurisdiction Policy Files.
Copy them into your /lib/security/ directory. Then restart the CF server to apply the changes.
Note: If you have multiple JVM's installed,
be sure to update the correct one. ie The one listed in
the CF Administrator).
After restarting, you will be able to encrypt with a 256 bit key. With ECB mode, the syntax is: encrypt(string, key, algorithm, encoding)
string - plain text to encrypt
key - a 256 bit key, must be base64 encoded
algorithm - single string representing the desired: algorithm, mode and padding scheme. Note: "AES" is actually a shorthand for the longer "AES/ECB/PKCS5Padding"
encoding - "The binary encoding in which to represent the data as a string"
Putting it all together:
<cfscript>
// some sample data to encrypt
stFields = { clientid = "AA-BB-CC-123"
, customername = "ABC Incorporated"
};
// some 256 bit key, must be base64 encoded
// hard coded for demo purposes only
keyInBase64 = "9NJU2L3FZ8Rr0WKZUFC3lyE/yRrQ7sIZmQRk3kx9MLE=";
// AES encrypt the value, and base64 encode the result
encryptedValue = encrypt( serializeJson(stFields)
, keyInBase64
, "AES/ECB/PKCS5Padding"
, "base64"
);
// Now decrypt the base64 encoded encryption string
decryptedValue = decrypt( encryptedValue
, keyInBase64
, "AES/ECB/PKCS5Padding"
, "base64"
);
// display results
WriteDump( variables );
</cfscript>
Update based on comments:
I notice you are manipulating the raw http response string. Without seeing the API, I would guess that either a) You need to decrypt the whole string first, then extract the parts -OR- b) the code is not extracting the values correctly and that is why you cannot decrypt it. The cfloop treats the response as a comma separated list. Based on the variable names, I am guessing it is actually separated by new lines instead, ie:
param1=xxxxx -- new line
param2=yyyyy -- new line
param3=zzzzz
Those are just guesses though. Check your API. What is the actual format of the returned response? Also, can you please edit your question to include the full error message?

Error with Decrypt for "Could not perform unpadding: invalid pad byte.."

Using CF8 and MySQL 5.1, I am trying to encrypt() a password upon creation and then decrypt() at login. I can get the decrypt() to work fine on a test page but when I put it in a cfincluded page with cflogin I get the error "An error occurred while trying to encrypt or decrypt your input string: com.rsa.jsafe.crypto.dr: Could not perform unpadding: invalid pad byte.. ". It is the same code and DB from my test page to my app.
application.cfc:
<cfif NOT IsDefined("Request.PasswordKey")>
<cfset request.PasswordKey = generateSecretKey("AES")>
<cfset request.algorithm = "AES">
<cfset request.encoding = "hex">
</cfif>
test page which works fine:
FORM DATA: <br/>
form password:<cfoutput>#form.passwd#</cfoutput><br/>
<cfset encrypted = Encrypt(form.passwd,Request.PasswordKey,Request.algorithm,Request.encoding)>
Encrypted: <cfoutput>#encrypted#</cfoutput><br/>
Decrypted: <cfoutput>#Decrypt(variables.encrypted,Request.PasswordKey,Request.algorithm,Request.encoding)#</cfoutput><br/>
<br/>
QUERY DATA<br/>
<cfinvoke component="components.userQ" method="login" returnvariable="qLogin">
<cfinvokeargument name="formData" value="#form#">
</cfinvoke>
<cfoutput>qLogin password: #qlogin.encPasswd#</cfoutput><br/>
<cfoutput>Decrypted encPasswd from qLogin: #Decrypt(qlogin.encPasswd,Request.PasswordKey,Request.algorithm,Request.encoding)#</cfoutput>
Decrypt() in app page that is erroring:
<cfset unEnPasswd = #Decrypt(qlogin.encPasswd,Request.PasswordKey,Request.algorithm,Request.encoding)#>
I can get the default CFMX_COMPAT encrypt() and decrypt() to work fine in my app with the same code, just changing the key, algorithm, and encoding variables.
BTW, I am also storing the encrypted strings as varchar() in the DB so it doesn't mess up the padding (so I read). I tried BLOB but get a bytearray error.
Any help or thoughts are greatly appreciated.
You're creating a new secret key on every request,
Really your code should be more like:
<cffunction name="onApplicationStart" returnType="boolean" output="false">
<cfset application.PasswordKey = generateSecretKey("AES")>
</cffunction>
<cffunction name="onRequestStart" returnType="boolean" output="false">
<cfset request.PasswordKey = application.PasswordKey />
<cfset request.algorithm = "AES" />
<cfset request.encoding = "hex" />
</cffunction>
Though really you want to have the password key hardcoded in a config file otherwise if you restart your server you won't be able to access any of your passwords ever again...
Disable jsafe. Add -Dcoldfusion.disablejsafe=true to your jvm config.

Resources