2009-10-23 56 views

Respuesta

13

La capacidad para firmar certificados no es parte de una biblioteca o extensión estándar de Java.

Gran parte del código que se necesita para hacerlo usted mismo es parte del núcleo. Existen clases para codificar y decodificar nombres X.500, extensiones de certificado X.509, claves públicas para varios algoritmos y, por supuesto, para realizar realmente la firma digital.

Implementar esto usted mismo no es trivial, pero es definitivamente factible — Probablemente pasé 4 o 5 días completos la primera vez que hice un prototipo funcional para la firma de certificados. Fue un ejercicio de aprendizaje fantástico para mí, pero es difícil justificar ese gasto cuando hay bibliotecas utilizables disponibles de forma gratuita.

+0

¿Sigue siendo exacta a partir de 2017? – user674669

4

Todos los componentes básicos para hacer un certificado autofirmado (firma, codificación X509, etc.) están disponibles en JRE. A diferencia de BC, Sun's JCE no proporciona ninguna llamada pública para firmar un certificado. Sin embargo, todas las funciones están disponibles en Keytool. Simplemente puede copiar el código de keytool para hacer esto. El método que necesita copiar es doSelfCert().

+4

Desafortunadamente, Keytool usa las clases 'sun. *' Para esto. Entonces esto no funcionará con todos los JRE. Sin embargo, aquí está el [código fuente] (https://github.com/openjdk-mirror/jdk7u-jdk/blob/master/src/share/classes/sun/security/tools/KeyTool.java) – Pith

1

Depende de qué es exactamente lo que quiere hacer (y probablemente su definición de "Sanely"). Como señaló ZZ Coder, puede crear un certificado autofirmado directamente copiando keytool. Pero no creo que pueda crear un objeto de solicitud de certificado PKCS10 con la JCE estándar, que probablemente necesite hacer si desea crear CEE estándar firmadas por CA.

+0

Hm, ¿por qué no? ? Keytool puede convertir un auto firmado en un csr, solo necesita copiar ese código también. – eckes

63

Sí, pero no con las clases documentadas públicamente. Documenté el proceso in this article.

import sun.security.x509.*; 
import java.security.cert.*; 
import java.security.*; 
import java.math.BigInteger; 
import java.util.Date; 
import java.io.IOException 

/**  
 * Create a self-signed X.509 Certificate 
 * @param dn the X.509 Distinguished Name, eg "CN=Test, L=London, C=GB" 
 * @param pair the KeyPair 
 * @param days how many days from now the Certificate is valid for 
 * @param algorithm the signing algorithm, eg "SHA1withRSA" 
 */  
X509Certificate generateCertificate(String dn, KeyPair pair, int days, String algorithm) 
  throws GeneralSecurityException, IOException 
{ 
  PrivateKey privkey = pair.getPrivate(); 
  X509CertInfo info = new X509CertInfo(); 
  Date from = new Date(); 
  Date to = new Date(from.getTime() + days * 86400000l); 
  CertificateValidity interval = new CertificateValidity(from, to); 
  BigInteger sn = new BigInteger(64, new SecureRandom()); 
  X500Name owner = new X500Name(dn); 
  
  info.set(X509CertInfo.VALIDITY, interval); 
  info.set(X509CertInfo.SERIAL_NUMBER, new CertificateSerialNumber(sn)); 
  info.set(X509CertInfo.SUBJECT, new CertificateSubjectName(owner)); 
  info.set(X509CertInfo.ISSUER, new CertificateIssuerName(owner)); 
  info.set(X509CertInfo.KEY, new CertificateX509Key(pair.getPublic())); 
  info.set(X509CertInfo.VERSION, new CertificateVersion(CertificateVersion.V3)); 
  AlgorithmId algo = new AlgorithmId(AlgorithmId.md5WithRSAEncryption_oid); 
  info.set(X509CertInfo.ALGORITHM_ID, new CertificateAlgorithmId(algo)); 
  
  // Sign the cert to identify the algorithm that's used. 
  X509CertImpl cert = new X509CertImpl(info); 
  cert.sign(privkey, algorithm); 
  
  // Update the algorith, and resign. 
  algo = (AlgorithmId)cert.get(X509CertImpl.SIG_ALG); 
  info.set(CertificateAlgorithmId.NAME + "." + CertificateAlgorithmId.ALGORITHM, algo); 
  cert = new X509CertImpl(info); 
  cert.sign(privkey, algorithm); 
  return cert; 
}    
+7

Un muy buen consejo. Me salvó de importar la temida (y amada) lib de Bouncycastle. Hincharse! –

+11

¿Hay alguna manera de hacerlo que NO implique llamar a sun.security.x509. *? Dado que, de hecho, no es algo que se supone que debes usar. –

+0

Excelente. Me ahorró mucho trabajo. El código es agradable y limpio. Estoy editando el código para asegurarme de que no desaparezca si el blog se cae. – Suma

2
import sun.security.x509.*; 

import java.security.cert.*; 
import java.security.*; 
import java.math.BigInteger; 
import java.security.cert.Certificate; 
import java.util.Date; 
import java.io.IOException; 

public class Example { 
    /** 
    * Create a self-signed X.509 Example 
    * 
    * @param dn  the X.509 Distinguished Name, eg "CN=Test, L=London, C=GB" 
    * @param pair  the KeyPair 
    * @param days  how many days from now the Example is valid for 
    * @param algorithm the signing algorithm, eg "SHA1withRSA" 
    */ 
    public X509Certificate generateCertificate(String dn, KeyPair pair, int days, String algorithm) 
      throws GeneralSecurityException, IOException { 
     PrivateKey privkey = pair.getPrivate(); 
     X509CertInfo info = new X509CertInfo(); 
     Date from = new Date(); 
     Date to = new Date(from.getTime() + days * 86400000l); 
     CertificateValidity interval = new CertificateValidity(from, to); 
     BigInteger sn = new BigInteger(64, new SecureRandom()); 
     X500Name owner = new X500Name(dn); 

     info.set(X509CertInfo.VALIDITY, interval); 
     info.set(X509CertInfo.SERIAL_NUMBER, new CertificateSerialNumber(sn)); 
     info.set(X509CertInfo.SUBJECT, owner); 
     info.set(X509CertInfo.ISSUER, owner); 
     info.set(X509CertInfo.KEY, new CertificateX509Key(pair.getPublic())); 
     info.set(X509CertInfo.VERSION, new CertificateVersion(CertificateVersion.V3)); 
     AlgorithmId algo = new AlgorithmId(AlgorithmId.md5WithRSAEncryption_oid); 
     info.set(X509CertInfo.ALGORITHM_ID, new CertificateAlgorithmId(algo)); 

     // Sign the cert to identify the algorithm that's used. 
     X509CertImpl cert = new X509CertImpl(info); 
     cert.sign(privkey, algorithm); 

     // Update the algorith, and resign. 
     algo = (AlgorithmId) cert.get(X509CertImpl.SIG_ALG); 
     info.set(CertificateAlgorithmId.NAME + "." + CertificateAlgorithmId.ALGORITHM, algo); 
     cert = new X509CertImpl(info); 
     cert.sign(privkey, algorithm); 
     return cert; 
    } 

    public static void main (String[] argv) throws Exception { 
     KeyPairGenerator keyPairGenerator = KeyPairGenerator.getInstance("RSA"); 
     KeyPair keyPair = keyPairGenerator.generateKeyPair(); 
     Example example = new Example(); 
     String distinguishedName = "CN=Test, L=London, C=GB"; 
     Certificate certificate = example.generateCertificateOriginal(distinguishedName, keyPair, 365, "SHA256withRSA"); 
     System.out.println("it worked!"); 
    } 
} 

me gustó la respuesta de vbence, pero seguí recibiendo la siguiente excepción:

java.security.cert.CertificateException: Asunto tipo de clase no válida.

Después de muchos intentos de averiguar era una clase de sujeto válida descubrí que X509CerInfo quería una instancia de X500Name.

1 info.set(X509CertInfo.SERIAL_NUMBER, new CertificateSerialNumber(sn)); 
2 info.set(X509CertInfo.SUBJECT, new CertificateSubjectName(owner)); 
3 info.set(X509CertInfo.ISSUER, new CertificateIssuerName(owner)); 
4 info.set(X509CertInfo.KEY, new CertificateX509Key(pair.getPublic())); 

Así líneas 2 3 & necesarios para cambiar a

2 info.set(X509CertInfo.SUBJECT, owner); 
3 info.set(X509CertInfo.ISSUER, owner); 
Cuestiones relacionadas