Java 在 C# 中将 X509Certificate 与文件和密钥一起使用

声明:本页面是StackOverFlow热门问题的中英对照翻译,遵循CC BY-SA 4.0协议,如果您需要使用它,必须同样遵循CC BY-SA许可,注明原文地址和作者信息,同时你必须将它归于原作者(不是我):StackOverFlow 原文地址: http://stackoverflow.com/questions/19840563/
Warning: these are provided under cc-by-sa 4.0 license. You are free to use/share it, But you must attribute it to the original authors (not me): StackOverFlow

提示:将鼠标放在中文语句上可以显示对应的英文。显示中英文
时间:2020-08-12 20:36:56  来源:igfitidea点击:

Using X509Certificate with file and key in c#

c#javassl-certificatex509certificate

提问by Saeid Yazdani

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.

我已将证书和密码提供给接受 ssl 连接的服务器。我试图与该服务器建立连接,但身份验证失败,主要是因为我不知道如何使用获得的文件和密码。

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!

如您所见,我的代码中没有任何代码以某种方式告诉服务器(TcpClient 或 SSLStream)我确实拥有此文件和密钥!

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!

我有一个连接到服务器的 javacode 没有问题,但我还没有将它转换为 c#。任何帮助都会很棒!

    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 gtrigthe problem now is IOException in AutheneticateAsClientmethod here:

根据gtrig将密钥转换为 p12 后,现在的问题是AutheneticateAsClient这里的方法中的IOException :

        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.Defaultthe error is: RemoteCertificateNameMismatch, RemoteCertificateChainErrors

此外,当我使用SSlProtocols.Default错误是:RemoteCertificateNameMismatch, RemoteCertificateChainErrors

采纳答案by Ceelie

You should use AuthenticateAsClient with a list of certificates:

您应该将 AuthenticateAsClient 与证书列表一起使用:

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

回答by gtrig

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

您获得了一个 Java KeyStore (JKS),其中包含一个私钥和相应的证书。根据您的密码打开 JKS 的密码是“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.

因为 JKS 包含一个私钥,并且通过查看您的 Java 代码,我相信您需要一个 2 向(相互)SSL 连接。这基本上意味着您作为客户端对服务器进行身份验证并且服务器对您进行身份验证。此 JKS 文件是您使用服务器的凭据。

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

那么如何在 C# 中使用它呢?首先,让我们使用以下命令将 JKS 转换为 PKCS12 密钥库:

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 X509Certificate2object with this code:

现在,您可以将 PKCS12 文件导入到您的 Windows 密钥库中,这样应该可以轻松地从 C# 访问它。或者,您可以使用以下代码将其导入X509Certificate2对象:

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.

现在,您可以使用 C# 中的 Windows 密钥库或 X509Certificate2 对象来建立 SSL 连接。