6

I have given certificate and password to a server that accepts ssl connection. I tried to make a connection to this server but authentication fails, well mainly because I dont know how to use that file and password that I was given.

Here is my code:

    X509Certificate certificate = new X509Certificate(
        @"c:\mySrvKeystore", KEY_PASSWORD);

    public static bool ValidateCertificate(
        object sender,
        X509Certificate certificate,
        X509Chain chain,
        SslPolicyErrors errors)
    {
        if (errors == SslPolicyErrors.None)
            return true;

        Console.WriteLine("Certificate error: {0}", errors);
        return false;
    }

    public void RunClient(string address, int port)
    {
        Console.WriteLine("Starting client...");
        var client = new TcpClient(address, port);


        Console.WriteLine("Client connected.");


        var stream = new SslStream(client.GetStream(),false,
            ValidateCertificate, null);

        try
        {
            stream.AuthenticateAsClient(address);

            Console.WriteLine(stream.IsAuthenticated ? "Client authenticated." : "Client is not authenticated.");

            //TODO constantly read from server!
        }
        catch (AuthenticationException ae)
        {
            Console.WriteLine("Exception occured: {0}", ae.Message);

            if (ae.InnerException != null)
            {
                Console.WriteLine("Inner exception: {0}", ae.InnerException.Message);
            }

            Console.WriteLine("Failed to authenticate! closing the client...");

            client.Close();

            return;
        }
        catch (Exception ex)
        {
            Console.WriteLine("General exception occured {0}", ex.Message);
            client.Close();
            return;
        }
    }

So as you can see, there is no code in my code that somehow tells the server (either TcpClient or SSLStream) that I do have this file and key!

I have a javacode that connects to server with no problem, but I failed to convert it to c# yet. Any helps would be great!

    String keyPassword = "123456";
    // String keyPassword = "importkey";

    try {
        KeyManagerFactory keyManagerFactory;

        keyManagerFactory = KeyManagerFactory.getInstance("SunX509");
        KeyStore keyStore = KeyStore.getInstance("JKS");
        InputStream keyInput = new FileInputStream("c:\\mySrvKeystore");
        keyStore.load(keyInput, keyPassword.toCharArray());
        keyInput.close();
        keyManagerFactory.init(keyStore, keyPassword.toCharArray());

        SSLContext sc = SSLContext.getInstance("SSL");
        sc.init(keyManagerFactory.getKeyManagers(), trustAllCerts, new java.security.SecureRandom());
        SSLSocketFactory sslsocketfactory = sc.getSocketFactory();
        this.sslsocket = (SSLSocket) sslsocketfactory.createSocket(host, port);

    } catch (java.security.NoSuchAlgorithmException e) {
        e.printStackTrace();
    } catch (java.security.KeyManagementException e) {
        e.printStackTrace();
    } catch (java.security.KeyStoreException e) {
        e.printStackTrace();
    } catch (java.security.cert.CertificateException e) {
        e.printStackTrace();
    } catch (java.security.UnrecoverableKeyException e) {
        e.printStackTrace();
    } finally {}
}

