Creating a credential for use with SAML in CFML - certificate

I am trying to create a SAML authentication request in CFML (Railo) with OpenSAML and have got stuck at the point of creating a certificate object to set the public key.
I found soem code that worked well with the private key butusing similar code with the public key certificate does not work. It fails on the call to keyFactory.generatePublic() with : "java.security.InvalidKeyException: IOException: algid parse error, not a sequence".
Does that mean I need a different format for my public key or that I have the keySpec set wrongly? I have tried using PKCS8EncodedKeySpec and RSAPublicKeySpec but neither of those work either. The certificate I'm using is a self signed certificate made with the OpenSSL utils and was created in .crt format.
My Code:
<cfset local.rawKey = replace( arguments.privateKey, "-----BEGIN PRIVATE KEY-----", "" )>
<cfset local.rawKey = replace( local.rawKey, "-----END PRIVATE KEY-----", "" )>
<cfset local.rawKey = trim(local.rawKey)>
<cfset local.keyBytes = binaryDecode(local.rawKey, "base64")>
<cfset local.keySpec = createObject("java", "java.security.spec.PKCS8EncodedKeySpec")>
<cfset local.keyFactory = createObject("java", "java.security.KeyFactory").getInstance("RSA")>
<cfset local.privateKey = keyFactory.generatePrivate(local.keySpec.init(local.keyBytes))>
<cfset local.rawCert = replace( arguments.certificate, "-----BEGIN CERTIFICATE-----", "" )>
<cfset local.rawCert = replace( local.rawCert, "-----END CERTIFICATE-----", "" )>
<cfset local.rawCert = trim(local.rawCert)>
<cfset local.keyBytes = binaryDecode(local.rawCert, "base64")>
<cfset local.keySpec = createObject("java", "java.security.spec.PKCS8EncodedKeySpec")>
<cfset local.keyFactory = createObject("java", "java.security.KeyFactory").getInstance("RSA")>
<cfset local.certificate = keyFactory.generatePublic(local.keySpec.init(local.keyBytes))>
<cfset local.credential = _create( "org.opensaml.xml.security.x509.BasicX509Credential" )>
<cfset local.credential.setPrivateKey( local.privateKey )>
<cfset local.credential.setEntityCertificate( local.certificate )>
<cfreturn credential>
</cffunction>
Thanks in advance, Kevin

In case anyone else has to solve this issue. The following appears to do what I need:
<cfset local.certBytes = binaryDecode(local.rawCert, "base64")>
<cfset local.certStream = createObject("java", "java.io.ByteArrayInputStream").init(local.certBytes)>
<cfset local.certFactory = createObject("java", "java.security.cert.CertificateFactory").getInstance("X.509")>
<cfset local.certificate = local.certFactory.generateCertificate(local.certStream)>

Related

ColdFusion & MongoDB 4 using Java Driver 3.8

