Java - 仅具有TLS的SSLServerSocket

时间:2015-02-26 13:15:41

标签: java sockets ssl java-8

我正在尝试使用自定义密钥库/信任库打开SSLServerSocket并且仅启用TLSv1.2。 这是我打开这种套接字的相关代码:

SSLContext sslContext = null;
ServerSocket serverSocket = null;
KeyManagerFactory kmf = null;
KeyStore keystore = loadKeyStore(KEYSTORE_FILE);
if (keystore == null) {
    // throw exception
}
char[] psw = System.console().readPassword("Enter password for the key materials in file \"%s\":", KEYSTORE_FILE);
try {
    kmf = KeyManagerFactory.getInstance("PKIX");
    kmf.init(keystore, psw);
} catch (NoSuchAlgorithmException | UnrecoverableKeyException | KeyStoreException e) {
    e.printStackTrace();
    kmf = null;
    // throw exception
}
try {
    sslContext = SSLContext.getInstance("TLSv1.2");
    System.out.println(kmf==null); // prints false
    sslContext.init(kmf==null?null:kmf.getKeyManagers(), null, null);
} catch (NoSuchAlgorithmException | KeyManagementException e) {
    // throw exception
}

try {
    serverSocket = sslContext.getServerSocketFactory().createServerSocket(PORT, BACKLOG, HOST);
    ((SSLServerSocket)serverSocket).setEnabledProtocols(new String[]{"TLSv1.2"});
} catch (IOException e) {
    // throw exception
}

loadKeyStore函数是,

private static KeyStore loadKeyStore(String filename) {
    KeyStore keystore = null;
    FileInputStream fis = null;
    try {
        keystore = KeyStore.getInstance("JKS");
        char[] psw = System.console().readPassword("Enter password for the KeyStore file \"%s\":", filename);
        if (psw != null) {
            fis = new FileInputStream(filename);
            keystore.load(fis, psw);
        }
    } catch (KeyStoreException | NoSuchAlgorithmException | CertificateException | IOException e) {
        keystore = null;
        LogManager.getLogger().fatal("cannot load KeyStore from file \"" + filename + "\".", e);
    } finally {
        if (fis != null) {
            try {
                fis.close();
            } catch (IOException e) {
                LogManager.getLogger().error("cannot close file " + filename, e);
            }
            fis = null;
        }
    }
    return keystore;
}

我接受另一个线程中的连接

while (!stopped) {
    Socket socket = null;
    try {
        socket = serverSocket.accept();
    } catch (IOException e) {
        if (!stopped) {
            logger.error("exception while accepting connections.", e);
        }
        break;
    }
    // start new threads to handle this connection
}

问题是,当我在Firefox上输入https://HOST:PORT时,它说:

  

Firefox无法保证数据在HOST上的安全性,因为它   使用 SSLv3 ,这是一个破坏的安全协议。高级信息:   ssl_error_no_cypher_overlap

如何打开仅接受TLSv1.2连接的服务器套接字?

P.S。我已经尝试将代码中的“TLSv1.2”字符串逐个更改为“TLS”,但没有任何改变。

编辑:我按如下方式编辑了代码:

serverSocket = sslContext.getServerSocketFactory().createServerSocket(port, backlog, host);
((SSLServerSocket)serverSocket).setEnabledProtocols(new String[]{"TLSv1.2"});
for (String s: ((SSLServerSocket)serverSocket).getEnabledCipherSuites()) {
    System.out.println(s);
}

,输出为,

  

