web-dev-qa-db-fra.com

Création d'un certificat X509 dans Java sans BouncyCastle?

Est-il possible de créer un certificat X509 en toute sécurité en Java sans utiliser le château gonflable X509V*CertificateGenerator Des classes?

37
Yuliy

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

Une grande partie du code nécessaire pour le faire vous-même fait partie du noyau. Il existe des classes pour coder et décoder les noms X.500, les extensions de certificat X.509, les clés publiques pour divers algorithmes et, bien sûr, pour effectuer réellement la signature numérique.

L'implémentation de vous-même n'est pas anodine, mais elle est certainement faisable - j'ai probablement passé 4 ou 5 jours complets la première fois que j'ai créé un prototype fonctionnel pour la signature de certificats. C'était un fantastique exercice d'apprentissage pour moi, mais il est difficile de justifier cette dépense quand des bibliothèques utilisables sont disponibles gratuitement.

18
erickson

Oui, mais pas avec des classes publiquement documentées. J'ai documenté le processus dans cet 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;
}   
75
Mike B
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 aimé la réponse de vbence, mais j'ai continué à recevoir l'exception suivante:

Java.security.cert.CertificateException: type de classe sujet non valide.

Après de nombreuses tentatives pour trouver était une classe de sujet 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()));

Les lignes 2 et 3 devaient donc

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

Tous les composants de base pour créer un certificat auto-signé (signature, encodage X509, etc.) sont disponibles dans JRE. Contrairement à BC, JCE de Sun ne fournit aucun appel public pour signer un certificat. Cependant, toutes les fonctions sont disponibles dans Keytool. Pour ce faire, vous pouvez simplement copier le code depuis keytool. La méthode que vous devez copier est doSelfCert().

4
ZZ Coder

Cela dépend de ce que vous voulez faire exactement (et probablement de votre définition de "Sanely"). Comme l'a souligné ZZ Coder, vous pouvez créer un certificat auto-signé directement 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 devrez probablement faire si vous voulez créer des EEC standard signés par une autorité de certification.

1
Von