Использование сертификата X509 с файлом и ключом в C#

Я дал сертификат и пароль на сервер, который принимает SSL-соединение. Я пытался установить соединение с этим сервером, но аутентификация не удалась, в основном потому, что я не знаю, как использовать этот файл и пароль, которые мне дали.

Вот мой код:

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

Итак, как вы можете видеть, в моем коде нет кода, который каким-то образом сообщает серверу (TcpClient или SSLStream), что у меня есть этот файл и ключ!

У меня есть 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!

ОБНОВИТЬ

После преобразования ключа в p12 в соответствии с gtrig проблема теперь заключается в IOException в AutheneticateAsClient метод здесь:

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

Также, когда я использую SSlProtocols.Default ошибка: RemoteCertificateNameMismatch, RemoteCertificateChainErrors

2 ответа

Решение

Вы должны использовать AuthenticateAsClient со списком сертификатов:

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

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

Чтобы избежать ошибочных сертификатов: измените

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

Этот ответ, возможно, не поможет вам пройти весь путь, но он должен приблизить вас.

Вам был предоставлен Java KeyStore (JKS), содержащий закрытый ключ и соответствующий сертификат. Пароль для открытия JKS в соответствии с вашим кодом "123456".

Поскольку JKS содержит закрытый ключ, и, глядя на ваш Java-код, я понимаю, что вам нужно двустороннее (взаимное) SSL-соединение. Это в основном означает, что вы как клиент аутентифицируете сервер, а сервер аутентифицирует вас. Этот файл JKS является вашими учетными данными для использования сервера.

Так как вы используете это в C#? Сначала давайте преобразуем JKS в хранилище ключей PKCS12 с помощью этой команды:

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

Теперь вы можете импортировать файл PKCS12 в хранилище ключей Windows, что должно сделать его легко доступным из C#. ИЛИ, вы можете импортировать его в объект X509Certificate2 с помощью этого кода:

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

Теперь вы можете использовать хранилище ключей Windows или объект X509Certificate2 в C# для установления соединения SSL.

Другие вопросы по тегам