I am trying to perform a wildcard search in Mongo using ColdFusion 11 & The Java MongoDB driver 3.8 on MongoDB 4.0.
The following code gives me errors saying that the method countDocuments() cannot be found or the method find() cannot be found.
<cfset Mongo = CreateObject("java","com.mongodb.MongoClient").init("localhost")>
<cffunction name="m" returntype="any">
<cfargument name="value" type="any">
<cfif IsJSON(arguments.value)>
<cfset local.retrun = CreateObject("java","com.mongodb.util.JSON").parse(arguments.value)>
<cfelse>
<cfset local.retrun = CreateObject("java","com.mongodb.util.JSON").parse( SerializeJSON(arguments.value) )>
</cfif>
<cfreturn local.retrun>
</cffunction>
<cfset db = Mongo.getDatabase('fda')>
<cfset DrugInfo = db.getCollection("druginfo")>
<cfset searchCount=druginfo.countDocuments(m({'openfda.brand_name':/Ty/ }))>
<cfset results = DrugInfo.find(m({'openfda.brand_name': /Ty/})).iterator()>
When attempting to do an exact match search, everything works fine.
<cfset searchCount=druginfo.countDocuments(m({'openfda.brand_name':'Tylenol'}))>
<cfset results = DrugInfo.find(m({'openfda.brand_name': 'Tylenol'})).iterator()>
I'm basically testing all my queries in Mongo Compass and pasting them into my code but it's not coming working as expected.
That error message is not very descriptive.
The Java driver won't take regex'es (or at least not via ColdFusion objects) like Compass does, so you'll have to $regex with the pattern between quotes like:
{ <field>: { $regex: 'pattern', $options: '<options>' } }
For example instead of
m({'openfda.brand_name':/Ty/ })
use
m({ 'openfda.brand_name': { '$regex': '^Ty', '$options': 'i' } })
There is more about how to use $regex here:
(https://docs.mongodb.com/manual/reference/operator/query/regex/)
I hope this helps.

How to check the signature of the request from Amazon Alexa using Coldfusion and OpenSSL

Amazon's documentation on how to verify is posted here: https://developer.amazon.com/docs/custom-skills/host-a-custom-skill-as-a-web-service.html#checking-the-signature-of-the-request
I'm stuck trying to get this to work:
Once you have determined that the signing certificate is valid, extract the public key from it.
Base64-decode the Signature header value on the request to obtain the encrypted signature.
Use the public key extracted from the signing certificate to decrypt the encrypted signature to produce the asserted hash value.
Generate a SHA-1 hash value from the full HTTPS request body to produce the derived hash value
Compare the asserted hash value and derived hash values to ensure that they match.
Here's what I have so far. I have the verification done as far as the subject and valid pem file, the issue is I have no idea how to 'Compare the asserted hash value and derived hash values to ensure that they match.'
<!---get the request sent from amazon---->
<cfset local.request = deserializeJson(
toString(getHttpRequestData().content)
)>
<!---collect headers for authentication---->
<cfif this.isTesting is False>
<cftry>
<cfset local.request.headers.SignatureCertChainUrl = getHttpRequestData().headers.SignatureCertChainUrl>
<cfcatch type="any">
<cfset local.request.request.type = "SessionEndedRequest"><!---end session if SignatureCertChainUrl not passed--->
</cfcatch>
</cftry>
<cftry>
<cfset local.request.headers.Signature = getHttpRequestData().headers.Signature>
<cflog file="Alexa" text="URL Sig: #local.request.headers.Signature#" >
<cfcatch type="any">
<cfset local.request.request.type = "SessionEndedRequest"><!---end session if Signature not passed--->
<cflog file="Alexa" text="Signature is not defined" >
</cfcatch>
</cftry>
<cfif isDefined("local.request.headers.SignatureCertChainUrl") and
isDefined("local.request.headers.Signature")>
<cfif find('https://s3.amazonaws.com/echo.api/',local.request.headers.SignatureCertChainUrl)
or find('https://s3.amazonaws.com:443/echo.api/',local.request.headers.SignatureCertChainUrl)><!---not ssl so kill it--->
<cflog file="Alexa" text="URL Chain: #local.request.headers.SignatureCertChainUrl#" >
<cfset local.request.headers.SignatureCertChainUrlFormatted = replacenocase(local.request.headers.SignatureCertChainUrl,'echo.api/../echo.api','echo.api')>
<cfhttp url="#local.request.headers.SignatureCertChainUrlFormatted#" method="get"></cfhttp>
<cffile action="write"
file="C:\inetpub\alexa\cert\echo.pem"
output="#cfhttp.FileContent#">
<!---check date is valid---->
<cfexecute name="C:\OpenSSL-Win32\bin\openssl.exe"
arguments="x509 -noout -dates -in C:\inetpub\alexa\cert\echo.pem"
variable="local.cert.OpenSSL_dates"
timeout="10" >
</cfexecute>
<cfset local.cert.datesFormatted = replacenoCase(local.cert.OpenSSL_dates,'notbefore','')>
<cfset local.cert.datesFormatted = replacenoCase(local.cert.datesFormatted,'notafter','')>
<cfset local.cert.datesFormatted = replacenoCase(local.cert.datesFormatted,'=','')>
<cfset local.cert.notBefore = trim(listgetat(local.cert.datesFormatted,1,'='))>
<cfset local.cert.notAfter = trim(listgetat(local.cert.datesFormatted,2,'='))>
<cfif datecompare(convertCertTime(local.cert.notBefore),now()) is '-1' and
datecompare(convertCertTime(local.cert.notAfter),now()) is '1'>
<!---cert date is valid--->
<cfelse>
<cfset local.request.request.type = "SessionEndedRequest">
</cfif>
<!---check 'echo-api.amazon.com' in subject---->
<cfexecute name="C:\OpenSSL-Win32\bin\openssl.exe"
arguments="x509 -noout -subject -in C:\inetpub\alexa\cert\echo.pem"
variable="local.cert.OpenSSL_Subject"
timeout="10" >
</cfexecute>
<cfif NOT findNOcase('echo-api.amazon.com',local.cert.OpenSSL_Subject)>
<cfset local.request.request.type = "SessionEndedRequest">
</cfif>
<!---check 'CN=Symantec Class 3 Secure Server CA' in issuer---->
<cfexecute name="C:\OpenSSL-Win32\bin\openssl.exe"
arguments="x509 -noout -issuer -in C:\inetpub\alexa\cert\echo.pem"
variable="local.cert.OpenSSL_issuer"
timeout="10" >
</cfexecute>
<cfif NOT findNOcase('Symantec Class 3 Secure Server CA',local.cert.OpenSSL_issuer)>
<cfset local.request.request.type = "SessionEndedRequest">
</cfif>
<cfexecute name="C:\OpenSSL-Win32\bin\openssl.exe"
arguments="x509 -noout -pubkey -in C:\inetpub\alexa\cert\echo.pem"
variable="local.cert.OpenSSL_Pubkey"
timeout="120" >
</cfexecute>
<cffile action="write"
file="C:\inetpub\alexa\keys\#local.request.request.requestId#.key"
output="#local.cert.OpenSSL_Pubkey#">
<!---decyrpt header signature---->
<!----Base64-decode the Signature header---->
<cfset local.cert.encryptedSig = toString(ToBinary(local.request.headers.Signature)) />
<cffile action="write"
file="C:\inetpub\alexa\keys\#local.request.request.requestId#.sig"
output="#local.cert.encryptedSig#">
<cfelse>
<cfset local.request.request.type = "SessionEndedRequest">
</cfif>
<!----end of header decrypting----->
<cfelse>
<!---if either of these are not defined kill the session---->
<cfset local.request.request.type = "SessionEndedRequest">
</cfif>
</cfif>
Try charsetDecode to utf-8 before Base64 decode
Use this before Base64 decode
<CFSET local.request.headers.Signature = charsetDecode( local.request.headers.Signature, "utf-8" )>

Using Coldfusion to create a REST request

Using Coldfusion I would like to create a REST service
I would like to post it to: https://www.test.co.za/ using the username "person1" and password "password1".
The data type would be: application/json
And I would like to pass the following parameters:
callbackURL (string)
amount (bigdecimal)
reference (string)
description (string)
This will then return:
reference (string)
redirectURL (string)
status (string)
This is what I have got so far:
index.cfm
<cfset restInitApplication("C:\ColdFusion10\cfusion\wwwroot\restApp","IIT")>
<cfhttp url="http://127.0.0.1:8500/rest/IIT/rest" method="get">
<cfoutput>#cfhttp.filecontent#</cfoutput>
rest.cfc
<cfcomponent rest="true" restpath="restService">
<cffunction name="sayHello" access="remote" returntype="String" httpmethod="GET">
<cfset callbackURL = "https://www.google.com">
<cfset amount = "100,50">
<cfset reference = "123456789">
<cfset description = "This is a description">
<cfreturn rest>
</cffunction>
</cfcomponent>
I would like to know if I am on the right track and how I would post my values to https://www.test.co.za/ with a username and password
I am not sure if it will look something like this:
<cfhttp
url="https://www.test.co.za/"
method="post"
port="8500"
result="res">
</cfhttp>
Thanks in advance

coldfusion struct in function argument

I'm trying to do server side facebook connect, but the sdk (get it here) provided gives the following error:
Invalid CFML construct found on line 523 at column 78.
ColdFusion was looking at the following text:
{
And it doesn't explain why it's throwing this error. I'm not good at cfscript, so I don't know whether the sdk uses the correct syntax, but it throws the error on this function, at the braces of the struct in the function arguments:
private String function getUrl(String path = "", Struct parameters = {})
{
var key = "";
var resultUrl = "https://www.facebook.com/" & arguments.path;
if (structCount(arguments.parameters)) {
resultUrl = resultUrl & "?" & serializeQueryString(arguments.parameters);
}
return resultUrl;
}
I had thought that using an sdk would be a no brainer, but apparently I'm missing something.
What am I doing wrong?
Part 2:
The code now comes to a halt at:
for (var propertyName in arguments.properties) {
httpService.addParam(type="formField", name=propertyName, value=arguments.properties[propertyName]);
}
Are you not allowed to use a for loop in cfscript?
Try structNew() or "#structNew()#" instead of {}
This should work for connecting to Facebook and getting an access token:
<cfset appID = ""/>
<cfset secret_key = ""/>
<cfset app_url = ""/>
<cfparam name="URL.Code" default="0">
<cfparam name="URL.state" default="0">
<cfparam name="SESSION.Redirect" default="0">
<cfset code_ = URL.Code>
<cfif code_ EQ "" OR code_ EQ 0>
<cfset SESSION.State = Hash(CreateUUID(),"MD5")>
<cfset dialog_url = "http://www.facebook.com/dialog/oauth?client_id=" & appID & "&redirect_uri=" & app_url & "&scope=email,user_photos,publish_stream" & "&state=" & SESSION.State>
<cflocation url="#dialog_url#" addtoken="no">
</cfif>
<cfif SESSION.State EQ URL.State>
<cfset token_url = "https://graph.facebook.com/oauth/access_token?client_id=" & appID & "&redirect_uri=" & app_url & "&client_secret=" & secret_key & "&code=" & code_>
<cfhttp url="#token_url#" result="AccessToken" method="GET">
<cfelse>
<p>The state does not match. You may be a victim of CSRF.</p>
</cfif>

Storing form values from a form in a session in coldfusion

I have these form values I'm trying capture in a session:
prop_zip
prop_st
address
city
fname
lname
email
pri_phone_1
pri_phone_2
pri_phone_3
Here is my session code:
<cfapplication name="qsErrorVals" clientmanagement="yes" sessionmanagement="yes" sessiontimeout="#createTimeSpan(0,0,360,0)#">
<cflock timeout="120" name="#session.sessionID#" type="exclusive">
<cfset session.prop_st="#prop_st#">
<cfset session.prop_zip="#prop_zip#">
<cfset session.address="#address#">
<cfset session.fname="#fname#">
<cfset session.lname="#lname#">
<cfset session.email="#email#">
<cfset session.pri_phone_1="#pri_phone_1#">
<cfset session.pri_phone_2="#pri_phone_2#">
<cfset session.pri_phone_3="#pri_phone_3#">
</cflock>
So far, I can only save two of the variables from the form:
prop_zip
prop_st
The rest I'm having trouble with saving the form values to the session. I've defined the variables I want to store in the session before creating the session and I've tried using this code:
<cfset session.email="#form.email#">
etc...
Still I can only save the two variables I mentioned. What am I doing wrong?
Eh, I usually take the easy way out. In addition to the recommendations about cfapplication, scoping and paraming form vars in Micah's answer, I'd copy the whole form struct into session.
session.theForm = structCopy(form);
It's difficult to answer specifically without looking at all of your code but hopefully this example will help.
The cfapplication tag should go in an Application.cfm or Application.cfc file.
The session code should most likely be placed in another file.
I'd recommend specifying a scope attribute of session in the cflock tag.
Application.cfm file:
<cfapplication name="qsErrorVals" clientmanagement="yes" sessionmanagement="yes" sessiontimeout="#createTimeSpan(0,0,360,0)#">
ErrorVals.cfm or whatever you want to call the file:
<cfparam name="form.prop_st" default="NY" > <!--- I've added default data for testing --->
<cfparam name="form.prop_zip" default="12345" >
<cfparam name="form.address" default="1st Main St." >
<cfparam name="form.fname" default="John" >
<cfparam name="form.lname" default="Doe" >
<cfparam name="form.email" default="me#domain.com" >
<cfparam name="form.pri_phone_1" default="123-123-1234" >
<cfparam name="form.pri_phone_2" default="123-123-1234" >
<cfparam name="form.pri_phone_3" default="123-123-1234" >
<cfscript>
prop_st = trim(form.prop_st); // validate, trim, etc.
prop_zip = trim(form.prop_zip);
address = trim(form.address );
fname = trim(form.fname );
lname = trim(form.lname );
email = trim(form.email);
pri_phone_1 = trim(form.pri_phone_1);
pri_phone_2 = trim(form.pri_phone_2);
pri_phone_3 = trim(form.pri_phone_3);
</cfscript>
<cflock scope="session" type="exclusive" timeout="10" >
<cfset session.prop_st = prop_st>
<cfset session.prop_zip = prop_zip>
<cfset session.address= address>
<cfset session.fname= fname>
<cfset session.lname= lname>
<cfset session.email= email>
<cfset session.pri_phone_1= pri_phone_1>
<cfset session.pri_phone_2= pri_phone_2>
<cfset session.pri_phone_3= pri_phone_3>
</cflock>
<cfdump var="#session#">