Solved

ColdFusion/Java Encryption.

Posted on 2014-04-01
17
852 Views
Last Modified: 2014-04-06
New work that I have never done before and I have wandered around long enough.     I have a key and IVSpec from the vendor that has to be decoded, then create keys, etc to encrypt my string I am sending back to the vendor.    This is what I have now.   I am currently getting a The getBytes method was not found. error message from the <cfset keySpec line...  

<cfset Cipher = createObject("java", "javax.crypto.Cipher")>
<cfset encryptor = Cipher.getInstance("DESede/CBC/PKCS5Padding")>

<CFSET MyKey = "TheEncryptedKeyfromtheVendor">
<CFSET IVSpec = "TheEncryptedIVSpecfromtheVendor">

<CFSET MyKeyProperty = BinaryDecode(MyKey, "Base64")>
<CFSET IVProperty = BinaryDecode(IVSpec, "Base64")>

<cfset keySpec = createObject("java", "javax.crypto.spec.SecretKeySpec").init(MyKeyProperty.getBytes(), "DESede")>
<cfset ivSpec = createObject("java", "javax.crypto.spec.IvParameterSpec").init(IVProperty.getBytes(), "DESede")>

<CFSET URLString = "Mystringstuff">

<cfset temp = encrypt(URLString, keySpec, "DESede/CBC/PKCS5Padding", "Base64", ivSpec)>


<cfset encryptedTextFromJava = encryptor.doFinal(URLString.getBytes())>

<cfset encryptedText = BinaryEncode(encryptedTextFromJava, "Base64")>

Open in new window

0
Comment
Question by:digitalwise
  • 9
  • 5
  • 3
17 Comments
 
LVL 52

Expert Comment

by:_agx_
ID: 39969148
Can you post the java example too? Also, did they give you a sanitized sample you could post publicly?  Obviously don't post anything that uses your real encryption key!
0
 

Author Comment

by:digitalwise
ID: 39969203
This is the sample code they sent:

Cipher cipher = Cipher.getInstance("DESede/CBC/PKCS5Padding");
//decode provided KEY and IV
byte[] key = Base64.decode(KEY); //KEY is Base64 encoded
byte[] iv = Base64.decode(IV); // IV is Base64 encoded
SecretKey secretkey= …; // create secret key using key byte array
IvParameterSpec ivSpec=…; //create initialization vector using the iv byte array
//initialize the cipher for encryption
cipher.init(Cipher.ENCRYPT_MODE, secretKey, ivSpec);
//use base64 encoding for resulting bytes
String dataStream = Base64.encode(cipher.doFinal(sb.toString().getBytes()));
//redirect to gateway (url encode the data stream as it is being attached to the url)
getRespone().redirect(gatewayURL + "?data=" + URLEncoder.encode(dataStream));

Open in new window

0
 
LVL 52

Expert Comment

by:_agx_
ID: 39969317
EDIT:

At first glance I don't see anything obviously wrong w/the java code, assuming the omitted parts are the same.  

Why not just use CF's encrypt() function instead? It's doing the same thing internally.  You've almost got it, but it should be something like this:

     <CFSET IVProperty = BinaryDecode( Base64EncodedIVString, "Base64")>
     <cfset result = encrypt(plainString
           , base64keyString
           , "DESede/CBC/PKCS5Padding"
           , "base64"
           , IVProperty ) >
0
 
LVL 4

Expert Comment

by:Rodrigo Munera
ID: 39969546
It seems to me like you're trying to access data in the binary object created by "BinaryDecode()" function using member functions ".getBytes()" (a functionality that is not available in ColdFusion but will be available in CF11), maybe check to see the contents of MyKeyProperty and address the portion of it you want to access using dot notation? (don't know what structure the binary object returned by BinaryDecode() is made up of).

Perhaps just pass MyKeyProperty to the init function instead of attempting to "getBytes()" from it?

<cfset keySpec = createObject("java", "javax.crypto.spec.SecretKeySpec").init(MyKeyProperty, "DESede")>

Open in new window

0
 

Author Comment

