O blog da AWS

Como integrar o AWS KMS com JCE e gerar um CSR

Por Lucas Lins, Arquiteto de Soluções na AWS
João Paulo Aragão Pereira, Arquiteto de Soluções na AWS

 

Na maioria dos contextos de Indústrias, como de Serviços Financeiros, são requeridas operações que envolvem à utilização de chaves assimétricas para assinatura e verificação digital de documentos e arquivos. Neste processo, geralmente são utilizados certificados digitais válidos que atestam a propriedade destas chaves assimétricas a uma pessoa, entidade ou sistema.

O AWS Key Management Service (KMS) é um serviço que facilita a criação e o gerenciamento de chaves criptográficas e o controle do seu uso em uma ampla variedade de serviços da AWS e em suas aplicações. Além disso, é um serviço totalmente gerenciado e altamente disponível, sendo uma excelente opção para suprir as necessidades mencionadas anteriormente. Entretanto, existem 2 características intrínsecas do AWS KMS que tornam a sua utilização um estímulo, no contexto de assinatura e verificação, para o desenvolvimento do AWS KMS JCE (Java Cryptography Extension) Provider.

Primeiro, os módulos de segurança de hardware multi-locatário (HSMs) usados no armazenamento de chaves KMS padrão são validados por um laboratório de terceiros sob o padrão FIPS 140-2 no nível 2, e com nível 3 em várias categorias. Além disso, o AWS KMS foi projetado para que ninguém, incluindo os funcionários da AWS, possa recuperar chaves de clientes e usá-las fora do serviço. Ou seja, a chave privada nunca deixa o AWS KMS. Porém, sem a chave privada, não é possível utilizar ferramentas, como o OpenSSL, para gerar o Certificate Signing Request (CSR) e, posteriormente, obter um certificado digital válido.

Enfim, o AWS KMS possui acesso apenas via API, que pode ser facilitado via AWS SDKs e, para algumas operações, com o AWS Encryption SDK. Neste momento em que estamos escrevendo este blogpost, o AWS Encryption SDK só é compatível com chaves chaves simétricas, e não sendo possível usar chaves assimétricas para criptografia (encriptar/decriptar) ou assinatura no AWS Encryption SDK.

Diversas linguagens de programação possuem um padrão para utilização de operações criptográficas. No caso de Java, por exemplo, é o JCA/JCE. Assim, o objetivo deste blogpost é apresentar o desenvolvimento de uma biblioteca chamada AWS KMS JCE Provider, a fim de realizar a integração entre o AWS KMS com JCE e gerar um CSR, com chaves assimétricas.

Mas primeiro, vale ressaltar outras características do AWS KMS.

 

AWS KMS

Além das características já mencionadas anteriormente a respeito do AWS KMS, vale destacar que o serviço possui:

  • API amigável.
  • Suporte à rotação automáticapara alguns tipos de chaves.
  • Impossibilidade de extração de chaves privadas.
  • Controle de acesso
  • Integração ao AWS CloudTrail para registrar todas as solicitações de API, incluindo ações de gerenciamento e uso de suas chaves.
  • Operações com CMK simétrica: representa uma única chave de criptografia secreta de 256 bits que nunca deixa o AWS KMS descriptografada. Para usar a CMK simétrica, é necessário uma chamada de API ao AWS KMS.
  • Operações com CMK assimétrica: representa uma chave pública e uma chave privada relacionadas matematicamente que podem ser usadas para criptografar e descriptografar ou para assinar e verificar, mas não ambos. A chave privada nunca deixa o AWS KMS descriptografada. É possível usar a chave pública no AWS KMS chamando as operações de API do AWS KMS ou fazer download da chave pública e usá-la fora do AWS KMS.
  • Operações com Chave de criptografia simétrica — uma chave de criptografia simétrica que pode ser usada para criptografar dados fora do AWS KMS. Essa chave é protegida por uma CMK simétrica no AWS KMS.
  • Operações com chaves de criptografia assimétricas — um par de chaves RSA ou de curvas elípticas (ECC) que consiste em uma chave pública e uma chave privada. É possível usar a chave pública fora do AWS KMS para criptografar e descriptografar dados ou para verificar assinaturas.
  • Baixo custo, e não há compromisso nem cobranças iniciais para usar o AWS KMS.

 

