Coldfusion Encryption/Decryption issue - encryption

I recently did a website for my company using ColdFusion 9. The issue I am having is with the ColdFusion encryption/decryption function. On certain strings that I decrypt I get these weird special characters that show up.
Example:
MK/_0 <---Encrypted String Outputted
�#5&z <---Decrypted String Outputted
I'm not sure why this is happening (and only on certain strings that get decrypted).
Here is the code:
<cfset ccNum = decrypt(getCCInfo.CUST_CARDNUMBER,myKey)>

Ok, well first, I have to point out that by not specifying an encryption algorithm you are using very POOR encryption. So you'll need to fix that. Second, you should probably be using some encoding to make your crypto storage more reliable.
So try this code.
<cfset key = generateSecretKey("AES") />
<!--- Set the ciphertext to a variable. This is the string you will store for later deciphering --->
<cfset cipherText = encrypt(plaintext, key, "AES/CBC/PKCS5Padding", "HEX") />
<cfoutput>#cipherText#</cfoutput>
<!--- Then when you decrypt --->
<cfset decipherText = decrypt(cipherText, key, "AES/CBC/PKCS5Padding", "HEX") />
<cfoutput>#decipherText#</cfoutput>
The above code will use a strong crypto algorithm and will put the ciphertext into a much easier to store format than the gibberish you showed as an example above. That way when you store it, it will be more reliable when you retrieve it again.
Here is an example of what the string will look like:
A51BBB284D6DCCDC17D26FB481584236087C3AB272918E17963BAF749438C06A484922820EDCCD25150732CC5CF8A096

Related

How to use Encryption in SAP