by:digitalwise
ID: 39969670
For Rodrigo - I get the error Unable to find a constructor for class javax.crypto.spec.IvParameterSpec that accepts parameters of type ( [B, java.lang.String ). when I remove Bytes property (it seems to work ok for the Key portion).

For _agx_ - I started there - but I get a An error occurred while trying to encrypt or decrypt your input string: '' Can not decode string "javax.crypto.spec.SecretKeySpec when I use that IVorSalt parameter with a straight decode.   Their sample code looks like I have to create an initialization vector based on the iv byte array...
0
 
LVL 52

Expert Comment

by:_agx_
ID: 39969676
a functionality that is not available in ColdFusion but will be available in CF11

Hm... what functionality is that? Reason for asking is getBytes() is actually a java method, so it works in any java based version of CF.  

But you're correct about using the wrong variable. I didn't look too closely at the java code once I realized it wasn't needed. But FWIW, BinaryDecode already returns a byte array. So no need to call getBytes(). Just pass in the array:

<CFSET keyByteArray = BinaryDecode(MyKey, "Base64")>
<cfset keySpec = createObject("java", "javax.crypto.spec.SecretKeySpec").init(keyByteArray, "DESede")>

You'd only use getBytes() if you were passing in a string
0
 
LVL 52

Expert Comment

by:_agx_
ID: 39969689
@digitalwise - That's because you're mixing java and CF.  It's complaining is that you're passing in java objects and that's not what the CF function is expecting.  

encrypt(URLString, keySpec, "DESede/CBC/PKCS5Padding", "Base64", ivSpec)>

Take a look at my example again.  You don't need any java objects.  Simply decode the base64 IV string into a byte array:

<!--- where Base64EncodedIVString is your iv *string* --->
<CFSET IVProperty = BinaryDecode( Base64EncodedIVString, "Base64")>

... then pass it in along with your base64 encoded key string:

     <cfset result = encrypt(plainString
           , base64keyString
           , "DESede/CBC/PKCS5Padding"
           , "base64"
           , IVProperty ) >
0
 
LVL 4

Expert Comment

by:Rodrigo Munera
ID: 39969697
I was referring to "Member Functions", http://blogs.coldfusion.com/post.cfm/language-enhancements-in-coldfusion-splendor-member-functions which would enable the use of code like:

<cfset myString = "Hello World">
<cfoutput>The length of #myString# is #Len(myString)#</cfoutput>

Open in new window


To be written as:

<cfset myString = "Hello World">
<cfoutput>The length of #myString# is #myString.length#</cfoutput>

Open in new window


But my point was that BinaryDecode() was a ColdFusion function, not a java object, so a member function that would exist in the java object does not apply to an object returned from a ColdFusion function that does not implement that member function or implicit getter.

 Edit:
However, I do agree with _agx_, using encode() decode() should be better and simpler in the long run.
0
Maximize Your Threat Intelligence Reporting

Reporting is one of the most important and least talked about aspects of a world-class threat intelligence program. Here’s how to do it right.

 
LVL 52

Expert Comment

by:_agx_
ID: 39969730
EDIT: Interesting, I'll have to read up on the CF object stuff. (I don't think it impacts regular java objects though).  But I think the problem here was even simpler ;-) He just accidentally swapped the variables and ended up using the wrong method on the wrong object. ie Used this:

     <CFSET MyKeyString = "TheEncryptedKeyfromtheVendor">
     <CFSET MyKeyBytes = BinaryDecode(MyKeyString , "Base64")>

         MyKeyBytes.getBytes()  <=== fails

... instead of

        MyKeyString.getBytes()

SecretKeySpec expects:   (byte[], string).  You can either generate the byte array by using binaryDecode OR using String.getBytes()  not both.  Besides, BinaryDecode already returns binary. So you don't need to call getBytes on something that's already a byte array ;-) Hence why that method doesn't exist on the resulting object.
0
 

Author Comment

by:digitalwise
ID: 39975067
So if I change the code to this:

<CFSET MyKey = "EncryptedKeyfromVEndor">
<CFSET IVSpec = "EncryptedIVSpecfromVendor">

