【问题标题】:TLS 1.3 server socket with Java 11 and self-signed certificates带有 Java 11 和自签名证书的 TLS 1.3 服务器套接字
【发布时间】:2021-05-27 10:09:53
【问题描述】:

我想创建一个简单的 TLS 1.3 服务器套接字侦听器,它使用自签名证书。

首先我创建了一个 RSA 公钥/私钥对:

openssl req -x509 -newkey rsa:2048 -sha256 -days 9125 -nodes -keyout test.key -out test.crt

我在一个简单的 Java 程序中将公钥和私钥作为字符串添加

public class TlsServer
{

    private static final String TEST_CRT = "-----BEGIN CERTIFICATE-----\n"
        // ...
        + "-----END CERTIFICATE-----";

    private static final String TEST_KEY
        = "-----BEGIN PRIVATE KEY-----\n"
        // ...
        + "-----END PRIVATE KEY-----";

    private static final int PORT = 32333;

    private static final String TLS_PROTOCOL = "TLSv1.3";

    private static final String[] CIPHER_SUITES = new String[]
    {
        "TLS_AES_128_GCM_SHA256",
        "TLS_AES_256_GCM_SHA384"
    };

    public static void main(String[] args) throws Exception
    {
        new TlsServer().start();
    }

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

        SSLContext ctx = SSLContext.getInstance(TLS_PROTOCOL);

        // Init Key Store.
        char[] password = "changeit".toCharArray();
        KeyStore ks = KeyStore.getInstance(KeyStore.getDefaultType());
        ks.load(null, password);
        Certificate cert = convertCertificate(TEST_CRT);
        ks.setCertificateEntry("test_crt", cert);
        Key pk = new SecretKeySpec(TEST_KEY.getBytes(StandardCharsets.ISO_8859_1), "RSA");
        ks.setKeyEntry("test_key", pk, password, new Certificate[]
        {
            cert
        });

        // Init Key Manager Factory.
        KeyManagerFactory kmf = KeyManagerFactory.getInstance(KeyManagerFactory.getDefaultAlgorithm());
        kmf.init(ks, password);

        // Init Trust Manager Factory.
        TrustManagerFactory tmf = TrustManagerFactory.getInstance(TrustManagerFactory.getDefaultAlgorithm());
        tmf.init(ks);

        ctx.init(kmf.getKeyManagers(), tmf.getTrustManagers(), null);

        SSLServerSocket serverSocket = (SSLServerSocket) ctx.getServerSocketFactory().createServerSocket(PORT);
        serverSocket.setNeedClientAuth(false);
        serverSocket.setEnabledProtocols(new String[]
        {
            TLS_PROTOCOL
        });
        serverSocket.setEnabledCipherSuites(CIPHER_SUITES);

        System.out.printf("Server started on port %d%n", PORT);
        while (true)
        {
            try (SSLSocket socket = (SSLSocket) serverSocket.accept())
            {
                System.out.println("Accept new connection: " + socket.getRemoteSocketAddress());
                InputStream is = new BufferedInputStream(socket.getInputStream());
                OutputStream os = new BufferedOutputStream(socket.getOutputStream());
                byte[] data = new byte[2048];
                int len = is.read(data);
                if (len <= 0)
                {
                    throw new IOException("No data received");
                }
                System.out.printf("Server received %d bytes: %s%n", len, new String(data, 0, len));
                os.write(data, 0, len);
                os.flush();
            }
            catch (Exception e)
            {
                e.printStackTrace();
            }
        }
    }

    private Certificate convertCertificate(String cert) throws CertificateException
    {
        InputStream in = new ByteArrayInputStream(cert.getBytes(StandardCharsets.ISO_8859_1));
        CertificateFactory certFactory = CertificateFactory.getInstance("X.509");
        return certFactory.generateCertificate(in);
    }
}

Java 版本

openjdk version "11.0.11" 2021-04-20
OpenJDK Runtime Environment AdoptOpenJDK-11.0.11+9 (build 11.0.11+9)
OpenJDK 64-Bit Server VM AdoptOpenJDK-11.0.11+9 (build 11.0.11+9, mixed mode)

服务器启动并监听连接,但如果我尝试使用客户端(例如 curl、Chrome 或简单的 Java 客户端)访问服务器,则会收到以下错误:

Server started on port 32333
Accept new connection: /0:0:0:0:0:0:0:1:49966
javax.net.ssl.SSLHandshakeException: No available authentication scheme
    at java.base/sun.security.ssl.Alert.createSSLException(Alert.java:131)
    at java.base/sun.security.ssl.Alert.createSSLException(Alert.java:117)
    at java.base/sun.security.ssl.TransportContext.fatal(TransportContext.java:336)
    at java.base/sun.security.ssl.TransportContext.fatal(TransportContext.java:292)
    at java.base/sun.security.ssl.TransportContext.fatal(TransportContext.java:283)
    at java.base/sun.security.ssl.CertificateMessage$T13CertificateProducer.onProduceCertificate(CertificateMessage.java:972)
    at java.base/sun.security.ssl.CertificateMessage$T13CertificateProducer.produce(CertificateMessage.java:961)
    at java.base/sun.security.ssl.SSLHandshake.produce(SSLHandshake.java:436)
    at java.base/sun.security.ssl.ClientHello$T13ClientHelloConsumer.goServerHello(ClientHello.java:1234)
    at java.base/sun.security.ssl.ClientHello$T13ClientHelloConsumer.consume(ClientHello.java:1170)
    at java.base/sun.security.ssl.ClientHello$ClientHelloConsumer.onClientHello(ClientHello.java:852)
    at java.base/sun.security.ssl.ClientHello$ClientHelloConsumer.consume(ClientHello.java:813)
    at java.base/sun.security.ssl.SSLHandshake.consume(SSLHandshake.java:392)
    at java.base/sun.security.ssl.HandshakeContext.dispatch(HandshakeContext.java:443)
    at java.base/sun.security.ssl.HandshakeContext.dispatch(HandshakeContext.java:421)
    at java.base/sun.security.ssl.TransportContext.dispatch(TransportContext.java:182)
    at java.base/sun.security.ssl.SSLTransport.decode(SSLTransport.java:171)
    at java.base/sun.security.ssl.SSLSocketImpl.decode(SSLSocketImpl.java:1418)
    at java.base/sun.security.ssl.SSLSocketImpl.readHandshakeRecord(SSLSocketImpl.java:1324)
    at java.base/sun.security.ssl.SSLSocketImpl.startHandshake(SSLSocketImpl.java:440)
    at java.base/sun.security.ssl.SSLSocketImpl.ensureNegotiated(SSLSocketImpl.java:829)
    at java.base/sun.security.ssl.SSLSocketImpl$AppInputStream.read(SSLSocketImpl.java:920)
    at java.base/java.io.BufferedInputStream.fill(BufferedInputStream.java:252)
    at java.base/java.io.BufferedInputStream.read1(BufferedInputStream.java:292)
    at java.base/java.io.BufferedInputStream.read(BufferedInputStream.java:351)
    at java.base/java.io.FilterInputStream.read(FilterInputStream.java:107)
    at TlsServer.start(TlsServer.java:143)
    at TlsServer.main(TlsServer.java:95)

