Estou tendo problemas com uma chamada para uma API de terceiros de um aplicativo Java. A API externa requer pelo menos uma das seguintes cifras:
TLS_ECDHE_ECDSA_WITH_AES_128_GCM_SHA256
TLS_ECDHE_ECDSA_WITH_AES_256_GCM_SHA384
TLS_ECDHE_ECDSA_WITH_AES_128_CBC_SHA256
TLS_ECDHE_ECDSA_WITH_AES_256_CBC_SHA384
TLS_ECDHE_RSA_WITH_AES_128_GCM_SHA256
TLS_ECDHE_RSA_WITH_AES_256_GCM_SHA384
TLS_ECDHE_RSA_WITH_AES_128_CBC_SHA256
TLS_ECDHE_RSA_WITH_AES_256_CBC_SHA384
TLS_DHE_RSA_WITH_AES_256_CBC_SHA256
TLS_DHE_RSA_WITH_AES_128_CBC_SHA256
TLS_DHE_RSA_WITH_AES_256_GCM_SHA384
TLS_DHE_RSA_WITH_AES_128_GCM_SHA256
Eu segui as instruções aqui: https://confluence.atlassian.com/stashkb/list-ciphers-used-by-jvm-679609085.html para tentar atualizar as cifras disponíveis no meu servidor de aplicativos. Após a atualização, no entanto, ainda não vejo nenhuma das cifras compatíveis listadas.
Eu suspeito que o problema é que meu servidor está executando o jdk 1.7 . Este é um aplicativo herdado, então, infelizmente, não consigo atualizar para um jdk mais recente. Existe alguma maneira de adicionar essas cifras à minha instalação java existente?
Atualização: descobri como adicionar cifras adicionais, mas não parece ter ajudado. Na minha fábrica de soquetes eu adicionei algum código como este:
private Socket acceptOnlyTLS12(Socket socket) {
if (!(socket instanceof SSLSocket))
return socket;
SSLSocket sslSocket = (SSLSocket) socket;
sslSocket.setEnabledProtocols(new String[] { "TLSv1.2" });
List<String> ciphers = new ArrayList<String>(Arrays.asList(sslSocket.getEnabledCipherSuites()));
ciphers.add("TLS_ECDHE_ECDSA_WITH_AES_128_CBC_SHA256");
sslSocket.setEnabledCipherSuites(ciphers.toArray(new String[] {}));
return sslSocket;
}
Existe outro passo que estou perdendo em algum lugar? Existe alguma maneira de obter informações adicionais sobre por que o handshake SSL está falhando?
(1) Essa página da Web é datada de 2014; política ilimitada não é mais usada para versões Oracle Java após 2017, e antes disso (por exemplo, 7u80) importava apenas para criptografia simétrica acima de 128 bits , que aqui afetaria apenas os conjuntos AES256 e não os AES128. ( Nunca foi aplicável ao OpenJDK, embora o OpenJDK abaixo de 8 estivesse/está disponível principalmente apenas nas principais distribuições Linux como RedHat e Debian, que poderiam alocar funcionários para construção e empacotamento; você não diz qual está usando.)
(2) Java (1.)7 suporta os ciphersuites CBC que você mostra (não os GCM, e para versões Oracle abaixo de 7u171 os AES256 exigem política ilimitada), mas SOMENTE quando TLS1.2 é usado (esses ciphersuites não existiam em protocolos de versão inferior) e, por padrão, o j7 desabilita o lado do cliente TLS1.2 (e 1.1).
Se você está explicitamente fazendo a conexão com esta API com
HttpsURLConnection
(por exemplonew URL("https://something").openConnection()
) você pode ajustar o socketfactory para usarSSLContext.getInstance("TLSv1.2")
e/ou explicitamentesetEnabledProtocols
no socket. Se você estiver usando outro middleware, por exemplo, Apache HttpComponents, geralmente existem métodos semelhantes, mas eles variam em detalhes; você precisaria nos mostrar o código, e isso provavelmente pertenceria ao StackOverflow e não aqui. Se você estiver chamando uma biblioteca que faz a conexão internamente, ela pode ter opções ou não. Para todos ou muitos métodos de chamada, você pode alterar os padrões comoSSLContext.setDefault()
ouHttpsURLConnection.setDefaultSSLSocketFacfory()
se esses padrões não forem substituídos no código relevante e fazer uma alteração global como essa não causará problemas para qualquer outra coisa em execução na mesma JVM.Alternativamente (e mais sobre o tópico!), se você tiver uma atualização j7 suficientemente recente, tenho certeza de que eles retroportaram a propriedade do sistema
jdk.tls.client.protocols
que você pode definir para, por exemploTLSv1,TLSv1.1,TLSv1.2
, alterar o padrão sem alteração de código (mas novamente apenas se não for substituído e não prejudicar qualquer outra coisa). Não me lembro exatamente quando isso foi, mas definitivamente depois de 7u80, então você teria apenas com suporte pago da Oracle ou OpenJDK suportado por outra pessoa com ou sem pagamento. Isso é fácil de tentar e pode funcionar.Eu finalmente encontrei uma solução alternativa, se um pouco feia. Eu criei um script groovy que faz uma solicitação cURL para a API externa. Como o cURL pode usar os certificados mais recentes, posso contornar as limitações impostas por estar preso a um JDK antigo. É chato mas funciona.