public void run() {
    try {
        InputStream inputstream = sslsocket.getInputStream();
        InputStreamReader inputstreamreader = new InputStreamReader(inputstream);
        BufferedReader bufferedreader = new BufferedReader(inputstreamreader);

        OutputStream outputstream = System.out;
        OutputStreamWriter outputstreamwriter = new OutputStreamWriter(outputstream);
        BufferedWriter bufferedwriter = new BufferedWriter(outputstreamwriter);

        //get text from server and stuff...no deal!

UPDATE

After converting the key to p12 according to gtrig the problem now is IOException in AutheneticateAsClient method here:

        try
        {
            var certificate = new X509Certificate2(@"d:\mySrvKeystore.p12", "123456");


            X509Certificate2[] X509Certificates = {certificate};
            var certsCollection = new X509CertificateCollection(X509Certificates);

            //IO EXCEPTION HERE-->
            stream.AuthenticateAsClient(address, certsCollection, SslProtocols.Ssl2, false);
            Console.WriteLine(stream.IsAuthenticated ? "Client authenticated." : "Client is not authenticated.");

            //TODO constantly read from server!
        }

Also when I use the SSlProtocols.Default the error is: RemoteCertificateNameMismatch, RemoteCertificateChainErrors

Dumbo
  • 13,555
  • 54
  • 184
  • 288
  • From your Java code, the file you have is a JKS keystore. Do you know if it contains just certificates, or does it contain a private key also? Is this a one-way or two-way SSL (client authentication) connection? – gtrig Nov 08 '13 at 02:45
  • @gtrig I am not really sure, but my guess is it only has the certificate and the private key is just the password in my code. Also I beleive it is a one way connection, because I know the server streams data and I shouldnt send anything to server. But judging from the java code, it might also include the key since KeyStore was used in Java code. – Dumbo Nov 08 '13 at 10:36
  • Please run this command on the JKS keystore file: `keytool -list -v -keystore mySrvKeystore`. Look to see if the output shows a "PrivateKeyEntry" or if there are only certificate entries. The password in your code is the password to open the JKS keystore file. – gtrig Nov 10 '13 at 00:30
  • @gtrig Yeah it says entry type is PrivateKeyEntry, chain length 1, – Dumbo Nov 10 '13 at 01:36
  • 1
    The errors are from the certificate you receive from the server. The address you specify doesn't match the name in the server certificate. You get that message sometimes in IE. You can always accept by allowing it in your ValidateCert function ( so always return true). Beter perhaps is to make sure the certs are alright. Or log the certs you allow regarding the error. When placing a breakpoint in de validatecert you can inspect the certs the server is returning. – Ceelie Nov 12 '13 at 20:57
  • I am sure that certificate file is ok because the Java program I have works fine. – Dumbo Nov 12 '13 at 21:32
  • 2
    In the java code you also just trust every certificate: trustAllCerts. Have you tried changing the false to True in the validatecert()? – Ceelie Nov 12 '13 at 22:31
  • Heeeey you were right....changing that to true solved the problem! Thanks... – Dumbo Nov 12 '13 at 23:09
  • @Saeid Yazdani does your java code work for authenticating the server? what is trustAllCerts in your java code – Labeo Sep 07 '15 at 05:41
  • @Saeid Yazdani i am working on authenticating a server using a certificate i am able to do it in c# but not able to do it in java if you help me that would be a great help to me see http://stackoverflow.com/questions/32411475/how-to-implement-below-c-sharp-code-in-java – Labeo Sep 07 '15 at 05:53
  • can you provide your java code that worked for communicating or authenticating a https server because above java code i tried using TrustManager=Null but didnt work i dont know where the problem is so i am asking thank you – Labeo Sep 07 '15 at 15:59
  • @Labeo Sorry I can not help it is a long time since I was working on that project and I do not have the files anymore – Dumbo Sep 07 '15 at 22:59

2 Answers2

5

This answer may not get you all the way there, but it should get you close.

You were given a Java KeyStore (JKS), containing a private key and corresponding certificate. The password to open the JKS according to your code is "123456".

Because the JKS contains a private key, and from looking at your Java code, it leads me to believe you need a 2-way (mutual) SSL connection. That basically means that you as the client authenticate the server AND the server authenticates you. This JKS file is your credential to use the server.

So how do you use this in C#? First, let's convert the JKS to a PKCS12 keystore with this command:

keytool -importkeystore -srckeystore mySrvKeystore -destkeystore mySrvKeystore.p12 -srcstoretype JKS -deststoretype PKCS12

Now, you can import the PKCS12 file into your Windows keystore, which should make it easily accessible from C#. OR, you can import it into an X509Certificate2 object with this code:

X509Certificate2 cert = X509Certificate2("C:\Path\mySrvKeystore.p12", "123456");

Now, you can use either the Windows keystore or the X509Certificate2 object in C# to establish the SSL connection.

gtrig
  • 12,550
  • 5
  • 28
  • 36
  • Thanks, I think this was a good step, atleast I dont get the `CryptographicException` while I was trying with the key before this convertion. No the problem is a new exception when I try to authenticate as client. please check my update above: – Dumbo Nov 10 '13 at 23:30
  • Instead of SslProtocols.Ssl2, try SslProtocols.Tls, SslProtocols.Tls11, or SslProtocols.Tls12. Ssl2 is not compatible with client certificates. – gtrig Nov 11 '13 at 01:05
  • Not really, I wish I could somehow send the files to you :P – Dumbo Nov 11 '13 at 12:25
  • When you did the `keytool -list ...` command, were the "Owner" and "Issuer" values identical or different for the PrivateKeyEntry? And were there any other entries listed in the keystore? – gtrig Nov 11 '13 at 22:53
2

You should use AuthenticateAsClient with a list of certificates:

X509Certificate[] X509Certificates = { certificate };
X509CertificateCollection certsCollection = new X509CertificateCollection(X509Certificates);

sslStream.AuthenticateAsClient(address, certsCollection, SslProtocols.Default, false);

To avoid cert errors: change

  public static bool ValidateCertificate(
   object sender,
   X509Certificate certificate,
   X509Chain chain,
   SslPolicyErrors errors)
   {
        if (errors == SslPolicyErrors.None)
            return true;
        if (certificate != null)
        {
            string SendingCertificateName = "";
            //List<string> Subject = CommaText(certificate.Subject); // decode commalist
            // SendingCertificateName = ExtractNameValue(Subject, "CN"); // get the CN= value
            report = string.Format(CultureInfo.InvariantCulture, "certificatename : {0}, SerialNumber: {1}, {2}, {3}", certificate.Subject, certificate.GetSerialNumberString(), SendingCertificateName, ServerName);
            Console.WriteLine(report);
         }

         Console.WriteLine("Certificate error: {0}", errors);
         int allow = AllowPolicyErrors << 1;  // AllowPolicyErrors property allowing you to pass certain errors
         return (allow & (int)sslPolicyErrors) == (int)sslPolicyErrors;  // or just True if you dont't mind.
    }
Ceelie
  • 241
  • 1
  • 3
  • Thanks for reminding the problem of not accepting all certificates, that was the problem, when I returned true in teh validate method it worked fine! – Dumbo Nov 18 '13 at 15:11