Certificado V3 – com CNPJ

Criar certificado digital V3 – com CNPJ

Este post apresenta exemplos de código para gerar certificados de teste usando a api Bouncy Castle.

Na primeira parte é feita a geração do certificado (e chave privada) para uma autoridade certificadora.
Depois é emitido um certificado contendo um CNPJ e assinado pelo certificado da autoridade certificadora.
Por fim é feita a assinatura de um documento Xml usando-se o certificado gerado.

É importante lembrar que não há nenhum segredo aqui, os códigos não são meus e podem ser copiados do próprio site da api.

Para dúvidas conceituais por favor consulte este outro post onde tentei sintetizar diversos conceitos: https://crestaniblog.wordpress.com/2013/03/28/criar-certificado-digital/

Neste post utilizei os seguintes programas rodando no windows7.
– Eclipse Juno
– Bouncy Castle (bcprov-jdk15on-149.jar)
– JDK 1.7 (1.7.0_10)
– a versão 1.7.0_45 apresentou problema na assinatura digital http://bugs.sun.com/bugdatabase/view_bug.do?bug_id=8017265

Geração de chave e certificado para a autoridade certificadora (AC).

import java.io.BufferedOutputStream;
import java.io.FileOutputStream;
import java.math.BigInteger;
import java.security.KeyPair;
import java.security.KeyPairGenerator;
import java.security.SecureRandom;
import java.security.Security;
import java.security.cert.X509Certificate;
import java.util.Date;
import javax.security.auth.x500.X500Principal;
import org.bouncycastle.asn1.x509.BasicConstraints;
import org.bouncycastle.asn1.x509.GeneralName;
import org.bouncycastle.asn1.x509.GeneralNames;
import org.bouncycastle.asn1.x509.KeyUsage;
import org.bouncycastle.asn1.x509.X509Extensions;
import org.bouncycastle.x509.X509V3CertificateGenerator;

public class CertificadoAC {

   public static void main(String[] args) throws Exception {
	Security.addProvider(new org.bouncycastle.jce.provider.BouncyCastleProvider());

	//Gera o par de chaves 
	KeyPairGenerator kpGen = KeyPairGenerator.getInstance("RSA", "BC");
	kpGen.initialize(1024, new SecureRandom());
	KeyPair pair = kpGen.generateKeyPair();
		
	//Gera o certificado da AC
	X509V3CertificateGenerator certGen = new X509V3CertificateGenerator();
	X500Principal principal = new X500Principal("CN=Autoridade Certificadora de Teste");
		
	certGen.setSerialNumber(BigInteger.valueOf(System.currentTimeMillis()));
	certGen.setIssuerDN(principal);		
	certGen.setNotBefore(new Date(System.currentTimeMillis() - 1000L * 60 * 60 * 24 * 30));
	certGen.setNotAfter(new Date(System.currentTimeMillis() + (1000L * 60 * 60 * 24 * 30)));		
	certGen.setSubjectDN(principal);//mesmo que o issuer quando AC
	certGen.setPublicKey(pair.getPublic());
	certGen.setSignatureAlgorithm("SHA256WithRSAEncryption");

        //qtde de AC intermediarias permitidas?
	certGen.addExtension(X509Extensions.BasicConstraints, true, new BasicConstraints(1));

	//certGen.addExtension(X509Extensions.BasicConstraints, true, new BasicConstraints(false));
	certGen.addExtension(X509Extensions.KeyUsage, true, new  
                 KeyUsage(KeyUsage.digitalSignature|KeyUsage.keyEncipherment|KeyUsage.keyCertSign |KeyUsage.nonRepudiation));
		
	certGen.addExtension(X509Extensions.SubjectAlternativeName, false, 
                 new GeneralNames(new GeneralName(GeneralName.rfc822Name, "ca-fake@teste.com")));
		
	X509Certificate cert = certGen.generateX509Certificate(pair.getPrivate(), "BC");
		
	//salva a chave privada em arquivo				
	BufferedOutputStream bos = new BufferedOutputStream(
                               new FileOutputStream("AC-chave-privada.der"));
	bos.write(pair.getPrivate().getEncoded());
	bos.close();

	//salva o certificado em arquivo
	bos = new BufferedOutputStream(new FileOutputStream("AC-certificado.crt"));// .cer, .crt
	bos.write(cert.getEncoded());
	bos.close();
   }
}

