Грешка при SSL връзка със самоподписан сертификат (C# Socket се свързва с Java Server Socket)

В началото... Тази програма е само 4-забавна и никога няма да бъде публикувана. Това е само за тестване на Sockets, така че не се интересувайте от сигурността на тази връзка. Кодирах малък месинджър, където можете да въведете съобщение в текстово поле и то ще бъде изпратено до сървъра и от сървъра до всички клиенти. Дотук добре. Просто за забавление исках да използвам SSL за тази връзка. Но когато се опитвам да се свържа със сървърния сокет, той връща RemoteCertificateNameMismatch при ValidateServerCertificate. След като добавих някои съобщения за отстраняване на грешки, открих, че Sslstream.LocalCertificate е null. Как мога да поправя този липсващ сертификат? Използвам самоподписан сертификат.

Моля, помогнете, ето моя код... и да... съжалявам за лошия ми английски :/ :

C# клиент

[...]

             public bool ValidateServerCertificate(
             object sender,
             X509Certificate certificate,
             X509Chain chain,
             SslPolicyErrors sslPolicyErrors)
             {                 
              //sslPolicyErrors returns RemoteCertificateNameMismatch
              return true; //Code shortened
             }

        public X509CertificateCollection getCertificates()
        {

            X509CertificateCollection cCollection = new X509CertificateCollection();
            cCollection.Add(getCertificate());

            return cCollection;
        }

        private X509Certificate getCertificate()
        {
            string Certificate = "D:/Wlad/Programmierung/Zertifikat/CA/CertificateAuthority.crt";
            string ClientCertificatePassword = "...";

            return new X509Certificate(Certificate, ClientCertificatePassword);
        }

        public X509Certificate SelectLocalCertificate(
            object sender,
            string targetHost,
            X509CertificateCollection localCertificates,
            X509Certificate remoteCertificate,
            string[] acceptableIssuers)
        {

            foreach(X509Certificate cer in localCertificates) {
                return cer;
            }

            return getCertificate();
        }

        public void connect(String username)
        {
                XMLConfigManager xml = XMLConfigManager.getInstance();
                String ip = xml.get("ip");


                tc = new Socket(AddressFamily.InterNetwork, SocketType.Stream, ProtocolType.Tcp); ;
                tc.Connect(ip, int.Parse(xml.get("port")));

                networdstream = new NetworkStream(tc);

                using(sslstream = new SslStream(networdstream, false, new RemoteCertificateValidationCallback(ValidateServerCertificate),  new LocalCertificateSelectionCallback(SelectLocalCertificate)))
                {

                        sslstream.AuthenticateAsClient(ip, getCertificates(), SslProtocols.Default, true);

                }



                sendMessage(username);

                t = new Thread(new ThreadStart(checkInput));
                t.Start();
                tm.addThread(t);

                th = new Thread(new ThreadStart(userTimer));
                th.Start();
                tm.addThread(th);

        }

        public void disconnect()
        {
            if(sslstream != null) {
                sslstream.Close();
            }

            if(tc != null) {
                tc.Close();
            }


        }

        [...]

        static byte[] GetBytes(string str)
        {
            byte[] bytes = new byte[str.Length * sizeof(char)];
            System.Buffer.BlockCopy(str.ToCharArray(), 0, bytes, 0, bytes.Length);
            return bytes;
        }

        public void sendMessage(String s)
        {
            try
            {
                if ((s != null) && (s.Length > 0))
                {
                    try
                    {
                        sslstream.Write(GetBytes(s));
                    }
                    catch (Exception)
                    {

                    }

                }
            }
            catch (Exception ex)
            {
                endError(ex);
                return;
            }

        }

        public void userTimer()
        {
            Thread.Sleep(2000);


            sendCommand("usercount");

            th = new Thread(new ThreadStart(userTimer));
            th.Start();
            tm.addThread(th);
        }

        [...]

        static String ReadMessage(SslStream sslStream)
        {
            byte[] buffer = new byte[2048];
            StringBuilder messageData = new StringBuilder();
            int bytes = -1;
            do
            {
                bytes = sslStream.Read(buffer, 0, buffer.Length);

                // Use Decoder class to convert from bytes to UTF8
                // in case a character spans two buffers.
                Decoder decoder = Encoding.UTF8.GetDecoder();
                char[] chars = new char[decoder.GetCharCount(buffer, 0, bytes)];
                decoder.GetChars(buffer, 0, bytes, chars, 0);
                messageData.Append(chars);
            } while (bytes != 0);

            return messageData.ToString();
        }

        public void checkInput()
        {
            try 
            {
                Form2 f2 = f1.f;

                if(!isConnected()) {
                    t.Abort();
                    return;
                }

                String s = ReadMessage(sslstream);

                if(s == null) {
                    t.Abort();
                    return;
                }



                CommandHandler ch = new CommandHandler();
                Boolean handle = ch.handle(s, f1.f);

                if (!handle) {

                    if (s == "Der Benutzername ist schon vergeben.")
                    {
                        endDuplicatename();
                        return;
                    }
                    f2.Invoke(new Action(() => f2.chat.Items.Add(s)));
                    f2.Invoke(new Action(() => f2.select_newest()));

                }




            } 
            finally 
            {
                t = new Thread(new ThreadStart(checkInput));
                t.Start();
                tm.addThread(t);
            }
        }

Java Server Socket

package de.wladhd.server;

import java.net.Socket;

import javax.net.ssl.SSLServerSocket;
import javax.net.ssl.SSLServerSocketFactory;
import javax.net.ssl.SSLSocket;

import de.wladhd.client.Client;
import de.wladhd.client.ClientManager;
import de.wladhd.logs.Log;
import de.wladhd.logs.LogType;

public class DateServer {

    private SSLServerSocket server;
    private boolean running = true;

    private String keyStore = "D:/Wlad/Programmierung/Zertifikat/CA/CertificateAuthority.pfx";
    private String keyStorePassword = "...";
    private String keyStoreType = "PKCS12";


    private String trustStore = "D:/Wlad/Programmierung/Zertifikat/CA/CertificateAuthority.pfx";
    private String trustStorePassword = "...";
    private String trustStoreType = "PKCS12";

    public void execute() throws Exception {
        //System.setProperty("javax.net.debug","all");

        System.setProperty("javax.net.ssl.keyStoreType", keyStoreType);
        System.setProperty("javax.net.ssl.keyStore", keyStore);
        System.setProperty("javax.net.ssl.keyStorePassword", keyStorePassword);

        System.setProperty("javax.net.ssl.trustStoreType", trustStoreType);
        System.setProperty("javax.net.ssl.trustStore", trustStore);
        System.setProperty("javax.net.ssl.trustStorePassword", trustStorePassword);
        //System.setProperty("javax.net.ssl.trustStore", "de.wladhd.server.Trusting");

        SSLServerSocketFactory serverFactory = (SSLServerSocketFactory) SSLServerSocketFactory.getDefault();

        if(serverFactory == null) {
            new Log("Error occured... Server Factory == null!", LogType.ERROR);
            return;
        }

        server = (SSLServerSocket) serverFactory.createServerSocket(9090);




        running = true;

        new Log("Server now running on query: " + server.getInetAddress().getHostAddress() + ":" + server.getLocalPort(), LogType.INFO);

        while (running) {
            try {
                if(server.isClosed()) {
                    return;
                }

                new Log("Client connecting...", LogType.DEBUG);

                final Socket rawsocket = server.accept();

                if(!(rawsocket instanceof SSLSocket)) {
                    new Log("Client isnt an instance of SSLSocket!", LogType.DEBUG);
                    return; 
                }

                final SSLSocket socket = (SSLSocket) rawsocket;

                try {
                    socket.startHandshake();
                } catch (Exception ex) {

                }


                new Log("Client - Connected: " + socket.isConnected(), LogType.DEBUG);

                new Log("Client - Protocol: " + socket.getSession().getProtocol(), LogType.DEBUG);

                new Log("Client - Session valid: " + socket.getSession().isValid(), LogType.DEBUG);

                new Log("Client - CipherSuite: " + socket.getSession().getCipherSuite(), LogType.DEBUG);

                new Log("Client - NeedClientAuth: " + socket.getNeedClientAuth(), LogType.DEBUG);

                new Log("Client - WantClientAuth: " + socket.getWantClientAuth(), LogType.DEBUG);

                Client c = new Client(socket);

                //socket.getHandshakeSession() returns null and peer not authenticated exception...

            } catch (Exception ex) {
                ex.printStackTrace();
                continue;
            }
        } 

    }

    public void disconnect() throws Exception {
        new Log("Server disconnecting...", LogType.INFO);
        setRunning(false);

        ClientManager.getInstance().disconnectClients();

        if(server != null) {
            server.close();
        }

        new Log("Server successfully disconnected!", LogType.INFO);

    }

    public boolean isRunning() {
        return running;
    }

    public void setRunning(boolean state) {
        running = state;
    }


}

Ако имате нужда от нещо, моля, кажете ми...


person Wlad4Coding    schedule 13.05.2015    source източник
comment
някой току-що зададе подобен въпрос - stackoverflow.com /questions/30224607/   -  person ZhongYu    schedule 14.05.2015
comment
вие обаче използвате C# от страна на клиента, за което не знам. Но основните проблеми са същите - и мисля, че можете лесно да потърсите в Google изключението.   -  person ZhongYu    schedule 14.05.2015
comment
Това не ми помогна... Опитах се да init() SSLContext... но това ми дава грешка. и мисля, че можете лесно да потърсите в Google изключението -› Бях... но не намерих нищо полезно.   -  person Wlad4Coding    schedule 14.05.2015


Отговори (1)


Грешката в правилата RemoteCertificateNameMismatch не е свързана с липсващ локален сертификат. Тази грешка просто ви казва, че SAN (алтернативно име на субект) в получения сертификат на сървъра или, ако няма SAN, тогава общото име на името на субекта на получения сертификат на сървъра, НЕ съответства на името на хоста, към който се свързвате. Можете да игнорирате тази грешка, ако не очаквате да видите името на хоста в сертификата на едно от тези две места.

Предполагам, че нямате локален сертификат, защото зареждате сертификата от файл и няма съответстващ частен ключ за този сертификат в хранилището на частни ключове на Windows.

Опитайте да импортирате сертификат и частен ключ (например от PFX файл, който съдържа и сертификата, и частния ключ) с помощта на MMC в хранилището на лични сертификати на локалния компютър и след това заредете този сертификат тук, като използвате .NET API за съхранение на сертификати. Докато имате достъп до частния ключ (което ще имате, ако вие сте потребителят на Windows, импортирал PFX), тогава трябва да можете да се свържете с този клиентски сертификат.

Във вашия код, вместо да четете сертификата от файл, заредете го от хранилището на сертификати с нещо подобно:

var store = new X509Store(StoreName.My, StoreLocation.CurrentUser);

store.Open(OpenFlags.ReadOnly | OpenFlags.OpenExistingOnly);

try
{
  X509Certificate2Collection collection = store.Certificates.Find(X509FindType.FindByThumbprint, thumbprint, false);
  return TakeFirstCertificate(collection);
}
finally
{
  store.Close();
}

Можете да го потърсите, като използвате нещо друго като този отпечатък - това е само пример. Това е, което връща SelectLocalCertificate.

person Jim Flood    schedule 14.05.2015
comment
Благодаря за страхотния отговор! Отивам да проверя това. Ще ви кажа дали това работи при мен :) - person Wlad4Coding; 14.05.2015
comment
Съжалявам... наистина съм нов в кодирането на C#. Преди кодирах само на Java... Но как мога да настроя сертификата? Добавих .pfx към секцията за съхранение на лични сертификати, но LocalCertificate все още е null... Освен това потърсих .NET Certificate Api, за да присвоя сертификата на моето приложение, но не намерих нищо... - person Wlad4Coding; 14.05.2015
comment
@Wlad4Coding Актуализирах отговора си с някакъв C# код, за да намеря сертификат от хранилището на лични сертификати на текущия потребител. - person Jim Flood; 15.05.2015