SAP servers are capable of encrypting and hashing data. But there doesn't appear to be a suitable API to call. SAP Note 1456433 talks about the class CL_SEC_SXML_XENCRYPTION. The signature of basic encryption is clearly geared towards SSF and unsuitable to basic private key encryption/decryption. I don't want/need envelopes and user certificates. Just private keys.
I found an AES library on GitHub AES library in ABAP and tweaked that to suit us. But it is very slow. I would like to use the encryption libraries SAP has. Clearly, the libraries are there but find a suitably exposed API seems the issue.
Does anybody know how to use basic encryption in SAP?
In SAP ABAP stack, using ABAP.
Eg (a call to use AES-CBC 128, with PKCS7 padding
where only a private key and data to encrypt is required. As example:
public static method encrypt_xstring
importing i_key type xstring
i_data type xstring
i_initialization_vector type xstring optional
i_padding_standard type char10 optional
i_encryption_mode type char10 optional
exporting e_data type xstring
Use case is encrypting data on clients with a private key and sending the data to SAP system. The source supports private keys and libraries like AES-CBC.
And we have encrypted data interchange working.
Next step is to use a supported and faster library.
EDIT: In case anyone needs to encryption / decryption properly in abap
And is looking at the answer. Use class CL_SEC_SXML_WRITER.
CL_SEC_SXML_WRITER was exactly what i was looking for
BUT SAP didnt expose it properly. It is only useful for encryption no decryption.
When interacting with external libraries. Where PKCS7 padding is used and SALTs
or Initialization vectors are required.
SAP offer an ENCRYPT_IV but no Decrypt_IV. Why ????
So you cant use the tool and remain compliant. :(
It is not considered safe to use AES-CBC without IV.
Why would SAP do that ?
ENCRYPT_IV instead of ENCRYPT but no DECRYPT_IV
The offer an Add Padding but no remove padding. OK roll your own padding removal, no big deal. Its like the must be another library for the other direction.
So i can use the tool to encrypt but not decrypt.
My main problem was decrypting quickly strings sent from a mobile device.
So still need to use the old ABAP code for that :(
I have similar requirements and I found the cl_sec_sxml_writer class. Please have a look at the following example. Note that the writer requires XSTRING parameters which is why I'm using conversion classes.
REPORT zged_aes.
DATA lv_message_string TYPE string.
" create message
DATA(lr_conv_sec) = cl_abap_conv_out_ce=>create( ).
lr_conv_sec->write( data = 'This is my secret' ).
" create key
DATA(lr_conv_key) = cl_abap_conv_out_ce=>create( ).
lr_conv_key->write( data = 'MySymmetricKey' ).
" encrypt using AES256
cl_sec_sxml_writer=>encrypt(
EXPORTING
plaintext = lr_conv_sec->get_buffer( )
key = lr_conv_key->get_buffer( )
algorithm = cl_sec_sxml_writer=>co_aes256_algorithm_pem
IMPORTING
ciphertext = DATA(lv_message) ).
" decrypt message
cl_sec_sxml_writer=>decrypt(
EXPORTING
ciphertext = lv_message
key = lr_conv_key->get_buffer( )
algorithm = cl_sec_sxml_writer=>co_aes256_algorithm_pem
IMPORTING
plaintext = DATA(lv_message_decrypted) ).
" convert xstring to string for output
cl_abap_conv_in_ce=>create( input = lv_message_decrypted)->read( IMPORTING data = lv_message_string ).
" output secret message
WRITE lv_message_string.
I tested it on a NetWeaver 7.50 SP 6 system.
I got the ENCRYPT_IV method to work alongside method DECRYPT of class CL_SEC_SXML_WRITER.
The caveat here is that I didn't generate the Symmetric Key and IV by making use of Class cl_abap_conv_out_ce.
I already had my keys and IV from a Java implementation test.
The only thing I needed was to create the Key and IV as an XSTRING and initializing them with the Hex format of my Java implementation (they were in Byte format.
Because of this, I first converted them to Hex and passed those values to the ABAP Xstring types).

How to specify in CryptoJS.AES.encrypt that you want no salt to be used? [duplicate]

We've got a Silent Login service written in Coldfusion9 that accepts encrypted strings from external systems and then decrypts based on an agreed Algorithm/Encoding setup. This has worked without issue for years now from systems running ASP/JAVA/PHP, but we now have a client who has no choice but to use CryptoJS to perform the encryption and for the life of me I cannot work out why this won't decrypt in Coldfusion.
My knowledge of encryption isn't brilliant but the thing I am noticing is the CryptoJS encrypted ciphertext for the exact same string/key differs every time i perform the encryption whereas in Coldfusion/Java i can always expect the exact same encrypted string. I'm not sure if this is encoding related or not but i've never run into this issue accepting encrypted strings from any other system before, so I am hoping it's the way I am encrypting in CryptoJS that is incorrect.
<cfoutput>
<!--- Set String and Key --->
<cfset theKey = toBase64("1234567812345678")>
<cfset string = "max.brenner#google.com.au">
<!--- CryptoJS AES Libraries --->
<script src="http://crypto-js.googlecode.com/svn/tags/3.1.2/build/rollups/aes.js"></script>
<script src="http://crypto-js.googlecode.com/svn/tags/3.1.2/build/components/enc-base64-min.js"></script>
<script>
// Encrypt String using CryptoJS AES
var encrypted = CryptoJS.AES.encrypt("#string#", "#theKey#");
console.log(encrypted.toString());
// Decrypt String using CryptoJS AES
var decrypted = CryptoJS.AES.decrypt(encrypted, "#theKey#");
console.log(decrypted.toString(CryptoJS.enc.Utf8));
</script>
<!--- Coldfusion Decrypt String / FAILS --->
Decrypted: #decrypt(encryptedEmail, "#theKey#", "AES", "BASE64")#
</cfoutput>
There seem to be two issues:
CryptoJS is not using your variable as the key. As #Miguel-F mentioned, when you pass in a string, "it's treated as a passphrase and used to derive [the] actual key and IV". Both are randomly generated, which is why your encrypted result keeps changing. But more importantly, this means that CryptoJS is using a completely different key than the one in your CF code and that is why decrypt() fails. (At least it is part of the reason ...)
The second problem is that in addition to the algorithm "AES", there are two other encryption settings which must match: mode and padding scheme. While CryptoJS and ColdFusion use the same defaults for padding scheme, the "modes" are different:
ColdFusion uses "ECB". "AES" is actually short for "AES/ECB/PKCS5Padding"
CryptoJS uses "CBC", which requires an additional iv (initialization vector) value.
You need to ensure all three settings are the same on both sides. Try using CBC mode in CF, since it is more secure than ECB anyway. Note: It requires adding an IV value.
CF Code:
<!--- this is the base64 encrypted value from CryptoJS --->
<cfset encrypted = "J2f66oiDpZkFlQu26BDKL6ZwgNwN7T3ixst4JtMyNIY=">
<cfset rawString = "max.brenner#google.com.au">
<cfset base64Key = "MTIzNDU2NzgxMjM0NTY3OA==">
<cfset base64IV = "EBESExQVFhcYGRobHB0eHw==">
<cfset ivBytes = binaryDecode(base64IV, "base64")>
<cfoutput>
#decrypt(encrypted, base64Key, "AES/CBC/PKCS5Padding", "base64", ivBytes)#
</cfoutput>
CryptoJS: (Adjusted Original Example)
<script src="http://crypto-js.googlecode.com/svn/tags/3.1.2/build/rollups/aes.js"></script>
<script src="http://crypto-js.googlecode.com/svn/tags/3.1.2/build/components/enc-base64-min.js"></script>
<script>
var text = "#rawString#";
var key = CryptoJS.enc.Base64.parse("#base64Key#");
var iv = CryptoJS.enc.Base64.parse("#base64IV#");
var encrypted = CryptoJS.AES.encrypt(text, key, {iv: iv});
console.log(encrypted.toString());
var decrypted = CryptoJS.AES.decrypt(encrypted, key, {iv: iv});
console.log(decrypted.toString(CryptoJS.enc.Utf8));
</script>
Edit:
All that said, what do you mean by the client "has no choice but to use CryptoJS to perform the encryption"? Why cannot they use server side encryption? I am not an encryption expert, but doing encryption in javascript, and exposing the key on the client, does not sound wildly secure to begin with ...

Encrypt for SagePay forms using ColdFusion

I am trying to follow a specification for an encrypted field in SagePay 3.00 using ColdFusion 10.
The requirement is to encrypt the string as 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.
It's the "using the provided password" that is causing the problem.
At the moment I have
myStr = 'assortednamevaluepairsetc';
providedPassword = 'abcdefghijklmnop';
myCrypt = Encrypt(myStr,providedPassword,'AES/CBC/PKCS5Padding','HEX',providedPassword,1);
but that won't work because the value I have been given by SagePay causes an error - "key specified is not a valid key for this encryption: Invalid AES key length" as its only 16 characters long
According to the CF docs you need to use generateSecretKey to guarantee the key length for AES, so I've tried this but although it gives a result, it's not the right result in terms of the encryption
myStr = 'assortednamevaluepairsetc';
providedPassword = 'abcdefghijklmnop';
mySecret = GenerateSecretKey('AES');
myCrypt = Encrypt(myStr,mySecret,'AES/CBC/PKCS5Padding','HEX',providedPassword,1);
Any help on this gratefully received.
use generateSecretKey to
guarantee the key length for AES
That function is only used when you need to generate a completely new encryption key. You already have one. The primary purpose of generateSecretKey is to ensure you generate a strong encryption key, that is sufficiently random.
won't work because the value I have been given by SagePay causes an
error - "key specified is not a valid key for this encryption: Invalid
AES key length" as its only 16 characters long
A key length of 16 bytes (ie 128 bits) is acceptable for AES. The problem is encrypt() expects the "key" to be a base64 encoded string, which is about thirty-three percent longer than a plain string. When you invoke encrypt(..), CF decodes the provided "key" string into bytes, ie Essentially doing this:
<cfset keyBytes = binaryDecode(yourPassword, "base64")>
<cfoutput>length = #arrayLen(keyBytes)# bytes</cfoutput>
Since your password string is not base64 encoded, the resulting key length is too small, ie (12) instead of (16) bytes. Hence the error message.
The solution is to base64 encode it first. How you do that depends on the encoding of the string. It sounds like it is just a plain text string (hopefully a sufficiently random one...). If so, use charsetDecode to decode the string from the relevant charset (ie utf-8, etcetera), then binaryEncode it to base64:
<cfset keyIVBytes = charsetDecode(yourKeyString, "utf-8")>
<cfset base64Key = binaryEncode(keyIVBytes, "base64")>
Also, the iv parameter should be binary. Since key and iv are one in the same, simply use the byte array from the previous step. Also, drop the iterations parameter, as it does not apply. With those changes it should work as expected:
encrypt(myStr, base64Key,"AES/CBC/PKCS5Padding", "hex", keyIVBytes)
NB: I am not an encryption expert but ... using the key as an iv is NOT a great idea... Might want to check with them to see if there are other options.

'=' symbol at the end shows encryption

I am using 128-bit Rijndael with ECB cipher mode encryption to convert my string. It formats some kind of string with symbols == or = at the end.
In my code I need some preliminary decision was this string encrypted. Can I suggest that if it contains = symbol at the end it is encrypted or possible cases when I will not get = symbol in the end in encrypted string?
First of all, if you are using ECB you are not really "encrypting" since it is a broken cipher mode (see: http://bobnalice.wordpress.com/2009/01/28/friends-don%E2%80%99t-let-friends-use-ecb-mode-encryption/ )
Secondly, the = signs you are seeing are the Base64 padding characters. They are only tagentially related to encryption since Base64 is used for any kind of binary data, not just encrypted date.
Thirdly, you can't even rely on the = sign always being present for Base64 data... it is only added for certain lengths of data (i.e. you could have encrypted, Base64 data that has no = sign)
As #JoelFan points out, those '=' characters are Base64 padding which aren't part of the encryption at all, and aren't always there either if the data comes out to the right number of characters naturally.
You need to add something "out of band". I would replace the potentially-encrypted string with a class which has a string member for the data, and an "encrypted" flag. Once you're there, throw in a getData() method that checks if the instance's data is encrypted, returns the unencrypted value of the data if so (and may cache the plaintext in a private member for later, if it'll be accessed a lot), or just returns the plain text if it's not encrypted.

ColdFusion Encryption from .NET Membership Tables

I have a client who is implementing ZNode which uses the aspnet_Membership table to store a password. This table contains an encrypted password, the password salt and is using the "PasswordFormat" of 2. From what I gather, "2" is a recoverable encrypted password.
The ColdFusion server is BlueDragon 9 Alpha. If you don't know BD, no worries, anything that ColdFusion supports "should" work and I have CF 10 to test it on as well.
If you know a better way to do this I'm all ears. I need to be able to create a user/password and store it in the ASP membership table via ColdFusion. In addition I need to be able to check the user/password for login.
When looking at the Web.config file, the ZnodeMembershipProvider is a "System.Web.Security.SqlMembershipProvider" type.
The machineKey entry looks like this: (took out the two key values)
<machineKey decryption="AES"
decryptionKey="[64 character string]"
validation="SHA1"
validationKey="[128 character string]"/>
If I try something like this:
Encrypt('myPassword', '[64 character string]', 'AES', 'Base64')
It says "Specified key is not a valid size for this algorithm."
I'm not very savy on encryption or .NET. Thanks in advance.
I believe that .NET Password tables use Triple-DES, not AES. Try this instead.
Encrypt('myPassword', '[64 character string]', '3DES', 'Base64')
This answer I wrote up, about DNN (Dot Net Nuke) authentication, should do the trick. (Assuming no differences between ACF and BD). Essentially there are few difference in how .NET and CF handle encryption. The primary differences are:
Encoding:
.NET uses UTF-16LE
CF always uses UTF-8. In ACF, this means you must use encryptBinary instead of encrypt. (I am not sure about OBD).
Key Format:
.NET uses hexadecimal
CF typically uses base64, so you may need to convert the keys first.
Encryption Mode:
.NET defaults to CBC mode (requires IV)
CF defaults to ECB (no IV required)
In case the other link dies, here is the full example. While it uses 3DES, the basic concept is the same for AES. Note: In Java, the larger key sizes (ie 192,256) are only available if the Sun Unlimited Strength Jurisdiction Policy Files are installed.
3DES Example:
// sample valus
plainPassword = "password12345";
base64Salt = "x7le6CBSEvsFeqklvLbMUw==";
hexDecryptKey = "303132333435363738393031323334353637383930313233";
// first extract the bytes of the salt and password
saltBytes = binaryDecode(base64Salt, "base64");
passBytes = charsetDecode(plainPassword, "UTF-16LE" );
// next combine the bytes. note, the returned arrays are immutable,
// so we cannot use the standard CF tricks to merge them
// NOTE: If BlueDragon does not include "org.apache.commons...."
// just loop through the arrays and merge them manually
ArrayUtils = createObject("java", "org.apache.commons.lang.ArrayUtils");
dataBytes = ArrayUtils.addAll( saltBytes, passBytes );
// convert DNN hex key to base64 for ColdFusion
base64Key = binaryEncode(binaryDecode( hexDecryptKey, "hex"), "base64");
// create an IV and intialize it with all zeroes
// block size: 16 => AES, 8=> DES or TripleDES
blockSize = 8;
iv = javacast("byte[]", listToArray(repeatString("0,", blocksize)));
// encrypt using CBC mode
bytes = encryptBinary(dataBytes, base64Key, "DESede/CBC/PKCS5Padding", iv);
// result: WBAnoV+7cLVI95LwVQhtysHb5/pjqVG35nP5Zdu7T/Cn94Sd8v1Vk9zpjQSFGSkv
WriteOutput("encrypted password="& binaryEncode( bytes, "base64" ));

Resources