I need to decrypt a base64 encoded binary in string format [0-9a-zA-Z+/]. Part of this string consists of the ivSalt, which is the first 16 Bytes, or 128bits. The rest is data; everything was encrypted using AES with 128 key and in 128 CBC blocks and PKCS7Padding.
First the string needs to converted to binary:
<cfset b64 = ToBinary(enc)/>
Then we create a java byte buffer:
<cfset objByteBuffer = CreateObject(
"java",
"java.nio.ByteBuffer") />
Then we initialize a buffer for the salt and a buffer for the data that needs to be decrypted.
<cfset objBufferA = objByteBuffer.Allocate(
JavaCast( "int", 16 )) />
<cfset objBufferB = objByteBuffer.Allocate(
JavaCast( "int", (Len(b64)-16) )) />
Then we fill the buffers with our bytes.
<cfset objBufferA.Put(
b64,
JavaCast( "int", 0 ),
JavaCast( "int", 16 )) />
<cfset objBufferB.Put(
b64,
JavaCast( "int", 16 ),
JavaCast( "int", (Len(b64)-16) )) />
<cfset ivStringBin = objBufferA.Array()/>
<cfset dataStringBin = objBufferB.Array()/>
Because the decrypt function takes a hexadecimal string we need to encode it to hex.
<!--- create byteArray output stream --->
<cfset baos = createObject("java", "java.io.ByteArrayOutputStream")>
<!--- write the byteArray stored in the DB to the output stream --->
<cfset baos.write(ivStringBin)>
<!--- convert binary content to text string --->
<cfset ivString=BinaryEncode(baos.toByteArray(),"hex")/>
<!--- create byteArray output stream --->
<cfset baos2 = createObject("java", "java.io.ByteArrayOutputStream")>
<!--- write the byteArray stored in the DB to the output stream --->
<cfset baos2.write(dataStringBin)>
<!--- convert binary content to text string --->
<cfset dataString=BinaryEncode(baos2.toByteArray(),"hex")/>
Should be:
<cfset dataString=BinaryEncode(baos2.toByteArray(),"base64")/>
Now I'm not sure what to do with the key, but it appears to be required to be in a hexadecimal string format.
<cfset key = BinaryEncode(ToBinary("{16 chars, [0-9a-zA-Z+/]}")/>
Should be:
<cfset key = "{32 chars, [0-9A-Z]}"/>
ColdFusion doesn't itself support PKCS7Padding with CBC, I get an unsupported provider error. So I go and install BouncyCastle and I found a wonderful ColdFusion example of how to decrypt with PKCS5Padding Un-encrypting / re-encrypting a ColdFusion encrypted string in PHP">here. I added it to my code but I need PKCS7Padding so I changed
var zbPadding = CreateObject('java', 'org.bouncycastle.crypto.paddings.ZeroBytePadding').init();
to
var zbPadding = CreateObject('java', 'org.bouncycastle.crypto.paddings.PKCS7Padding').init();
according to the Bouncy Castle docs .
With all this I get the error:
"Key length not 128/160/192/224/256 bits."
I will be trying off and on but I am completely stuck, I can't figure out what I am doing wrong.
Edit 1:
I got it working!
The dataString needed to be in base64 instead of hex.
The key, when converted to hex using coldFusion is 24 [0-9A-F] chars representing 24x4=96 bits which is 12 Bytes. It needs to be 16 Bytes, so somehow the BinaryEncode(ToBinary()) function doesn't do its job. So I used a string to hex tool to convert it and then subsequently got 32 chars.
Edit 2:
Also, to install Bouncy Castle I followed the bouncy castle installe instructions and downloaded jce from the bottom of the java downloads page.
I got it working!
The dataString needed to be in base64 instead of hex.
The key, when converted to hex using coldFusion is 24 [0-9A-F] chars representing 24x4=96 bits which is 12 Bytes. It needs to be 16 Bytes, so somehow the BinaryEncode(ToBinary()) function doesn't do its job. So I used a string to hex tool to convert it and then subsequently got 32 chars.
Edit 2: Also, to install Bouncy Castle I followed the bouncy castle installe instructions and downloaded jce from the bottom of the java downloads page.
Related
In ColdFusion 2016, I am trying to index a large file contains more than 100000 characters.
I getting the below error
org.apache.tika.sax.WriteOutContentHandler$WriteLimitReachedException: Your document contained more than 100000 characters, and so your requested limit has been reached. To receive the full text of the document, increase your limit. (Text up to the limit is however available).
Where do I increase the limit from 100000 to 10000000 ?
<cfindex
action="update"
type="file"
collection="#collection_name#"
recurse="yes"
key="#key#"
urlpath="#urlpath#"
status = "insert"/>
<cfdump var="#insert#">
As anyone had this issue with parsing from html file with setting <cfcase> values that contain spaces in the value? If the value contains spaces ColdFusion isn't able to find the data match in the parsed html file.
For example:
<CFCASE value="LGT RAIN">
<CFSET application.condition="Lt Rain">
</CFCASE>`
The html file has the value "LGT RAIN", but due to the way its being parsed it is not defining the city's condition. Is there a work around like check the condition prior to running your switch statements? Like if "LGT RAIN" is found then set cfset value it to "LGTRAIN".
Here is sample link that I am parsing from.
https://forecast.weather.gov/product.php?site=CAE&issuedby=CAE&product=RWR&format=CI&version=1
<CFSET WeatherData = CFHTTP.FileContent>
Code:
<!--- Check For Condition with Two Word Phrase --------------->
<CFSET condition= #GetToken("#content#",#attributes.citystring# + 1, " ")#>
<br>
<CFSET city = #attributes.citystring#>
<CFIF (condition is "LGT")
or (condition is "HVY")
or (condition is "FRZ")
or (condition is "MIX")
or (condition is "NOT")>
<CFSET condition= (
#GetToken("#content#",#attributes.citystring# + 1, " ")#
& " " &
#GetToken("#content#",#attributes.citystring# + 2, " ")#
)>
<br>
<CFSET Temp = #GetToken("#content#",#attributes.citystring# + 3, " ")#>'
.......
There is a lot of missing context on what you're trying to ultimately do with the weather you get, so I made a couple of assumptions on ways to parse through the weather block.
You'll have to actually parse the page to get that actual block, but once you do, you can use my suggestions below to split those weather elements out. I got rid of the getToken() calls, because I think that may have been more work than you needed, and was essentially led to the problem with spaces. My code is still much wordier than I intended, but I tried to make sure I wasn't using anything above CF8. I think it should all work, but I don't have a CF8 server to test on.
https://trycf.com/gist/1993243eb476a629ec25f8c6e8ddff3c/acf?theme=monokai
I've included some notes inside the code as I went along.
<!--- Create a block of the parsed weather. --->
<cfsavecontent variable="myWX">ANDERSON FAIR 49 16 26 W12G20 30.01F
LIBERTY FAIR 49 14 24 W12G21 29.99F
SPARTANBURG FAIR 45 9 23 NW12G21 30.00F
CLEMSON FAIR 48 10 21 NW13G22 30.02F
GREENVILLE FAIR 48 13 24 VRB3 29.99F
GREENWOOD FAIR 49 13 23 W10G21 30.03F
GREER FAIR 48 9 20 VRB7 29.99F
ROCK HILL FAIR 46 17 31 NW10G22 29.99F
CHESTER FAIR 45 12 26 W12G17 30.01F
LAURENS FAIR 48 16 27 NW12G18 30.01F
</cfsavecontent>
<!--- end of line character (this may change if your feed uses other eol)--->
<cfset crlf = chr(13)&chr(10)>
<!--- Make weather an array of strings for each city. --->
<cfset wxArray = ListToArray(myWX, crlf, false, true)>
My suggestion is to put the different elements in a struct, then work with that. 1 row of city weather = 1 row of Struct.
My code below assumes you are parsing rows of a 65 character fixed-width record from the HTML, which is what it appears to be when you get at the individual rows.
<!--- Loop through the new weather array and make a new array of parsed weather. --->
<!--- Create initial weather array for output. --->
<cfset newWXArray = []>
<cfloop array="#wxArray#" index="wxRow">
<!--- NOTE: Yay go CF8s implicit struct creation! --->
<cfset WXRow = {
city = trim(mid(wxRow,1,15)) , <!--- trim() will remove leading/trailing spaces --->
sky = trim(mid(wxRow,16,10)) ,
tmp = trim(mid(wxRow,26,4)) ,
dp = trim(mid(wxRow,30,4)) ,
rh = trim(mid(wxRow,34,3)) ,
wind = trim(mid(wxRow,37,10)) ,
pres = trim(mid(wxRow,47,6)) ,
rmks = trim(mid(wxRow,53,11))
}>
<!--- ArrayAppend() returns true/false, so this variable isn't used. --->
<cfset throwaway = ArrayAppend(newWXArray, WXRow)>
</cfloop>
<cfdump var="#newWXArray#">
Now you have an array of all the cities, and can pull the individual pieces out for the city you need.
What's the weather like in Liberty? <br>
<cfset whichRow = 0>
<cfloop from="1" to="#arrayLen(newWXArray)#" index="i">
<cfif newWXArray[i].CITY IS "LIBERTY">
<cfset whichRow = i>
</cfif>
</cfloop>
<cfoutput>
City >> #newWXArray[whichRow].CITY# <br>
Sky >> #newWXArray[whichRow].SKY# <br>
Temp >> #newWXArray[whichRow].TMP# <br>
DewPoint >> #newWXArray[whichRow].DP# <br>
Relative Humidity >> #newWXArray[whichRow].RH# <br>
Wind >> #newWXArray[whichRow].WIND# <br>
Pressure >> #newWXArray[whichRow].PRES# <br>
Remarks >> #newWXArray[whichRow].RMKS# <br>
</cfoutput>
Final Note:
I had forgotten how limited CF8 was. There is a lot of stuff you can do to make this MUCH easier if you are able to upgrade to a current version of ACF or Lucee.
Also, as I suggested, changing your link to pull the Text-Only version (https://forecast.weather.gov/product.php?site=CAE&issuedby=CAE&product=RWR&format=txt&version=1&glossary=1) will significantly reduce the amount of text you have to parse through.
My experience with white space in text data messing up conditional logic has been with trailing spaces but the principles are the same. When in doubt look at your data. Here is a simple example:
MyVar = 'Lgt Rain'; // two spaces between the words
if (MyVar == 'Lgt Rain'){ // one space between words
applicable code
}
else {
writeoutput(replace(MyVar, ' ', '-space-', 'all'));
}
We have been running into date formatting issues for over a year now. We have a EU formatting in our database (dd/mm/yyyy) and we also want to output that on our website. Problem is that we're running the dates through the date formatting functions of coldfusion, to be certain we're always outputting our dates the same way (and for other reasons).
That's where it goes wrong. The code below outputs 2 different dates, where we would expect the same date.
<cfoutput>
#LSDateFormat('01/02/2015', 'dd/mm/yyyy', 'nl_BE')# <br />
#LSDateTimeFormat('01/02/2015', 'dd/mm/yyyy HH:nn', 'nl_BE')#
</cfoutput>
// output
// 01/02/2015
// 02/01/2015 00:00
I have tried on trycf.com, using all different available engines.
Please explain to me what I'm doing wrong here. Or tell me this is a bug that no-one ever has mentioned. But I would prefer me being wrong here..
I think you are misunderstanding the 'format' functions. They are designed for presentation. Their purpose is to convert a date object into a string: ie LSDateTimeFormat (date , mask). The 'mask' is used to determine what that output string looks like, not to parse the input. Notice if you pass in a date object, NOT a string, it works exactly as you expected? The result is 01/02/2015 00:00
dateObject = createDate(2015,2,1);
writeDump("dateObject = "& LSDateTimeFormat(dateObject, 'dd/mm/yyyy HH:nn', 'nl_BE'));
Yes, CF allows you to be lazy, and pass in a string instead. However, CF must still convert that string into a date object before it can apply the mask - and you have no control over how CF does that. When you use strings, you are essentially leaving the interpretation of that string entirely up to CF. In this case, CF interprets the ambiguous string "01/02/2015" according to U.S. date rules ie month first. That produces January 2, 2015. Hence why the output of the mask dd/mm/... is 02/01/2015 00:00. So in essence, what your code is really doing is this:
// parse string according to U.S. rules - mm/dd/yyyy
HowCFInterpretsYourString = parseDateTime(dateString);
LSDateTimeFormat(HowCFInterpretsYourString, 'dd/mm/yyyy HH:nn', 'nl_BE');
Results:
HowCFInterpretsYourString = {ts '2015-01-02 00:00:00'} <=== January 2nd
LSDateTimeFormat = 02/01/2015 00:00 <=== Day = 2, Month = 1
If you do not want CF doing the interpretation for you, pass in date objects - not strings.
As for why LSDateFormat's behavior seems inconsistent with LSDateTimeFormat, I do not know. However, strings are ambiguous. So when you use them instead of date objects, well ... expect the unexpected.
We should just convert to the correct date object first and then format using the normal dateFormat method.
Just because you are only using the format functions to output the numeric date parts, does not mean that is all they do ;-) The format functions also output names, which are locale specific. For example, "MMMMM" might produce "September" or "septiembre" depending on the current locale. There are also other region specific rules, such as the placement of "month" and "day" and the exact capitalization of names. The standard Date/TimeFormat functions always use U.S. date conventions. Whereas LSDateTimeFormat uses whatever locale is supplied. In this specific case, there is not much difference because you are only outputting the numeric date parts:
Numeric date parts (only)
dateObject = createDate(2015,2,1);
writeDump("LSDateTimeFormat = "& LSDateTimeFormat(dateObject, 'dd/mm/yyyy', 'nl_BE'));
writeDump("DateTimeFormat = "& DateTimeFormat(dateObject, 'dd/mm/yyyy'));
Results:
LSDateTimeFormat = 01/02/2015
DateTimeFormat = 01/02/2015
However, for other formats there is a big difference. A date object may not be tied to a locale, but a string representation of a date is .. so the two functions are not interchangeable.
Date Names:
dateObject = createDate(2015,2,1);
writeDump("LSDateTimeFormat = "& LSDateTimeFormat(dateObject, 'full', 'nl_BE'));
writeDump("DateTimeFormat = "& DateTimeFormat(dateObject, 'full'));
Results:
LSDateTimeFormat = zondag 1 februari 2015 0.00 u. UTC
DateTimeFormat = Sunday, February 1, 2015 12:00:00 AM UTC
"EU formatting in our database (dd/mm/yyyy)"
Not sure what you mean by that. Date/time objects do not have a format. Your IDE may display them as human friendly strings, but the date values themselves are stored as numbers. Based on what you described, it sounds like either the values are stored as strings OR are being converted to strings, which would explain the results. Instead, store the values in a date/time column, then retrieve them and pass them into the function "as is" and it should work fine.
I use English (U.S.) locale conventions, so I can use ParseDateTime(), DateFormat() or DateTimeFormat()... but since your dates are UE, you must use LSParseDateTime() on the string that represents the date and the results should be consistent
<cfset D = "01/02/2015">
<cfoutput>
0. #D#<br />
1. #DateFormat(D)#<br />
2. #ParseDateTime(D)# (Parse default US)<br />
3. #LSParseDateTime(D,'en_GB')# (Parse en_GB)<br />
4. #LSDateFormat(D, 'dd/mm/yyyy', 'nl_BE')# (no parsing, default US)<br />
5. #LSDateTimeFormat(D, 'dd/mm/yyyy HH:nn', 'nl_BE')# (no parsing, default US)<br />
6. #LSDateFormat(ParseDateTime(D), 'dd/mm/yyyy', 'nl_BE')# (parsed as default US)<br />
7. #LSDateTimeFormat(ParseDateTime(D), 'dd/mm/yyyy HH:nn', 'nl_BE')# (parsed as default US)<br />
8. #LSDateFormat(LSParseDateTime(D,'en_GB'), 'dd/mm/yyyy', 'nl_BE')# (parsed as en_GB locale)<br />
9. #LSDateTimeFormat(LSParseDateTime(D,'en_GB'), 'dd/mm/yyyy HH:nn', 'nl_BE')# (parsed as en_GB locale)<br />
</cfoutput>
Results for text string "01/02/2015":
0. 01/02/2015
1. 02-Jan-15
2. {ts '2015-01-02 00:00:00'} (Parse default US)
3. {ts '2015-02-01 00:00:00'} (Parse en_GB)
4. 01/02/2015 (no parsing, default US)
5. 02/01/2015 00:00 (no parsing, default US)
6. 02/01/2015 (parsed as default US)
7. 02/01/2015 00:00 (parsed as default US)
8. 01/02/2015 (parsed as en_GB locale)
9. 01/02/2015 00:00 (parsed as en_GB locale)
You could use SQL to re-format the query data to standardize on US locale conventions:
http://www.sql-server-helper.com/tips/date-formats.aspx
SELECT CONVERT(VARCHAR(10), DateField, 101) AS USDate_MMDDYYYY
Either way, I recommend creating a UDF so that you can modify this rule in one single place in case you need to make any any future modifications.
I am trying to use a cent sign in my ColdFusion program. It appears to be ascii 155. The function Chr() only interprets values up to 127, although the documentation says otherwise. I found a clue in that I may need to enable high ascii characters in the ColdFusion administrator, but I could not find a place to do that. This codes works:
<cfset x = Chr(127)>
<cfoutput> this is what you get with #x# </cfoutput>
I get a nice box. But this returns only a blank:
<cfset x = Chr(155)>
<cfoutput> this is what you get with #x# </cfoutput>
How do I get Chr() working with higher numbers?
The "cent sign" is ¢, which is chr(162) (which works fine) or ¢ as a HTML entity.
If you want the › symbol then use chr(8250) or ›.
Looks like the standard american ascii chr that we are all used to, and coldfusion chr numbering do not match up.
According to the Livedocs (version 8)
ColdFusion MX: Changed Unicode support: ColdFusion supports the Java UCS-2 representation of Unicode characters, up to a value of 65535. (Earlier releases supported 1-255.)
If you look here, this blog shows some of CF's and the HTML equiv... so you can find some of them more easily.
cf and html entities
Out of interest, i made a simple loop, and thought i'd look through them, and there are plenty of chars... the hard part is finding the right one.
162 is a cent though, as stated in another answer, but this might help explain why.
<cfoutput>
<cfloop index="i" from="1" to="10000">
<pre>Chr #i# = #chr(i)#</pre>
</cfloop>
</cfoutput>
Java UCS-2 has lots of weird characters, as you can see here.
Some Sample output:
Chr 2922 = ୪
Chr 2923 = ୫
Chr 2924 = ୬
Chr 2925 = ୭
Chr 2926 = ୮
Chr 2927 = ୯
Chr 2928 = ୰
Chr 2929 = ୱ
Chr 3207 = ಇ
Chr 3208 = ಈ
Chr 3209 = ಉ
Chr 3210 = ಊ
Chr 3211 = ಋ
Chr 3212 = ಌ
I'm trying to format numbers so that 2 decimal places show up, unless it is a whole number - then I don't want a decimal point to show. I've tried 0.00, _.__, 9.99 and several combinations. Is there a mask for the numberFormat function that can get this result?
<cfif int(x) eq x>
#int(x)#
<cfelse>
#DecimalFormat(x)#
</cfif>
You could divide the variable by 1 and then the whole number is shown without a decimal place.
<cfset a = 5.00>
<cfset b = 5.55>
<cfoutput>#a/1#, #b/1#</cfoutput>
Output is 5, 5.55. Instead of 5.00, 5.55 if you don't divide by 1.
I do not think there is an existing function, but this rounding example might do the trick:
round(yourNumber * 100) / 100
EDIT:
As JasonM mentioned in the comments this will not produce two decimal places for numbers like 1.1.
Ok, I know this question is years old and my solution is dumb as hell but it worked for my needs:
#replace(dollarformat(list_price), '.00', '')#
I know this was dollarformat not number format, but the concept is the same.
LOL. If it's dumb and it works it's not dumb, right?
Still super gross. WTF Adobe?
#REReplace(REReplace(DecimalFormat(value), "0+$", "", "ALL"), "\.+$", "")#
credit: http://www.webveteran.com/blog/web-coding/coldfusion/trimming-a-decimal-with-coldfusion-regex/
I know it's been ages, but if you first do the Numberformat of the value, then Trim it, you will get the desired result ... NumberFormat makes it have trailing digits after the decimal - TRIM will eliminate the trailing decimals if they are just zeroes.
5.55 would result in 5.55 , 5.00 would result in 5 , 5.5 would result in 5.5
etc
I prefer my code as flat as a pancake and try to avoid conditionals whenever possible. So if you feel comfortable dropping down a level to Java you can use DecimalFormat to get the results you want.
<cfset n = JavaCast("double", 1.0) />
<cfset x = CreateObject("java", "java.text.DecimalFormat").init() />
<cfset x.applyPattern("##.##") />
<cfset y = x.format(n) />
The following one liner would also work.
<cfset y = CreateObject("java", "java.text.DecimalFormat").init( "##.##" ).format( JavaCast("double", 1.0) ) />
I'd use precisionEvaluate() + numberFormat() + replaceFirst(regex)
<cfscript>
numberFormat( precisionEvaluate( num ), '_.00' ).replaceFirst( '\.00$', '' );
</cfscript>
For example:
50.00 -> 50
0.00 -> 0
0.001 -> 0
0.006 -> 0.01
123.45 -> 123.45
4/3 -> 1.33
5.24596 -> 5.25
pi() -> 3.14
12345678901234567890 -> 12345678901234567000
reReplace(numberFormat(val, "__.00"), "\.00$", "")