FrameworkDemoiselle.gov.brCommunity Documentation
A cifragem e decifragem de dados são providas pela interface Cryptography
e o componente se utiliza de uma fábrica dessa interface. Segue abaixo um exemplo ilustrativo:
public class App {
public static void main(String[] args) {
String frase = "conteudo original";
Cryptography cryptography = CryptographyFactory.getInstance().factoryDefault();
/* Geracao da chave unica */
Key key = cryptography.generateKey();
cryptography.setKey(key);
/* Cifragem */
byte[] conteudo_criptografado = cryptography.cipher(frase.getBytes());
System.out.println(conteudo_criptografado);
/* Decifragem */
byte[] conteudo_descriptografado = cryptography.decipher(conteudo_criptografado);
System.out.println(new String(conteudo_descriptografado));
}
}
Os métodos
cipher
e
decipher
recebem como entrada um array de bytes e retornam o array de bytes processado.
Para que a criptografia simétrica seja realizada é necessário o uso de uma única chave, para criptografar e descriptografar.
Neste caso, é necessário gerar a chave através do método generateKey
.
Caso não seja informado o algoritmo de criptografia o componente utilizará como padrão o algoritmo
AES (Advanced Encryption Standard). Caso necessite utilizar outro algoritmo invoque o método
setAlgorithm
informando um
SymmetricAlgorithmEnum
ou um
AsymmetricAlgorithmEnum.
public class App {
public static void main(String[] args) {
String frase = "conteudo original";
Cryptography cryptography = CryptographyFactory.getInstance().factoryDefault();
/* Alterando algoritmo */
cryptography.setAlgorithm(SymmetricAlgorithmEnum.TRI_DES);
/* Geracao da chave unica */
Key key = cryptography.generateKey();
cryptography.setKey(key);
byte[] conteudo_criptografado = cryptography.cipher(frase.getBytes());
System.out.println(conteudo_criptografado);
byte[] conteudo_descriptografado = cryptography.decipher(conteudo_criptografado);
System.out.println(new String(conteudo_descriptografado));
}
}
Caso as opções de criptografia definidas pelo SymmetricAlgorithmEnum
não atendam é possível customizar os parâmetros de criptografia através dos
métodos
setAlgorithm,
setKeyAlgorithm
e
setSize.
public static void main(String[] args) {
String frase = "conteudo original";
Cryptography cryptography = CryptographyFactory.getInstance().factoryDefault();
/* Customizacao de parametros */
cryptography.setAlgorithm("AES/ECB/PKCS5Padding");
cryptography.setKeyAlgorithm("AES");
cryptography.setSize(128);
/* Cifragem */
byte[] conteudo_criptografado = cryptography.cipher(frase.getBytes());
System.out.println(conteudo_criptografado);
/* Decifragem */
byte[] conteudo_descriptografado = cryptography.decipher(conteudo_criptografado);
System.out.println(new String(conteudo_descriptografado));
}
Na criptografia assimétrica é necessário um par de chaves para realizar a cifragem e decifram das mensagens. A primeira chave é denominada chave privada e é de posse exclusiva de seu detentor. A segunda chave do par é denominada de chave pública e pode ser enviada a qualquer indivíduo.
/* Cifragem */
Cryptography crypto = CryptographyFactory.getInstance().factoryDefault();
crypto.setKey(privateKey);
byte[] conteudoCriptografado = crypto.cipher("SERPRO".getBytes());
System.out.println(conteudoCriptografado);
/* Decifragem */
Cryptography crypto2 = CryptographyFactory.getInstance().factoryDefault();
crypto2.setKey(publicKey);
byte[] conteudoDescriptografado = crypto2.decipher(conteudoCriptografado);
System.out.println(new String(conteudoDescriptografado));
Perceba a utilização do método
setKey
para informar qual chave será utilizada no processo de cifragem e decifragem.
Vale lembrar que na criptografia assimétrica a cifragem realizada com a chave privada só poderá ser decifrada com a chave pública e vice-versa.
Na sequência, demonstramos a utilização do componente com certificados digitais do tipo A1 e A3.
O certificado A1 é aquele que encontra-se armazenado no sistema de arquivo do sistema operacional. Para exemplificar sua manipulação, segue o código abaixo:
public static void main(String[] args) {
try {
/* Obtendo a chave publica */
File file = new File("/home/{usuario}/public.der");
byte[] encodedPublicKey = new byte[(int) file.length()];
InputStream inputStreamPublicKey = new FileInputStream(file);
inputStreamPublicKey.read(encodedPublicKey);
inputStreamPublicKey.close();
X509EncodedKeySpec publicKeySpec = new X509EncodedKeySpec(encodedPublicKey);
KeyFactory kf = KeyFactory.getInstance("RSA");
PublicKey publicKey = kf.generatePublic(publicKeySpec);
/* Obtendo a chave privada */
file = new File("/home/{usuario}/private.pk8");
byte[] encodedPrivateKey = new byte[(int) file.length()];
InputStream inputStreamPrivateKey = new FileInputStream(file);
inputStreamPrivateKey.read(encodedPrivateKey);
inputStreamPrivateKey.close();
PKCS8EncodedKeySpec privateKeySpec = new PKCS8EncodedKeySpec(encodedPrivateKey);
kf = KeyFactory.getInstance("RSA");
PrivateKey privateKey = kf.generatePrivate(privateKeySpec);
/* Cifragem */
Cryptography cripto = CryptographyFactory.getInstance().factoryDefault();
crypto.setAlgorithm(AsymmetricAlgorithmEnum.RSA);
crypto.setKey(privateKey);
byte[] conteudoCriptografado = crypto.cipher("SERPRO".getBytes());
System.out.println(conteudoCriptografado);
/* Decifragem */
Cryptography cripto2 = CryptographyFactory.getInstance().factoryDefault();
crypto2.setAlgorithm(AsymmetricAlgorithmEnum.RSA);
crypto2.setKey(publicKey);
byte[] conteudoDescriptografado = crypto2.decipher(conteudoCriptografado);
System.out.println(new String(conteudoDescriptografado));
} catch (Exception e) {
e.printStackTrace();
Assert.assertTrue("Configuracao nao carregada: " + e.getMessage(), false);
}
}
Neste exemplo é demonstrada a obtenção das chaves pública e privada do certificado A1. Note que, apesar do código para manipulação dos certificados, a forma de uso do componente Demoiselle Cryptography para cifragem e decifragem de mensagens é a mesma.
O certificado A3 é armazenado em dispositivos eletrônicos como smart card ou tokens usb que criptografam o certificado provendo maior segurança. No exemplo abaixo utilizamos o componente Demoiselle Core para obtenção do keyStore a partir de um token usb. Você pode ver mais sobre esse componente em Capítulo 1, Configuração do Signer-Core
public static void main(String[] args) {
/* Senha do dispositivo */
String PIN = "senha_do_token";
try {
/* Obtendo a chave privada */
KeyStore keyStore = KeyStoreLoaderFactory.factoryKeyStoreLoader().getKeyStore(PIN);
String alias = (String) keyStore.aliases().nextElement();
PrivateKey privateKey = (PrivateKey) keyStore.getKey(alias, PIN.toCharArray());
/* Obtendo a chave publica */
CertificateLoader cl = new CertificateLoaderImpl();
X509Certificate x509 = cl.loadFromToken(PIN);
PublicKey publicKey = x509.getPublicKey();
/*Configurando o Criptography */
Cryptography crypto = CryptographyFactory.getInstance().factoryDefault();
crypto.setAlgorithm(AsymmetricAlgorithmEnum.RSA);
crypto.setProvider(keyStore.getProvider());
/* criptografando com a chave privada */
crypto.setKey(privateKey);
byte[] conteudoCriptografado = crypto.cipher("SERPRO".getBytes());
System.out.println(conteudoCriptografado);
/* descriptografando com a chave publica */
crypto.setKey(publicKey);
byte[] conteudoAberto = crypto.decipher(conteudoCriptografado);
System.out.println(new String(conteudoAberto));
} catch (UnrecoverableKeyException e) {
e.printStackTrace();
} catch (KeyStoreException e) {
e.printStackTrace();
} catch (NoSuchAlgorithmException e) {
e.printStackTrace();
}
}
O componente utiliza como padrão o provider SUN JCE, mas caso necessite de outro provider utilize o método
setProvider
da classe
Cryptography.
No exemplo acima foi utilizado o método
setProvider
para informar o provedor, ou seja, quem executará os algoritmos de criptografia. Até então, nos
exemplos anteriores, o provedor era a biblioteca SUN JCE contida na própria JVM. Como o token é o único a ter acesso a chave privada do certificado
ele também será o único capaz de executar os processos
de cifragem e decifragem.
Desta forma foi utilizado o objeto KeyStore do próprio token usb para informar o novo provider ao Cryptography.
A criptografia de hash tem a finalidade de criar um valor único que identifique um dado original. Este recurso pode ser utilizado por exemplo para finalidades de autenticação nas quais deseja-se armazenar as senhas cifradas por meio de um valor hash.
A fábrica
DigestFactory
do componente constrói um objeto padrão do tipo Digest que calcula o valor hash de um array de bytes retornando outro array
de bytes.
public static void main(String[] args) {
Digest digest = DigestFactory.getInstance().factoryDefault();
byte[] resumo = digest.digest("SERPRO".getBytes());
System.out.println(resumo);
}
Caso queira obter o valor hash no formato caractere hexadecimal utilize o método
digestHex
. Este formato é bastante utilizado para representar o hash de arquivos.
public static void main(String[] args) {
Digest digest = DigestFactory.getInstance().factoryDefault();
String resumo = digest.digestHex("SERPRO".getBytes());
System.out.println(resumo);
}
O hash de arquivo pode ser utilizado quando se deseja verificar a integridade física de um arquivo. No caso de ferramentas de download é possível ao final do processo de transferência de dados, verificar se o arquivo obtido apresenta o mesmo hash do arquivo original.
A interface
Digest
possui os métodos
digestFile
e
digestFileHex
para retornar respectivamente o valor hash em array de bytes ou caracter
hexadecimal:
public static void main(String[] args) {
Digest digest = DigestFactory.getInstance().factoryDefault();
digest.setAlgorithm(DigestAlgorithmEnum.SHA_256);
String resumo = digest.digestFileHex(new File("/home/{usuario}/relatorio.pdf"));
System.out.println(resumo);
}
Os algoritmos de hash recomendados pelo componente são definidos pelo enum
DigestAlgorithmEnum
. Caso não seja informado o componente utilizará o "SHA-1" por ser considerado mais seguro quanto a quebras em relação ao MD5.