A seguir, o procedimento para instalar o certificado da AC – que acabamos de gerar – no windows. É importante ressaltar que o propósito inicial deste post é demonstrar a geração dos certificados usando a api Bouncy castle. Você pode instar o certificado no cacerts do java para uso de aplicações na Jre mas isso é outro assunto.

img_cert_ca_1

É bem melhor especificar manualmente um repositório:

img_cert_ca_2

Especifique o repositório de de autoridades de certificação confiáveis:

img_cert_ca_3

Finalizando:

img_cert_ca_4

Remover um certificado também é importante. Veja o uso da console de gerenciamento do windows7 (e 8).

1. Clique em Iniciar, clique em Iniciar Pesquisa, digite mmc e, em seguida, pressione ENTER.
2. No menu Arquivo, clique em Adicionar ou Remover snap-in.

mmc_1

3. Em Snap-ins disponíveis, clique duas vezes em Certificados e, em seguida em Adicionar.
4. Em Este snap-in sempre gerenciará certificados para, clique em Conta de computador e clique em Avançar.
5. Clique em Computador local e, em seguida, clique em Concluir.

mmc_2

Se você não encontrar o certificado, tente adicionar um Snap-in escolhendo a opção de gerenciar certificados para ‘minha conta de usuario’.

Se o certificado da AC foi instalado conforme o exemplo ele vai aparecer nas informações sobre autoridades de certificação Raiz confiáveis.

E finalmente, o código usado para a geração de um certificado assinado pela autoridade certificadora gerada anteriormente.
Na extensão é adicionado o CNPJ.

import java.io.BufferedOutputStream;
import java.io.File;
import java.io.FileInputStream;
import java.io.FileOutputStream;
import java.math.BigInteger;
import java.security.KeyFactory;
import java.security.KeyPair;
import java.security.KeyPairGenerator;
import java.security.PrivateKey;
import java.security.SecureRandom;
import java.security.Security;
import java.security.cert.CertificateFactory;
import java.security.cert.X509Certificate;
import java.security.spec.PKCS8EncodedKeySpec;
import java.util.Date;
import javax.security.auth.x500.X500Principal;
import org.bouncycastle.asn1.ASN1EncodableVector;
import org.bouncycastle.asn1.DERObjectIdentifier;
import org.bouncycastle.asn1.DEROctetString;
import org.bouncycastle.asn1.DERSequence;
import org.bouncycastle.asn1.DERTaggedObject;
import org.bouncycastle.asn1.x509.BasicConstraints;
import org.bouncycastle.asn1.x509.GeneralName;
import org.bouncycastle.asn1.x509.GeneralNames;
import org.bouncycastle.asn1.x509.KeyUsage;
import org.bouncycastle.asn1.x509.X509Extensions;
import org.bouncycastle.x509.X509V3CertificateGenerator;
import org.bouncycastle.x509.extension.AuthorityKeyIdentifierStructure;
import org.bouncycastle.x509.extension.SubjectKeyIdentifierStructure;

public class CertificadoCnpj {

