Este guia aborda a utilização da biblioteca BouncyCastle para implementar algoritmos criptográficos nacionais, incluindo SM2, SM3 e SM4, em um ambiente Java. O foco está na prática de geração de chaves, criptografia e descriptografia.
Objetivos da Prática
- Executar as tarefas em um sistema operacional como openEuler (recomendado), Ubuntu ou Windows (menos recomendado).
- Implementar a criptografia e descriptografia com base no algoritmo SM2, documentando os resultados através de capturas de tela.
- Integrar os algoritmos SM3 e SM4 no processo, fornecendo o código-fonte e evidências de execução como parte opcional da atividade.
Implementação Técnica
Para começar, certifique-se de que a dependência do BouncyCastle está configurada no seu projeto. A seguir, estão os exemplos de código reescritos com estrutura e nomenclatura alteradas, mantendo a funcionalidade original.
Código Principal de Demonstração
import org.bouncycastle.jce.provider.BouncyCastleProvider;
import java.security.*;
import java.security.spec.ECGenParameterSpec;
import java.util.Arrays;
public class AplicacaoCriptografica {
private static final String MENSAGEM_ORIGINAL = "texto_exemplo_123";
public static void main(String[] args) throws Exception {
SM2Criptografia sm2Helper = new SM2Criptografia();
final ECGenParameterSpec parametrosSM2 = new ECGenParameterSpec("sm2p256v1");
final KeyPairGenerator geradorParChaves = KeyPairGenerator.getInstance("EC", new BouncyCastleProvider());
geradorParChaves.initialize(parametrosSM2);
KeyPair parDeChaves = geradorParChaves.generateKeyPair();
PublicKey chavePublica = parDeChaves.getPublic();
PublicKey chavePrivada = parDeChaves.getPrivate();
String dadosCriptografados = sm2Helper.criptografar(chavePublica, MENSAGEM_ORIGINAL);
System.out.println("Dados criptografados (hex): " + dadosCriptografados);
String textoDescriptografado = sm2Helper.descriptografar(chavePrivada, dadosCriptografados);
System.out.println("Texto descriptografado: " + textoDescriptografado);
SM3Hash sm3Hasher = new SM3Hash();
byte[] resumo = sm3Hasher.calcularHash(MENSAGEM_ORIGINAL);
System.out.println("Hash SM3: " + Arrays.toString(resumo));
SM4Cifra sm4Cifra = new SM4Cifra();
byte[] chaveSM4 = SM4GeradorChave.criarChave();
byte[] vetorInicializacao = null;
byte[] dadosSM4Criptografados = sm4Cifra.encriptar(resumo, chaveSM4, ModoPreenchimentoSM4.ECB_SEM_PREENCHIMENTO, vetorInicializacao);
System.out.println("Dados SM4 criptografados: " + Arrays.toString(dadosSM4Criptografados));
byte[] dadosSM4Descriptografados = sm4Cifra.decriptar(dadosSM4Criptografados, chaveSM4, ModoPreenchimentoSM4.ECB_SEM_PREENCHIMENTO, vetorInicializacao);
System.out.println("Dados SM4 descriptografados: " + Arrays.toString(dadosSM4Descriptografados));
}
}
Utilitário para SM2
import org.bouncycastle.crypto.engines.SM2Engine;
import org.bouncycastle.crypto.params.*;
import org.bouncycastle.jcajce.provider.asymmetric.ec.BCECPrivateKey;
import org.bouncycastle.jcajce.provider.asymmetric.ec.BCECPublicKey;
import org.bouncycastle.jce.spec.ECParameterSpec;
import org.bouncycastle.util.encoders.Hex;
import java.nio.charset.StandardCharsets;
import java.security.PrivateKey;
import java.security.PublicKey;
import java.security.SecureRandom;
public class SM2Criptografia {
public String criptografar(PublicKey chavePublica, String dados) {
ECPublicKeyParameters parametrosChavePublica = extrairParametrosPublicos(chavePublica);
SM2Engine motorSM2 = new SM2Engine();
motorSM2.init(true, new ParametersWithRandom(parametrosChavePublica, new SecureRandom()));
byte[] bytesSaida = null;
try {
byte[] bytesEntrada = dados.getBytes(StandardCharsets.UTF_8);
bytesSaida = motorSM2.processBlock(bytesEntrada, 0, bytesEntrada.length);
} catch (Exception e) {
throw new RuntimeException("Erro durante a criptografia SM2", e);
}
return Hex.toHexString(bytesSaida);
}
public String descriptografar(PrivateKey chavePrivada, String dadosCriptografados) {
byte[] bytesCriptografados = Hex.decode(dadosCriptografados);
ECPrivateKeyParameters parametrosChavePrivada = extrairParametrosPrivados(chavePrivada);
SM2Engine motorSM2 = new SM2Engine();
motorSM2.init(false, parametrosChavePrivada);
try {
byte[] bytesResultado = motorSM2.processBlock(bytesCriptografados, 0, bytesCriptografados.length);
return new String(bytesResultado, StandardCharsets.UTF_8);
} catch (Exception e) {
throw new RuntimeException("Erro durante a descriptografia SM2", e);
}
}
private ECPublicKeyParameters extrairParametrosPublicos(PublicKey chave) {
BCECPublicKey chaveBCEC = (BCECPublicKey) chave;
ECParameterSpec specParametros = chaveBCEC.getParameters();
ECDomainParameters dominioParametros = new ECDomainParameters(
specParametros.getCurve(), specParametros.getG(), specParametros.getN()
);
return new ECPublicKeyParameters(chaveBCEC.getQ(), dominioParametros);
}
private ECPrivateKeyParameters extrairParametrosPrivados(PrivateKey chave) {
BCECPrivateKey chaveBCEC = (BCECPrivateKey) chave;
ECParameterSpec specParametros = chaveBCEC.getParameters();
ECDomainParameters dominioParametros = new ECDomainParameters(
specParametros.getCurve(), specParametros.getG(), specParametros.getN()
);
return new ECPrivateKeyParameters(chaveBCEC.getD(), dominioParametros);
}
}
Utilitário para SM3
import org.bouncycastle.crypto.digests.SM3Digest;
import org.bouncycastle.jce.provider.BouncyCastleProvider;
import java.security.Security;
public class SM3Hash {
static {
Security.addProvider(new BouncyCastleProvider());
}
public byte[] calcularHash(String entrada) {
SM3Digest digestSM3 = new SM3Digest();
byte[] dadosEntrada = entrada.getBytes();
digestSM3.update(dadosEntrada, 0, dadosEntrada.length);
byte[] hashResultado = new byte[digestSM3.getDigestSize()];
digestSM3.doFinal(hashResultado, 0);
return hashResultado;
}
}
Geração de Chaves para SM4
import org.bouncycastle.jce.provider.BouncyCastleProvider;
import javax.crypto.KeyGenerator;
import java.security.SecureRandom;
import java.security.Security;
public class SM4GeradorChave {
static {
Security.addProvider(new BouncyCastleProvider());
}
public static byte[] criarChave() throws Exception {
KeyGenerator gerador = KeyGenerator.getInstance("SM4", BouncyCastleProvider.PROVIDER_NAME);
gerador.init(128, new SecureRandom());
return gerador.generateKey().getEncoded();
}
}
Enumeração para Modos e Preenchimentos do SM4
public enum ModoPreenchimentoSM4 {
ECB_SEM_PREENCHIMENTO("SM4/ECB/NoPadding"),
ECB_PKCS5("SM4/ECB/PKCS5Padding"),
ECB_PKCS7("SM4/ECB/PKCS7Padding"),
CBC_SEM_PREENCHIMENTO("SM4/CBC/NoPadding"),
CBC_PKCS5("SM4/CBC/PKCS5Padding"),
CBC_PKCS7("SM4/CBC/PKCS7Padding");
private final String nomeAlgoritmo;
ModoPreenchimentoSM4(String nome) {
this.nomeAlgoritmo = nome;
}
public String obterNome() {
return nomeAlgoritmo;
}
}
Utilitário para SM4
import org.bouncycastle.jce.provider.BouncyCastleProvider;
import javax.crypto.Cipher;
import javax.crypto.spec.IvParameterSpec;
import javax.crypto.spec.SecretKeySpec;
import java.security.Security;
public class SM4Cifra {
static {
Security.addProvider(new BouncyCastleProvider());
}
public byte[] encriptar(byte[] dados, byte[] chave, ModoPreenchimentoSM4 modo, byte[] iv) throws Exception {
return processarDados(dados, chave, modo, iv, Cipher.ENCRYPT_MODE);
}
public byte[] decriptar(byte[] dadosCriptografados, byte[] chave, ModoPreenchimentoSM4 modo, byte[] iv) throws Exception {
return processarDados(dadosCriptografados, chave, modo, iv, Cipher.DECRYPT_MODE);
}
private byte[] processarDados(byte[] entrada, byte[] chave, ModoPreenchimentoSM4 modo, byte[] iv, int operacao) throws Exception {
IvParameterSpec especVetor = iv != null ? new IvParameterSpec(iv) : null;
SecretKeySpec chaveSecreta = new SecretKeySpec(chave, "SM4");
Cipher cifra = Cipher.getInstance(modo.obterNome(), BouncyCastleProvider.PROVIDER_NAME);
if (especVetor == null) {
cifra.init(operacao, chaveSecreta);
} else {
cifra.init(operacao, chaveSecreta, especVetor);
}
return cifra.doFinal(entrada);
}
}