Using X509Certificate with file and key in c#

江枫思渺然 提交于 2019-12-07 05:37:10

问题


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


回答1:


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.
    }



回答2:


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.



来源:https://stackoverflow.com/questions/19840563/using-x509certificate-with-file-and-key-in-c-sharp

易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!