TransWikia.com

Como utilizar certificado de cliente durante o handshake da conexão com o servidor?

Stack Overflow em Português Asked by Marcelo Ferreira Vasconcelos on August 6, 2020

Olá!
Eu estou tentando realizar uma autenticação em um servidor do governo que utiliza certificado digital. Pra realizar tal autenticação é preciso importar as cadeias de certificado da SERPRO referente ao servidor que estou tentando acessar. Além disso também é necessário enviar um certificado cliente na requisição para concluir o handshake.

Eu importei as cadeias de certificados do SERPRO na minha JVM utilizando o keytool:

    $JAVA_HOME/bin/keytool -importcert -trustcacerts -file certificado1.crt -alias AC.SERPRO.certificado1 -cacerts -storepass changeit -noprompt -v
    $JAVA_HOME/bin/keytool -importcert -trustcacerts -file certificado2.crt -alias AC.SERPRO.certificado2 -cacerts -storepass changeit -noprompt -v
    $JAVA_HOME/bin/keytool -importcert -trustcacerts -file certificado3.crt -alias AC.SERPRO.certificado3 -cacerts -storepass changeit -noprompt -v
    $JAVA_HOME/bin/keytool -importcert -trustcacerts -file certificado4.crt -alias AC.SERPRO.certificado4 -cacerts -storepass changeit -noprompt -v

E estou tentando passar o certificado cliente via argumento durante a execução da aplicação:

java $JAVA_OPTS
    -Djavax.net.debug=all
    -Djavax.net.ssl.trustStoreType=pkcs12
    -Djavax.net.ssl.trustStore=$JAVA_HOME/lib/security/cacerts
    -Djavax.net.ssl.trustStorePassword=changeit
    -Djavax.net.ssl.keyStoreType=pkcs12
    -Djavax.net.ssl.keyStore=clientCertificate.pfx
    -Djavax.net.ssl.keyStorePassword=clientCertPass
    -jar app.jar

No código estou usando o okhttp3 para fazer a requisição GET para obter o token do servidor:

package testes;

import java.io.IOException;
import java.net.URL;
import java.util.Collections;
import java.util.concurrent.TimeUnit;

import okhttp3.ConnectionSpec;
import okhttp3.OkHttpClient;
import okhttp3.Request;
import okhttp3.Response;

public class ClienteCertificateHandshake {
    
    public static void main(String[] args) throws Exception {
        System.out.println("Running...");
        try {
            new ClienteCertificateHandshake().run();
        } catch (Exception e) {
            e.printStackTrace();
        }
    }

    public final void run() {
        try {
            URL url = new URL("https://server.gov.br/authentication");
            Request request = new Request.Builder().url(url).build();
    
            Response response = getClient().newCall(request).execute();
    
            int statusCode = response.code();
            String statusMessage = response.message();
            
            System.out.println("StatusCode => " + statusCode);
            System.out.println("StatusMessage => " + statusMessage);
            
            String bodyString = response.body().string();
            response.body().close();
            
            System.out.println("body => " + bodyString);
            
        } catch (Exception ex) {
            ex.printStackTrace();
        }
    }

    private OkHttpClient getClient() throws IOException {
        long timeout = 120;
        return new OkHttpClient.Builder()
                .connectTimeout(timeout, TimeUnit.SECONDS)
                .readTimeout(timeout, TimeUnit.SECONDS)
                .writeTimeout(timeout, TimeUnit.SECONDS)
                .connectionSpecs(Collections.singletonList(ConnectionSpec.MODERN_TLS))
                .build();
    }
}

Mas está dando erro na request e o handshake não finaliza como deveria:

app    | Running...
app    | javax.net.ssl|DEBUG|01|main|2020-07-20 17:35:58.214 BRT|null:-1|jdk.tls.keyLimits:  entry = AES/GCM/NoPadding KeyUpdate 2^37. AES/GCM/NOPADDING:KEYUPDATE = *****
app    | javax.net.ssl|WARNING|01|main|2020-07-20 17:35:58.813 BRT|null:-1|Signature algorithm, ed25519, is not supported by the underlying providers
app    | javax.net.ssl|WARNING|01|main|2020-07-20 17:35:58.815 BRT|null:-1|Signature algorithm, ed448, is not supported by the underlying providers
app    | javax.net.ssl|DEBUG|01|main|2020-07-20 17:35:58.827 BRT|null:-1|Ignore, context unavailable extension: cookie
app    | javax.net.ssl|DEBUG|01|main|2020-07-20 17:35:58.874 BRT|null:-1|No session to resume.
app    | javax.net.ssl|DEBUG|01|main|2020-07-20 17:35:58.875 BRT|null:-1|Ignore, context unavailable extension: pre_shared_key
app    | javax.net.ssl|DEBUG|01|main|2020-07-20 17:35:58.887 BRT|null:-1|Produced ClientHello handshake message (
app    | "ClientHello": {
app    |   "client version"      : "TLSv1.2",
app    |   "random"              : "****",
app    |   "session id"          : "****",
app    |   "cipher suites"       : "[TLS_AES_128_GCM_SHA256(0x1301), TLS_AES_256_GCM_SHA384(0x1302), TLS_ECDHE_ECDSA_WITH_AES_256_GCM_SHA384(0xC02C), TLS_ECDHE_ECDSA_WITH_AES_128_GCM_SHA256(0xC02B), TLS_ECDHE_RSA_WITH_AES_256_GCM_SHA384(0xC030), TLS_RSA_WITH_AES_256_GCM_SHA384(0x009D), TLS_ECDHE_RSA_WITH_AES_128_GCM_SHA256(0xC02F), TLS_RSA_WITH_AES_128_GCM_SHA256(0x009C), TLS_ECDHE_RSA_WITH_AES_256_CBC_SHA(0xC014), TLS_RSA_WITH_AES_256_CBC_SHA(0x0035), TLS_ECDHE_RSA_WITH_AES_128_CBC_SHA(0xC013), TLS_RSA_WITH_AES_128_CBC_SHA(0x002F)]",
app    |   "compression methods" : "00",
app    |   "extensions"          : [
app    |     "server_name (0)": {
app    |       type=host_name (0), value=server.gov.br
app    |     },
app    |     ...)
app    | javax.net.ssl|DEBUG|01|main|2020-07-20 17:35:58.896 BRT|null:-1|Ignore unavailable extension: supported_versions
app    | javax.net.ssl|DEBUG|01|main|2020-07-20 17:35:58.897 BRT|null:-1|Negotiated protocol version: TLSv1.2
app    | javax.net.ssl|DEBUG|01|main|2020-07-20 17:35:58.897 BRT|null:-1|Consumed extension: renegotiation_info
app    | javax.net.ssl|DEBUG|01|main|2020-07-20 17:35:58.898 BRT|null:-1|Consumed extension: server_name
app    | javax.net.ssl|DEBUG|01|main|2020-07-20 17:35:58.898 BRT|null:-1|Ignore unavailable extension: max_fragment_length
app    | javax.net.ssl|DEBUG|01|main|2020-07-20 17:35:58.898 BRT|null:-1|Ignore unavailable extension: status_request
app    | javax.net.ssl|DEBUG|01|main|2020-07-20 17:35:58.899 BRT|null:-1|Consumed extension: ec_point_formats
app    | javax.net.ssl|DEBUG|01|main|2020-07-20 17:35:58.899 BRT|null:-1|Ignore unavailable extension: status_request_v2
app    | javax.net.ssl|DEBUG|01|main|2020-07-20 17:35:58.900 BRT|null:-1|Ignore unsupported extension: supported_versions
app    | javax.net.ssl|DEBUG|01|main|2020-07-20 17:35:58.901 BRT|null:-1|Ignore unsupported extension: key_share
app    | javax.net.ssl|DEBUG|01|main|2020-07-20 17:35:58.902 BRT|null:-1|Consumed extension: renegotiation_info
app    | javax.net.ssl|DEBUG|01|main|2020-07-20 17:35:58.905 BRT|null:-1|Ignore unsupported extension: pre_shared_key
app    | javax.net.ssl|WARNING|01|main|2020-07-20 17:35:58.905 BRT|null:-1|Ignore impact of unsupported extension: server_name
app    | javax.net.ssl|DEBUG|01|main|2020-07-20 17:35:58.906 BRT|null:-1|Ignore unavailable extension: max_fragment_length
app    | javax.net.ssl|DEBUG|01|main|2020-07-20 17:35:58.906 BRT|null:-1|Ignore unavailable extension: status_request
app    | javax.net.ssl|WARNING|01|main|2020-07-20 17:35:58.907 BRT|null:-1|Ignore impact of unsupported extension: ec_point_formats
app    | javax.net.ssl|DEBUG|01|main|2020-07-20 17:35:58.908 BRT|null:-1|Ignore unavailable extension: application_layer_protocol_negotiation
app    | javax.net.ssl|DEBUG|01|main|2020-07-20 17:35:58.909 BRT|null:-1|Ignore unavailable extension: status_request_v2
app    | javax.net.ssl|DEBUG|01|main|2020-07-20 17:35:58.910 BRT|null:-1|Ignore unavailable extension: extended_master_secret
app    | javax.net.ssl|DEBUG|01|main|2020-07-20 17:35:58.911 BRT|null:-1|Ignore unavailable extension: supported_versions
app    | javax.net.ssl|DEBUG|01|main|2020-07-20 17:35:58.911 BRT|null:-1|Ignore unavailable extension: key_share
app    | javax.net.ssl|WARNING|01|main|2020-07-20 17:35:58.912 BRT|null:-1|Ignore impact of unsupported extension: renegotiation_info
app    | javax.net.ssl|DEBUG|01|main|2020-07-20 17:35:58.913 BRT|null:-1|Ignore unavailable extension: pre_shared_key
app    | javax.net.ssl|DEBUG|01|main|2020-07-20 17:35:58.943 BRT|null:-1|Consuming server Certificate handshake message (...)
app    | javax.net.ssl|DEBUG|01|main|2020-07-20 17:35:58.985 BRT|null:-1|Consuming ECDH ServerKeyExchange handshake message (...)
app    | javax.net.ssl|DEBUG|01|main|2020-07-20 17:35:58.989 BRT|null:-1|Consuming ServerHelloDone handshake message (
app    | <empty>
app    | )
app    | javax.net.ssl|DEBUG|01|main|2020-07-20 17:35:59.003 BRT|null:-1|Produced ECDHE ClientKeyExchange handshake message (... )
app    | javax.net.ssl|DEBUG|01|main|2020-07-20 17:35:59.034 BRT|null:-1|Produced ChangeCipherSpec message
app    | javax.net.ssl|DEBUG|01|main|2020-07-20 17:35:59.035 BRT|null:-1|Produced client Finished handshake message (...)
app    | javax.net.ssl|DEBUG|01|main|2020-07-20 17:35:59.060 BRT|null:-1|Consuming ChangeCipherSpec message
app    | javax.net.ssl|DEBUG|01|main|2020-07-20 17:35:59.062 BRT|null:-1|Consuming server Finished handshake message (...
app    | javax.net.ssl|DEBUG|01|main|2020-07-20 17:35:59.092 BRT|null:-1|Consuming HelloRequest handshake message (
app    | <empty>
app    | )
app    | javax.net.ssl|DEBUG|01|main|2020-07-20 17:35:59.095 BRT|null:-1|Ignore, context unavailable extension: cookie
app    | javax.net.ssl|DEBUG|01|main|2020-07-20 17:35:59.104 BRT|null:-1|No session to resume.
app    | javax.net.ssl|DEBUG|01|main|2020-07-20 17:35:59.105 BRT|null:-1|Ignore, context unavailable extension: pre_shared_key
app    | javax.net.ssl|DEBUG|01|main|2020-07-20 17:35:59.108 BRT|null:-1|Produced ClientHello handshake message (
app    | "ClientHello": {
app    |   "client version"      : "TLSv1.2",
app    |   "random"              : "****",
app    |   "session id"          : "****",
app    |   "cipher suites"       : "[TLS_AES_128_GCM_SHA256(0x1301), TLS_AES_256_GCM_SHA384(0x1302), TLS_ECDHE_ECDSA_WITH_AES_256_GCM_SHA384(0xC02C), TLS_ECDHE_ECDSA_WITH_AES_128_GCM_SHA256(0xC02B), TLS_ECDHE_RSA_WITH_AES_256_GCM_SHA384(0xC030), TLS_RSA_WITH_AES_256_GCM_SHA384(0x009D), TLS_ECDHE_RSA_WITH_AES_128_GCM_SHA256(0xC02F), TLS_RSA_WITH_AES_128_GCM_SHA256(0x009C), TLS_ECDHE_RSA_WITH_AES_256_CBC_SHA(0xC014), TLS_RSA_WITH_AES_256_CBC_SHA(0x0035), TLS_ECDHE_RSA_WITH_AES_128_CBC_SHA(0xC013), TLS_RSA_WITH_AES_128_CBC_SHA(0x002F)]",
app    |   "compression methods" : "00",
app    |   "extensions"          : [
app    |     "server_name (0)": {
app    |       type=host_name (0), value=server.gov.br
app    |     },
app    |  ...)
app    | javax.net.ssl|DEBUG|01|main|2020-07-20 17:35:59.124 BRT|null:-1|Consuming ServerHello handshake message (...)
app    | javax.net.ssl|DEBUG|01|main|2020-07-20 17:35:59.125 BRT|null:-1|Ignore unavailable extension: supported_versions
app    | javax.net.ssl|DEBUG|01|main|2020-07-20 17:35:59.125 BRT|null:-1|Negotiated protocol version: TLSv1.2
app    | javax.net.ssl|DEBUG|01|main|2020-07-20 17:35:59.126 BRT|null:-1|Consumed extension: renegotiation_info
app    | javax.net.ssl|DEBUG|01|main|2020-07-20 17:35:59.127 BRT|null:-1|Consumed extension: server_name
app    | javax.net.ssl|DEBUG|01|main|2020-07-20 17:35:59.128 BRT|null:-1|Ignore unavailable extension: max_fragment_length
app    | javax.net.ssl|DEBUG|01|main|2020-07-20 17:35:59.128 BRT|null:-1|Ignore unavailable extension: status_request
app    | javax.net.ssl|DEBUG|01|main|2020-07-20 17:35:59.129 BRT|null:-1|Consumed extension: ec_point_formats
app    | javax.net.ssl|DEBUG|01|main|2020-07-20 17:35:59.129 BRT|null:-1|Ignore unavailable extension: status_request_v2
app    | javax.net.ssl|DEBUG|01|main|2020-07-20 17:35:59.130 BRT|null:-1|Ignore unsupported extension: supported_versions
app    | javax.net.ssl|DEBUG|01|main|2020-07-20 17:35:59.130 BRT|null:-1|Ignore unsupported extension: key_share
app    | javax.net.ssl|DEBUG|01|main|2020-07-20 17:35:59.131 BRT|null:-1|Consumed extension: renegotiation_info
app    | javax.net.ssl|DEBUG|01|main|2020-07-20 17:35:59.132 BRT|null:-1|Ignore unsupported extension: pre_shared_key
app    | javax.net.ssl|WARNING|01|main|2020-07-20 17:35:59.133 BRT|null:-1|Ignore impact of unsupported extension: server_name
app    | javax.net.ssl|DEBUG|01|main|2020-07-20 17:35:59.139 BRT|null:-1|Ignore unavailable extension: max_fragment_length
app    | javax.net.ssl|DEBUG|01|main|2020-07-20 17:35:59.139 BRT|null:-1|Ignore unavailable extension: status_request
app    | javax.net.ssl|WARNING|01|main|2020-07-20 17:35:59.139 BRT|null:-1|Ignore impact of unsupported extension: ec_point_formats
app    | javax.net.ssl|DEBUG|01|main|2020-07-20 17:35:59.140 BRT|null:-1|Ignore unavailable extension: application_layer_protocol_negotiation
app    | javax.net.ssl|DEBUG|01|main|2020-07-20 17:35:59.141 BRT|null:-1|Ignore unavailable extension: status_request_v2
app    | javax.net.ssl|DEBUG|01|main|2020-07-20 17:35:59.141 BRT|null:-1|Ignore unavailable extension: extended_master_secret
app    | javax.net.ssl|DEBUG|01|main|2020-07-20 17:35:59.142 BRT|null:-1|Ignore unavailable extension: supported_versions
app    | javax.net.ssl|DEBUG|01|main|2020-07-20 17:35:59.142 BRT|null:-1|Ignore unavailable extension: key_share
app    | javax.net.ssl|WARNING|01|main|2020-07-20 17:35:59.143 BRT|null:-1|Ignore impact of unsupported extension: renegotiation_info
app    | javax.net.ssl|DEBUG|01|main|2020-07-20 17:35:59.144 BRT|null:-1|Ignore unavailable extension: pre_shared_key
app    | javax.net.ssl|DEBUG|01|main|2020-07-20 17:35:59.156 BRT|null:-1|Consuming server Certificate handshake message (... )
app    | javax.net.ssl|DEBUG|01|main|2020-07-20 17:35:59.164 BRT|null:-1|Consuming ECDH ServerKeyExchange handshake message (...
app    | )
app    | javax.net.ssl|DEBUG|01|main|2020-07-20 17:35:59.205 BRT|null:-1|Consuming CertificateRequest handshake message (
app    | "CertificateRequest": {
app    |   "certificate types": [rsa_sign, dss_sign, ecdsa_sign]
app    |   "supported signature algorithms": [rsa_pkcs1_sha512, dsa_sha512, ecdsa_secp521r1_sha512, rsa_pkcs1_sha384, dsa_sha384, ecdsa_secp384r1_sha384, rsa_pkcs1_sha256, dsa_sha256, ecdsa_secp256r1_sha256, rsa_sha224, dsa_sha224, ecdsa_sha224, rsa_pkcs1_sha1, dsa_sha1, ecdsa_sha1]
app    |   "certificate authorities": [CN=SERASA CD SSL V5, O=ICP-Brasil, ...]
app    | }
app    | )
app    | javax.net.ssl|ALL|01|main|2020-07-20 17:35:59.208 BRT|null:-1|No X.509 cert selected for RSA
app    | javax.net.ssl|WARNING|01|main|2020-07-20 17:35:59.211 BRT|null:-1|Unavailable authentication scheme: rsa_pkcs1_sha512
app    | javax.net.ssl|ALL|01|main|2020-07-20 17:35:59.211 BRT|null:-1|No X.509 cert selected for EC
app    | javax.net.ssl|WARNING|01|main|2020-07-20 17:35:59.211 BRT|null:-1|Unavailable authentication scheme: ecdsa_secp521r1_sha512
app    | javax.net.ssl|ALL|01|main|2020-07-20 17:35:59.212 BRT|null:-1|No X.509 cert selected for RSA
app    | javax.net.ssl|WARNING|01|main|2020-07-20 17:35:59.212 BRT|null:-1|Unavailable authentication scheme: rsa_pkcs1_sha384
app    | javax.net.ssl|ALL|01|main|2020-07-20 17:35:59.213 BRT|null:-1|No X.509 cert selected for EC
app    | javax.net.ssl|WARNING|01|main|2020-07-20 17:35:59.213 BRT|null:-1|Unavailable authentication scheme: ecdsa_secp384r1_sha384
app    | javax.net.ssl|ALL|01|main|2020-07-20 17:35:59.214 BRT|null:-1|No X.509 cert selected for RSA
app    | javax.net.ssl|WARNING|01|main|2020-07-20 17:35:59.214 BRT|null:-1|Unavailable authentication scheme: rsa_pkcs1_sha256
app    | javax.net.ssl|ALL|01|main|2020-07-20 17:35:59.215 BRT|null:-1|No X.509 cert selected for DSA
app    | javax.net.ssl|WARNING|01|main|2020-07-20 17:35:59.215 BRT|null:-1|Unavailable authentication scheme: dsa_sha256
app    | javax.net.ssl|ALL|01|main|2020-07-20 17:35:59.216 BRT|null:-1|No X.509 cert selected for EC
app    | javax.net.ssl|WARNING|01|main|2020-07-20 17:35:59.217 BRT|null:-1|Unavailable authentication scheme: ecdsa_secp256r1_sha256
app    | javax.net.ssl|ALL|01|main|2020-07-20 17:35:59.217 BRT|null:-1|No X.509 cert selected for RSA
app    | javax.net.ssl|WARNING|01|main|2020-07-20 17:35:59.218 BRT|null:-1|Unavailable authentication scheme: rsa_sha224
app    | javax.net.ssl|ALL|01|main|2020-07-20 17:35:59.219 BRT|null:-1|No X.509 cert selected for DSA
app    | javax.net.ssl|WARNING|01|main|2020-07-20 17:35:59.219 BRT|null:-1|Unavailable authentication scheme: dsa_sha224
app    | javax.net.ssl|ALL|01|main|2020-07-20 17:35:59.220 BRT|null:-1|No X.509 cert selected for EC
app    | javax.net.ssl|WARNING|01|main|2020-07-20 17:35:59.220 BRT|null:-1|Unavailable authentication scheme: ecdsa_sha224
app    | javax.net.ssl|ALL|01|main|2020-07-20 17:35:59.221 BRT|null:-1|No X.509 cert selected for RSA
app    | javax.net.ssl|WARNING|01|main|2020-07-20 17:35:59.221 BRT|null:-1|Unavailable authentication scheme: rsa_pkcs1_sha1
app    | javax.net.ssl|ALL|01|main|2020-07-20 17:35:59.222 BRT|null:-1|No X.509 cert selected for DSA
app    | javax.net.ssl|WARNING|01|main|2020-07-20 17:35:59.222 BRT|null:-1|Unavailable authentication scheme: dsa_sha1
app    | javax.net.ssl|ALL|01|main|2020-07-20 17:35:59.223 BRT|null:-1|No X.509 cert selected for EC
app    | javax.net.ssl|WARNING|01|main|2020-07-20 17:35:59.223 BRT|null:-1|Unavailable authentication scheme: ecdsa_sha1
app    | javax.net.ssl|WARNING|01|main|2020-07-20 17:35:59.223 BRT|null:-1|No available authentication scheme
app    | javax.net.ssl|DEBUG|01|main|2020-07-20 17:35:59.224 BRT|null:-1|Consuming ServerHelloDone handshake message (
app    | <empty>
app    | )
app    | javax.net.ssl|DEBUG|01|main|2020-07-20 17:35:59.225 BRT|null:-1|No X.509 certificate for client authentication, use empty Certificate message instead
app    | javax.net.ssl|DEBUG|01|main|2020-07-20 17:35:59.225 BRT|null:-1|Produced client Certificate handshake message (
app    | "Certificates": <empty list>
app    | )
app    | javax.net.ssl|DEBUG|01|main|2020-07-20 17:35:59.233 BRT|null:-1|Produced ECDHE ClientKeyExchange handshake message (
app    | "ECDH ClientKeyExchange": {
app    |   "ecdh public": {
...
app    |   },
app    | }
app    | )
app    | javax.net.ssl|DEBUG|01|main|2020-07-20 17:35:59.243 BRT|null:-1|Produced ChangeCipherSpec message
app    | javax.net.ssl|WARNING|01|main|2020-07-20 17:35:59.247 BRT|null:-1|handling exception (
app    | "throwable" : {
app    |   javax.net.ssl.SSLException: readApplicationRecord
app    |    at java.base/sun.security.ssl.SSLSocketImpl.readApplicationRecord(Unknown Source)
app    |    at java.base/sun.security.ssl.SSLSocketImpl$AppInputStream.read(Unknown Source)
app    |    at okio.InputStreamSource.read(JvmOkio.kt:90)
app    |    at okio.AsyncTimeout$source$1.read(AsyncTimeout.kt:129)
app    |    at okio.RealBufferedSource.indexOf(RealBufferedSource.kt:449)
app    |    at okio.RealBufferedSource.readUtf8LineStrict(RealBufferedSource.kt:333)
app    |    at okhttp3.internal.http1.HeadersReader.readLine(HeadersReader.kt:29)
app    |    at okhttp3.internal.http1.Http1ExchangeCodec.readResponseHeaders(Http1ExchangeCodec.kt:178)
app    |    at okhttp3.internal.connection.Exchange.readResponseHeaders(Exchange.kt:106)
app    |    at okhttp3.internal.http.CallServerInterceptor.intercept(CallServerInterceptor.kt:79)
app    |    at okhttp3.internal.http.RealInterceptorChain.proceed(RealInterceptorChain.kt:109)
app    |    at okhttp3.internal.connection.ConnectInterceptor.intercept(ConnectInterceptor.kt:34)
app    |    at okhttp3.internal.http.RealInterceptorChain.proceed(RealInterceptorChain.kt:109)
app    |    at okhttp3.internal.cache.CacheInterceptor.intercept(CacheInterceptor.kt:95)
app    |    at okhttp3.internal.http.RealInterceptorChain.proceed(RealInterceptorChain.kt:109)
app    |    at okhttp3.internal.http.BridgeInterceptor.intercept(BridgeInterceptor.kt:83)
app    |    at okhttp3.internal.http.RealInterceptorChain.proceed(RealInterceptorChain.kt:109)
app    |    at okhttp3.internal.http.RetryAndFollowUpInterceptor.intercept(RetryAndFollowUpInterceptor.kt:76)
app    |    at okhttp3.internal.http.RealInterceptorChain.proceed(RealInterceptorChain.kt:109)
app    |    at okhttp3.internal.connection.RealCall.getResponseWithInterceptorChain$okhttp(RealCall.kt:201)
app    |    at okhttp3.internal.connection.RealCall.execute(RealCall.kt:154)
app    |    at testes.ClienteCertificateHandshake.run(ClienteCertificateHandshake.java:30)
app    |    at testes.ClienteCertificateHandshake.main(Main.java:18)
app    |   Caused by: java.net.SocketException: Broken pipe (Write failed)
app    |    at java.base/java.net.SocketOutputStream.socketWrite0(Native Method)
app    |    at java.base/java.net.SocketOutputStream.socketWrite(Unknown Source)
app    |    at java.base/java.net.SocketOutputStream.write(Unknown Source)
app    |    at java.base/sun.security.ssl.SSLSocketOutputRecord.encodeChangeCipherSpec(Unknown Source)
app    |    at java.base/sun.security.ssl.OutputRecord.changeWriteCiphers(Unknown Source)
app    |    at java.base/sun.security.ssl.ChangeCipherSpec$T10ChangeCipherSpecProducer.produce(Unknown Source)
app    |    at java.base/sun.security.ssl.Finished$T12FinishedProducer.onProduceFinished(Unknown Source)
app    |    at java.base/sun.security.ssl.Finished$T12FinishedProducer.produce(Unknown Source)
app    |    at java.base/sun.security.ssl.SSLHandshake.produce(Unknown Source)
app    |    at java.base/sun.security.ssl.ServerHelloDone$ServerHelloDoneConsumer.consume(Unknown Source)
app    |    at java.base/sun.security.ssl.SSLHandshake.consume(Unknown Source)
app    |    at java.base/sun.security.ssl.HandshakeContext.dispatch(Unknown Source)
app    |    at java.base/sun.security.ssl.HandshakeContext.dispatch(Unknown Source)
app    |    at java.base/sun.security.ssl.TransportContext.dispatch(Unknown Source)
app    |    at java.base/sun.security.ssl.SSLTransport.decode(Unknown Source)
app    |    at java.base/sun.security.ssl.SSLSocketImpl.decode(Unknown Source)
app    |    ... 23 more}
app    | 
app    | )
app    | javax.net.ssl|ERROR|01|main|2020-07-20 17:35:59.250 BRT|null:-1|Fatal (UNEXPECTED_MESSAGE): readApplicationRecord (
app    | "throwable" : {
app    |   javax.net.ssl.SSLException: readApplicationRecord
app    |    at java.base/sun.security.ssl.SSLSocketImpl.readApplicationRecord(Unknown Source)
app    |    at java.base/sun.security.ssl.SSLSocketImpl$AppInputStream.read(Unknown Source)
app    |    at okio.InputStreamSource.read(JvmOkio.kt:90)
app    |    at okio.AsyncTimeout$source$1.read(AsyncTimeout.kt:129)
app    |    at okio.RealBufferedSource.indexOf(RealBufferedSource.kt:449)
app    |    at okio.RealBufferedSource.readUtf8LineStrict(RealBufferedSource.kt:333)
app    |    at okhttp3.internal.http1.HeadersReader.readLine(HeadersReader.kt:29)
app    |    at okhttp3.internal.http1.Http1ExchangeCodec.readResponseHeaders(Http1ExchangeCodec.kt:178)
app    |    at okhttp3.internal.connection.Exchange.readResponseHeaders(Exchange.kt:106)
app    |    at okhttp3.internal.http.CallServerInterceptor.intercept(CallServerInterceptor.kt:79)
app    |    at okhttp3.internal.http.RealInterceptorChain.proceed(RealInterceptorChain.kt:109)
app    |    at okhttp3.internal.connection.ConnectInterceptor.intercept(ConnectInterceptor.kt:34)
app    |    at okhttp3.internal.http.RealInterceptorChain.proceed(RealInterceptorChain.kt:109)
app    |    at okhttp3.internal.cache.CacheInterceptor.intercept(CacheInterceptor.kt:95)
app    |    at okhttp3.internal.http.RealInterceptorChain.proceed(RealInterceptorChain.kt:109)
app    |    at okhttp3.internal.http.BridgeInterceptor.intercept(BridgeInterceptor.kt:83)
app    |    at okhttp3.internal.http.RealInterceptorChain.proceed(RealInterceptorChain.kt:109)
app    |    at okhttp3.internal.http.RetryAndFollowUpInterceptor.intercept(RetryAndFollowUpInterceptor.kt:76)
app    |    at okhttp3.internal.http.RealInterceptorChain.proceed(RealInterceptorChain.kt:109)
app    |    at okhttp3.internal.connection.RealCall.getResponseWithInterceptorChain$okhttp(RealCall.kt:201)
app    |    at okhttp3.internal.connection.RealCall.execute(RealCall.kt:154)
app    |    at testes.ClienteCertificateHandshake.run(ClienteCertificateHandshake.java:30)
app    |    at testes.ClienteCertificateHandshake.Main.main(Main.java:13)
app    |   Caused by: java.net.SocketException: Broken pipe (Write failed)
app    |    at java.base/java.net.SocketOutputStream.socketWrite0(Native Method)
app    |    at java.base/java.net.SocketOutputStream.socketWrite(Unknown Source)
app    |    at java.base/java.net.SocketOutputStream.write(Unknown Source)
app    |    at java.base/sun.security.ssl.SSLSocketOutputRecord.encodeChangeCipherSpec(Unknown Source)
app    |    at java.base/sun.security.ssl.OutputRecord.changeWriteCiphers(Unknown Source)
app    |    at java.base/sun.security.ssl.ChangeCipherSpec$T10ChangeCipherSpecProducer.produce(Unknown Source)
app    |    at java.base/sun.security.ssl.Finished$T12FinishedProducer.onProduceFinished(Unknown Source)
app    |    at java.base/sun.security.ssl.Finished$T12FinishedProducer.produce(Unknown Source)
app    |    at java.base/sun.security.ssl.SSLHandshake.produce(Unknown Source)
app    |    at java.base/sun.security.ssl.ServerHelloDone$ServerHelloDoneConsumer.consume(Unknown Source)
app    |    at java.base/sun.security.ssl.SSLHandshake.consume(Unknown Source)
app    |    at java.base/sun.security.ssl.HandshakeContext.dispatch(Unknown Source)
app    |    at java.base/sun.security.ssl.HandshakeContext.dispatch(Unknown Source)
app    |    at java.base/sun.security.ssl.TransportContext.dispatch(Unknown Source)
app    |    at java.base/sun.security.ssl.SSLTransport.decode(Unknown Source)
app    |    at java.base/sun.security.ssl.SSLSocketImpl.decode(Unknown Source)
app    |    ... 23 more}
app    | 
app    | )
app  exited with code 0

2 Answers

[RESOLVIDO] Consegui resolver o problema importando a keystore e o truststore via código e configurando o contexto. Usei como exemplo uma issue do projeto okhttp3: https://github.com/square/okhttp/issues/2786 e ajustei pro meu caso e funcionou.

package testes;

import java.io.FileInputStream;
import java.io.InputStream;
import java.security.KeyStore;
import java.security.SecureRandom;
import java.util.Arrays;
import java.util.concurrent.TimeUnit;

import javax.net.ssl.KeyManager;
import javax.net.ssl.KeyManagerFactory;
import javax.net.ssl.SSLContext;
import javax.net.ssl.SSLSocketFactory;
import javax.net.ssl.TrustManager;
import javax.net.ssl.TrustManagerFactory;
import javax.net.ssl.X509TrustManager;

import com.squareup.okhttp.OkHttpClient;
import com.squareup.okhttp.Request;
import com.squareup.okhttp.Response;

public class HttpsClientOkHttp {
    /*
     * // truststore default, caso importe as cadeias de certificados para o truststore default do java
     * private final String truststoreType = KeyStore.getDefaultType();
     * private final String truststorePath = System.getenv("JAVA_HOME") + "/lib/security/cacerts";
     * private final String truststorePassword = "changeit";
     */

    // truststore personalizado usando o keytool a partir das cadeias de certificados do SERPRO
    private final String truststoreType = "JKS";
    private final String truststorePath = "/truststore.jks";
    private final String truststorePassword = "trustorePass";

    private final String keystoreType = "PKCS12";
    private final String keystorePath = "clientCertificate.pfx";
    private final String keystorePassword = "clientCertPass";
    private final String url = "https://server.gov.br/authentication";

    public static void main(String[] args) throws Exception {
        System.out.println("Running...");
        try {
            new HttpsClientOkHttp().run();
        } catch (Exception e) {
            e.printStackTrace();
        }
    }

    public final void run() {
        try {
            OkHttpClient httpsClient = createHttpsClient();
            Request request = new Request.Builder().url(url).build();

            Response response = httpsClient.newCall(request).execute();

            if (!response.isSuccessful()) {
                throw new RuntimeException("Unexpected code " + response);
            }

            System.out.println(response.body().string());

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

    public OkHttpClient createHttpsClient() throws Exception {
        long timeout = 120l;
        OkHttpClient httpsClient = new OkHttpClient();
        httpsClient.setConnectTimeout(timeout, TimeUnit.SECONDS);
        httpsClient.setWriteTimeout(timeout, TimeUnit.SECONDS);
        httpsClient.setReadTimeout(timeout, TimeUnit.SECONDS);
        httpsClient.setSslSocketFactory(createSslSocketFactory());
        return httpsClient;
    }

    public SSLSocketFactory createSslSocketFactory() throws Exception {
        try (InputStream keyInputStream = new FileInputStream(keystorePath);
                InputStream trustInputStream = new FileInputStream(truststorePath)) {

            KeyStore clientStore = KeyStore.getInstance(keystoreType);
            char[] clientPassArray = keystorePassword.toCharArray();
            clientStore.load(keyInputStream, clientPassArray);

            KeyStore trustStore = KeyStore.getInstance(truststoreType);
            trustStore.load(trustInputStream, truststorePassword.toCharArray());

            KeyManagerFactory keyManagerFactory = KeyManagerFactory
                    .getInstance(KeyManagerFactory.getDefaultAlgorithm());
            keyManagerFactory.init(clientStore, clientPassArray);
            KeyManager[] keyManagers = keyManagerFactory.getKeyManagers();

            TrustManagerFactory trustManagerFactory = TrustManagerFactory
                    .getInstance(TrustManagerFactory.getDefaultAlgorithm());
            trustManagerFactory.init(trustStore);
            TrustManager[] trustManagers = trustManagerFactory.getTrustManagers();

            if (trustManagers.length != 1 || !(trustManagers[0] instanceof X509TrustManager)) {
                throw new IllegalStateException("Unexpected default trust managers:" + Arrays.toString(trustManagers));
            }

            SSLContext sslContext = SSLContext.getInstance("TLSv1.2");
            sslContext.init(keyManagers, trustManagers, new SecureRandom());

            return sslContext.getSocketFactory();
        } catch (Exception ex) {
            throw ex;
        }
    }
}

Answered by Marcelo Ferreira Vasconcelos on August 6, 2020

Uma forma que você pode fazer é setar a keyStore e a trustStore na classe System, veja se o código a seguir te ajuda.

try {
    String url = "https://server.gov.br/authentication";
    
    //set keyStore
    System.setProperty("javax.net.ssl.keyStore", "C:\temp\yourKeyStore.jks");
    System.setProperty("javax.net.ssl.keyStorePassword", "password");
    System.setProperty("javax.net.ssl.keyStoreType", "JKS");

    //set trustStore
    System.setProperty("javax.net.ssl.trustStore", "C:\temp\yourTrustStore.jks");
    System.setProperty("javax.net.ssl.trustPassword", "password");
    System.setProperty("javax.net.ssl.trustType", "JKS");

    HttpsURLConnection con = (HttpsURLConnection) new URL(url).openConnection();
    con.setRequestMethod("POST");

    //add request header
    con.setRequestProperty("Content-Type", "application/json; utf-8");
    con.setRequestProperty("Accept", "application/json");

    con.setDoOutput(true);

    try(OutputStream os = con.getOutputStream()) {
        byte[] input = jsonBody.getBytes("utf-8");
        os.write(input, 0, input.length);
    }

    try(BufferedReader br = new BufferedReader(
            new InputStreamReader(con.getInputStream(), "utf-8"))) {
        StringBuilder response = new StringBuilder();
        String responseLine = null;
        while ((responseLine = br.readLine()) != null) {
            response.append(responseLine.trim());
        }
    }

    con.disconnect();

} catch (Exception e) {
    System.out.println(e);
}

Answered by furstmartins on August 6, 2020

Add your own answers!

Ask a Question

Get help from others!

© 2024 TransWikia.com. All rights reserved. Sites we Love: PCI Database, UKBizDB, Menu Kuliner, Sharing RPP