1

我正在尝试构建一个使用基于 ECC 的证书的服务器。到目前为止,我已经采取了以下步骤:

1) 使用 openssl 创建签名的服务器证书:

openssl ecparam -param_enc explicit -out server\server.key.pem -name secp256k1 -genkey
openssl req -new -nodes -key server\server.key.pem -outform pem -out server\server.csr -sha256
openssl ca -keyfile CA\private\ca.key.pem -cert CA\ca.cert.pem -in server\server.csr -out server\server-cert.pem -md sha256 -outdir .\server
openssl pkcs12 -export -out server\server-cert.p12 -inkey server\server.key.pem -in server\server-cert.pem -certfile CA\ca.cert.pem

ca.sert.pem 是用于签署服务器证书的自签名 CA 证书。它同样生成:

openssl ecparam -param_enc explicit -out CA\private\ca.key.pem -name secp256k1 -genkey 
openssl req -new -key CA\private\ca.key.pem -x509 -nodes -days 365 -outform pem -sha256 -out CA\ca.cert.pem

2)设置服务器

    public void startServer() {
    threadPool = (ThreadPoolExecutor) Executors.newCachedThreadPool();
    threadPool.setCorePoolSize(20);

    try {
        SSLContext sslContext = getSSLContext();
  SSLServerSocketFactory ssf = sslContext.getServerSocketFactory();
  SSLServerSocket serverSocket = (SSLServerSocket) ssf.createServerSocket(8888);
        String[] ciphers = {"TLS_ECDHE_ECDSA_WITH_AES_128_CBC_SHA256"};
  serverSocket.setEnabledCipherSuites(ciphers);
  serverSocket.setEnabledProtocols(new String[] {"TLSv1.2"});

  while (true) {
    SSLSocket clientSocket = (SSLSocket) serverSocket.accept();
    threadPool.execute(new ClientSession(clientSocket));
  }

    } catch (Exception e) {
        e.printStackTrace();
    }
}

    protected SSLContext getSSLContextForGWA() throws Exception {
    SSLContext sslContext = SSLContext.getInstance("TLS");
    try {
        KeyStore ks = KeyStore.getInstance("PKCS12", "BC");
        File keyFile = new File(System.getProperty("user.dir") + File.separator + "certs" + File.separator + "server" + File.separator + "server-cert.p12");
        FileInputStream keyInput = new FileInputStream(keyFile);
        ks.load(keyInput, keyStorePassword.toCharArray());
        keyInput.close();

        // Set up key manager factory to use our key store
        KeyManagerFactory kmf = KeyManagerFactory.getInstance(KeyManagerFactory.getDefaultAlgorithm());
        kmf.init(ks, keyStorePassword.toCharArray());

        // Initialize the SSLContext to work with our key managers.
        sslContext.init(kmf.getKeyManagers(), null, null);

        return sslContext;

    } catch (Exception e) {
        e.printStackTrace();
    }

    return null;
}

备注:我只成功使用 BouncyCastle 将服务器证书加载到密钥库中。否则我会得到“仅支持命名的 ECParameters”错误。

3) 设置客户端 客户端设置类似,只是将服务器证书导入到信任库中。

    SSLContext sslContext = getSSLContextForSMGW();
    SSLSocketFactory sf = sslContext.getSocketFactory();

    SSLSocket clientSocket = (SSLSocket) sf.createSocket(host, port);
    String[] ciphers = {"TLS_ECDHE_ECDSA_WITH_AES_128_CBC_SHA256"};
    clientSocket.setEnabledCipherSuites(ciphers);
    clientSocket.setEnabledProtocols(new String[] {"TLSv1.2"});
    printClientSocketInfo(clientSocket);
    BufferedReader in = new BufferedReader(new InputStreamReader(clientSocket.getInputStream()));
    BufferedWriter out = new BufferedWriter(new OutputStreamWriter(clientSocket.getOutputStream()));

String line = null;
while (((line = in.readLine()) != null)) {
  System.out.println(line);
  break;
}

现在,当客户端连接到服务器时,我得到以下错误堆栈:

Caused by: java.io.IOException: Only named ECParameters supported
at sun.security.ec.ECParameters.engineInit(ECParameters.java:150)
at java.security.AlgorithmParameters.init(AlgorithmParameters.java:293)
at sun.security.x509.AlgorithmId.decodeParams(AlgorithmId.java:132)
at sun.security.x509.AlgorithmId.<init>(AlgorithmId.java:114)
at sun.security.x509.AlgorithmId.parse(AlgorithmId.java:372)
at sun.security.x509.X509Key.parse(X509Key.java:168)
at sun.security.x509.CertificateX509Key.<init>(CertificateX509Key.java:75)
at sun.security.x509.X509CertInfo.parse(X509CertInfo.java:667)
at sun.security.x509.X509CertInfo.<init>(X509CertInfo.java:167)
... 15 more

我猜,客户端无法验证服务器证书(与我不使用 BouncyCastle 导入证书相同的错误)。

我已经打印了命名的 EC 曲线

        System.out.println("Named EC-Curves:");
    String[] curves = Security.getProvider("SunEC")
            .getProperty("AlgorithmParameters.EC SupportedCurves")
            .split("\\|");
    for (String curve : curves) {
        System.out.println(curve.substring(1, curve.indexOf(",")));
    }

它清楚地输出了其中的名称“secp256k1”。那么我在这里错过了什么?或者我怎样才能让 SSLContext 使用 CertificateX509Key 的另一个实现?

客户端的调试输出为:

*** ClientHello, TLSv1.2
RandomCookie:  GMT: 1427463640 bytes = { 227, 188, 124, 163, 124, 55, 134, 199, 234, 241, 234, 51, 8, 208, 199, 117, 90, 125, 214, 134, 226, 110, 149, 92, 194, 25, 70, 59 }
Session ID:  {}
Cipher Suites: [TLS_ECDHE_ECDSA_WITH_AES_128_CBC_SHA256]
Compression Methods:  { 0 }
Extension elliptic_curves, curve names: {secp256r1, sect163k1, sect163r2, secp192r1, secp224r1, sect233k1, sect233r1, sect283k1, sect283r1, secp384r1, sect409k1, sect409r1, secp521r1, sect571k1, sect571r1, secp160k1, secp160r1, secp160r2, sect163r1, secp192k1, sect193r1, sect193r2, secp224k1, sect239k1, secp256k1}
Extension ec_point_formats, formats: [uncompressed]
Extension signature_algorithms, signature_algorithms: SHA512withECDSA, SHA512withRSA, SHA384withECDSA, SHA384withRSA, SHA256withECDSA, SHA256withRSA, SHA224withECDSA, SHA224withRSA, SHA1withECDSA, SHA1withRSA, SHA1withDSA, MD5withRSA
Extension renegotiation_info, renegotiated_connection: <empty>
***
main, WRITE: TLSv1.2 Handshake, length = 144
main, READ: TLSv1.2 Handshake, length = 1540
*** ServerHello, TLSv1.2
RandomCookie:  GMT: 1427463640 bytes = { 155, 184, 16, 18, 186, 249, 50, 70, 182, 75, 129, 217, 165, 78, 228, 255, 138, 98, 193, 58, 229, 70, 45, 1, 244, 84, 187, 235 }
Session ID:  {85, 21, 94, 216, 168, 186, 180, 208, 21, 40, 84, 147, 20, 244, 43, 97, 214, 18, 173, 27, 52, 175, 44, 1, 127, 20, 62, 126, 65, 229, 79, 1}
Cipher Suite: TLS_ECDHE_ECDSA_WITH_AES_128_CBC_SHA256
Compression Method: 0
Extension renegotiation_info, renegotiated_connection: <empty>
***
%% Initialized:  [Session-1, TLS_ECDHE_ECDSA_WITH_AES_128_CBC_SHA256]
** TLS_ECDHE_ECDSA_WITH_AES_128_CBC_SHA256
main, handling exception: javax.net.ssl.SSLProtocolException: java.io.IOException: Only named ECParameters supported
%% Invalidated:  [Session-1, TLS_ECDHE_ECDSA_WITH_AES_128_CBC_SHA256]
main, SEND TLSv1.2 ALERT:  fatal, description = unexpected_message
main, WRITE: TLSv1.2 Alert, length = 2
main, called closeSocket()
main, IOException in getSession():  javax.net.ssl.SSLProtocolException: java.io.IOException: Only named ECParameters supported
   Cipher suite = SSL_NULL_WITH_NULL_NULL
   Protocol = NONE

该曲线也在曲线名称中列出。我现在尝试了很多东西,我有点卡住了。非常感谢任何帮助。

4

0 回答 0