<CFSET MyKeyProperty = BinaryDecode(MyKey, "Base64")>
<CFSET IVProperty = BinaryDecode(IVSpec, "Base64")>

<CFSET URLString = "MyString">

<cfset encoded = encrypt(
URLString,
MyKeyProperty,
"DESede/CBC/PKCS5Padding",
"Base64",
IVProperty
) />

Open in new window


I get the error message:  
ByteArray objects cannot be converted to strings.

It seems that in the pure java sample provided by the vendor that they are decrypted and then created a new key based on the ivspec, etc.   Feel like I am missing something easy!
0
 
LVL 52

Accepted Solution

by:
_agx_ earned 500 total points
ID: 39975360
> <CFSET MyKeyProperty = BinaryDecode(MyKey, "Base64")>

I think you misread my example.  The key value should be a string, not binary.  (Only the IV is binary):

<CFSET URLString = "MyString">
<CFSET KeyAsBase64String = "EncryptedKeyfromVEndor">
<CFSET IVAsBase64String = "EncryptedIVSpecfromVendor">

<!--- convert IV string => binary --->
<CFSET IVAsBinary = BinaryDecode(IVAsBase64String , "Base64")>

<cfset encoded = encrypt(
URLString,
KeyAsBase64String ,  <=== this should be a string
"DESede/CBC/PKCS5Padding",
"Base64",
IVAsBinary
) />
0
 
LVL 4

Expert Comment

by:Rodrigo Munera
ID: 39975371
EDIT: Ignore this post, _agx_ is right, binaryDecode returns a binary object which is what you need for the IV parameter. Use his example in the comment above
0
 

Author Comment

by:digitalwise
ID: 39976332
OK - so I used what you suggested and I get an invalid data stream back when I try to connect which means I am not sending them the right stuff.    If you look at the sample code they sent over

Cipher cipher = Cipher.getInstance("DESede/CBC/PKCS5Padding");
//decode provided KEY and IV
byte[] key = Base64.decode(KEY); //KEY is Base64 encoded
byte[] iv = Base64.decode(IV); // IV is Base64 encoded
SecretKey secretkey= …; // create secret key using key byte array
IvParameterSpec ivSpec=…; //create initialization vector using the iv byte array
//initialize the cipher for encryption
cipher.init(Cipher.ENCRYPT_MODE, secretKey, ivSpec);
//use base64 encoding for resulting bytes
String dataStream = Base64.encode(cipher.doFinal(sb.toString().getBytes()));
//redirect to gateway (url encode the data stream as it is being attached to the url)
getRespone().redirect(gatewayURL + "?data=" + URLEncoder.encode(dataStream));

Open in new window


it looks like I should be decoding BOTH of them, then creating a new key.   Am I misreading this?
0
 
LVL 52

Expert Comment

by:_agx_
ID: 39976509
it looks like I should be decoding BOTH of them, then creating a new key.   Am I misreading this?

No, java and CF just have different ways of doing things. Java requires byte arrays for *both* the key and iv. Whereas CF requires a base64 string for the key and a byte array for the iv. It won't work any other way, Encrypt() doesn't accept a binary key.

I asked this earlier - don't know if you saw it. Did they give you a *sanitized* sample you can share?  Like an API example that uses a dummy key? Encryption is incredibly picky about even the slightest difference. It would help us cut to the chase faster if we could see and test the sample value you're trying to match.

Feels like there's something in the code we're not aware of ... like maybe the final "data" value isn't being URL encoded properly, etc...?  That's just a guess though. The thing is CF is actually doing all the same java stuff above behind the scenes (aside from using charset=UTF8), so there's really no reason you should be getting different results.  I've done it before  w/no problem ie encrypt=>java and decrypt=>CF and vice versa. So there's definitely a piece missing here...
0
 
LVL 52

Expert Comment

by:_agx_
ID: 39977760
Yeah, I think you're definitely doing something different on your end...  I ran both as a sanity check and as expected it produced the same results in java and CF10 (shouldn't make any difference).  

