digitalwise
asked on
ColdFusion/Java Encryption.
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")>
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!
ASKER
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));
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 ) >
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 ) >
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?
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")>
ASKER
For Rodrigo - I get the error Unable to find a constructor for class javax.crypto.spec.IvParame terSpec 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.SecretK eySpec 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...
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.SecretK
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.SecretK
You'd only use getBytes() if you were passing in a string
@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 ) >
encrypt(URLString, keySpec, "DESede/CBC/PKCS5Padding",
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 ) >
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:
To be written as:
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.
<cfset myString = "Hello World">
<cfoutput>The length of #myString# is #Len(myString)#</cfoutput>
To be written as:
<cfset myString = "Hello World">
<cfoutput>The length of #myString# is #myString.length#</cfoutput>
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.
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 = "TheEncryptedKeyfromtheVen dor">
<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.
<CFSET MyKeyString = "TheEncryptedKeyfromtheVen
<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.
ASKER
So if I change the code to this:
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!
<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
) />
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!
ASKER CERTIFIED SOLUTION
membership
This solution is only available to members.
To access this solution, you must be a member of Experts Exchange.
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
ASKER
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
it looks like I should be decoding BOTH of them, then creating a new key. Am I misreading this?
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));
it looks like I should be decoding BOTH of them, then creating a new key. Am I misreading this?
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...
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 = jCBXQLnsq8Ljfz4cWLXBjyMv8X oHl/1P
IV = AAAAAAAAAAA=
text = something
encrypted = fFZi2Pa0LYgpuligcw/KFw==
ColdFusion:
ColdFusion:
encrypted = fFZi2Pa0LYgpuligcw/KFw==
Java: (using dummy key)
KEY = jCBXQLnsq8Ljfz4cWLXBjyMv8X
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>
Javaimport 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();
}
}
}
ASKER
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.
You're welcome :)