    public static void main(String[] args) throws Exception {

	Security.addProvider(new org.bouncycastle.jce.provider.BouncyCastleProvider());

	//Gera o par de chaves
	KeyPairGenerator kpGen = KeyPairGenerator.getInstance("RSA", "BC");
	kpGen.initialize(1024, new SecureRandom());
		
	KeyPair pair = kpGen.generateKeyPair();

	// Carrega a chave privada da CA (in PKCS#8 DER encoding).
	File keyFile = new File("AC-chave-privada.der");
	byte[] encodedKey = new byte[(int)keyFile.length()];

	FileInputStream keyInputStream = new FileInputStream(keyFile);
	keyInputStream.read(encodedKey);
	keyInputStream.close();

	KeyFactory rSAKeyFactory = KeyFactory.getInstance("RSA");
	PrivateKey caPrivateKey = rSAKeyFactory.generatePrivate(new PKCS8EncodedKeySpec(encodedKey));

	//Carrega o 'certificado da AC'
	CertificateFactory cf = CertificateFactory.getInstance("X.509");
	X509Certificate caCertificate = (X509Certificate) cf.generateCertificate(
                        new FileInputStream(new File("AC-certificado.crt")));

	X509V3CertificateGenerator certGen = new X509V3CertificateGenerator();
	X500Principal dnName = new X500Principal("CN=Cnpj Empresa Fulado Silva Ltda.");
	certGen.setSerialNumber(BigInteger.valueOf(System.currentTimeMillis()));
	certGen.setSubjectDN(dnName);
	certGen.setIssuerDN(caCertificate.getSubjectX500Principal());		
	certGen.setNotBefore(new Date(System.currentTimeMillis() - 1000L * 60 * 60 * 24 * 30));
	certGen.setNotAfter(new Date(System.currentTimeMillis() + (1000L * 60 * 60 * 24 * 30)));
	certGen.setPublicKey(pair.getPublic());
	certGen.setSignatureAlgorithm("SHA256WithRSAEncryption");

	certGen.addExtension(X509Extensions.BasicConstraints,false,	 new BasicConstraints(true));	
	certGen.addExtension(X509Extensions.SubjectKeyIdentifier, false, new SubjectKeyIdentifierStructure(pair.getPublic()));
	certGen.addExtension(X509Extensions.KeyUsage, true, new KeyUsage(KeyUsage.digitalSignature|KeyUsage.nonRepudiation));

	//chave da AC
	certGen.addExtension(X509Extensions.AuthorityKeyIdentifier, false, new AuthorityKeyIdentifierStructure(caCertificate));
		
        GeneralName eMail = new GeneralName(GeneralName.rfc822Name, "fulano-ltdaa@nomail.com");		       
	GeneralName cnpj = getGeneralName("2.16.76.1.3.3","99996060616909");
			
	GeneralNames genNames = new GeneralNames(new GeneralName[]{eMail,cnpj});		
              certGen.addExtension(X509Extensions.SubjectAlternativeName, false, genNames);
		
	X509Certificate cert = certGen.generate(caPrivateKey, "BC");
		
	//salva a chave privada				
	BufferedOutputStream bos = new BufferedOutputStream(
                      new FileOutputStream("Cnpj-chave-privada.der"));
	bos.write(pair.getPrivate().getEncoded());
	bos.close();

	//salva o certificado
	bos = new BufferedOutputStream(new FileOutputStream("Cnpj-certificado.crt"));
	bos.write(cert.getEncoded());
	bos.close();
   }

   private static GeneralName getGeneralName(String objetoId, String valor) {		 
        ASN1EncodableVector v = new ASN1EncodableVector();
        v.add(new DERObjectIdentifier(objetoId));	    
        v.add(new DERTaggedObject(true, 0, new DEROctetString(valor.getBytes())));	 
       return new GeneralName(GeneralName.otherName, new DERTaggedObject(false, 0, new DERSequence(v)));
   }		
}

O procedimento de instalação é o semelhante ao utilizado para a instalação do certificado da CA. Exceto que não
é necessário instalar no repositório como se fosse uma AC (porque esse certificado não é uma AC).

img_cert_cnpj_1

img_cert_cnpj_2

E agora, um exemplo de utilização do certificado CNPJ na assinatura de trecho de código xml.


import java.io.File;
import java.io.FileInputStream;
import java.io.FileOutputStream;
import java.io.InputStream;
import java.io.OutputStream;
import java.security.KeyFactory;
import java.security.KeyStore;
import java.security.PrivateKey;
import java.security.cert.CertificateFactory;
import java.security.cert.X509Certificate;
import java.security.spec.PKCS8EncodedKeySpec;
import java.util.ArrayList;
import java.util.Collections;
import java.util.List;

import javax.xml.crypto.dsig.CanonicalizationMethod;
import javax.xml.crypto.dsig.DigestMethod;
import javax.xml.crypto.dsig.Reference;
import javax.xml.crypto.dsig.SignatureMethod;
import javax.xml.crypto.dsig.SignedInfo;
import javax.xml.crypto.dsig.Transform;
import javax.xml.crypto.dsig.XMLSignature;
import javax.xml.crypto.dsig.XMLSignatureFactory;
import javax.xml.crypto.dsig.dom.DOMSignContext;
import javax.xml.crypto.dsig.keyinfo.KeyInfo;
import javax.xml.crypto.dsig.keyinfo.KeyInfoFactory;
import javax.xml.crypto.dsig.keyinfo.X509Data;
import javax.xml.crypto.dsig.spec.C14NMethodParameterSpec;
import javax.xml.crypto.dsig.spec.TransformParameterSpec;
import javax.xml.parsers.DocumentBuilderFactory;
import javax.xml.transform.Transformer;
import javax.xml.transform.TransformerFactory;
import javax.xml.transform.dom.DOMSource;
import javax.xml.transform.stream.StreamResult;

public class CertificadoCnpjAssinarXml {
	
