Это правильный способ реализации SSL-сокетов в Java? - PullRequest
0 голосов
/ 31 января 2019

Я пытаюсь реализовать защищенные соединения Socket между Spring Server и клиентом Android, но обнаружил некоторые проблемы с дизайном.

Вначале я реализовал решение, предоставленное в этого урока (это на испанском языке, но это легко понять с помощью Google Traductor).Это руководство защищает сокет-соединения с ключом на каждой стороне (по одному для сервера и по одному для клиента) и использует trustedKeys.jks для хранения доверенных ключей на каждой стороне.Это означает, что для каждого клиента я должен:

  1. Создать новое хранилище ключей для каждого нового клиента
  2. Добавить этот новый ключ клиента в доверенные ключи на стороне сервера
  3. Добавитьхранилище ключей сервера для каждого нового клиента

Мне показалось нереальным, учитывая рост числа клиентов.

Я нашел другой подход, который лучше подходит моим требованиям, он использует сертификат серверакак открытый ключ (я думаю) и шифрует данные, которые будут расшифрованы хранилищем ключей сервера:

Код сервера

public SSLServerSocket getSSLServerSocket(int port) throws IOException, ...
{
        KeyStore keyStore = KeyStore.getInstance("JKS");
        keyStore.load(resourceKeyFile.getInputStream(), keystorePassword.toCharArray());

        KeyManagerFactory kmf = KeyManagerFactory.getInstance(KeyManagerFactory.getDefaultAlgorithm());
        kmf.init(keystore, keystorePassword.toCharArray());

        SSLContext sc = SSLContext.getInstance("SSL");
        sc.init(kmf.getKeyManagers(), null, null);

        SSLServerSocketFactory sslServerSocketfactory = sc.getServerSocketFactory();
        return (SSLServerSocket) sslServerSocketfactory.createServerSocket(port);
}

Вот моя функция для создания SSLServerSocket.В инициализации KeyManagerFactory kmf.init(keyStore, keystorePassword.toCharArray()); я установил хранилище ключей сервера keystore с его паролем keystorePassword, который читается с помощью @Value Spring Annotation вне функции.Эта функция вернет SSLServerSocket, который будет принимать клиентские соединения в новом потоке, например:

    while(true) {
        SSLSocket clientSocket = (SSLSocket) serverSocket.accept();
    }

Код клиента

    SSLContext context = socketUtil.createSSLContext(); 
    SSLSocketFactory sf = context.getSocketFactory();

    SSLSocket socket = (SSLSocket) sf.createSocket(serverUrl, port);

Этот код создает контекст, который я хочу использовать, который представленниже, и создает сокет с адресом serverUrl и портом port:

public final SSLContext createSSLContext()
            throws Exception {

        CertificateFactory cf = CertificateFactory.getInstance("X.509");
        InputStream in = getClass().getResourceAsStream("serverCert.pem");
        KeyStore trustStore = KeyStore.getInstance("JKS");
        trustStore.load(null);
        try {
            X509Certificate cacert = (X509Certificate) cf.generateCertificate(in);
            trustStore.setCertificateEntry("serverKey", cacert);
        } finally {
            IOUtils.closeQuietly(in);
        }

        TrustManagerFactory tmf = TrustManagerFactory.getInstance("SunX509");
        tmf.init(trustStore);

        SSLContext sslContext = SSLContext.getInstance("SSL");
        sslContext.init(null, tmf.getTrustManagers(), new SecureRandom());
        return sslContext;
    }

Здесь я загружаю сертификат сервера serverCert.pem, который я сгенерировал из хранилища ключей с помощью утилиты keytool,Затем устанавливаются другие параметры, такие как алгоритм или протокол.Таким образом, сокет будет доверять соединениям от издателя сертификата.

Код работает просто замечательно и гладко, но я обеспокоен безопасностью и хорошими практиками.Итак, еще раз, это хороший подход для решения моей проблемы?

Любые разъяснения о процессе, коде или недоразумениях приветствуются.Большое вам спасибо!

...