Wednesday, November 01, 2006

Two-Way SSL in Weblogic for Developers

I've been scratching my head, throwing my hands up, even questioning my abilities as a developer for the past few working days trying to work out SSL.
And not because the theory is over my head, or because I couldn't find information on the subject - but because I was asked to simulate an existing production environment in development. A Two-Way SSL (or Mutual Authentication) setup, to be specific. A configuration that takes SSL setup to the limit!

If you need a primer on this subject, the theory and an example using Weblogic's demo setup is laid out clearly in this excellent article:
http://monduke.com/2006/06/04/the-fifteen-minute-guide-to-mutual-authentication/

Usually in development environments you can cut corners - but not with security. In fact I discovered it is more of a challenge for a couple of reasons:
1) Just about everyone who writes about it assumes you're setting it up for real and skips over the details if you don't want to get involved with a signing authority like Verisign - or, like the article above, you're using a test CA that has already been set up.
2) You have to simulate all the parts - including the bits the Verisigns and Thawtes usually take care of.

So to try to do the noble thing and save one or two of you that may be tortured with the same task, I am going to reveal the secrets of setting up Two-Way SSL using Java and Weblogic Tools using a Self-Signed CA certificate for development environments.

The tools we need for the job are:
  • Java keytool
  • BEA's modifed keytool: ImportPrivateKey
  • BEA's CertGen - Certificate Generator

All these tools do similar things and it's the subtle differences that'll kill ya. Be warned - you may be safer playing with a chainsaw! ;)
(I have a hunch there may be a way you can do this with just Java keytool, but I'll try to crack that one later on. If I do I'll post the solution here.)

I'm assuming, of course, that you've got Weblogic installed. For the record I'm using 9.2 and you should be somewhere in that vicinity too.
Note: Before you start, run setDomainEnv in the bin directory of your server domain.
e.g. on Windows:

path_to_bea\user_projects\domains\my_domain\bin\setDomainEnv.cmd


  1. Use CertGen to Generate Server Private Key and Certificate
    What we need at the outset is for everyone to trust us. We're all going to trust each other here because I say so. That's what the selfsigned switch is all about. In the real world, we trust each other because we mutually trust a Certificate Authority (CA) like Verisign. Here we're saying "I am the CA".

    java utils.CertGen -selfsigned -certfile MyOwnSelfCA.cer -keyfile MyOwnSelfKey.key -keyfilepass mykeypass -cn "My Own Self CA"

    You should see this response in the command window:
    Generating a self signed certificate with common name My Own Self CA and key strength 1024

  2. Create the Identity Keystore
    CertGen created a unique and secret Private Key for the server we're using and the Self-signed Root Certificate for us. But Java wants them packaged up neatly into a keystore.
    The one thing Java keytool doesn't do is import a ready-made private key...
    Drat!
    Fortunately BEA are a smart bunch and created a utility to help.
    And just to make sure there was no confusion about what it does, they called it ImportPrivateKey.
    Told you they were smart, didn't I?

    Now run this:
    java utils.ImportPrivateKey -keystore MyOwnIdentityStore.jks -storepass identitypass -keypass keypassword -alias trustself -certfile MyOwnSelfCA.cer.pem -keyfile MyOwnSelfKey.key.pem -keyfilepass mykeypass

    Imported private key MyOwnSelfKey.key.pem and certificate MyOwnSelfCA.cer.pem
    into a new keystore MyOwnIdentityStore.jks of type jks under alias trustself

  3. Import the Certificate into a new Trust keystore
    If you read the Monduke article from above, you'll know the name of the game is trust.
    When the client asks the server for a connection, the server will only allow access if it trusts the signer of the client's certificate. This is going to be the "My Own Self CA" and to make it happen we need our trusty MyOwnSelf certificate packed up into a separate keystore called the Trust Keystore. When the client presents it's certificate, this is where the server will look to see if it trusts the signature of the CA.
    keytool -import -trustcacerts -alias trustself -keystore TrustMyOwnSelf.jks -file MyOwnSelfCA.cer.der -keyalg RSA

    (Replace with equivilent ImportPrivateKey command?)

    Here's the tool's response:
    Enter keystore password: trustpass
    Owner: CN=My Own Self CA, OU=FOR TESTING ONLY, O=MyOrganization, L=MyTown, ST=My
    State, C=US
    Issuer: CN=My Own Self CA, OU=FOR TESTING ONLY, O=MyOrganization, L=MyTown, ST=M
    yState, C=US
    Serial number: ...

    Trust this certificate? [no]: yes
    Certificate was added to keystore

  4. Configure WLS with Identity and Trust stores

    Now we have an Identity Keystore for Server to Client communication (to supply certificates to the client) and a Trust Keystore for Client to Server communication (to accept certificates supplied by the client). We now need to tell Weblogic to use them.

    In the Weblogic Admin Console jump to the Keystores page and choose "Custom Identity and Custom Trust"

    Enter the locations of your Identity and Trust keystores, the passphrases identitypass and trustpass respectively, along with the alias in the SSL tab (I used 'trustself' above). The Private Key password in this example is 'keypassword'.

    When you've saved and activated your changes in the admin console, check the Weblogic command output window to verify that your Identity and Trust keystores were loaded with no problems.

  5. Test One Way SSL
    Under the SSL tab, make sure Two Way Client Cert Behavior is set to "Client Certificates Not Requested".

    This is important - make sure you have these entries in your config.xml file in the config directory of your domain:
    <client-certificate-enforced>false</client-certificate-enforced>
    <two-way-ssl-enabled>false</two-way-ssl-enabled>
    <server-private-key-alias>trustself</server-private-key-alias>

    If any are different, edit and save the config.xml to match, and then restart the Weblogic server.

    Now browse to https://localhost:7002/console
    (Assuming defaults.)



    All being well, the server should present the client with a certificate.
    However, the client has no reason to trust our Self-Signed Certificate yet, so it will throw up a dialog. (Also the name doesn't match that of the server. This isn't too important in a development environment - but something you'd definitely fix for production.)

  6. Install the Server Certificate on the Client
    To have the client trust the server permanently, we need to Install the certificate. Hit install and follow the instructions. When you next go into the Certificate Management screen you will see the "My Own Self CA" listed under "Trusted Root Certification Authorities"

  7. Test Two Way SSL without Client Certs required

  8. Now go into the WLS Admin Console and switch the
    Take a look at the WebLogic server console output:

    NO_CERTIFICATE

    The only way we got to the page was because we set Weblogic to ignore the fact that there was no client certificate. For truly secure Two-Way SSL where only authorized clients can talk to our server, we need to put a certificate on the client to send and require that the server check it.

  9. Create a client certificate using the Self-certified CA certificate

    Now we basically need to set up the opposite situation on the client that we did on the server. But, of course, there are some crucial differences. Wouldn't be any fun otherwise...

    It's time to generate the certificate for the client. This time we want the Certificate to identify the client machine (usually the user of the machine - you can set up one client certificate per user and have more than one on a machine if you need to), AND we want to ensure that the Client is linked to the Trusted CA Root Certificate we fabricated earlier. (This is why the ou (operating unit) of the client certificate must match the identity of the Trusted CA Certificate - in this case "My Own Self CA".)

    java utils.CertGen -certfile MyClientCert.cer -keyfile MyClientKey.key -keyfilepass clientkeypass -cacert MyOwnSelfCA.cer.der -cakey MyOwnSelfKey.key.der -cakeypass mykeypass -cn "My Client" -e "my.own@self.com" -ou "My Own Self CA"

    Generating a certificate with common name Client User and key strength 1024 issued by CA with certificate from MyOwnSelfCA.cer.der file and key from MyOwnSelfKey.key.der file

  10. Bundle up the Certificate and Key into a Format the Browser will like (it's PKCS12 if you have to know)

    Having the client certificate in bits won't be much appreciated by the browser, so we need to package it up - like a identity keystore, but in a different format that browsers like.

    java utils.ImportPrivateKey -keystore MyClientCert.p12 -storepass clientpass -storetype pkcs12 -keypass clientkeypass -alias clientcert -certfile MyClientCert.cer.pem -keyfile MyClientKey.key.pem -keyfilepass clientkeypass

  11. Import Trusted CA Certificate and Client Certificate into Browser
  12. There are essentially two pieces to the pie. First you need to import the Root CA Certificate so the browser trusts certificates sent from the server.Locate the MyOwnSelfCA.cer.der file that was made in the very first step, and import it into your browser as a Trusted Root Certification Authority (Tools > Options > Content > Certificates in IE)If using IE doesn't make you go weak at the knees, the easiest thing to do now is double-click the certificate file you just made. (MyClientCert.p12) IE will launch it's import certificate wizard and you'll be ready to roll.

    If you want to make life hard for yourself, then I'll assume you know how to import client certificates in your favourite browser and move on...

  13. Test Two-Way SSL
    The moment of truth:
    Browse to https://localhost:7002/console

    This is what should happen:
    1. Client request to server
    2. Server response - sends certificate signed by "My Own Self CA" and requests a certificate from the client
    3. Client examines certificate - decides to Trust it since it has the CA certificate for "My Own Self CA"
    4. Client sends its certificate to the server, again signed by "My Own Self CA"
    5. Server finds up "My Own Self CA" in its Trust store and decides to trust the client
    6. Server sends requested resource back to the client in encrypted form
    7. Client deciphers the encryption and displays the result - in this case the Weblogic Admin Console login page.

12 comments:

Jonathan said...

This post really saved me so much time. I was experiencing the same exact issues you describe. Firefox users should note that you will receive an Error: -8102 unless you specify a keyusage extension when using CertGen.

Anonymous said...

Thank you man!
God Bless you.

I have been very frustrated with this SSL business as well.

This is very helpful. Keep up the good work.

Anonymous said...

This is a excellent article. I've been looking for a step by step configuration and was lucky to have seen this article. But one thing though, when I tried to import the client certificate, it's asking for private key password, and if I give clientkeypass, it says invalid password. Not sure if I'm missing something. Can you help me on this?

Anonymous said...

Hi, first of all i would like to thank you for such a helpful article. But I am facing an error and was hoping if you can help me. When i try to import MyclientCert.p12 i am getting an error like "Internal Error. The private key that you are trying to import might require crypto service provider which is not installed".

AR said...

Thanks dude!
My situation was similar to yours.
:)

generic cialis said...

Hello, I do not agree with the previous commentator - not so simple

Sanjay @ Work said...

Great article, thanks a ton!

#7 seems incomplete:
"Now go into the WLS Admin Console and switch the" ??

ahmad said...

it's perfect
thank you

Anonymous said...

thank you man!!
you saved me!

Android app developers said...

Thanks for share with us.This is one of the special post.I like your blog quality.

Arjun said...

Its was a very helpful article, simple and straight!, and I too faced exactly the same problem what you have mentioned in your article.
But my hurdles doesn't end.
I configured the SSL in weblogic 10.3.0 (one-way) using custom identity and custom trust and successfully able to launch.
I am able to open my application in https mode and initially got all the exceptions you have mentioned. After resolving "access denied", i'm got struck when I am trying to create the InitialContext using JNDI for JMS.
I am trying this from my client applet and in java console I am getting BAD_CERTIFICATE : An unuseable or corrupted certificate received.

Here is the code:
Hashtable properties = new Hashtable();
properties.put(Context.INITIAL_CONTEXT_FACTORY,
"weblogic.jndi.WLInitialContextFactory");
properties.put(Context.PROVIDER_URL, "t3s://localhost:7002");
properties.put(Context.SECURITY_PRINCIPAL, "weblogicloginname");
properties.put(Context.SECURITY_CREDENTIALS, "weblogicloginpwd");
try {
ctx = new InitialContext(properties);
} catch (NamingException ne) {
ne.printStackTrace(System.err);
System.exit(0);
}
But when I am trying to run the same code from Eclipse or cmd i am able to send messages it successfully.

I assume that the problem is when i am using applets.

Do I need to configure keystore or trust on my system to successfully connect weblogic JMS through client applets.

Anonymous said...

Thanks, nice tips