TLS_ECDHE_ECDSA_WITH_AES_128_CBC_SHA256   TLS_ECDHE_RSA_WITH_AES_128_CBC_SHA256 TLS_RSA_WITH_AES_128_CBC_SHA256   TLS_ECDH_ECDSA_WITH_AES_128_CBC_SHA256   TLS_ECDH_RSA_WITH_AES_128_CBC_SHA256   TLS_DHE_RSA_WITH_AES_128_CBC_SHA256   TLS_DHE_DSS_WITH_AES_128_CBC_SHA256   TLS_ECDHE_ECDSA_WITH_AES_128_CBC_SHA   TLS_ECDHE_RSA_WITH_AES_128_CBC_SHA TLS_RSA_WITH_AES_128_CBC_SHA   TLS_ECDH_ECDSA_WITH_AES_128_CBC_SHA TLS_ECDH_RSA_WITH_AES_128_CBC_SHA   TLS_DHE_RSA_WITH_AES_128_CBC_SHA TLS_DHE_DSS_WITH_AES_128_CBC_SHA   TLS_ECDHE_ECDSA_WITH_AES_128_GCM_SHA256   TLS_ECDHE_RSA_WITH_AES_128_GCM_SHA256 TLS_RSA_WITH_AES_128_GCM_SHA256   TLS_ECDH_ECDSA_WITH_AES_128_GCM_SHA256   TLS_ECDH_RSA_WITH_AES_128_GCM_SHA256   TLS_DHE_RSA_WITH_AES_128_GCM_SHA256   TLS_DHE_DSS_WITH_AES_128_GCM_SHA256   TLS_ECDHE_ECDSA_WITH_3DES_EDE_CBC_SHA   TLS_ECDHE_RSA_WITH_3DES_EDE_CBC_SHA SSL_RSA_WITH_3DES_EDE_CBC_SHA   TLS_ECDH_ECDSA_WITH_3DES_EDE_CBC_SHA   TLS_ECDH_RSA_WITH_3DES_EDE_CBC_SHA SSL_DHE_RSA_WITH_3DES_EDE_CBC_SHA   SSL_DHE_DSS_WITH_3DES_EDE_CBC_SHA TLS_ECDHE_ECDSA_WITH_RC4_128_SHA   TLS_ECDHE_RSA_WITH_RC4_128_SHA SSL_RSA_WITH_RC4_128_SHA   TLS_ECDH_ECDSA_WITH_RC4_128_SHA TLS_ECDH_RSA_WITH_RC4_128_SHA   SSL_RSA_WITH_RC4_128_MD5 TLS_EMPTY_RENEGOTIATION_INFO_SCSV

我不确定,但似乎问题不在于缺少启用的密码套件。正确?

EDIT2:我尝试了openssl s_client -connect HOST:PORT,结果是 output

1 个答案:

答案 0 :(得分:8)

(有一个非常相似的问题,我answered here。)

基本上,SSLContext.getInstance("TLSv1.2")也可以返回支持其他协议的实例。

如果您想使用一组特定的协议,则需要使用setEnabledProtocols(...),这是您在首次编辑后所做的工作。您现在获得一些名称以SSL_开头的密码套件,但这只是名称,这些仍然适用于TLS 1.2。正如Java Cryptography Architecture Standard Algorithm Name Documentation for JDK 8所说:

  

在TLSv1.0最终确定之前定义了一些JSSE密码套件名称,因此给出了SSL_前缀。前缀为TLS_的TLS RFC中提到的名称在功能上等同于前缀为SSL _的JSSE密码套件。

您的上一个问题(“没有可用的对等证书”,以及握手失败)似乎表明在您尝试使用的密钥库中找不到证书(带有私钥)

实际上,虽然您提到的密码套件已启用,但如果无法使用它们,它们将自动禁用。所有这些都是RSA或DSS密码套件,这意味着他们需要一个带有RSA或DSA密钥的证书 其私钥可用。如果在密钥库中找不到具有私钥条目的证书,则KeyManagerSSLContext将无法使用该证书。因此,当实际尝试握手时,它们将被禁用。这通常会导致在服务器端握手中间抛出异常(“javax.net.ssl.SSLHandshakeException: no cipher suites in common”),以及通过OpenSSL在客户端获得的错误消息。