El proceso de solicitud de certificado estándar (o al menos convencional) requiere que se certifique la clave privada que coincide con la publicación. Las operaciones preempaquetadas de la utilidad keytool
de Java y la línea de comando openssl
están limitadas a este proceso estándar, al igual que varias otras Qs existentes.
Sin embargo, no es fundamentalmente necesario. Siempre que tenga una clave privada para el certificado emisor , nominalmente una Autoridad de Certificación y un certificado para esa clave de CA o al menos algunos metadatos críticos normalmente incluidos en dicho certificado, es posible emitir una certificado de niño para un niño dado publickey.
Esto se puede hacer con OpenSSL llamando a las rutinas X509_*
en libcrypto desde un código personalizado; en Java, las clases utilizadas por el estándar keytool
no son oficialmente parte de la especificación JRE, pero son de código abierto, pero si puede usar las bibliotecas de terceros BouncyCastle (bcprov + bcpkix), exponen directamente las funciones necesarias de forma conveniente y documentada. / Forma garantizada, asi que lo demuestro. Esto crea un certificado muy mínimo con poca información y, especialmente, ninguna de las extensiones que están comúnmente presentes en los certificados reales, emitidos por CA. Si esto no es suficiente para sus necesidades, haga una pregunta más completa.
Por simplicidad leí el PEM de pubkey (sujeto) de un archivo con nombre, y la clave de CA y el certificado de un JKS en un archivo con nombre en un alias con nombre. Estas fuentes de información pueden ser reemplazadas por otras a su gusto. Tampoco hago ningún manejo de errores dejando que Java haga su seguimiento de pila predeterminado; Es posible que necesites algo mejor.
import java.security.*;
import java.security.cert.X509Certificate;
import java.security.interfaces.RSAPrivateKey;
import java.util.Date;
import org.bouncycastle.asn1.x500.X500Name;
import org.bouncycastle.asn1.x509.SubjectPublicKeyInfo;
import org.bouncycastle.cert.X509v3CertificateBuilder;
import org.bouncycastle.openssl.PEMParser;
import org.bouncycastle.openssl.jcajce.JcaPEMWriter;
import org.bouncycastle.operator.ContentSigner;
import org.bouncycastle.operator.jcajce.JcaContentSignerBuilder;
static void Se179526FakeCert (String[] args) throws Exception {
// pubkey.pem cakey.jks pw alias
Object spki = new PEMParser (new FileReader (args[0])).readObject();
KeyStore ks = KeyStore.getInstance("JKS");
ks.load (new FileInputStream (args[1]), args[2].toCharArray());
PrivateKey cakey = (PrivateKey) ks.getKey (args[3], args[2].toCharArray());
X509Certificate cacert = (X509Certificate) ks.getCertificate (args[3]);
Date now = new Date(), exp = new Date(now.getTime()+30*86400*1000); // 30 days validity
ContentSigner signer = new JcaContentSignerBuilder ("SHA256with"+cakey.getAlgorithm()).build(cakey);
byte[] enc = new X509v3CertificateBuilder(
/*issuer*/new X500Name (cacert.getIssuerX500Principal().getName()), /*serial*/BigInteger.valueOf(now.getTime()),
/*validity*/now, exp, /*subject*/new X500Name ("CN=dummy"), /*spki*/(SubjectPublicKeyInfo) spki
) .build(signer) .getEncoded();
// .addExtension's if&as needed before .build
Certificate cert = CertificateFactory.getInstance ("X.509") .generateCertificate(new ByteArrayInputStream(enc));
// could now store in a keystore; or to write out in PEM:
JcaPEMWriter out = new JcaPEMWriter (new OutputStreamWriter (System.out));
out.writeObject (cert); out.flush();
}