调试输出如下所示:

javax.net.ssl|DEBUG|01|main|2021-05-27 12:02:13.010 CEST|ServerHello.java:577|Produced ServerHello handshake message (
"ServerHello": {
  "server version"      : "TLSv1.2",
  "random"              : "78 B9 53 93 89 F9 8F 8C 2B 03 33 72 5D 21 4D A5 DC 42 59 CE 54 24 CD 75 9F 26 15 09 FB 53 91 00",
  "session id"          : "EC 09 75 BD 7F 00 40 CB 9F DC F4 BA FD 03 57 82 6C D0 6E 4C 2D FD 79 AA 07 91 86 A8 1C A8 99 38",
  "cipher suite"        : "TLS_AES_128_GCM_SHA256(0x1301)",
  "compression methods" : "00",
  "extensions"          : [
    "supported_versions (43)": {
      "selected version": [TLSv1.3]
    },
    "key_share (51)": {
      "server_share": {
        "named group": x25519
        "key_exchange": {
          0000: B3 C2 7D 1D 77 D1 CE 3F   1D C2 15 0A 3A 21 B5 3B  ....w..?....:!.;
          0010: ED D0 BB 79 D1 C0 88 46   98 71 DD 2D 62 40 E1 1E  ...y...F.q.-b@..
        }
      },
    }
  ]
}
)
javax.net.ssl|DEBUG|01|main|2021-05-27 12:02:13.010 CEST|SSLSocketOutputRecord.java:241|WRITE: TLS13 handshake, length = 122
javax.net.ssl|DEBUG|01|main|2021-05-27 12:02:13.011 CEST|SSLSocketOutputRecord.java:255|Raw write (
  0000: 16 03 03 00 7A 02 00 00   76 03 03 78 B9 53 93 89  ....z...v..x.S..
  0010: F9 8F 8C 2B 03 33 72 5D   21 4D A5 DC 42 59 CE 54  ...+.3r]!M..BY.T
  0020: 24 CD 75 9F 26 15 09 FB   53 91 00 20 EC 09 75 BD  $.u.&...S.. ..u.
  0030: 7F 00 40 CB 9F DC F4 BA   FD 03 57 82 6C D0 6E 4C  ..@.......W.l.nL
  0040: 2D FD 79 AA 07 91 86 A8   1C A8 99 38 13 01 00 00  -.y........8....
  0050: 2E 00 2B 00 02 03 04 00   33 00 24 00 1D 00 20 B3  ..+.....3.$... .
  0060: C2 7D 1D 77 D1 CE 3F 1D   C2 15 0A 3A 21 B5 3B ED  ...w..?....:!.;.
  0070: D0 BB 79 D1 C0 88 46 98   71 DD 2D 62 40 E1 1E     ..y...F.q.-b@..
)
javax.net.ssl|DEBUG|01|main|2021-05-27 12:02:13.017 CEST|SSLCipher.java:1840|KeyLimit read side: algorithm = AES/GCM/NOPADDING:KEYUPDATE
countdown value = 137438953472
javax.net.ssl|DEBUG|01|main|2021-05-27 12:02:13.018 CEST|SSLCipher.java:1994|KeyLimit write side: algorithm = AES/GCM/NOPADDING:KEYUPDATE
countdown value = 137438953472
javax.net.ssl|DEBUG|01|main|2021-05-27 12:02:13.018 CEST|SSLSocketOutputRecord.java:225|Raw write (
  0000: 14 03 03 00 01 01                                  ......
)
javax.net.ssl|ALL|01|main|2021-05-27 12:02:13.019 CEST|ServerNameExtension.java:527|Ignore unavailable extension: server_name
javax.net.ssl|DEBUG|01|main|2021-05-27 12:02:13.019 CEST|SSLExtensions.java:260|Ignore, context unavailable extension: server_name
javax.net.ssl|ALL|01|main|2021-05-27 12:02:13.019 CEST|MaxFragExtension.java:469|Ignore unavailable max_fragment_length extension
javax.net.ssl|DEBUG|01|main|2021-05-27 12:02:13.019 CEST|SSLExtensions.java:260|Ignore, context unavailable extension: max_fragment_length
javax.net.ssl|DEBUG|01|main|2021-05-27 12:02:13.020 CEST|AlpnExtension.java:365|Ignore unavailable extension: application_layer_protocol_negotiation
javax.net.ssl|DEBUG|01|main|2021-05-27 12:02:13.020 CEST|SSLExtensions.java:260|Ignore, context unavailable extension: application_layer_protocol_negotiation
javax.net.ssl|DEBUG|01|main|2021-05-27 12:02:13.020 CEST|EncryptedExtensions.java:137|Produced EncryptedExtensions message (
"EncryptedExtensions": [
  "supported_groups (10)": {
    "versions": [x25519, secp256r1, secp384r1, secp521r1, x448, ffdhe2048, ffdhe3072, ffdhe4096, ffdhe6144, ffdhe8192]
  }
]
)
javax.net.ssl|DEBUG|01|main|2021-05-27 12:02:13.020 CEST|SSLSocketOutputRecord.java:241|WRITE: TLS13 handshake, length = 32
javax.net.ssl|DEBUG|01|main|2021-05-27 12:02:13.028 CEST|SSLCipher.java:2036|Plaintext before ENCRYPTION (
  0000: 08 00 00 1C 00 1A 00 0A   00 16 00 14 00 1D 00 17  ................
  0010: 00 18 00 19 00 1E 01 00   01 01 01 02 01 03 01 04  ................
  0020: 16 00 00 00 00 00 00 00   00 00 00 00 00 00 00 00  ................
  0030: 00                                                 .
)
javax.net.ssl|DEBUG|01|main|2021-05-27 12:02:13.028 CEST|SSLSocketOutputRecord.java:255|Raw write (
  0000: 17 03 03 00 41 7D 95 DC   2E 14 CB 2C B5 B3 D4 79  ....A......,...y
  0010: 67 4C D6 01 7E 7C EE 31   58 A3 63 33 E1 30 0E 3C  gL.....1X.c3.0.<
  0020: FB 73 DD 85 57 95 36 B5   93 17 73 3A E6 2E 6C A9  .s..W.6...s:..l.
  0030: A1 F0 49 15 93 28 39 A8   E3 ED D5 02 85 05 09 37  ..I..(9........7
  0040: 28 3F B3 52 62 94                                  (?.Rb.
)
javax.net.ssl|ALL|01|main|2021-05-27 12:02:13.028 CEST|X509Authentication.java:295|No X.509 cert selected for EC
javax.net.ssl|WARNING|01|main|2021-05-27 12:02:13.029 CEST|CertificateMessage.java:1083|Unavailable authentication scheme: ecdsa_secp256r1_sha256
javax.net.ssl|ALL|01|main|2021-05-27 12:02:13.029 CEST|X509Authentication.java:295|No X.509 cert selected for EC
javax.net.ssl|WARNING|01|main|2021-05-27 12:02:13.029 CEST|CertificateMessage.java:1083|Unavailable authentication scheme: ecdsa_secp384r1_sha384
javax.net.ssl|ALL|01|main|2021-05-27 12:02:13.029 CEST|X509Authentication.java:295|No X.509 cert selected for EC
javax.net.ssl|WARNING|01|main|2021-05-27 12:02:13.029 CEST|CertificateMessage.java:1083|Unavailable authentication scheme: ecdsa_secp521r1_sha512
javax.net.ssl|ALL|01|main|2021-05-27 12:02:13.029 CEST|X509Authentication.java:295|No X.509 cert selected for RSA
javax.net.ssl|WARNING|01|main|2021-05-27 12:02:13.029 CEST|CertificateMessage.java:1083|Unavailable authentication scheme: rsa_pss_rsae_sha256
javax.net.ssl|ALL|01|main|2021-05-27 12:02:13.029 CEST|X509Authentication.java:295|No X.509 cert selected for RSA
javax.net.ssl|WARNING|01|main|2021-05-27 12:02:13.029 CEST|CertificateMessage.java:1083|Unavailable authentication scheme: rsa_pss_rsae_sha384
javax.net.ssl|ALL|01|main|2021-05-27 12:02:13.029 CEST|X509Authentication.java:295|No X.509 cert selected for RSA
javax.net.ssl|WARNING|01|main|2021-05-27 12:02:13.029 CEST|CertificateMessage.java:1083|Unavailable authentication scheme: rsa_pss_rsae_sha512
javax.net.ssl|ALL|01|main|2021-05-27 12:02:13.029 CEST|X509Authentication.java:295|No X.509 cert selected for RSASSA-PSS
javax.net.ssl|WARNING|01|main|2021-05-27 12:02:13.029 CEST|CertificateMessage.java:1083|Unavailable authentication scheme: rsa_pss_pss_sha256
javax.net.ssl|ALL|01|main|2021-05-27 12:02:13.029 CEST|X509Authentication.java:295|No X.509 cert selected for RSASSA-PSS
javax.net.ssl|WARNING|01|main|2021-05-27 12:02:13.029 CEST|CertificateMessage.java:1083|Unavailable authentication scheme: rsa_pss_pss_sha384
javax.net.ssl|ALL|01|main|2021-05-27 12:02:13.030 CEST|X509Authentication.java:295|No X.509 cert selected for RSASSA-PSS
javax.net.ssl|WARNING|01|main|2021-05-27 12:02:13.030 CEST|CertificateMessage.java:1083|Unavailable authentication scheme: rsa_pss_pss_sha512
javax.net.ssl|ALL|01|main|2021-05-27 12:02:13.030 CEST|X509Authentication.java:295|No X.509 cert selected for RSA
javax.net.ssl|WARNING|01|main|2021-05-27 12:02:13.030 CEST|CertificateMessage.java:1083|Unavailable authentication scheme: rsa_pkcs1_sha256
javax.net.ssl|ALL|01|main|2021-05-27 12:02:13.030 CEST|X509Authentication.java:295|No X.509 cert selected for RSA
javax.net.ssl|WARNING|01|main|2021-05-27 12:02:13.030 CEST|CertificateMessage.java:1083|Unavailable authentication scheme: rsa_pkcs1_sha384
javax.net.ssl|ALL|01|main|2021-05-27 12:02:13.030 CEST|X509Authentication.java:295|No X.509 cert selected for RSA
javax.net.ssl|WARNING|01|main|2021-05-27 12:02:13.030 CEST|CertificateMessage.java:1083|Unavailable authentication scheme: rsa_pkcs1_sha512
javax.net.ssl|ALL|01|main|2021-05-27 12:02:13.030 CEST|X509Authentication.java:295|No X.509 cert selected for EC
javax.net.ssl|WARNING|01|main|2021-05-27 12:02:13.030 CEST|CertificateMessage.java:1083|Unavailable authentication scheme: ecdsa_sha1
javax.net.ssl|ALL|01|main|2021-05-27 12:02:13.030 CEST|X509Authentication.java:295|No X.509 cert selected for RSA
javax.net.ssl|WARNING|01|main|2021-05-27 12:02:13.030 CEST|CertificateMessage.java:1083|Unavailable authentication scheme: rsa_pkcs1_sha1
javax.net.ssl|WARNING|01|main|2021-05-27 12:02:13.030 CEST|CertificateMessage.java:1093|No available authentication scheme
javax.net.ssl|ERROR|01|main|2021-05-27 12:02:13.031 CEST|TransportContext.java:341|Fatal (HANDSHAKE_FAILURE): No available authentication scheme (
"throwable" : {
  javax.net.ssl.SSLHandshakeException: No available authentication scheme
    at java.base/sun.security.ssl.Alert.createSSLException(Alert.java:131)
    at java.base/sun.security.ssl.Alert.createSSLException(Alert.java:117)
    at java.base/sun.security.ssl.TransportContext.fatal(TransportContext.java:336)
    at java.base/sun.security.ssl.TransportContext.fatal(TransportContext.java:292)
    at java.base/sun.security.ssl.TransportContext.fatal(TransportContext.java:283)
    at java.base/sun.security.ssl.CertificateMessage$T13CertificateProducer.onProduceCertificate(CertificateMessage.java:972)
    at java.base/sun.security.ssl.CertificateMessage$T13CertificateProducer.produce(CertificateMessage.java:961)
    at java.base/sun.security.ssl.SSLHandshake.produce(SSLHandshake.java:436)
    at java.base/sun.security.ssl.ClientHello$T13ClientHelloConsumer.goServerHello(ClientHello.java:1234)
    at java.base/sun.security.ssl.ClientHello$T13ClientHelloConsumer.consume(ClientHello.java:1170)
    at java.base/sun.security.ssl.ClientHello$ClientHelloConsumer.onClientHello(ClientHello.java:852)
    at java.base/sun.security.ssl.ClientHello$ClientHelloConsumer.consume(ClientHello.java:813)
    at java.base/sun.security.ssl.SSLHandshake.consume(SSLHandshake.java:392)
    at java.base/sun.security.ssl.HandshakeContext.dispatch(HandshakeContext.java:443)
    at java.base/sun.security.ssl.HandshakeContext.dispatch(HandshakeContext.java:421)
    at java.base/sun.security.ssl.TransportContext.dispatch(TransportContext.java:182)
    at java.base/sun.security.ssl.SSLTransport.decode(SSLTransport.java:171)
    at java.base/sun.security.ssl.SSLSocketImpl.decode(SSLSocketImpl.java:1418)
    at java.base/sun.security.ssl.SSLSocketImpl.readHandshakeRecord(SSLSocketImpl.java:1324)
    at java.base/sun.security.ssl.SSLSocketImpl.startHandshake(SSLSocketImpl.java:440)
    at java.base/sun.security.ssl.SSLSocketImpl.ensureNegotiated(SSLSocketImpl.java:829)
    at java.base/sun.security.ssl.SSLSocketImpl$AppInputStream.read(SSLSocketImpl.java:920)
    at java.base/java.io.BufferedInputStream.fill(BufferedInputStream.java:252)
    at java.base/java.io.BufferedInputStream.read1(BufferedInputStream.java:292)
    at java.base/java.io.BufferedInputStream.read(BufferedInputStream.java:351)
    at java.base/java.io.FilterInputStream.read(FilterInputStream.java:107)
    at TlsServer.start(TlsServer.java:143)
    at TlsServer.main(TlsServer.java:95)}

)
javax.net.ssl|ALL|01|main|2021-05-27 12:02:13.031 CEST|SSLSessionImpl.java:784|Invalidated session:  Session(1622109721099|SSL_NULL_WITH_NULL_NULL)
javax.net.ssl|ALL|01|main|2021-05-27 12:02:13.031 CEST|SSLSessionImpl.java:784|Invalidated session:  Session(1622109733001|TLS_AES_128_GCM_SHA256)
javax.net.ssl|DEBUG|01|main|2021-05-27 12:02:13.031 CEST|SSLSocketOutputRecord.java:71|WRITE: TLS13 alert(handshake_failure), length = 2
javax.net.ssl|DEBUG|01|main|2021-05-27 12:02:13.031 CEST|SSLCipher.java:2036|Plaintext before ENCRYPTION (
  0000: 02 28 15 00 00 00 00 00   00 00 00 00 00 00 00 00  .(..............
  0010: 00 00 00                                           ...
)
javax.net.ssl|DEBUG|01|main|2021-05-27 12:02:13.032 CEST|SSLSocketOutputRecord.java:85|Raw write (
  0000: 17 03 03 00 23 96 86 5D   95 12 6A 81 E7 77 F0 B5  ....#..]..j..w..
  0010: 45 7F F3 A4 98 D9 1B E6   FF C7 C1 BC 5F 1B B4 55  E..........._..U
  0020: DD 5A FE B9 B1 98 47 CF                            .Z....G.
)
javax.net.ssl|DEBUG|01|main|2021-05-27 12:02:13.032 CEST|SSLSocketImpl.java:1638|close the underlying socket
javax.net.ssl|DEBUG|01|main|2021-05-27 12:02:13.032 CEST|SSLSocketImpl.java:1657|close the SSL connection (initiative)
javax.net.ssl.SSLHandshakeException: No available authentication scheme
    at java.base/sun.security.ssl.Alert.createSSLException(Alert.java:131)
    at java.base/sun.security.ssl.Alert.createSSLException(Alert.java:117)
    at java.base/sun.security.ssl.TransportContext.fatal(TransportContext.java:336)
    at java.base/sun.security.ssl.TransportContext.fatal(TransportContext.java:292)
    at java.base/sun.security.ssl.TransportContext.fatal(TransportContext.java:283)
    at java.base/sun.security.ssl.CertificateMessage$T13CertificateProducer.onProduceCertificate(CertificateMessage.java:972)
    at java.base/sun.security.ssl.CertificateMessage$T13CertificateProducer.produce(CertificateMessage.java:961)
    at java.base/sun.security.ssl.SSLHandshake.produce(SSLHandshake.java:436)
    at java.base/sun.security.ssl.ClientHello$T13ClientHelloConsumer.goServerHello(ClientHello.java:1234)
    at java.base/sun.security.ssl.ClientHello$T13ClientHelloConsumer.consume(ClientHello.java:1170)
    at java.base/sun.security.ssl.ClientHello$ClientHelloConsumer.onClientHello(ClientHello.java:852)
    at java.base/sun.security.ssl.ClientHello$ClientHelloConsumer.consume(ClientHello.java:813)
    at java.base/sun.security.ssl.SSLHandshake.consume(SSLHandshake.java:392)
    at java.base/sun.security.ssl.HandshakeContext.dispatch(HandshakeContext.java:443)
    at java.base/sun.security.ssl.HandshakeContext.dispatch(HandshakeContext.java:421)
    at java.base/sun.security.ssl.TransportContext.dispatch(TransportContext.java:182)
    at java.base/sun.security.ssl.SSLTransport.decode(SSLTransport.java:171)
    at java.base/sun.security.ssl.SSLSocketImpl.decode(SSLSocketImpl.java:1418)
    at java.base/sun.security.ssl.SSLSocketImpl.readHandshakeRecord(SSLSocketImpl.java:1324)
    at java.base/sun.security.ssl.SSLSocketImpl.startHandshake(SSLSocketImpl.java:440)
    at java.base/sun.security.ssl.SSLSocketImpl.ensureNegotiated(SSLSocketImpl.java:829)
    at java.base/sun.security.ssl.SSLSocketImpl$AppInputStream.read(SSLSocketImpl.java:920)
    at java.base/java.io.BufferedInputStream.fill(BufferedInputStream.java:252)
    at java.base/java.io.BufferedInputStream.read1(BufferedInputStream.java:292)
    at java.base/java.io.BufferedInputStream.read(BufferedInputStream.java:351)
    at java.base/java.io.FilterInputStream.read(FilterInputStream.java:107)
    at TlsServer.start(TlsServer.java:143)
    at TlsServer.main(TlsServer.java:95)

如果我错过了配置中的某些内容,有人知道吗?

【问题讨论】:

    标签: java ssl ssl-certificate java-11 tls1.3


    【解决方案1】:

    Meta:我不敢相信这不是骗局,但我找不到。

    SSL/TLS 身份验证使用public-key or asymmetric cryptography 而非密钥或对称。而不是这个

        Key pk = new SecretKeySpec(TEST_KEY.getBytes(StandardCharsets.ISO_8859_1), "RSA");
        ks.setKeyEntry("test_key", pk, password, new Certificate[]
        {
            cert
        });
    

    这样做

        byte[] der = Base64.getDecoder().decode(TEST_KEY.replaceAll("-----(BEGIN|END) PRIVATE KEY-----\r?\n","").replaceAll("\r?\n",""));
        // or make these changes already in the value coded as Michael Fehr did
        // or leave the internal breaks and use .getMimeDecoder() (but remove the BEGIN/END lines)
        PrivateKey pk = KeyFactory.getInstance("RSA") .generatePrivate(new PKCS8EncodedKeySpec(der));
        ks.setKeyEntry("test_key", pk, password, new Certificate[]{cert} );
    

    然后像现在一样将其用于KeyManagerFactory.init。您不需要将服务器自己的证书放在其 TrustManager 中,您可以使用null 作为SSLContext.init 的第一个参数,特别是因为您不要求客户端身份验证。但是,连接到您的服务器的客户端确实需要将此证书添加到他们的信任库中;对于 curl,您可以在命令行中使用 --cacert $pemfile,但其他客户端会有所不同,可能会更复杂。

    PS:密钥和证书,以及 KeyManager 和 TrustManager(如果有)在 1.3 中可以与早期协议相同,只是 DSA 在 1.3 中不能再使用;只有版本和密码套件不同。但是,in 1.3 you have the option of using a cert restricted to RSA-PSS 而不是普通的 RSA,并且在从 OpenSSL 创建时,密钥文件也表明了这一点(尽管实际上不需要限制密钥)。

    【讨论】:

    • 谢谢,PKCS8 转换为我解决了问题。
    【解决方案2】:

    我正在使用第二个答案,因为代码太长,无法将其粘贴到第一个答案中。

    是的,可以使用 临时 信任和密钥库运行 TLS 服务器。我无法判断它带来的任何安全问题 - 所以在使用此代码时要小心。

    为了让您的代码开箱即用,我使用了 RSA 私钥和自签名证书 - 我去掉了“--- BEGIN ... END ---”,只留下了(Base64 编码)获取密钥/证书的部分。别担心,这是一个示例密钥

    public static X509Certificate getcertificate() throws CertificateException {
        String certificate = "MIIDSzCCAjMCBFc5m4cwDQYJKoZIhvcNAQELBQAwajELMAkGA1UEBhMCVVMxCzAJ" +
                "BgNVBAgMAkNBMRIwEAYDVQQHDAlDdXBlcnRpbm8xDjAMBgNVBAoMBUR1bW15MQ4w" +
                "DAYDVQQLDAVEdW1teTEaMBgGA1UEAwwRZHVtbXkuZXhhbXBsZS5jb20wHhcNMTYw" +
                "NTE2MTAwNjM4WhcNMjYwNTE2MTAwNjM4WjBqMQswCQYDVQQGEwJVUzELMAkGA1UE" +
                "CAwCQ0ExEjAQBgNVBAcMCUN1cGVydGlubzEOMAwGA1UECgwFRHVtbXkxDjAMBgNV" +
                "BAsMBUR1bW15MRowGAYDVQQDDBFkdW1teS5leGFtcGxlLmNvbTCCASIwDQYJKoZI" +
                "hvcNAQEBBQADggEPADCCAQoCggEBAMkbQD5byG7xnyOWVzeIwbtHVPem/LJlXAHc" +
                "WJwz8pZ75rGBnZmAtgunXU++yPTZZOgYTZpM+qQhrFy2Z1Di/vVzhlVpcOZBw1RB" +
                "lI3s2uQYaWVtBtKtNELYWXEbhfxVJFBs5lLmsYJufVasMT+3XzBERuIEUV43JTKS" +
                "5ttwkoVgYRX6KDrIj0hYA7ZmOB/y5E1BG2mLsSkX0SoTCpuhgYwlWO4kvYoGu9vH" +
                "SfugUzQBqSiE1yN+Qfl3+76U23Pmbm/by9n3M3AoULxk2XnQmgBYtzViV0f1E3rr" +
                "/77XvIydWLHB0kotpND+ZN4IvTwpr3v7Iy6wbofg+Ounvwf7MKMCAwEAATANBgkq" +
                "hkiG9w0BAQsFAAOCAQEAWRWiibTf9UZAlXr+2ZmBMMMONpBOUaSbexLNs6MahBmj" +
                "cdEwOQ+l5xAdV4xVEDoDhu0m1JeDXy6SXo7mYkM2l9u/uz7tRStuEiM+tbCDaclb" +
                "W/H5QOdpiIx9uh86cEgBpk4xUO5vSaLI/9uSKgGkTNAFrjy3ME3wfc0cd5ntS8ca" +
                "8xobcmQ59SYZb9Gi0u/YN9yro9RwGDL8O1Bmp9+9rn5hor/oOVGHlbz2MN43moSZ" +
                "EiIfegW9OWiaWqIGqBOXvoEGp0exN+lhhmonI3zdYCB0jss2QYm3E1IWqZUkNOg3" +
                "yulIxph3FYvF+cA/vTVlohq5VB3DsCrnkDmqbU2mlw==";
        byte[] encodedCertificate = Base64.getDecoder().decode(certificate);
        CertificateFactory certificateFactory = CertificateFactory.getInstance("X.509");
        return (X509Certificate) certificateFactory.generateCertificate(new ByteArrayInputStream(encodedCertificate));
    }
    
    // don't worry, it is a sample rsa private key
    public static PrivateKey getPrivateKey() throws NoSuchAlgorithmException, InvalidKeySpecException {
        String privateKeyPem = "MIIEvwIBADANBgkqhkiG9w0BAQEFAASCBKkwggSlAgEAAoIBAQDJG0A+W8hu8Z8j" +
                "llc3iMG7R1T3pvyyZVwB3FicM/KWe+axgZ2ZgLYLp11Pvsj02WToGE2aTPqkIaxc" +
                "tmdQ4v71c4ZVaXDmQcNUQZSN7NrkGGllbQbSrTRC2FlxG4X8VSRQbOZS5rGCbn1W" +
                "rDE/t18wREbiBFFeNyUykubbcJKFYGEV+ig6yI9IWAO2Zjgf8uRNQRtpi7EpF9Eq" +
                "EwqboYGMJVjuJL2KBrvbx0n7oFM0AakohNcjfkH5d/u+lNtz5m5v28vZ9zNwKFC8" +
                "ZNl50JoAWLc1YldH9RN66/++17yMnVixwdJKLaTQ/mTeCL08Ka97+yMusG6H4Pjr" +
                "p78H+zCjAgMBAAECggEAMh7yEHiUiB39AQQPoZ4aVoANK5m5IgcD+sy9YtTJkXq4" +
                "wKWirya2eEoShfTxJaDmtreT47BqySxBRmwJbM3eKDNOGAxq4GAke+PKT+LnnPB+" +
                "mBInoKsdOsmr5PYsmvpnTgoODzxColTCNS8+KPidJyzlE6Bq3RXWVffpxGgWhFnT" +
                "JM1MTpJJ/tKeZAUKOXbSU9T079gduPHTkrFeUnAt179ruSLov2Nbt8XhqDiY+pXm" +
                "EjMb+Q8JlBJ1F82i43BR6w6fjdSReGUNs8NDbmjpMasuk2U1/t28Idnt3M07jDUP" +
                "LGPrAeTZd/PLNpbflZMJoDT5SoCFycXNE3rHrsr8YQKBgQDveZzFcZctuYVd9iDd" +
                "SNLS6a2MuSwLrEPtUXRC7QR8BO2OkEvqMD5tVv3+a0ZPQpNg+4KzaKdzbsTNifpY" +
                "qjNzfydZ3CZzKlRVt/88icFxicWvI+fKiDdcGUt7agS4o9Ce+EkkqBySvVJB5Blz" +
                "vKLNyMQsJb2y6jRTekl/iO2XIQKBgQDW+9j128OmtPBERMw0RKxS5KZEYIhvZ+kw" +
                "lRe7D21o1d7MQx0zxKw58AWDW/epiVyno/FDuWmj2pQQ/oLpZl3ERg+hf3yUp48A" +
                "MziyuI3+INni/+uMWR3RN6sp4IbebXb6itGAIOm7ljgYwFV9crpLd7GZz3wi3/iZ" +
                "+zOVLl9DQwKBgQDXbYOGayUg0SAU4vG1n2loqyagzYO+DH4e44O/IRFDr/s0oMJq" +
                "LnQ6UGO1mDNr4exK9nchhif9Q8xvSoyXbqVSZTS1NcKxH4c2hYtqnlITHWlkoNxH" +
                "6jpC885fe4Q7xcJK//hsrX7m0sFI3TW4VB3xGYbAYENCzEW+QugTfs6dgQKBgQCi" +
                "nB5QYOkNWID/8lXPFz6M+Jv2zlmEgsF0WOF5QUMNb++06vLUrGdk73MMF+0tlFO8" +
                "DZo5Eq6gHH2wmQImTqKQCjpaepadzluw2A2DyWrFlM2aEN926hVOod/arhT1ezDq" +
                "c0PhuYNxuz81IY3IdJYK7T8tyy3nJyfgOIycw1WVBwKBgQDQ+iZpSMp1wIqnIeNb" +
                "zdlaVYXBu7d0tBlG/JGnPRlNVJkVpKd4woWtMwkqMGuAmmSkdjzhFYca9SWgzV1R" +
                "gzQzf1GuClF8KO7drdyvKGNqDFhyuCgE9mXI34ovjNcva9u+aZGHxuEYlCTKA0cV" +
                "UT/0leMkegvP/kFKOvzfYRBNFg==";
        byte[] encodedPrivateKey = Base64.getDecoder().decode(privateKeyPem);
        KeyFactory keyFactory = KeyFactory.getInstance("RSA");
        PKCS8EncodedKeySpec pkcs8EncodedKeySpec = new PKCS8EncodedKeySpec(encodedPrivateKey);
        return (PrivateKey) keyFactory.generatePrivate(pkcs8EncodedKeySpec);
    }
    

    现在代码 - 将其粘贴在“//System.setProperty("javax.net.debug", "all");" 之间和“SSLServerSocket serverSocket = (SSLServerSocket) ctx.getServerSocketFactory().createServerSocket(PORT);”和删除之间的代码行。粘贴此代码行:

        //Create a temp truststore with the server certificate
        KeyStore ksTemp = KeyStore.getInstance("JKS");
        ksTemp.load(null, null); //Initialize it
        ksTemp.setCertificateEntry("Alias", getcertificate());
        ByteArrayOutputStream bOut = new ByteArrayOutputStream();
        // save the temp keystore
        ksTemp.store(bOut, "passphrase".toCharArray());
        //Now create the keystore to be used by jsse
        KeyStore keyStoreTs = KeyStore.getInstance("JKS");
        keyStoreTs.load(new ByteArrayInputStream(bOut.toByteArray()), "passphrase".toCharArray());
    
        // now lets do the same with the keystore
        KeyStore ksTemp2 = KeyStore.getInstance("JKS");
        ksTemp2.load(null, null); //Initialize it
        ksTemp2.setCertificateEntry("Alias", getcertificate());
        ByteArrayOutputStream bOut2 = new ByteArrayOutputStream();
        // save the temp keystore
        ksTemp2.store(bOut2, "passphrase".toCharArray());
        //Now create the keystore to be used by jsse
        KeyStore keyStore = KeyStore.getInstance("JKS");
        keyStore.load(new ByteArrayInputStream(bOut2.toByteArray()), "passphrase".toCharArray());
        X509Certificate[] chain = new X509Certificate[1];
        chain[0] = getcertificate();
        keyStore.setKeyEntry("privateCert", getPrivateKey(), "passphrase".toCharArray(), chain);
    
        TrustManagerFactory tmf = TrustManagerFactory.getInstance(TrustManagerFactory.getDefaultAlgorithm());
        tmf.init(keyStoreTs);
    
        KeyManagerFactory kmf = KeyManagerFactory.getInstance(KeyManagerFactory.getDefaultAlgorithm());
        kmf.init(keyStore, "passphrase".toCharArray());
    
        // create SSLContext to establish the secure connection
        SSLContext ctx = SSLContext.getInstance(TLS_PROTOCOL);
        ctx.init(kmf.getKeyManagers(), tmf.getTrustManagers(), null);
    

    再次警告:这是粗略的代码 - 请自行检查以确保其安全!

    运行服务器并请求与https://localhost:32333/ 的连接,服务器协议显示:

    Server started on port 32333
    Accept new connection: /127.0.0.1:58640
    Server received 375 bytes: GET / HTTP/1.1
    Host: localhost:32333
    User-Agent: Mozilla/5.0 (Windows NT 10.0; Win64; x64; rv:88.0) Gecko/20100101 Firefox/88.0
    Accept: text/html,application/xhtml+xml,application/xml;q=0.9,image/webp,*/*;q=0.8
    Accept-Language: de,en-US;q=0.7,en;q=0.3
    Accept-Encoding: gzip, deflate, br
    Connection: keep-alive
    Upgrade-Insecure-Requests: 1
    Cache-Control: max-age=0
    
    
    Accept new connection: /127.0.0.1:58642
    Server received 331 bytes: GET /favicon.ico HTTP/1.1
    Host: localhost:32333
    User-Agent: Mozilla/5.0 (Windows NT 10.0; Win64; x64; rv:88.0) Gecko/20100101 Firefox/88.0
    Accept: image/webp,*/*
    Accept-Language: de,en-US;q=0.7,en;q=0.3
    Accept-Encoding: gzip, deflate, br
    Connection: keep-alive
    Referer: https://localhost:32333/
    Cache-Control: max-age=0
    

    我的浏览器 (Firefox) 显示此数据:

    GET / HTTP/1.1
    Host: localhost:32333
    User-Agent: Mozilla/5.0 (Windows NT 10.0; Win64; x64; rv:88.0) Gecko/20100101 Firefox/88.0
    Accept: text/html,application/xhtml+xml,application/xml;q=0.9,image/webp,*/*;q=0.8
    Accept-Language: de,en-US;q=0.7,en;q=0.3
    Accept-Encoding: gzip, deflate, br
    Connection: keep-alive
    Upgrade-Insecure-Requests: 1
    Cache-Control: max-age=0
    

    嗯,这应该可以解决您的问题。周末快乐。

    【讨论】:

    • 这比需要的要复杂得多;唯一相关的一点是通过 KeyFactory 作为 PKCS8 运行私钥,并将结果放入使用的密钥库中。
    • 感谢 Michael Fehr 的扩展示例。它工作正常。我的问题是私钥转换。我接受 dave_thompson_085 的回答,因为在我的示例中我只需要替换 3 行。
    【解决方案3】:

    这可能不是答案,但为了更好地阅读,我正在使用它。下面是我使用 1.3 版的 TLS 服务器的示例代码(来源取自 https://blog.gypsyengineer.com/en/security/an-example-of-tls-13-client-and-server-on-java.html)。

    重要的事实是在服务器启动时有一个密钥库可用的信任库并使用(我使用“System.setProperty”而不是“-Djavax.net.. .")。

    也许这可以帮助您自己找到解决方案:-)

    import javax.net.ssl.SSLServerSocket;
    import javax.net.ssl.SSLServerSocketFactory;
    import javax.net.ssl.SSLSocket;
    import javax.net.ssl.SSLSocketFactory;
    import java.io.*;
    
    /*
     * Don't forget to set the following system properties when you run the class:
     *
     *     javax.net.ssl.keyStore
     *     javax.net.ssl.keyStorePassword
     *     javax.net.ssl.trustStore
     *     javax.net.ssl.trustStorePassword
     *
     * More details can be found in JSSE docs.
     *
     * For example:
     *
     *     java -cp classes \
     *         -Djavax.net.ssl.keyStore=keystore \
     *         -Djavax.net.ssl.keyStorePassword=passphrase \
     *         -Djavax.net.ssl.trustStore=keystore \
     *         -Djavax.net.ssl.trustStorePassword=passphrase \
     *             com.gypsyengineer.tlsbunny.jsse.TLSv13Test
     *
     * For testing purposes, you can download the keystore file from
     *
     *     https://github.com/openjdk/jdk/tree/master/test/jdk/javax/net/ssl/etc
     */
    
    public class TLSv13Test {
        // source: https://blog.gypsyengineer.com/en/security/an-example-of-tls-13-client-and-server-on-java.html
        private static final int delay = 1000; // in millis
        private static final String[] protocols = new String[] {"TLSv1.3"};
        private static final String[] cipher_suites = new String[] {"TLS_AES_128_GCM_SHA256"};
        private static final String message =
                "Like most of life's problems, this one can be solved with bending!";
    
        public static void main(String[] args) throws Exception {
            // system settings for keystores
            System.setProperty("javax.net.ssl.keyStore", "keystores\\tls\\keystore");
            System.setProperty("javax.net.ssl.keyStorePassword", "passphrase");
            System.setProperty("javax.net.ssl.trustStore", "keystores\\tls\\truststore");
            System.setProperty("javax.net.ssl.trustStorePassword", "passphrase");
            System.setProperty("javax.net.debug", "all");
    
            try (EchoServer server = EchoServer.create()) {
                new Thread(server).start();
                Thread.sleep(delay);
    
                try (SSLSocket socket = createSocket("localhost", server.port())) {
                    InputStream is = new BufferedInputStream(socket.getInputStream());
                    OutputStream os = new BufferedOutputStream(socket.getOutputStream());
                    os.write(message.getBytes());
                    os.flush();
                    byte[] data = new byte[2048];
                    int len = is.read(data);
                    if (len <= 0) {
                        throw new IOException("no data received");
                    }
                    System.out.printf("client received %d bytes: %s%n",
                            len, new String(data, 0, len));
                }
            }
        }
    
        public static SSLSocket createSocket(String host, int port) throws IOException {
            SSLSocket socket = (SSLSocket) SSLSocketFactory.getDefault()
                    .createSocket(host, port);
            socket.setEnabledProtocols(protocols);
            socket.setEnabledCipherSuites(cipher_suites);
            return socket;
        }
    
        public static class EchoServer implements Runnable, AutoCloseable {
            private static final int FREE_PORT = 0;
            private final SSLServerSocket sslServerSocket;
            private EchoServer(SSLServerSocket sslServerSocket) {
                this.sslServerSocket = sslServerSocket;
            }
    
            public int port() {
                return sslServerSocket.getLocalPort();
            }
    
            @Override
            public void close() throws IOException {
                if (sslServerSocket != null && !sslServerSocket.isClosed()) {
                    sslServerSocket.close();
                }
            }
    
            @Override
            public void run() {
                System.out.printf("server started on port %d%n", port());
    
                try (SSLSocket socket = (SSLSocket) sslServerSocket.accept()) {
                    System.out.println("accepted");
                    InputStream is = new BufferedInputStream(socket.getInputStream());
                    OutputStream os = new BufferedOutputStream(socket.getOutputStream());
                    byte[] data = new byte[2048];
                    int len = is.read(data);
                    if (len <= 0) {
                        throw new IOException("no data received");
                    }
                    System.out.printf("server received %d bytes: %s%n",
                            len, new String(data, 0, len));
                    os.write(data, 0, len);
                    os.flush();
                } catch (Exception e) {
                    System.out.printf("exception: %s%n", e.getMessage());
                }
    
                System.out.println("server stopped");
            }
    
            public static EchoServer create() throws IOException {
                return create(FREE_PORT);
            }
    
            public static EchoServer create(int port) throws IOException {
                SSLServerSocket socket = (SSLServerSocket)
                        SSLServerSocketFactory.getDefault().createServerSocket(port);
                socket.setEnabledProtocols(protocols);
                socket.setEnabledCipherSuites(cipher_suites);
                return new EchoServer(socket);
            }
        }
    }
    

    【讨论】:

    • 感谢您提供此示例。其实我的示例代码就是基于此。不幸的是,它并没有解决我的问题,因为我的目标的关键方面是在内存中使用给定格式的键,并且不指向文件系统上的任何存储引用。
    猜你喜欢
    • 2015-02-09
    • 2014-05-05
    • 1970-01-01
    • 2021-10-22
    • 2018-02-02
    • 2021-05-21
    • 2012-08-14
    • 2018-12-23
    相关资源
    最近更新 更多