带有 Java 11 和自签名证书的 TLS 1.3 服务器套接字

如何解决带有 Java 11 和自签名证书的 TLS 1.3 服务器套接字

我想创建一个简单的 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,len));
                os.write(data,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)

有谁知道,如果我在配置中遗漏了什么?

解决方法

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",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 创建时,密钥文件也会指出这一点(尽管实际上不需要限制密钥)。

,

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

是的,可以使用临时信任和密钥库来运行 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,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,br
Connection: keep-alive
Upgrade-Insecure-Requests: 1
Cache-Control: max-age=0

好吧,那应该可以解决您的问题。周末快乐。

,

这可能不是答案,但为了更好地阅读,我正在使用它。下面是我使用 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,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));
                os.write(data,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);
        }
    }
}

版权声明:本文内容由互联网用户自发贡献,该文观点与技术仅代表作者本人。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如发现本站有涉嫌侵权/违法违规的内容, 请发送邮件至 dio@foxmail.com 举报,一经查实,本站将立刻删除。

相关推荐


使用本地python环境可以成功执行 import pandas as pd import matplotlib.pyplot as plt # 设置字体 plt.rcParams[&#39;font.sans-serif&#39;] = [&#39;SimHei&#39;] # 能正确显示负号 p
错误1:Request method ‘DELETE‘ not supported 错误还原:controller层有一个接口,访问该接口时报错:Request method ‘DELETE‘ not supported 错误原因:没有接收到前端传入的参数,修改为如下 参考 错误2:cannot r
错误1:启动docker镜像时报错:Error response from daemon: driver failed programming external connectivity on endpoint quirky_allen 解决方法:重启docker -&gt; systemctl r
错误1:private field ‘xxx‘ is never assigned 按Altʾnter快捷键,选择第2项 参考:https://blog.csdn.net/shi_hong_fei_hei/article/details/88814070 错误2:启动时报错,不能找到主启动类 #
报错如下,通过源不能下载,最后警告pip需升级版本 Requirement already satisfied: pip in c:\users\ychen\appdata\local\programs\python\python310\lib\site-packages (22.0.4) Coll
错误1:maven打包报错 错误还原:使用maven打包项目时报错如下 [ERROR] Failed to execute goal org.apache.maven.plugins:maven-resources-plugin:3.2.0:resources (default-resources)
错误1:服务调用时报错 服务消费者模块assess通过openFeign调用服务提供者模块hires 如下为服务提供者模块hires的控制层接口 @RestController @RequestMapping(&quot;/hires&quot;) public class FeignControl
错误1:运行项目后报如下错误 解决方案 报错2:Failed to execute goal org.apache.maven.plugins:maven-compiler-plugin:3.8.1:compile (default-compile) on project sb 解决方案:在pom.
参考 错误原因 过滤器或拦截器在生效时,redisTemplate还没有注入 解决方案:在注入容器时就生效 @Component //项目运行时就注入Spring容器 public class RedisBean { @Resource private RedisTemplate&lt;String
使用vite构建项目报错 C:\Users\ychen\work&gt;npm init @vitejs/app @vitejs/create-app is deprecated, use npm init vite instead C:\Users\ychen\AppData\Local\npm-
参考1 参考2 解决方案 # 点击安装源 协议选择 http:// 路径填写 mirrors.aliyun.com/centos/8.3.2011/BaseOS/x86_64/os URL类型 软件库URL 其他路径 # 版本 7 mirrors.aliyun.com/centos/7/os/x86
报错1 [root@slave1 data_mocker]# kafka-console-consumer.sh --bootstrap-server slave1:9092 --topic topic_db [2023-12-19 18:31:12,770] WARN [Consumer clie
错误1 # 重写数据 hive (edu)&gt; insert overwrite table dwd_trade_cart_add_inc &gt; select data.id, &gt; data.user_id, &gt; data.course_id, &gt; date_format(
错误1 hive (edu)&gt; insert into huanhuan values(1,&#39;haoge&#39;); Query ID = root_20240110071417_fe1517ad-3607-41f4-bdcf-d00b98ac443e Total jobs = 1
报错1:执行到如下就不执行了,没有显示Successfully registered new MBean. [root@slave1 bin]# /usr/local/software/flume-1.9.0/bin/flume-ng agent -n a1 -c /usr/local/softwa
虚拟及没有启动任何服务器查看jps会显示jps,如果没有显示任何东西 [root@slave2 ~]# jps 9647 Jps 解决方案 # 进入/tmp查看 [root@slave1 dfs]# cd /tmp [root@slave1 tmp]# ll 总用量 48 drwxr-xr-x. 2
报错1 hive&gt; show databases; OK Failed with exception java.io.IOException:java.lang.RuntimeException: Error in configuring object Time taken: 0.474 se
报错1 [root@localhost ~]# vim -bash: vim: 未找到命令 安装vim yum -y install vim* # 查看是否安装成功 [root@hadoop01 hadoop]# rpm -qa |grep vim vim-X11-7.4.629-8.el7_9.x
修改hadoop配置 vi /usr/local/software/hadoop-2.9.2/etc/hadoop/yarn-site.xml # 添加如下 &lt;configuration&gt; &lt;property&gt; &lt;name&gt;yarn.nodemanager.res