ColdFusion:
encrypted = fFZi2Pa0LYgpuligcw/KFw==

Java: (using dummy key)
KEY = jCBXQLnsq8Ljfz4cWLXBjyMv8XoHl/1P
IV = AAAAAAAAAAA=
text = something
encrypted = fFZi2Pa0LYgpuligcw/KFw==



ColdFusion:
<CFSET URLString = "something">
<CFSET KeyAsBase64String = "jCBXQLnsq8Ljfz4cWLXBjyMv8XoHl/1P">
<CFSET IVAsBase64String = "AAAAAAAAAAA=">

<!--- convert IV string => binary --->
<CFSET IVAsBinary = BinaryDecode(IVAsBase64String , "Base64")>

<cfset encryptedText = encrypt(
			URLString
			, KeyAsBase64String 
			,  "DESede/CBC/PKCS5Padding"
			, "Base64"
			, IVAsBinary
		) /> 

<cfoutput>encrypted  = #encryptedText#</cfoutput>

Open in new window

Java
import javax.crypto.*;
import javax.crypto.spec.*;
import org.apache.commons.codec.binary.Base64;

public class Test {

	public static void main(String[] args) {
		try {
			String text = args[0];
			String KEY = args[1]; //KEY is Base64 encoded
			String IV = args[2]; // IV is Base64 encoded
			
			Cipher cipher = Cipher.getInstance("DESede/CBC/PKCS5Padding");
			Base64 util = new Base64();
			byte[] key = util.decode(KEY); 
			byte[] iv = util.decode(IV); 
			SecretKey secretkey = new SecretKeySpec(key, "DESede"); 
			IvParameterSpec ivSpec = new IvParameterSpec(iv); 
			//initialize the cipher for encryption
			cipher.init(Cipher.ENCRYPT_MODE, secretkey, ivSpec);
			//use base64 encoding for resulting bytes
			String dataStream = util.encodeToString(cipher.doFinal(text.getBytes()));

			System.out.printf("KEY = %s\n", KEY);  	
		    System.out.printf("IV = %s\n", IV);  	
		    System.out.printf("text = %s\n", text);  	
		    System.out.printf("encrypted = %s\n", dataStream);  	

		} catch (Exception e) {
			e.printStackTrace();
		}
	}
}

Open in new window

0
 

Author Closing Comment

by:digitalwise
ID: 39980534
They sent me something to try and it is definitely working properly with your solution.    Now we need to figure out why they are rejecting the string at their end!    Thanks for your help and patience.
0
 
LVL 52

Expert Comment

by:_agx_
ID: 39981268
You're welcome :)
0

Featured Post

6 Surprising Benefits of Threat Intelligence

All sorts of threat intelligence is available on the web. Intelligence you can learn from, and use to anticipate and prepare for future attacks.

Join & Write a Comment

Suggested Solutions

Title # Comments Views Activity
wordcount challenge 11 74
for i loop in grovy 1 32
base64 decode encode 12 96
ArrayIndexOutOfBoundException 9 30
CFGRID Custom Functionality Series -  Part 1 Hi Guys, I was once asked how it is possible to to add a hyperlink in the cfgrid and open the window to show the data. Now this is quite simple, I have to use the EXT JS library for this and I achiev…
Java functions are among the best things for programmers to work with as Java sites can be very easy to read and prepare. Java especially simplifies many processes in the coding industry as it helps integrate many forms of technology and different d…
Viewers learn how to read error messages and identify possible mistakes that could cause hours of frustration. Coding is as much about debugging your code as it is about writing it. Define Error Message: Line Numbers: Type of Error: Break Down…
Viewers will learn about arithmetic and Boolean expressions in Java and the logical operators used to create Boolean expressions. We will cover the symbols used for arithmetic expressions and define each logical operator and how to use them in Boole…

708 members asked questions and received personalized solutions in the past 7 days.

Join the community of 500,000 technology professionals and ask your questions.

Join & Ask a Question

Need Help in Real-Time?

Connect with top rated Experts

13 Experts available now in Live!

Get 1:1 Help Now