Home All Groups Group Topic Archive Search About

Using a Java Keytool created certificate in HTTPWebRequest.ClientCertificates

Author
3 Dec 2008 10:07 PM
bigAPE
I am writing some code to connect to a custom Java HTTP server. I have
a working version where I POST in a multi-part form including a file,
etc, using HTTPWebRequest. Works fine via HTTP, however I now want to
ge tit working over HTTPS.

The instructions to enable HTTPS on this custom server require you to
install a certificate created using the Java Keytool with the
following parameters

keytool -selfcert -genkey -keystore certs -alias myalias -dname
"cn=www.somedomain.com,ou=SomeUnit,o=Some
Pty.Ltd.,l=Melbourne,st=Victoria,c=AU" -keyalg "RSA" –validity 365

In the above example command line I have replaced the alias and dname
with some other non-relevant text but apart from that it's all
correct.

The output is a file called "certs" which I then install into the HTTP
engine. This all seems to work fine.

However, I can't quite figure out how to get the "certs" keystore
installed on my Vista client so that the following code can read it

  string certificateName = "SomeCertName";
  X509Store store = new X509Store(StoreName.My,
StoreLocation.CurrentUser);
  store.Open(OpenFlags.ReadOnly);
  X509Certificate2Collection certificates = store.Certificates.Find
(X509FindType.FindBySubjectName, certificateName, true);
  X509Certificate certificate = certificates[0];
  webRequest.ClientCertificates.Add(certificate);

I have looked everywhere for some information on this, but the best I
could come up with was installing the certificate using "Internet
Options > Content > Certificates > Import..." but this requires the
"certs" keystore file to be of a certain type when importing and I
can't work out which.

Am I going about this in the wrong way? Can anyone point me at a good
resource for this kind of stuff ? At the very least does anyone know
which type of Certificate the Java Keytool creates ?

Any help very much appreciated

Al

Author
3 Dec 2008 10:31 PM
Joe Kaplan
Is your goal here to just get SSL working with the server or to also
authenticate the client with SSL client certificate auth?

If you just want an HTTPS connection to the server, you don't need to
specify anything with client certs on the client side.  You just need to get
the client to trust the server's certificate.  There are two ways to do
this:
- Fix the chain such that the client trusts the server's certificate with
default policy, or
- Implement a custom policy that ignores some or all of the SSL policy
errors that you don't want to fix

To fix the chain locally, the Windows client must trust the server's
certificate.  For that, the issuer must chain up to a trusted root, the
subject name must match the host name in the URL and the certificate must be
in its validity period.

If you don't want to fix the trust chain because you don't care about
proving the identity of the server and just want encryption, then you can
implement a callback that allows you to ignore SSL errors.

If you really do want client cert auth, that is likely more complex.  You'll
have to deal with the above mentioned stuff, but then you'll also need to
issue a valid client certificate to the client.  You can't use the server's
certificate on the client (unless you've created both a client and server
authentication cert, but that would be a weird PKI usage).

--
Joe Kaplan-MS MVP Directory Services Programming
Co-author of "The .NET Developer's Guide to Directory Services Programming"
http://www.directoryprogramming.net
"bigAPE" <alex.madd***@gmail.com> wrote in message
news:fd56c8f6-f821-4b7f-bf30-9f7fac73c3dc@g1g2000pra.googlegroups.com...
I am writing some code to connect to a custom Java HTTP server. I have
a working version where I POST in a multi-part form including a file,
etc, using HTTPWebRequest. Works fine via HTTP, however I now want to
ge tit working over HTTPS.

The instructions to enable HTTPS on this custom server require you to
install a certificate created using the Java Keytool with the
following parameters

keytool -selfcert -genkey -keystore certs -alias myalias -dname
"cn=www.somedomain.com,ou=SomeUnit,o=Some
Pty.Ltd.,l=Melbourne,st=Victoria,c=AU" -keyalg "RSA" –validity 365

In the above example command line I have replaced the alias and dname
with some other non-relevant text but apart from that it's all
correct.

The output is a file called "certs" which I then install into the HTTP
engine. This all seems to work fine.

