I am fond of ColdFusion but not so fond of the most recent releases (since MX). Just did not like where it was going and was not fond of the cost. I like the earlier releases though I thought the move to a true Java core was spot-on. I kept a lookout at the various open source efforts but was not committed.
I had reason to look back into this a couple of months ago (not for this project but close) and came across the Railo project. I am impressed. The quick start version is just fantastic. Download, unzip, and launch. There you go. A local CF development environment at no cost. So far it is really compatible (with the CF versions that I like) and I have not run into any issues to speak of. What is not to love?
Back to my current project.
So, I have to retrieve a public RSA key from a remote web service (any one of many keys will be returned and so when I submit my encrypted data I have to include the ID of the key used), rebuild the public key, use the public key to encrypt sensitive card data and then submit that data to a web service for processing. I had everything done but the encryption part. I searched the web for CF examples/samples of doing RSA and most of the examples I came across assumed you are issuing the key and so show you how to make both the public and private keys and how to encode using the private key. Not a lot on rebuilding a public key. Also, a good bit of "Bouncy Castle" but I wanted to stick with the basic Java (and I had some issues with the BC solution).
To make it function, I wound up using a number of Java classes from within CF. I have stripped out most/all of the error checking and the non-encryption related code. I also slimmed down the comments to fit within the formatting limitations. Even with that, this should still be plenty to get you going and I hope it saves someone the effort I had to go through. If you already have the key parts and just need to build a public key, lines 50 through 83 are the meat and 96 to 99 do the actual data encryption.
Have fun!
1: <!--- create a web service object --->
2: <cfset webService =
3: createobject("webservice", "https://URL/RequestHandler.svc?wsdl")>
4:
5: <!--- build request for public RSA encryption key --->
6: <cfxml variable="xmldoc" caseSensitive="yes">
7: <requestHeader>
8: <stuff />
9: </requestHeader>
10: </cfxml>
11:
12: <!--- make xml document internet safe --->
13: <cfset xmldoc64 = toBase64( #xmldoc# )>
14:
15: <!--- make the request (via web services) --->
16: <cfset resultsRaw =
17: webService.ProcessRequest( "#xmldoc64#" )>
18:
19: <!--- convert/format results into an xml object --->
20: <cfxml variable="resultsXmlDoc" caseSensitive="yes">
21: <cfoutput>#ToString( ToBinary( resultsRaw ) )#</cfoutput>
22: </cfxml>
23:
24: <!--- check the results code to see if it worked --->
25: <cfif resultsXmlDoc.EncryptionKey.ResponseCode.XmlText eq "0">
26: <!--- now convert the encryption key information
27: into an xml object so it is easier to use (the
28: initial XML doc actually contains another XML doc
29: with the key info) --->
30: <cfxml variable="xmlResultsPublicKey" caseSensitive="yes">
31: <cfoutput>#resultsXmlDoc.EncryptionKey.responseMessage.PublicKey.XmlText#</cfoutput>
32: </cfxml>
33:
34: <!--- save the parts of the RSA key data --->
35: <!--- service can return many keys, save ID --->
36: <cfset publicKeyID =
37: resultsXmlDoc.EncryptionKey.responseMessage.ID.XmlText>
38: <!--- main part of the public key (to be) --->
39: <cfset epublicKeyModulus =
40: xmlResultsPublicKey.RSAKeyValue.Modulus.XmlText>
41: <!--- minor part of the public key (to be) --->
42: <cfset epublicKeyExponent =
43: xmlResultsPublicKey.RSAKeyValue.Exponent.XmlText>
44: </cfif>
45:
46:
47: <!--- now that we have all of the parts, we need to
48: make a valid java RSA key object from the parts --->
49:
50: <!--- create java string object for key modulus --->
51: <cfset encodedKeyModulus =
52: createObject( "java", "java.lang.String" ).init( epublicKeyModulus )>
53: <!--- create java string object for key exponent --->
54: <cfset encodedKeyExponent =
55: createObject( "java", "java.lang.String" ).init( epublicKeyExponent )>
56:
57: <!--- need a java BigInt AND decoding on the fly --->
58: <cfset modulusKey =
59: createObject( "java", "java.math.BigInteger" ).init( 1, BinaryDecode( encodedKeyModulus.getBytes( "UTF-8" ), "base64" ) )>
60: <cfset exponentKey =
61: createObject( "java", "java.math.BigInteger" ).init( 1, BinaryDecode( encodedKeyExponent.getBytes( "UTF-8" ), "base64" ) )>
62:
63: <!--- build the KeySpec and load it with key parts --->
64: <cfset javaKeySpec =
65: createObject( "java", "java.security.spec.RSAPublicKeySpec" ).init( modulusKey, exponentKey )>
66:
67: <!--- build RSA key factory for rebuilt public key --->
68: <cfset javaKeyObject =
69: createObject( "java", "java.security.KeyFactory" ).getInstance( "RSA" )>
70:
71: <!--- use the keyspec to initialize the key factory,
72: instantiating a RSA public key from its encoding and
73: recreating a valid public key --->
74: <cfset javaKey = javaKeyObject.generatePublic( javaKeySpec )>
75:
76:
77: <!--- create a new java cipher object and load it with
78: our new (rebuilt) RSA public key --->
79:
80: <!--- mode tells the Cipher we will be encrypting --->
81: <cfset cipher =
82: createObject( "java", "javax.crypto.Cipher" ).getInstance( "RSA" )>
83: <cfset i = cipher.init( cipher.ENCRYPT_MODE, javaKey )>
84:
85: <!--- convert strings into java strings --->
86: <cfset stringCardData =
87: createObject( "java", "java.lang.String" ).init( Session.data.card )>
88: <cfset stringCvvData =
89: createObject( "java", "java.lang.String" ).init( Session.data.cvv )>
90:
91: <!--- ensure strings are the proper character set --->
92: <cfset stringCardDataBytes = stringCardData.getBytes( "UTF8" )>
93: <cfset stringCvvDataBytes = stringCvvData.getBytes( "UTF8" )>
94:
95: <!--- now to perform the encryption on each string --->
96: <cfset encryptedCardData =
97: cipher.doFinal( stringCardDataBytes, 0, len( StringCardData ) )>
98: <cfset encryptedCvvData =
99: cipher.doFinal( stringCvvDataBytes, 0, len( StringCvvData ) )>
100:
101: <!--- base64 them so they are internet safe --->
102: <cfset base64CardData =
103: BinaryEncode( encryptedCardData, "base64" )>
104: <cfset base64CvvData =
105: BinaryEncode( encryptedCvvData, "base64" )>
106:
107: <!--- DONE, we should now have proper, public key
108: encrypted data! Congratulations! --->
I then include the web safe, encrypted strings in my larger XML document, BineryEncode the entire thing and submit.
Good luck and hope this helps!
Update: 5/12/2016
OK, looks like Railo is dead and a replacement is available called Lucee. I am just starting to use it so can not say much about it at this time. If I get a chance [and think about it], I will update with additional information.
No comments:
Post a Comment