   public static void main(String[] args) throws Exception {	
		
	// carrega a chave privada do certificado (cnpj) ( DER encoding)
	File keyFile = new File("Cnpj-chave-privada.der");
	byte[] encodedKey = new byte[(int)keyFile.length()];

	FileInputStream keyInputStream = new FileInputStream(keyFile);
	keyInputStream.read(encodedKey);
	keyInputStream.close();

	KeyFactory rSAKeyFactory = KeyFactory.getInstance("RSA");
	PrivateKey cnpjPrivateKey = rSAKeyFactory.generatePrivate(new PKCS8EncodedKeySpec(encodedKey));
				
	// carrega o certificado
	CertificateFactory cf = CertificateFactory.getInstance("X.509");
	X509Certificate cnpjCertificate = (X509Certificate) cf.generateCertificate(new FileInputStream(new File("Cnpj-certificado.crt")));
		
	String alias="my";
	String senhaChavePrivada="123456";
		
	KeyStore ks = KeyStore.getInstance("pkcs12");
	try { 
		ks.load(null);
		//carrega a chave privada e o certificado em um keystore para ser utilizado pelo codigo java
		ks.setKeyEntry(alias, cnpjPrivateKey,senhaChavePrivada.toCharArray(), new java.security.cert.Certificate[]{cnpjCertificate});
			
	}	catch (Exception e) {
		e.printStackTrace();
	}	
		

	/**
	 * Segue o mesmo conteúdo apresentado em outro post 
* * https://crestaniblog.wordpress.com/2013/03/27/assinatura-digital-em-xml/ * */ InputStream in = new FileInputStream(new File("xmlSemAssinatura.xml")); OutputStream os = new FileOutputStream(new File("xmlAssinado.xml")); //elemento que deve ser assinado String tagName="pedidoDeCompra"; String elementoID = "pedido12345"; DocumentBuilderFactory dbf = DocumentBuilderFactory.newInstance(); dbf.setNamespaceAware(true); org.w3c.dom.Document doc = dbf.newDocumentBuilder().parse(in); KeyStore.PrivateKeyEntry keyEntry = (KeyStore.PrivateKeyEntry) ks.getEntry(alias, new KeyStore.PasswordProtection(senhaChavePrivada.toCharArray())); DOMSignContext dsc = new DOMSignContext(keyEntry.getPrivateKey(), doc.getElementsByTagName(tagName).item(0)); //Assembling the XML Signature XMLSignatureFactory fac = javax.xml.crypto.dsig.XMLSignatureFactory.getInstance("DOM"); List transforms = new ArrayList(); transforms.add(fac.newTransform(Transform.ENVELOPED, (TransformParameterSpec) null)); transforms.add(fac.newCanonicalizationMethod(CanonicalizationMethod.INCLUSIVE, (C14NMethodParameterSpec) null)); //http://bugs.sun.com/bugdatabase/view_bug.do?bug_id=8017265 Reference ref = fac.newReference("#" + elementoID, // fac.newDigestMethod(DigestMethod.SHA1, null),// transforms, null, null); SignedInfo si = fac.newSignedInfo(// fac.newCanonicalizationMethod(CanonicalizationMethod.INCLUSIVE, // (C14NMethodParameterSpec) null), // fac.newSignatureMethod(SignatureMethod.RSA_SHA1, null),// Collections.singletonList(ref)); KeyInfoFactory kif = fac.getKeyInfoFactory(); List x509Content = new ArrayList(); x509Content.add(keyEntry.getCertificate()); X509Data kv = kif.newX509Data(x509Content); KeyInfo ki = kif.newKeyInfo(Collections.singletonList(kv)); XMLSignature signature = fac.newXMLSignature(si, ki); signature.sign(dsc); TransformerFactory tf = TransformerFactory.newInstance(); Transformer trans = tf.newTransformer(); //salva resultado no arquivo de saída trans.transform(new DOMSource(doc), new StreamResult(os)); } }

Links que podem ser úteis:
http://www.bouncycastle.org/
http://technet.microsoft.com/pt-br/library/cc754841.aspx
http://computerworld.uol.com.br/seguranca/2010/06/17/icp-brasil-reforca-seguranca-na-certificacao-digital/

Deixe uma resposta

Preencha os seus dados abaixo ou clique em um ícone para log in:

Logotipo do WordPress.com

Você está comentando utilizando sua conta WordPress.com. Sair / Alterar )

Imagem do Twitter

Você está comentando utilizando sua conta Twitter. Sair / Alterar )

Foto do Facebook

Você está comentando utilizando sua conta Facebook. Sair / Alterar )

Foto do Google+

Você está comentando utilizando sua conta Google+. Sair / Alterar )

Conectando a %s