This tip is from my old blog - but it is worth repeating. The Java runtime must trust a cert to get it to work with it properly.
These notes are the result of solving a particularly tricky problem with webservices on CF Talk. This helpful "keystore" procedure came from the diligent investigation of Mike Chambers and Trevor Baker.
There is a tricky nuance to using with SSL in CFMX. In order to make an outgoing SSL request, the requesting agent must first obtain the "public" key. This public key is available from a "trusted certificate authority". Verisign, Thawt, and equifax are 3 well-known "trusted authorities". In your browser, if the certificate is not a "trusted athority", a warning message informs you that, while the cert may be good in other ways (not expired etc.) it is not from a source you have listed as trusted. If you choose, you can simply accept the certificate anyway. Note, encryption is determined by the type and size of the key - not by whether the authority is trusted or not. All things being equal, a certificate from a non-trusted authority will result in the same level of protection as that from a trusted authority.
In CF 5 an outgoing SSL request using was successfully negotiated if the cert was found in the "root certificate store" of the server. In other words, if you had indicated the cert was trusted and allowed the key to be installed, CF 5 was able to use it. In CFMX however, the Java Run-time is unaware of the root certificate store. Instead, it has it's own cache of "trusted authorities" and it installs certs as needed based on this cache.
To discover the list of trusted authorities in your Java run time, try the following command line code:
C:\CFusionMX\runtime\jre\lib>keytool -list -storepass changeit -noprompt -keystore
C:\CFusionMX\runtime\jre\lib\security\cacerts
Of course you will want to change the path to your CFMX runtime directory. When I run this command on my CFMX dev box with the standard 1.3 JRE I get the following:
Keystore type: jks
Keystore provider: SUN
Your keystore contains 10 entries
thawtepersonalfreemailca, Feb 12, 1999, trustedCertEntry,
Certificate fingerprint (MD5): 1E:74:C3:86:3C:0C:35:C5:3E:C2:7F:EF:3C:AA:3C:D9
thawtepersonalbasicca, Feb 12, 1999, trustedCertEntry,
Certificate fingerprint (MD5): E6:0B:D2:C9:CA:2D:88:DB:1A:71:0E:4B:78:EB:02:41
verisignclass3ca, Jun 29, 1998, trustedCertEntry,
Certificate fingerprint (MD5): 78:2A:02:DF:DB:2E:14:D5:A7:5F:0A:DF:B6:8E:9C:5D
thawtepersonalpremiumca, Feb 12, 1999, trustedCertEntry,
Certificate fingerprint (MD5): 3A:B2:DE:22:9A:20:93:49:F9:ED:C8:D2:8A:E7:68:0D
thawteserverca, Feb 12, 1999, trustedCertEntry,
Certificate fingerprint (MD5): C5:70:C4:A2:ED:53:78:0C:C8:10:53:81:64:CB:D0:1D
verisignclass4ca, Jun 29, 1998, trustedCertEntry,
Certificate fingerprint (MD5): 1B:D1:AD:17:8B:7F:22:13:24:F5:26:E2:5D:4E:B9:10
verisignclass1ca, Jun 29, 1998, trustedCertEntry,
Certificate fingerprint (MD5): 51:86:E8:1F:BC:B1:C3:71:B5:18:10:DB:5F:DC:F6:20
verisignserverca, Jun 29, 1998, trustedCertEntry,
Certificate fingerprint (MD5): 74:7B:82:03:43:F0:00:9E:6B:B3:EC:47:BF:85:A5:93
thawtepremiumserverca, Feb 12, 1999, trustedCertEntry,
Certificate fingerprint (MD5): 06:9F:69:79:16:66:90:02:1B:8C:8C:A2:C3:07:6F:3A
verisignclass2ca, Jun 29, 1998, trustedCertEntry,
Certificate fingerprint (MD5): EC:40:7D:2B:76:52:67:05:2C:EA:F2:3A:4F:65:F0:D8
Obviously I trust Thawte and Versign certificates.
What happens when I need to make an SSL request to a site using a cert that is not from one of these authorities? The request will fail. For it to succeed, I must tell my JRE to "trust" the authority in question. To do that you will need to use the keytool to "import" the cert from the authority in question. For example, if you wanted to import the "instant SSL" certificate, you would need to import the 2 signing certificates they use to create their own certs. They use the following 2 certs:
GTE CyberTrust Root CA
Comodo Class 3 Security Services CA
Both of these certs are available through links at instant SSL installation support. Save each of them into a text file - cert1.crt and cert2.crt (or whatever), then use the key tool to import them into your store. Here is the command line syntax:
C:\CFusionMX\runtime\jre\lib>keytool -import -keystore
c:\CFusionMx\runtime\jre\
lib\security\cacerts -alias instantssl -storepass changeit -noprompt -trustcacer
ts -file
c:\temp\cert1.crt
More information regarding the "keytool" is available at
Sun Keytool docs. Once this code is run, you can re-run the initial command line code and look at your entries again. You should see something like this:
instantssl, Dec 5, 2002, trustedCertEntry,
Certificate fingerprint (MD5): C4:D7:F0:B2:A3:C5:7D:61:67:F0:04:CD:43:D3:BA:58
Now, when your outgoing request negotiates the connection it will "trust" the public key provided to complete the SSL negotiation.
Thanks again.
To figure out the java directory, click on "System Information" in the coldfusion administrator and look at the entry under "Java Home"
The path to your cacert file will be:
[java home directory]\lib\security
So the command line code would be:
[java home directory]\lib>keytool -list -storepass changeit -noprompt -keystore [java home directory]\lib\security\cacerts
I just want to clarify your post, I have stored the certificate from a client which web service we want to invoke. Are you saying that I also need signing certificates that the client use to produce his certificate?
Can you embellish on what your problem is? I cannot determine from what you say your particular situation, but if you have a client certificate (.cer) and you are calling their program from ColdFusion, then all you need to do is register the certificate (using keytool utility) to the keystore of the java instance that your CF server is running under. See Jill's comment for details about locating the correct jre. Once you have done that, restart your CF server and you should be able to connect.
These are the steps that I took to install the certificate:
1. Generate a CSR using openSSL tool.
2. Supply the client with this CSR, the returned us a CRT file.
3. I stored the CRT file using keytool on CF8 jre/bin
4. Restarted the CF server
5. Invoke the client web service URL, got the error: javax.net.ssl.SSLPeerUnverifiedException: peer not authenticated.
6. Client suggested us to store the root CRT from his site as well, so we did that too (same as step 3).
7. The error still persist..
:(
My client is requiring SSLv3 which not yet supported (it's still in CF feature wishlist):
http://labs.adobe.com/wiki/index.php/Scorpio:Customer_Wish_List no 35
I think we will go with creating .NET object and invoke it via CF.
Thanks for all the help!
You can retrieve a certificate in Firefox by double-clicking the padlock, same as IE. Go to details and export.
And the one that got me for the last half hour...
Do not use directories with extended names - keytool doesn't like "Documents and Settings" (even with double quotes). Just stick it in c:\temp - it's much easier.
Oh yes - the keytool in CF8 is in C:\ColdFusion8\runtime\jre\bin (not jre\lib)
Thanks a lot for creating this simple guide - can't understand why Adobe (and Macromedia) make no mention of it anywhere under web services.
This exception was caused by: javax.mail.MessagingException: Connect failed; nested exception is: javax.net.ssl.SSLException: untrusted server cert chain.
I'm basically trying to connect to Gmail using CFPOP. Now CFPOP doesn't really support SSL so I've had to change the necessary java properties to get it to connect by
<CFSET javaSystem = createObject("java", "java.lang.System") />
<CFSET prop = javaSystem.getProperties() />
<CFSET prop.setProperty("mail.pop3.socketFactory.class", "javax.net.ssl.SSLSocketFactory") />
<CFPOP name = "rsCheckNew" action = "getHeaderOnly"
server = "pop.gmail.com" port = "995" timeout = "10"
username = "username@gmail.com" password = "password">
I've also gotten the Gmail cert from the browser and executed the necessary keytool command. keytool -list -keystore c:\cfusionmx\runtime\jre\lib\security\cacerts shows
thawtepersonalfreemailca, Sat Feb 13 04:12:16 SGT 1999, trustedCertEntry,
Certificate fingerprint (MD5): 1E:74:C3:86:3C:0C:35:C5:3E:C2:7F:EF:3C:AA:3C:D9
thawtepersonalbasicca, Sat Feb 13 04:11:01 SGT 1999, trustedCertEntry,
Certificate fingerprint (MD5): E6:0B:D2:C9:CA:2D:88:DB:1A:71:0E:4B:78:EB:02:41
verisignclass3ca, Tue Jun 30 01:05:51 SGT 1998, trustedCertEntry,
Certificate fingerprint (MD5): 78:2A:02:DF:DB:2E:14:D5:A7:5F:0A:DF:B6:8E:9C:5D
thawteserverca, Sat Feb 13 04:14:33 SGT 1999, trustedCertEntry,
Certificate fingerprint (MD5): C5:70:C4:A2:ED:53:78:0C:C8:10:53:81:64:CB:D0:1D
thawtepersonalpremiumca, Sat Feb 13 04:13:21 SGT 1999, trustedCertEntry,
Certificate fingerprint (MD5): 3A:B2:DE:22:9A:20:93:49:F9:ED:C8:D2:8A:E7:68:0D
verisignclass4ca, Tue Jun 30 01:06:57 SGT 1998, trustedCertEntry,
Certificate fingerprint (MD5): 1B:D1:AD:17:8B:7F:22:13:24:F5:26:E2:5D:4E:B9:10
gmail, Mon Nov 03 11:38:23 SGT 2008, trustedCertEntry,
Certificate fingerprint (MD5): 63:1E:F3:56:B0:B0:F7:8D:E4:8C:8F:7D:8E:F5:68:D0
verisignclass1ca, Tue Jun 30 01:06:17 SGT 1998, trustedCertEntry,
Certificate fingerprint (MD5): 51:86:E8:1F:BC:B1:C3:71:B5:18:10:DB:5F:DC:F6:20
verisignserverca, Tue Jun 30 01:07:34 SGT 1998, trustedCertEntry,
Certificate fingerprint (MD5): 74:7B:82:03:43:F0:00:9E:6B:B3:EC:47:BF:85:A5:93
thawtepremiumserverca, Sat Feb 13 04:15:26 SGT 1999, trustedCertEntry,
Certificate fingerprint (MD5): 06:9F:69:79:16:66:90:02:1B:8C:8C:A2:C3:07:6F:3A
verisignclass2ca, Tue Jun 30 01:06:39 SGT 1998, trustedCertEntry,
Certificate fingerprint (MD5): EC:40:7D:2B:76:52:67:05:2C:EA:F2:3A:4F:65:F0:D8
gmail is the alias I created using the import tool. Anything else that I've missed out ? I'm going thru my keyboards real quick and I'm pretty sure my skull is cracked from all the banging on the wall.
The problem is not the cert you installed. The cert is a part of a "chain" of certs from the provider with a governing heirarchy. You probably need to add an "intermediate" certificate to get it working. As it stands the cert you installed is telling the system to look further up the chain but there is no trusted cert further up the chain.
Do a google for "intermediate certificates" for more information. You can probably also examine the certs in your local repo for FF or IE and figure out which ones are google intermediates.
Like Mark said, the certification chain was required; not merely the certificate. If you're using some kind of GUI tool (ie browser) you should be able to view those fairly easily. If you're not, you'll have to traverse the certificate manually from its cert structure and figure out where to download it from. I strongly recommend that you go for the first option :)
Secondly, if you're connecting to gmail like I was, exporting the cert from your browser doesn't work. You'll be getting a cert registered to www.google.com and NOT pop.gmail.com (which is what you're connecting to). Makes a world of a difference. I did an Ethereal trace before realising that. I'd imagine that you could use some kind of SSL investigator to retrieve the cert if you need to.
Thirdly, the actual keystore used is also important. The VM has its default keystore location (usually cfusionmx/runtime/jre/lib/security/cacerts) *but* if you've done any reconfiguration to your CF process and ran it under another user account or changed ownership, it'll look by default into your "document and settings" folder (/user/local) for unix I think.
To specify a custom keystore you could either
a. Set it as your VM parameter in CF admin using the
-Djavax.net.ssl.trustStore=[keystore location] -Djavax.net.ssl.trustStorePassword=[keystore password]
or you could set it as a java system property since you're already messing with that before calling CFPOP by doing
<CFSET javaSystem = createObject("java", "java.lang.System") />
<CFSET prop = javaSystem.getProperties() />
<CFSET prop.setProperty("mail.pop3.socketFactory.class", "javax.net.ssl.SSLSocketFactory") />
<CFSET prop.setProperty("javax.net.ssl.trustStore", "C:\CFusionMX\runtime\jre\lib\security\cacerts") />
<CFSET prop.setProperty("javax.net.ssl.trustStorePassword", "changeit") />
Seems to connect fine with no issues. Have to say Gmail's implementation of the POP protocol is kinda funky tho. Your CFPOP is going to retrieve *all* messages from a certain date which would be painful down the line. Doesn't seem like gmail removes those mail from server once you've read them so if the user has set to download all mails, your CFPOP is going to go thru *ALL* mails the user has ever received since day 1.
Various ways to get around that I guess depending on what you're trying to achieve. As far as connectivity goes tho, the above should resolve any issues you have.
Awesome explanation... thanks!!
Here's my code:
<cfset ws = CreateObject("webservice","https://financial3.symetra.com/sws2008/service.asm... wsargs)>
<cfset ws._setProperty("axis.transport.version", "1.1")>
<cfset resp = ws.symetraQuote(xmlstr)>
I downloaded the cert by browsing the URL in IE and downloaded all three levels of certs from the "Certification Path" tab. The bottom two levels installed successfully, the top (verisign) was already there so I didn't overwrite it.
The createObject() works and I can cfdump the returned variable and see the methods. The second call _setProperty() also seems to work. But whenever I try to call the method() I get the error...
Cannot perform web service invocation symetraQuote.
The fault returned when invoking the web service operation is:
AxisFault
faultCode: {http://schemas.xmlsoap.org/soap/envelope/}Server.userException
faultSubcode:
faultString: java.net.ConnectException: Connection timed out: connect
faultActor:
faultNode:
faultDetail:
{http://xml.apache.org/axis/}stackTrace:java.net.ConnectException: Connection timed out: connect
This was tested from two different servers, one over the internet and one in the same LAN. Within the LAN, if SSL is removed, it works fine, but going via SSL fails every time.
Any help would be HUGELY appreciated!