Criptografar e descriptografar

As operações Encrypt, Decrypt e ReEncrypt na API do AWS KMS foram projetadas para criptografar e descriptografar chaves de dados. Elas usam uma chave mestra do cliente do AWS KMS (CMK) nas operações de criptografia e não podem aceitar mais do que os valores da tabela abaixo:

Chave Algoritmo Tamanho máximo (bytes)
Simétrica SYMMETRIC_DEFAULT 4096
Assimétrica RSA 2048 RSAES_OAEP_SHA_1 214
RSAES_OAEP_SHA_256 190
Assimétrica RSA 3072 RSAES_OAEP_SHA_1 342
RSAES_OAEP_SHA_256 318
Assimétrica RSA 4096 RSAES_OAEP_SHA_1 470
RSAES_OAEP_SHA_256 446

Embora você possa usar as chaves no KMS para criptografar pequenas quantidades de dados, como uma senha ou chave RSA, elas não foram projetadas para criptografar dados de aplicações.

Embora o AWS KMS ofereça suporte ao envio de dados conforme a tabela anterior, para criptografia simétrica a criptografia de envelope pode oferecer benefícios significativos de performance. Quando você criptografa diretamente os dados com o AWS KMS, eles precisam ser transferidos pela rede. A criptografia de envelope reduz a carga de rede, pois apenas a solicitação e a entrega da chave de dados que geralmente é muito menor são transmitidas pela rede. A chave de dados é usada localmente em sua aplicação ou serviço de criptografia da AWS, evitando a necessidade de enviar o bloco de dados inteiro para o AWS KMS e sofrer latência de rede.

Esses foram os motivos por não desenvolver a parte de encriptação/decriptação para o AWS KMS JCE Provider, mas apenas as operações de assinatura e verificação.

 

AWS KMS JCE Provider

A biblioteca de software de AWS KMS JCE Provider para Java é uma implementação de fornecedor para a estrutura de provedor de Sun Java JCE (Java Cryptography Extension) com o foco em utilização de chaves assimétricas para assinar e verificar. Isto inclui implementações para interfaces e classes de mecanismo no padrão JCA (Java Cryptography Architecture). O código-fonte está disponível no repositório aws-samples/aws-kms-jce no GitHub.

Para utilizar o AWS KMS JCE Provider é necessário, primeiramente, criar uma CMK assimétrica no AWS KMS para assinatura e verificação. Uma CMK assimétrica representa um par de chaves pública e privada relacionadas matematicamente. É possível dar a chave pública para qualquer pessoa, mesmo se ela não for confiável, porém mantendo a chave privada em sigilo.

O AWS KMS oferece suporte a dois tipos de CMKs assimétricas para assinatura e verificação.

  • CMKs RSA: uma CMK com um par de chaves RSA. O KMS oferece suporte a vários tamanhos de chave para diferentes requisitos de segurança.
  • CMKs de curva elíptica (ECC): uma CMK com um par de chaves de curva elíptica. O KMS oferece suporte a várias curvas comumente usadas.

Ambos os tipos são suportados no AWS KMS JCE Provider, bem como todas as suas variações de tamanhos e todos os seus algoritmos de assinatura.

 

Inicializando o Provider

Para utilizar o AWS KMS JCE Provider é necessário primeiro a criação do provider. O provider necessita que o cliente do KMS seja informado em sua criação. Vale ressaltar que a criação do cliente pode e deve ser customizada para cada caso.

KmsClient kmsClient = KmsClient.builder().build();
KmsProvider kmsProvider = new KmsProvider(kmsClient);