However, I can't quite figure out how to get the "certs" keystore
installed on my Vista client so that the following code can read it

  string certificateName = "SomeCertName";
  X509Store store = new X509Store(StoreName.My,
StoreLocation.CurrentUser);
  store.Open(OpenFlags.ReadOnly);
  X509Certificate2Collection certificates = store.Certificates.Find
(X509FindType.FindBySubjectName, certificateName, true);
  X509Certificate certificate = certificates[0];
  webRequest.ClientCertificates.Add(certificate);

I have looked everywhere for some information on this, but the best I
could come up with was installing the certificate using "Internet
Options > Content > Certificates > Import..." but this requires the
"certs" keystore file to be of a certain type when importing and I
can't work out which.

Am I going about this in the wrong way? Can anyone point me at a good
resource for this kind of stuff ? At the very least does anyone know
which type of Certificate the Java Keytool creates ?

Any help very much appreciated

Al
Author
4 Dec 2008 12:49 AM
bigAPE
Outstanding. Thanks for all your advice. I was about to post back
saying pretty exactly that.

I now have the connection working but I did the following:

(1) Created the server X509 DER certificate using OpenSSL with the CN
set to the server name (the cause of my previous
RemoteCertificateNameMismatch)
(2) Installed and configured HTTPS on the Server using this X509 DER
certificate
(3) Connected to the server via HTTP using Internet Explorer
(4) Installed the Certificate from the Server to the Personal store
(5) Ran the test C# application which loaded up all the Certificates
found in the Personal Store using

    X509Store store = new X509Store(StoreName.My,
StoreLocation.CurrentUser);
    store.Open(OpenFlags.ReadOnly);
    foreach (X509Certificate cert in store.Certificates)
    {
        webRequest.ClientCertificates.Add(cert);
    }


(6) Implemented the ServerCertificateValidationCallback as follows

    ServicePointManager.ServerCertificateValidationCallback = new
RemoteCertificateValidationCallback
(RemoteServerCertificateValidationCallback);
    ...
    public static bool RemoteServerCertificateValidationCallback
(Object sender, X509Certificate certificate, X509Chain chain,
SslPolicyErrors sslPolicyErrors)
    {
        return true; // sorry, awful I know
    }

(7) HTTPS calls now work at the expense of ignoring the
RemoteCertificateChainErrors.ChainStatus = "Untrusted Root" error.

Is this what have suggested. Do I not need to deal with steps 3 or 4 ?

Al


On Dec 4, 9:31 am, "Joe Kaplan"
<joseph.e.kap***@removethis.accenture.com> wrote:
Show quoteHide quote
> Is your goal here to just get SSL working with the server or to also
> authenticate the client with SSL client certificate auth?
>
> If you just want an HTTPS connection to the server, you don't need to
> specify anything with client certs on the client side.  You just need to get
> the client to trust the server's certificate.  There are two ways to do
> this:
>  - Fix the chain such that the client trusts the server's certificate with
> default policy, or
>  - Implement a custom policy that ignores some or all of the SSL policy
> errors that you don't want to fix
>
> To fix the chain locally, the Windows client must trust the server's
> certificate.  For that, the issuer must chain up to a trusted root, the
> subject name must match the host name in the URL and the certificate must be
> in its validity period.
>
> If you don't want to fix the trust chain because you don't care about
> proving the identity of the server and just want encryption, then you can
> implement a callback that allows you to ignore SSL errors.
>
> If you really do want client cert auth, that is likely more complex.  You'll
> have to deal with the above mentioned stuff, but then you'll also need to
> issue a valid client certificate to the client.  You can't use the server's
> certificate on the client (unless you've created both a client and server
> authentication cert, but that would be a weird PKI usage).
>
> --
> Joe Kaplan-MS MVP Directory Services Programming
> Co-author of "The .NET Developer's Guide to Directory Services Programming"http://www.directoryprogramming.net"bigAPE" <alex.madd***@gmail.com> wrote in message
Author
4 Dec 2008 12:59 AM
bigAPE
Hi Joe,

Sorry, just answered my own question.

Setting the following just accepts the Server Certificate without
having to adding any to HTTPWebRequest.ClientCertificates

    ServicePointManager.ServerCertificateValidationCallback = new
