2009-10-23 12 views

Répondre

13

La possibilité de signer des certificats ne fait pas partie d'une bibliothèque ou d'une extension Java standard.

Une grande partie du code nécessaire pour le faire vous-même fait partie du noyau. Il existe des classes pour encoder et décoder des noms X.500, des extensions de certificat X.509, des clés publiques pour divers algorithmes, et bien sûr, pour effectuer réellement la signature numérique. — J'ai probablement passé 4 ou 5 jours entiers la première fois que j'ai fait un prototype fonctionnel pour la signature de certificat. C'était un exercice d'apprentissage fantastique pour moi, mais il est difficile de justifier cette dépense quand il y a des bibliothèques utilisables disponibles gratuitement.

+0

Est-ce toujours exact à partir de 2017? – user674669

4

Tous les composants de base pour créer un certificat auto-signé (signature, encodage X509, etc.) sont disponibles dans JRE. Contrairement à la Colombie-Britannique, l'ECC de Sun n'offre aucun appel public pour signer un certificat. Cependant, toutes les fonctions sont disponibles dans Keytool. Vous pouvez simplement copier le code de keytool pour le faire. La méthode que vous devez copier est doSelfCert().

+4

Malheureusement, Keytool utilise des classes 'sun. *' Pour cela. Donc, cela ne fonctionnera pas avec tous les JRE. Cependant, voici le [code source] (https://github.com/openjdk-mirror/jdk7u-jdk/blob/master/src/share/classes/sun/security/tools/KeyTool.java) – Pith

1

Dépend de ce que vous voulez exactement faire (et probablement de votre définition de "Sanely"). Comme l'a souligné ZZ Coder, vous pouvez directement créer un certificat auto-signé en copiant keytool. Mais je ne pense pas que vous puissiez créer un objet de demande de certificat PKCS10 avec le JCE standard, ce que vous devez probablement faire si vous voulez créer des EECs signés par une autorité de certification standard.

+0

Hm, pourquoi pas ? Keytool peut convertir un auto-signé en un CSR, vous avez juste besoin de copier ce code aussi bien. – eckes

63

Oui, mais pas avec les classes documentées publiquement. J'ai documenté le processus 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 très bon pourboire. M'a sauvé de l'importation de la redoutée (et bien-aimée) Bouncycastle lib. Le ballonnement s'en vient! –

+11

Existe-t-il un moyen de le faire qui n'implique PAS d'appeler sun.security.x509. *? Étant donné que ce n'est pas quelque chose que vous êtes censé utiliser. –

+0

Excellent. M'a sauvé beaucoup de travail. Le code est gentil et propre. Je suis en train d'éditer le code pour m'assurer qu'il ne disparaît pas si le blog tombe en panne. – 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!"); 
    } 
} 

J'ai bien aimé la réponse de vbence, mais je continué à obtenir l'exception suivante:

java.security.cert.CertificateException: Sujet type de classe non valide.

Après beaucoup de tentatives pour savoir était une classe d'objet valide j'ai découvert que X509CerInfo voulait une instance 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())); 

Donc, les lignes 2 & 3 nécessaires pour changer

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