Uma vez criado o provider, é possível registrá-lo.

Security.addProvider(kmsProvider);

 

Obtendo as referências das chaves

É possível obter as referências das chaves de duas formas:

  • Via KeyFactory: KmsRSAKeyFactoryKmsECKeyFactory
  • Via KeyStore: KmsKeyStore

A diferença entre as duas formas citadas é que para utilização do KeyFactory é necessário informar o key Id e para a utilização do KeyStore é utilizado o key alias. Vale ressaltar que o KeyStore utiliza o KeyFactory, ou seja, a partir do alias informado é recuperado o key Id e, então, é utilizado o KeyFactory. Priorize sempre a utilização do KeyFactory para obter melhor performance.

Para utilizar o KeyFactory (KmsRSAKeyFactory ou KmsECKeyFactory) basta invocar seus métodos estáticos:

KmsECKeyFactory.getKeyPair(...);
KmsECKeyFactory.getPrivateKey(...);
KmsECKeyFactory.getPublicKey(...);
...
KmsRSAKeyFactory.getKeyPair(...);
KmsRSAKeyFactory.getPrivateKey(...);
KmsRSAKeyFactory.getPublicKey(...);

Para utilizar o KeyStore é preciso obter o KeyStore via padrão JCE e inicializa-lo.

KeyStore keyStore = KeyStore.getInstance("KMS");
keyStore.load(null, null);
...
keyStore.aliases();
keyStore.containsAlias(...);
keyStore.size();
keyStore.getKey(...);

 

Assinando e verificando

Para facilitar a utilização da biblioteca, foi criada a enum KmsSigningAlgorithm que possui o mapeamento para utilização do algoritmo desejado conforme a seguir:

Algoritmo de assinatura do KMS Algoritmo de assinatura do Java
RSASSA_PSS_SHA_256 RSASSA-PSS/SHA256
RSASSA_PSS_SHA_384 RSASSA-PSS/SHA384
RSASSA_PSS_SHA_512 RSASSA-PSS/SHA512
RSASSA_PKCS1_V1_5_SHA_256 SHA256withRSA
RSASSA_PKCS1_V1_5_SHA_384 SHA384withRSA
RSASSA_PKCS1_V1_5_SHA_512 SHA512withRSA
ECDSA_SHA_256 SHA256withECDSA
ECDSA_SHA_384 SHA384withECDSA
ECDSA_SHA_512 SHA512withECDSA

Para assinar e/ou verificar é preciso obter o Signature via JCE:

KmsSigningAlgorithm kmsSigningAlgorithm = KmsSigningAlgorithm.<X>;

Signature kmsSignature = Signature.getInstance(kmsSigningAlgorithm.getAlgorithm());

// assinando...
kmsSignature.initSign(privateKey);
kmsSignature.update(message.getBytes());
byte[] signatureBytes = kmsSignature.sign();

// verificando...
kmsSignature.initVerify(publicKey);
kmsSignature.update(message.getBytes());
boolean valid = kmsSignature.verify(signatureBytes);

Gerando o Certificate Signing Request (CSR) e Self-Signed Certificate

Antes de gerar o CSR é necessário, primeiro, definir as informações que estarão presentes no CSR. Para isso, é utilizado o CsrInfo.

CsrInfo csrInfo = CsrInfo.builder()
.cn("...") //Common Name
.ou("...") //Department Name / Organizational Unit
.o("...") //Business name / Organization
.l("...") //Town / City
.st("...") //Province, Region, County or State
.c("...") //Country
.mail("...") //Email address
.build();

Após a criação do CsrInfo, é possível gerar o CSR:

String csr = CsrGenerator.generate(keyPair, csrInfo, kmsSigningAlgorithm);

Com o CSR gerado, é possível gerar o Self-Signed Certificate (caso necessário):

