For starters, some details:
THE OID 2.16.76.1.3.1
does not only contain the CPF, but a series of information that must be in the correct format for the certificate data to be read correctly. The very document you quoted says the following:
In the first 8 (eight) positions, the date of birth of the natural person holding the certificate, in the format ddmmaaaa; in the 11 (eleven) subsequent positions, the number of registration in the Register of Physical Person (CPF) of the natural person holding the certificate; in the 11 (eleven) subsequent positions, the Social Identification number of the natural person holding the certificate - NIS (PIS, PASEP or CI); in the 15 (fifteen) subsequent positions, the General Registry number - RG of the natural person holding the certificate; in the 6 (six) subsequent positions, the acronyms of the sending agency of the RG and its UF.
Remembering that voter registration and ID are optional, following these rules (also explained in the same document):
When NIS (PIS/PASEP/CI) RG, IEC, or Voter Registration numbers are not available, the corresponding fields must be completed in full with
"zero" characters.
If the RG number or the registration number of the Voter Registration Title is not available, do not fill in the fields of shipping agency and UF or the fields Electoral Zone, Session, Municipality and UF, respectively.
All information of variable size, referring to numbers, such as RG, must be filled with "zero" characters on the left to complete its maximum
size possible.
The 6 (six) positions of the RG and UF consignor body information shall refer to the maximum size and only the positions necessary for its storage shall be used, from left to right.
Therefore, it is not enough to just put the number on this OID. Applications that read certificate data look for the fields in the correct positions, and putting fields in wrong formats will not work.
Another point is that eCPF should also have Oids 2.16.76.1.3.5
and 2.16.76.1.3.6
, whose formats are also described in detail in the same document you quoted.
There are also details about the order in which these Oids appear. The Certificate Policies of AC Serasa and of AC Certisign, for example, they say that the order of Oids should be 2.16.76.1.3.1
, 2.16.76.1.3.6
and 2.16.76.1.3.5
. Already the document you quoted says nothing about the order of the same.
Already the eCNPJ should have the Oids 2.16.76.1.3.4
, 2.16.76.1.3.2
, 2.16.76.1.3.3
and 2.16.76.1.3.7
(and again, AC’s mention that it should be in this order, but the Recipe document does not), and each has its own format, in a similar way to the eCPF fields.
And how I love it?
The keytool
has the option -ext
to place extensions in the certificate. How Oids are in the extension Subject Alternative Name, just put this extension. Something like this:
keytool -genkeypair ..... -ext san=oid:2.16.76.1.3.1,oid:2.16.76.1.3.6,oid:2.16.76.1.3.5
Unfortunately, I was unable to pass the values to the Oids, as the keytool seems to only support the OID number itself as the value.
So the way is to use Java itself. I made a code that generates an AC certificate of any kind (saving the files "actest.jks", with the AC private key, and "actest.cer", with the AC certificate, in case you need to add in some truststore, for example).
Then I use the private key that is in "actest.jks" and use it to generate an e-CPF or e-CNPJ (also saving in PFX and .cer with only the certificate). The detail is that I used the Bouncy Castle, a library that I recommend much to work with certificates (and encryption in general). As I am using Maven, I put these dependencies:
<dependency>
<groupId>org.bouncycastle</groupId>
<artifactId>bcprov-jdk15on</artifactId>
<version>1.57</version>
</dependency>
<dependency>
<groupId>org.bouncycastle</groupId>
<artifactId>bcmail-jdk15on</artifactId>
<version>1.57</version>
</dependency>
<dependency>
<groupId>org.bouncycastle</groupId>
<artifactId>bctls-jdk15on</artifactId>
<version>1.57</version>
</dependency>
<dependency>
<groupId>org.bouncycastle</groupId>
<artifactId>bcpg-jdk15on</artifactId>
<version>1.57</version>
</dependency>
But if you want, you can download this version on the website https://www.bouncycastle.org/.
As I originally made this code in Java 7, I also used the Threeten Backport, which is a date library:
<dependency>
<groupId>org.threeten</groupId>
<artifactId>threetenbp</artifactId>
<version>1.3.6</version>
</dependency>
But if you’re using Java >= 8, you can remove it and use the package java.time
(there are comments in the code about this, but basically just change the import
of org.threeten.bp
for java.time
, except in the case of the class DateTimeUtils
, which is commented below how to proceed).
First the class that creates the certificate and CA keys:
import java.io.FileOutputStream;
import java.io.FileWriter;
import java.io.IOException;
import java.io.OutputStream;
import java.math.BigInteger;
import java.security.KeyPair;
import java.security.KeyPairGenerator;
import java.security.KeyStore;
import java.security.NoSuchAlgorithmException;
import java.security.NoSuchProviderException;
import java.security.PrivateKey;
import java.security.SecureRandom;
import java.security.Security;
import java.security.cert.Certificate;
import java.security.cert.X509Certificate;
import org.bouncycastle.asn1.DERSequence;
import org.bouncycastle.asn1.x500.X500Name;
import org.bouncycastle.asn1.x509.BasicConstraints;
import org.bouncycastle.asn1.x509.CertificatePolicies;
import org.bouncycastle.asn1.x509.ExtendedKeyUsage;
import org.bouncycastle.asn1.x509.Extension;
import org.bouncycastle.asn1.x509.KeyPurposeId;
import org.bouncycastle.asn1.x509.KeyUsage;
import org.bouncycastle.asn1.x509.PolicyInformation;
import org.bouncycastle.asn1.x509.PolicyQualifierId;
import org.bouncycastle.asn1.x509.PolicyQualifierInfo;
import org.bouncycastle.cert.X509v3CertificateBuilder;
import org.bouncycastle.cert.jcajce.JcaX509CertificateConverter;
import org.bouncycastle.cert.jcajce.JcaX509ExtensionUtils;
import org.bouncycastle.cert.jcajce.JcaX509v3CertificateBuilder;
import org.bouncycastle.jce.provider.BouncyCastleProvider;
import org.bouncycastle.openssl.jcajce.JcaPEMWriter;
import org.bouncycastle.operator.ContentSigner;
import org.bouncycastle.operator.jcajce.JcaContentSignerBuilder;
import org.threeten.bp.DateTimeUtils;
import org.threeten.bp.Instant;
import org.threeten.bp.temporal.ChronoUnit;
public class CriarAcTest {
private static JcaX509ExtensionUtils extUtils;
static SecureRandom rand;
static {
Security.addProvider(new BouncyCastleProvider());
try {
rand = SecureRandom.getInstance("SHA1PRNG");
extUtils = new JcaX509ExtensionUtils();
} catch (NoSuchAlgorithmException e) {
rand = new SecureRandom();
}
}
/**
* Cria um novo certificado de AC (a partir do qual serão emitidos outros certificados)
*/
public static void main(String[] args) throws Exception {
KeyPair acKeyPair = genKeyPair(4096);
String acSubject = "C=BR,O=TRT2,CN=AC Test";
// criar AC com validade de 30 anos (365 * 30)
X509Certificate acCert = createAcCert(acSubject, new BigInteger("1234"), 365 * 30, acKeyPair);
saveToKeystore(acCert, acKeyPair.getPrivate(), "actest.jks", "JKS");
saveToFile(acCert, "actest.cer");
System.out.println(acCert);
}
static void saveToKeystore(X509Certificate certificate, PrivateKey privKey, String file, String type) throws Exception {
char[] password = "123456".toCharArray();
KeyStore ks = KeyStore.getInstance(type);
ks.load(null, password);
ks.setKeyEntry("main", privKey, password, new Certificate[] { certificate });
OutputStream out = new FileOutputStream(file);
ks.store(out, password);
out.close();
}
static void saveToFile(X509Certificate cert, String filename) throws IOException {
JcaPEMWriter pw = new JcaPEMWriter(new FileWriter(filename));
pw.writeObject(cert);
pw.close();
}
static X509Certificate createAcCert(String subject, BigInteger serialNumber, int validityInDays, KeyPair keyPair) throws Exception {
X500Name issuer = new X500Name(subject);
// data-inicio 24 horas antes, pra evitar dessincronizacao entre maquinas, horario de verao
Instant validityStart = Instant.now().minus(24, ChronoUnit.HOURS);
Instant validityEnd = validityStart.plus(validityInDays, ChronoUnit.DAYS);
X509v3CertificateBuilder certBuilder = new JcaX509v3CertificateBuilder(issuer, serialNumber,
// se estiver usando Java >= 8, use o java.time e troque esta linha para Date.from(validityStart), Date.from(validityEnd)
DateTimeUtils.toDate(validityStart), DateTimeUtils.toDate(validityEnd),
issuer, keyPair.getPublic());
KeyUsage usage = new KeyUsage(
KeyUsage.digitalSignature | KeyUsage.keyEncipherment | KeyUsage.dataEncipherment | KeyUsage.keyCertSign | KeyUsage.cRLSign);
certBuilder.addExtension(Extension.keyUsage, false, usage);
ExtendedKeyUsage eku = new ExtendedKeyUsage(new KeyPurposeId[] { KeyPurposeId.id_kp_OCSPSigning, KeyPurposeId.id_kp_timeStamping });
certBuilder.addExtension(Extension.extendedKeyUsage, false, eku);
BasicConstraints bc = new BasicConstraints(true);
certBuilder.addExtension(Extension.basicConstraints, true, bc);
boolean isCritical = true;
PolicyQualifierInfo pqInfo = new PolicyQualifierInfo("http://www.test.com");
PolicyInformation policyInfo = new PolicyInformation(PolicyQualifierId.id_qt_cps, new DERSequence(pqInfo));
CertificatePolicies policies = new CertificatePolicies(policyInfo);
certBuilder.addExtension(Extension.certificatePolicies, isCritical, policies);
certBuilder.addExtension(Extension.subjectKeyIdentifier, true, extUtils.createSubjectKeyIdentifier(keyPair.getPublic()));
certBuilder.addExtension(Extension.authorityKeyIdentifier, true, extUtils.createAuthorityKeyIdentifier(keyPair.getPublic()));
ContentSigner signer = new JcaContentSignerBuilder("SHA512WithRSAEncryption").setProvider(BouncyCastleProvider.PROVIDER_NAME)
.build(keyPair.getPrivate());
X509Certificate cert = new JcaX509CertificateConverter().setProvider(BouncyCastleProvider.PROVIDER_NAME).getCertificate(certBuilder.build(signer));
return cert;
}
static KeyPair genKeyPair(int size) throws NoSuchAlgorithmException, NoSuchProviderException {
KeyPairGenerator gen = KeyPairGenerator.getInstance("RSA", BouncyCastleProvider.PROVIDER_NAME);
gen.initialize(size, rand);
return gen.generateKeyPair();
}
}
Code that reads the AC certificate and generates an eCPF (you can change the main
to generate an eCNPJ as well, just change the parameters):
import java.io.FileInputStream;
import java.io.FileOutputStream;
import java.io.FileWriter;
import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStream;
import java.math.BigInteger;
import java.security.KeyPair;
import java.security.KeyPairGenerator;
import java.security.KeyStore;
import java.security.NoSuchAlgorithmException;
import java.security.NoSuchProviderException;
import java.security.PrivateKey;
import java.security.SecureRandom;
import java.security.Security;
import java.security.cert.Certificate;
import java.security.cert.X509Certificate;
import org.bouncycastle.asn1.ASN1EncodableVector;
import org.bouncycastle.asn1.ASN1Object;
import org.bouncycastle.asn1.ASN1ObjectIdentifier;
import org.bouncycastle.asn1.DERPrintableString;
import org.bouncycastle.asn1.DERSequence;
import org.bouncycastle.asn1.DERTaggedObject;
import org.bouncycastle.asn1.x500.X500Name;
import org.bouncycastle.asn1.x509.ExtendedKeyUsage;
import org.bouncycastle.asn1.x509.Extension;
import org.bouncycastle.asn1.x509.GeneralName;
import org.bouncycastle.asn1.x509.KeyPurposeId;
import org.bouncycastle.asn1.x509.KeyUsage;
import org.bouncycastle.cert.X509v3CertificateBuilder;
import org.bouncycastle.cert.jcajce.JcaX509CertificateConverter;
import org.bouncycastle.cert.jcajce.JcaX509ExtensionUtils;
import org.bouncycastle.cert.jcajce.JcaX509v3CertificateBuilder;
import org.bouncycastle.jce.provider.BouncyCastleProvider;
import org.bouncycastle.openssl.jcajce.JcaPEMWriter;
import org.bouncycastle.operator.ContentSigner;
import org.bouncycastle.operator.jcajce.JcaContentSignerBuilder;
import org.threeten.bp.DateTimeUtils;
import org.threeten.bp.Instant;
import org.threeten.bp.temporal.ChronoUnit;
public class CriarCertificadoTest {
private static JcaX509ExtensionUtils extUtils;
static SecureRandom rand;
static {
Security.addProvider(new BouncyCastleProvider());
try {
rand = SecureRandom.getInstance("SHA1PRNG");
extUtils = new JcaX509ExtensionUtils();
} catch (NoSuchAlgorithmException e) {
rand = new SecureRandom();
}
}
/**
* Cria um certificado de teste.
*
* O certificado é emitido pela AC criada pela classe {@link CriarAcTest}
*/
public static void main(String[] args) throws Exception {
KeyPair myKeyPair = genKeyPair(2048);
String acSubject = "C=BR,O=TRT2,CN=AC Test";
char[] password = "123456".toCharArray();
KeyStore ks = KeyStore.getInstance("JKS");
// carrega o certificado da AC
InputStream in = new FileInputStream("actest.jks");
ks.load(in, password);
in.close();
// obtém o certificado e as chaves da AC
X509Certificate acCert = (X509Certificate) ks.getCertificate("main");
KeyPair acKeyPair = new KeyPair(acCert.getPublicKey(), (PrivateKey) ks.getKey("main", password));
System.out.println(acCert);
// mudar os dados conforme necessário
String cpf = "23446147292";
String filename = "cpf" + cpf;
String nome = "FULANO DE TAL";
filename = "certificado_" + cpf; // nome do PFX e .cer
// validade do certificado (em dias) - a data inicial é a atual menos 24 horas
int validityDays = 365 * 3;
X509Certificate cert = createCert("C=BR,O=ICP-Brasil,OU=AR Teste,OU=RFB e-CPF A3,OU=TESTE,CN=" + nome + ":" + cpf,
new BigInteger("3333333333", 16), validityDays, myKeyPair, acKeyPair, acSubject, cpf, acCert);
saveToKeystore(cert, myKeyPair.getPrivate(), filename + ".pfx", "PKCS12", acCert);
saveToFile(cert, filename + ".cer");
System.out.println(cert);
}
static void saveToKeystore(X509Certificate certificate, PrivateKey privKey, String file, String type, X509Certificate acCert) throws Exception {
char[] password = "123456".toCharArray();
KeyStore ks = KeyStore.getInstance(type);
ks.load(null, password);
ks.setKeyEntry("main", privKey, password, new Certificate[] { certificate, acCert });
OutputStream out = new FileOutputStream(file);
ks.store(out, password);
out.close();
}
static void saveToFile(X509Certificate cert, String filename) throws IOException {
JcaPEMWriter pw = new JcaPEMWriter(new FileWriter(filename));
pw.writeObject(cert);
pw.close();
}
public static X509Certificate createCert(String subject, BigInteger serialNumber, int validityInDays, KeyPair myKeyPair, KeyPair acKeyPair,
String acSubject, String cpf, X509Certificate acCert)
throws Exception {
// data-inicio 24 horas antes, pra evitar dessincronizacao entre maquinas, horario de verao
Instant validityStart = Instant.now().minus(24, ChronoUnit.HOURS);
Instant validityEnd = validityStart.plus(validityInDays, ChronoUnit.DAYS);
// data de validade do certificado não pode ser maior que da AC
// a partir do Java 8, troque esta linha por acCert.getNotAfter().toInstant();
Instant validadeAC = DateTimeUtils.toInstant(acCert.getNotAfter());
if (!validityEnd.isBefore(validadeAC)) {
validityEnd = validadeAC.minus(24 * 20, ChronoUnit.HOURS);
}
X509v3CertificateBuilder certBuilder = new JcaX509v3CertificateBuilder(new X500Name(acSubject), serialNumber,
// se estiver usando Java >= 8, use o java.time e troque esta linha para Date.from(validityStart), Date.from(validityEnd)
DateTimeUtils.toDate(validityStart), DateTimeUtils.toDate(validityEnd),
new X500Name(subject), myKeyPair.getPublic());
KeyUsage usage = new KeyUsage(KeyUsage.digitalSignature | KeyUsage.keyEncipherment | KeyUsage.nonRepudiation);
certBuilder.addExtension(Extension.keyUsage, false, usage);
ExtendedKeyUsage eku = new ExtendedKeyUsage(new KeyPurposeId[] { KeyPurposeId.id_kp_clientAuth });
certBuilder.addExtension(Extension.extendedKeyUsage, false, eku);
certBuilder.addExtension(Extension.subjectKeyIdentifier, false, extUtils.createSubjectKeyIdentifier(myKeyPair.getPublic()));
certBuilder.addExtension(Extension.authorityKeyIdentifier, false, extUtils.createAuthorityKeyIdentifier(acKeyPair.getPublic()));
// --------------------------------------------------------------------
// Subject Alternative Names
ASN1EncodableVector subjAltNames = new ASN1EncodableVector();
// OID 1
ASN1EncodableVector otherName = new ASN1EncodableVector();
otherName.add(new ASN1ObjectIdentifier("2.16.76.1.3.1"));
// data de nascimento
StringBuilder strOid1 = new StringBuilder("10101970")
// CPF
.append(cpf)
// nis
.append("00000000000")
// RG
.append("000000226148452SSPSP");
otherName.add(new DERTaggedObject(true, 0, new DERPrintableString(strOid1.toString())));
ASN1Object oid1 = new DERTaggedObject(false, GeneralName.otherName, new DERSequence(otherName));
subjAltNames.add(oid1);
// OID 6
otherName = new ASN1EncodableVector();
otherName.add(new ASN1ObjectIdentifier("2.16.76.1.3.6"));
// CEI
String strOid6 = "000000000000";
otherName.add(new DERTaggedObject(true, 0, new DERPrintableString(strOid6)));
ASN1Object oid6 = new DERTaggedObject(false, GeneralName.otherName, new DERSequence(otherName));
subjAltNames.add(oid6);
// OID 5
otherName = new ASN1EncodableVector();
otherName.add(new ASN1ObjectIdentifier("2.16.76.1.3.5"));
// titulo de eleitor
StringBuilder strOid5 = new StringBuilder("850544450191")
// zona eleitoral
.append("001")
// secao
.append("0401")
// municipio e UF
.append("SAO PAULOSP");
otherName.add(new DERTaggedObject(true, 0, new DERPrintableString(strOid5.toString())));
ASN1Object oid5 = new DERTaggedObject(false, GeneralName.otherName, new DERSequence(otherName));
subjAltNames.add(oid5);
certBuilder.addExtension(Extension.subjectAlternativeName, false, new DERSequence(subjAltNames));
// --------------------------------------------------------------------
ContentSigner signer = new JcaContentSignerBuilder("SHA256WithRSAEncryption").setProvider(BouncyCastleProvider.PROVIDER_NAME)
.build(acKeyPair.getPrivate());
X509Certificate cert = new JcaX509CertificateConverter().setProvider(BouncyCastleProvider.PROVIDER_NAME).getCertificate(certBuilder.build(signer));
return cert;
}
public static X509Certificate createCertPJ(String subject, BigInteger serialNumber, int validityInDays, KeyPair myKeyPair, KeyPair acKeyPair,
String acSubject, String cpfResp, String nomeResp, String cnpj, X509Certificate acCert)
throws Exception {
// data-inicio 24 horas antes, pra evitar dessincronizacao entre maquinas, horario de verao
Instant validityStart = Instant.now().minus(24, ChronoUnit.HOURS);
Instant validityEnd = validityStart.plus(validityInDays, ChronoUnit.DAYS);
// data de validade do certificado não pode ser maior que da AC
// a partir do Java 8, troque esta linha por acCert.getNotAfter().toInstant();
Instant validadeAC = DateTimeUtils.toInstant(acCert.getNotAfter());
if (!validityEnd.isBefore(validadeAC)) {
validityEnd = validadeAC.minus(24 * 20, ChronoUnit.HOURS);
}
X509v3CertificateBuilder certBuilder = new JcaX509v3CertificateBuilder(new X500Name(acSubject), serialNumber,
// se estiver usando Java >= 8, use o java.time e troque esta linha para Date.from(validityStart), Date.from(validityEnd)
DateTimeUtils.toDate(validityStart), DateTimeUtils.toDate(validityEnd),
new X500Name(subject), myKeyPair.getPublic());
KeyUsage usage = new KeyUsage(KeyUsage.digitalSignature | KeyUsage.keyEncipherment | KeyUsage.nonRepudiation);
certBuilder.addExtension(Extension.keyUsage, false, usage);
ExtendedKeyUsage eku = new ExtendedKeyUsage(new KeyPurposeId[] { KeyPurposeId.id_kp_clientAuth });
certBuilder.addExtension(Extension.extendedKeyUsage, false, eku);
certBuilder.addExtension(Extension.subjectKeyIdentifier, false, extUtils.createSubjectKeyIdentifier(myKeyPair.getPublic()));
certBuilder.addExtension(Extension.authorityKeyIdentifier, false, extUtils.createAuthorityKeyIdentifier(acKeyPair.getPublic()));
// --------------------------------------------------------------------
// Subject Alternative Names
ASN1EncodableVector subjAltNames = new ASN1EncodableVector();
// OID 4
ASN1EncodableVector otherName = new ASN1EncodableVector();
otherName.add(new ASN1ObjectIdentifier("2.16.76.1.3.4"));
// data de nascimento
StringBuilder strOid1 = new StringBuilder("10101970")
// CPF
.append(cpfResp)
// nis
.append("00000000000")
// RG
.append("000000226148452SSPSP");
otherName.add(new DERTaggedObject(true, 0, new DERPrintableString(strOid1.toString())));
ASN1Object oid4 = new DERTaggedObject(false, GeneralName.otherName, new DERSequence(otherName));
subjAltNames.add(oid4);
// OID 2
otherName = new ASN1EncodableVector();
otherName.add(new ASN1ObjectIdentifier("2.16.76.1.3.2"));
// Nome do responsavel
otherName.add(new DERTaggedObject(true, 0, new DERPrintableString(nomeResp)));
ASN1Object oid2 = new DERTaggedObject(false, GeneralName.otherName, new DERSequence(otherName));
subjAltNames.add(oid2);
// OID 3
otherName = new ASN1EncodableVector();
otherName.add(new ASN1ObjectIdentifier("2.16.76.1.3.3"));
// CNPJ
otherName.add(new DERTaggedObject(true, 0, new DERPrintableString(cnpj)));
ASN1Object oid3 = new DERTaggedObject(false, GeneralName.otherName, new DERSequence(otherName));
subjAltNames.add(oid3);
// OID 7
otherName = new ASN1EncodableVector();
otherName.add(new ASN1ObjectIdentifier("2.16.76.1.3.7"));
// CEI
String strOid7 = "000000000000";
otherName.add(new DERTaggedObject(true, 0, new DERPrintableString(strOid7)));
ASN1Object oid7 = new DERTaggedObject(false, GeneralName.otherName, new DERSequence(otherName));
subjAltNames.add(oid7);
certBuilder.addExtension(Extension.subjectAlternativeName, false, new DERSequence(subjAltNames));
// --------------------------------------------------------------------
ContentSigner signer = new JcaContentSignerBuilder("SHA256WithRSAEncryption").setProvider(BouncyCastleProvider.PROVIDER_NAME)
.build(acKeyPair.getPrivate());
X509Certificate cert = new JcaX509CertificateConverter().setProvider(BouncyCastleProvider.PROVIDER_NAME).getCertificate(certBuilder.build(signer));
return cert;
}
public static KeyPair genKeyPair(int size) throws NoSuchAlgorithmException, NoSuchProviderException {
KeyPairGenerator gen = KeyPairGenerator.getInstance("RSA", BouncyCastleProvider.PROVIDER_NAME);
gen.initialize(size, rand);
return gen.generateKeyPair();
}
}
I made this code some time ago and to run a few times, which is a great excuse to be so full of redundancies and without any optimization. Feel free to change it.
Thank you very much.
– Gladson Bruno
I’ll test it here. I would fix the OID part contain more things but as I don’t have many points on stackoverflow yet I don’t have editing permission.
– Gladson Bruno
@Gladsonbruno Which part were you going to correct? If it was in my reply, you can suggest an edition. Feel free to fix, it’s been a while since I read these pdf’s and I may have missed some detail
– hkotsubo
would be the correction of the OID description that I had said contained only the CPF and the formatting of the entire question that came out unformatted. I believe I’ve got the editing privilege now.
– Gladson Bruno
hktosubo Thank you very much. Solve my problem with certificate E-CPF I was hours away looking for how to do this in keytool
– Gladson Bruno
I’m having a different problem, the generated customer certificate worked, however the chain of certificates I tried to generate is not valid, could you take a look @hkotsubo? https://answall.com/questions/382087/gerar-certificados-a1-para-teste-no-padr%C3%a3o-icp-brasil
– Vinicius Farias