Coldfusion 9 Encryption Key from Java Byte Array - encryption

I am working on a project where we are passing encrypted data between C#, Java and ColdFusion applications. To generate the key and iv for 256 bit AES encryption. I have the following array which I need to convert in ColdFusion 9 to useable key
The Java Code for the Key
new byte[]{
(byte)172, (byte)181, (byte)241, (byte)21, (byte)129,
(byte)236, (byte)96, (byte)46, (byte)92, (byte)211,
(byte)187, (byte)106, (byte)90,(byte)69, (byte)29,
(byte)186, (byte)99, (byte)65, (byte)134, (byte)125,
(byte)218,(byte)117, (byte)9, (byte)223, (byte)13,
(byte)207, (byte)20, (byte)62, (byte)31,(byte)226, (byte)129, (byte)33
}
The ColdFusion Code to encrypt (Can't seem to get this to all show up here):
<cfset awsSecret = "[172,181,241,21,129,236,96,46,92,211,187,106,90,69,29,186,99,65,134,125,218,117,9,223,13,207,20,62,31,226,129,33]"
Using .getBytes() and base64 encoding - I also end up with a key that is 113 bytes. Any help would be fantastic!

One way is to convert the int values to a byte array, then to base64
<cfset ints = [172,181,241,21,129,236,96,46,92,211,187,106,90,69,29,186,99,65,134,125,218,117,9,223,13,207,20,62,31,226,129,33]>
<cfset bytes = []>
<cfloop array="#ints#" index="i">
<cfset arrayAppend(bytes, javacast("int", i).byteValue())>
</cfloop>
<cfset keyAsBase64 = BinaryEncode(javacast("byte[]", bytes), "base64")>

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.

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?

Problem transmiting a RSA public key, javaME , bouncy castle

I'm working on the porting of an instance messaging application from Java to JavaME ,that also implements cryptography. The problem is that I want to send my public key to the server. The desktop client has this code for this job:
byte[] encoded_public_key=publick_key.getEncoded();
And the server has this code to retrieve the key:
EncodedKeySpec publicKeySpec = new X509EncodedKeySpec(encoded_public_key);
KeyFactory keyFactory = KeyFactory.getInstance("RSA");
PublicKey puKey = keyFactory.generatePublic(publicKeySpec);
Now I've looked the API for the getEncoded and it says that it returns the DER-encoded form of the key as a byte array (http://www.docjar.com/docs/api/sun/s...tml#getEncoded)
My implementation for that in JavaME was this:
RSAPublicKeyStructure public_key_JAVAME=new RSAPublicKeyStructure(modulus,exponent);
byte[] DER_encoded_public_key_JAVAME=public_key_JAVAME.getDEREncoded();
//the getEncoded functions returns exact the same byte array.
However when I try to retrieve the JavaME created DER encoded key with the server code ,in other words when I try this:
EncodedKeySpec publicKeySpec = new X509EncodedKeySpec(DER_encoded_public_key_JAVAME);
KeyFactory keyFactory = KeyFactory.getInstance("RSA");
PublicKey puKey = keyFactory.generatePublic(publicKeySpec);
I get
java.security.spec.InvalidKeySpecException: java.security.InvalidKeyException: IOException: algid parse error, not a sequence
at sun.security.rsa.RSAKeyFactory.engineGeneratePublic(RSAKeyFactory.java:188)
at java.security.KeyFactory.generatePublic(KeyFactory.java:304)
Caused by: java.security.InvalidKeyException: IOException: algid parse error, not a sequence
at sun.security.x509.X509Key.decode(X509Key.java:380)
at sun.security.x509.X509Key.decode(X509Key.java:386)
at sun.security.rsa.RSAPublicKeyImpl.<init>(RSAPublicKeyImpl.java:66)
at sun.security.rsa.RSAKeyFactory.generatePublic(RSAKeyFactory.java:281)
at sun.security.rsa.RSAKeyFactory.engineGeneratePublic(RSAKeyFactory.java:184)
Interesting point : The DER encoded key from the normal Java (using the getencoded() function) is a byte array is 162 bytes long while the SAME key DER encoded in JavaME using bouncy castle is 140 bytes long. Shouldn't these 2 DER encoded key be of the same lenght?I mean it's the same key in DER encoded format so they should be the same.
What am I doing wrong?
True I didn't notice that.Problem is do you know how to create a subjectPublickeyInfo object from a PublicKey in bouncyCastle? I've tried:
ByteArrayInputStream bIn = new ByteArrayInputStream(RSApublickey.toString().getbytes());
SubjectPublicKeyInfo info = new SubjectPublicKeyInfo((ASN1Sequence)new ASN1InputStream(bIn).readObject());
But it didn't work. I also tried :
ByteArrayInputStream(RSApublicKeyStructure.getEncoded());
SubjectPublicKeyInfo info = new SubjectPublicKeyInfo((ASN1Sequence)new ASN1InputStream(bIn).readObject());
Actually I did expect that not to work , but well I had to try it . So how can I create a Subjectpublickeyinfo from RSAkeyparameters?( This is one of the points where the obscurity of bouncy's castle API really shines I guess)
Again thank you for your response you've been of great help.You've put me on the right track.
DER-encoding is just a standard for encoding. Saying that a key is DER-encoded is equivalent to saying it is XML-encoded: you need to agree on how it is DER-/XML-encoded to be able to decode it.
In this case your RSAPublicKeyStructure.getEncoded() returns the key as the DER-encoding of an ASN.1 RSAPublicKey:
RSAPublicKey ::= SEQUENCE {
modulus INTEGER, -- n
publicExponent INTEGER -- e
}
The X509EncodedKeySpec on the other hand expects to be handed the DER-encoding of an ASN.1 PublicKeyInfo:
PublicKeyInfo ::= SEQUENCE {
algorithm AlgorithmIdentifier,
PublicKey BIT STRING
}
To create a PublicKeyInfo using BouncyCastle do this (courtesy of GregS):
RSAPublicKeyStructure rsaPublicKey = /* ... */
AlgorithmIdentifier rsaEncryption = new AlgorithmIdentifier(PKCSObjectIdentifiers.rsaEncryption, DERNull.INSTANCE);
SubjectPublicKeyInfo publicKeyInfo = new SubjectPublicKeyInfo(rsaEncryption, rsaPublicKey);
byte[] encodedPublicKeyInfo = publicKeyInfo.getEncoded();

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