int validity = 365; //Em dias
String crt = SelfSignedCrtGenerator.generate(keyPair, csr, kmsSigningAlgorithm, validity);

Um exemplo completo de geração de CSR e Self-Signed Certificate pode ser visto abaixo:

public class Example {

public static void main(String[] args) {
KmsClient kmsClient = KmsClient.builder().build();
Security.addProvider(new KmsProvider(kmsClient));

KeyPair keyPair = KmsRSAKeyFactory.getKeyPair(kmsClient, KeyIds.SIGN_RSA);
KmsSigningAlgorithm kmsSigningAlgorithm = KmsSigningAlgorithm.RSASSA_PKCS1_V1_5_SHA_256;

CsrInfo csrInfo = CsrInfo.builder()
.cn("kms.aws.amazon.com")
.ou("AWS")
.o("Amazon")
.l("Sao Paulo")
.st("Sao Paulo")
.c("BR")
.mail("kms@amazon.com")
.build();

System.out.println("CSR Info: " + csrInfo.toString());
System.out.println();

String csr = CsrGenerator.generate(keyPair, csrInfo, kmsSigningAlgorithm);
System.out.println("CSR:");
System.out.println(csr);

String crt = SelfSignedCrtGenerator.generate(keyPair, csr, kmsSigningAlgorithm, 365);
System.out.println("CRT:");
System.out.println(crt);
}

}

 

Custos e limites

Cada chave mestra do cliente (CMK) que você criar no AWS KMS, custa 1 USD/mês até que seja excluída, independentemente de onde o material da chave subjacente foi gerado pelo serviço, um armazenamento de chaves personalizado ou uma importação.

O AWS KMS oferece um nível gratuito de 20.000 solicitações/mês, calculadas em todas as regiões nas quais o serviço está disponível. Estão excluídas do nível gratuito as solicitações para as APIs GenerateDataKeyPair e GenerateDataKeyPairWithoutPlaintext, bem como solicitações para APIs como Sign, Verify, Encrypt, Decrypt e GetPublicKey que fazem referência a CMKs assimétricas.

Cada solicitação de API para o AWS KMS (fora do nível gratuito) custa, na região América do Sul (São Paulo):

  • 0,03 USD por 10.000 solicitações
  • 0,03 USD por 10.000 solicitações envolvendo chaves RSA 2048
  • 0,10 USD por 10.000 solicitações ECC GenerateDataKeyPair
  • 0,15 USD por 10.000 solicitações assimétricas, exceto RSA 2048
  • 12,00 USD por 10.000 solicitações RSA GenerateDataKeyPair

Quanto aos limites, mais especificamente sobre operações utilizando chaves assimétricas, o AWS KMS possui limites de:

  • 500 requisições/segundo (compartilhado) para CMKs RSA
  • 300 requisições/segundo (compartilhado) para CMKs de curva elíptica (ECC)

Caso haja a necessidade, é possível solicitar o aumento para atender a necessidade do seu caso de uso.

 

Conclusão

Neste post mostramos como utilizar a biblioteca de software AWS KMS JCE Provider desenvolvida, a fim de fazer a integração entre o AWS KMS com JCE e gerar um CSR, para suportar a assinatura de documentos em Java e que necessitem certificado digital válido.

 


Sobre os autores

Lucas Lins é arquiteto de soluções na AWS, com mais de 10 anos de experiência em arquitetura de software e desenvolvimento de soluções envolvendo sistemas empresariais e de missão crítica. Atua com ênfase no segmento Enterprise, orientando e suportando os clientes em sua jornada para a nuvem.

 

 

 

 

João Paulo Aragão Pereira é arquiteto de soluções da AWS, focado no setor de serviços financeiros (LATAM) e seus principais tópicos: prevenção e detecção de fraudes, Open Banking, modernização de sistemas legados, Prova de Vida, Sistemas de Pagamentos Instantâneos. Trabalho com arquiteturas de bancos e seguradoras há mais de 15 anos.