Este exemplo tem como objetivo mostrar como um cliente TLS usa um certificado de cliente e uma CA para conectar-se no servidor EPP do Registro.br. É um código exemplo apenas com fins didáticos. Foi usado o Java 8 e somente sua biblioteca padrão.
Os arquivos disponibilizados pelo Registro.br e usados neste exemplo podem ser encontrados em ftp://ftp.registro.br/pub/libepp-nicbr/test-certificates/
Em Java, um TrustManager gerencia as CAs e um KeyManager gerencia o certificado e a chave privada. Neste exemplo usamos estes mecanismos para facilitar a manipulação e validação dos certificados.
Como o Registro.br disponibiliza os certificados EPP no formato X.509, precisamos convertê-los para arquivos no formato KeyStore e TrustStore.
- Convertendo o certificado da raíz (CA) para um arquivo no formato TrustStore:
$ keytool -import -file root.pem -alias root -keystore root.truststore -storepass truststorepasswd
Um arquivo root.truststore será criado e está protegido com a senha truststorepasswd. Vale lembrar que o arquivo root.pem pode conter mais de um certificado raíz. Para manter este exemplo o mais simples possível estamos importando apenas o primeiro certificado.
2.1. O Registro.br disponibiliza o certificado do cliente e sua chave em um único arquivo. Precisamos separá-los:
Edite o arquivo client-pwd.pem e separe o conteúdo do certificado e da chave em dois arquivos diferentes: client-pwd.pem e client.key
2.2. A versão mais recente do keytool disponível atualmente não consegue importar uma chave privada no formato X.509. Para resolver este problema precisamos de um passo intermediário, converter o certificado do cliente e sua chave privada para o formato PKCS12.
$ openssl pkcs12 -export -in client-pwd.pem -inkey client.key -out client.p12 -name shepp -CAfile root.pem -caname root
Neste passo será requisitada uma senha para o novo arquivo PKCS12.
2.3. Agora podemos converter o PKCS12 para um KeyStore.
keytool -importkeystore -deststorepass keystorepasswd -destkeypass privkeypasswd -destkeystore client.keystore -srckeystore client.p12 -srcstoretype PKCS12 -srcstorepass <senha_usada_no_passo_anterior>
Neste passo um novo arquivo client.keystore será criado. Este arquivo está protegido com a senha keystorepasswd e contém o certificado do cliente e a chave privada do cliente - que está protegida pela senha privkeypasswd.
2.4. Temos dois arquivos: root.truststore e client.keystore. O código a seguir espera que estes arquivos estejam no pacote 'certs'.
package core;
import java.io.DataInputStream;
import java.io.IOException;
import java.io.InputStream;
import java.security.KeyStore;
import java.security.SecureRandom;
import javax.net.ssl.KeyManagerFactory;
import javax.net.ssl.SSLContext;
import javax.net.ssl.SSLSocket;
import javax.net.ssl.SSLSocketFactory;
import javax.net.ssl.TrustManagerFactory;
import javax.security.cert.X509Certificate;
public class Main {
// pra deixar o código mais simples estamos ignorando as exceções
public static void main(String[] args) throws Exception {
char[] trustStorePassword = "truststorepasswd".toCharArray();
char[] keyStorePassword = "keystorepasswd".toCharArray();
char[] privateKeyPassword = "privkeypasswd".toCharArray();
// Supondo que os certificados estão no pacote "certs".
InputStream inputStream =
ClassLoader.getSystemClassLoader().getResourceAsStream("certs/client.keystore");
KeyStore keyStore = KeyStore.getInstance("JKS");
keyStore.load(inputStream, keyStorePassword);
KeyManagerFactory keyManagerFactory =
KeyManagerFactory.getInstance(KeyManagerFactory.getDefaultAlgorithm());
keyManagerFactory.init(keyStore, privateKeyPassword);
inputStream = ClassLoader.getSystemClassLoader().getResourceAsStream("certs/root.truststore");
keyStore.load(inputStream, trustStorePassword);
TrustManagerFactory tmf = TrustManagerFactory.getInstance("SunX509");
tmf.init(keyStore);
SSLContext context = SSLContext.getInstance("TLS");
context.init(keyManagerFactory.getKeyManagers(), tmf.getTrustManagers(), new SecureRandom());
SSLSocketFactory sslsocketfactory = context.getSocketFactory();
SSLSocket socket = (SSLSocket) sslsocketfactory.createSocket("beta.registro.br", 700);
// para debug da cadeia de certificados do servidor
X509Certificate chain[] = socket.getSession().getPeerCertificateChain();
for (int i = 0; i < chain.length; i++) {
System.out.println("peer-certificate " + i);
System.out.println(" Subject : " + chain[i].getSubjectDN().getName());
System.out.println(" Issuer : " + chain[i].getIssuerDN().getName());
System.out.println(" Not Before : " + chain[i].getNotBefore());
System.out.println(" Not After : " + chain[i].getNotAfter());
}
DataInputStream input = new DataInputStream(socket.getInputStream());
int len = input.readInt();
if (len <= 4 || len > 1024) {
throw new IOException("invalid response length");
}
len -= 4;
byte[] bytes = new byte[len];
input.readFully(bytes,0,len);
// aqui temos o XML da resposta que veio do servidor
String text = new String(bytes, "UTF-8");
System.out.println(text);
}
}