Я пытаюсь реализовать защищенные соединения Socket между Spring Server и клиентом Android, но обнаружил некоторые проблемы с дизайном.
Вначале я реализовал решение, предоставленное в этого урока (это на испанском языке, но это легко понять с помощью Google Traductor).Это руководство защищает сокет-соединения с ключом на каждой стороне (по одному для сервера и по одному для клиента) и использует trustedKeys.jks для хранения доверенных ключей на каждой стороне.Это означает, что для каждого клиента я должен:
- Создать новое хранилище ключей для каждого нового клиента
- Добавить этот новый ключ клиента в доверенные ключи на стороне сервера
- Добавитьхранилище ключей сервера для каждого нового клиента
Мне показалось нереальным, учитывая рост числа клиентов.
Я нашел другой подход, который лучше подходит моим требованиям, он использует сертификат серверакак открытый ключ (я думаю) и шифрует данные, которые будут расшифрованы хранилищем ключей сервера:
Код сервера
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
,Затем устанавливаются другие параметры, такие как алгоритм или протокол.Таким образом, сокет будет доверять соединениям от издателя сертификата.
Код работает просто замечательно и гладко, но я обеспокоен безопасностью и хорошими практиками.Итак, еще раз, это хороший подход для решения моей проблемы?
Любые разъяснения о процессе, коде или недоразумениях приветствуются.Большое вам спасибо!