RemoteCertificateValidationCallback
(RemoteServerCertificateValidationCallback);

    public static bool RemoteServerCertificateValidationCallback
(Object sender, X509Certificate certificate, X509Chain chain,
SslPolicyErrors sslPolicyErrors)
    {
        return true;
    }

Is that what you were referring to when you talked about not needing
the Certificate ?

Al
Author
4 Dec 2008 5:16 PM
Joe Kaplan
You only specify a client certficate if you need client certificate
authentication.  If you just want SSL encryption of the channel, the client
certificate is not needed.  Essentially, the client will remain anonymous
(at least at the SSL level) and the server will be identified by the
certificate it presents.

So, basically you tell me if you need client cert auth.  :)  From what
you've said so far, it doesn't sound like it to me.

As to whether you want to use the callback approach to avoid the chain
verification issue or fix the chain, that is up to you and your
requirements.  In a closed network where there is no chance of an attacker
trying to spoof the server, you may not care if the server is properly
identified by its certificate and may only wish to ensure that an encrypted
connection is negotiated.  In a public-facing scenario, that would be a bad
decision.

You also wouldn't use a self-signed certificate for a "real" deployment in
most cases either.  They should only be for testing.  Getting a certificate
from a real CA is the better way to go.

If you want to fix the chain so the client can verify without having to use
the callback, you probably just need to ensure that the server's certificate
is added to the local machine store trusted root certificates container on
the client machine.  That will make the server's certificate a root
certificate and will allow it to chain.  Then, as long as there is no name
mismatch or validity period issue, the cert should be accepted with no
error.

Up to you how to proceed. :)

--
Joe Kaplan-MS MVP Directory Services Programming
Co-author of "The .NET Developer's Guide to Directory Services Programming"
http://www.directoryprogramming.net
Show quoteHide quote
"bigAPE" <alex.madd***@gmail.com> wrote in message
news:1341d466-ace7-429d-804d-d19d0f21a16d@w1g2000prk.googlegroups.com...
> Hi Joe,
>
> Sorry, just answered my own question.
>
> Setting the following just accepts the Server Certificate without
> having to adding any to HTTPWebRequest.ClientCertificates
>
>    ServicePointManager.ServerCertificateValidationCallback = new
> RemoteCertificateValidationCallback
> (RemoteServerCertificateValidationCallback);
>
>    public static bool RemoteServerCertificateValidationCallback
> (Object sender, X509Certificate certificate, X509Chain chain,
> SslPolicyErrors sslPolicyErrors)
>    {
>        return true;
>    }
>
> Is that what you were referring to when you talked about not needing
> the Certificate ?
>
> Al
Author
3 Dec 2008 10:50 PM
bigAPE
OK, I have managed to get a little further. I decided to go an
alternate route and use OpenSSL to create the certificate according to
another set of instructions with the custom HTTP server. The result is
a DER encoded X509 certificate which the server is now using on the
HTTPS port. I know this because if I view the servers HTML page using
HTTPS Firefox and IE throw a fit because the Certificate is not
recognised. I can then Import the Certificate into my personal store.

When running the C# code for the HTTPWebRequest if I cycle through the
certificates using the following it is there and can be added as
follows

    X509Store store = new X509Store(StoreName.My,
StoreLocation.CurrentUser);
    store.Open(OpenFlags.ReadOnly);
    foreach (X509Certificate cert in store.Certificates)
    {
        webRequest.ClientCertificates.Add(cert);  // this finds the
certificate (validated by examining the certs in here in Debug)
    }
    Stream requestStream = webRequest.GetRequestStream();   <---
EXCEPTION THROWN HERE
    requestStream.Write(reqBytes, 0, reqBytes.Length);

I get the following Exception when attempting to GetRequestStream()

[System.Net.WebExceptionStatus.TrustFailure'
"The underlying connection was closed: Could not establish trust
relationship for the SSL/TLS secure channel."
"The remote certificate is invalid according to the validation
procedure"

So my question is... I have the certificate in my Personal store, I
can see it in C#.  IE can connect to the site happily now that I have
installed the cert. So WHY am I getting this failure in the Trust
Relationship ?

Any thoughts

Al