2009-10-23 7 views

Odpowiedz

13

Możliwość podpisywania certyfikatów nie jest częścią standardowej biblioteki lub rozszerzenia Java.

Dużo kodu potrzebnego do zrobienia tego samodzielnie jest częścią rdzenia. Istnieją klasy służące do kodowania i dekodowania nazw X.500, rozszerzeń certyfikatów X.509, kluczy publicznych dla różnych algorytmów i oczywiście do wykonywania podpisu cyfrowego.

Wykonanie tego samodzielnie nie jest trywialne, ale jest zdecydowanie wykonalne — Prawdopodobnie spędziłem 4 lub 5 pełnych dni przy pierwszym uruchomieniu działającego prototypu do podpisywania certyfikatu. Było to dla mnie wspaniałe ćwiczenie edukacyjne, ale trudno to uzasadnić, gdy dostępne biblioteki są dostępne za darmo.

+0

Czy to nadal dokładne zgodnie z 2017 roku? – user674669

4

Wszystkie podstawowe komponenty do samodzielnego podpisania certyfikatu (podpisywanie, kodowanie X509 itp.) Są dostępne w środowisku JRE. W przeciwieństwie do BC, Sun's JCE nie zapewnia żadnych publicznych połączeń do podpisania certyfikatu. Jednak wszystkie funkcje są dostępne w Keytool. Możesz po prostu skopiować kod z keytool, aby to zrobić. Metodą, którą należy skopiować, jest doSelfCert().

+4

Niestety, Keytool używa do tego klas 'sun. *'. Tak więc nie będzie działać z każdym JRE. Jednak tutaj jest [kod źródłowy] (https://github.com/openjdk-mirror/jdk7u-jdk/blob/master/src/share/classes/sun/security/tools/KeyTool.java) – Pith

1

Zależy od tego, co dokładnie chcesz zrobić (i prawdopodobnie Twoja definicja "Sanely"). Jak zauważył ZZ Coder, możesz utworzyć samopodpisany certyfikat bezpośrednio, kopiując keytool. Ale nie sądzę, że można utworzyć obiekt żądania certyfikatu PKCS10 ze standardową JCE, co prawdopodobnie trzeba zrobić, jeśli chcesz utworzyć standardowe EWG-podpisane EEC.

+0

Hm, dlaczego nie ? Keytool może przekonwertować autograf na csr, wystarczy skopiować również ten kod. – eckes

63

Tak, ale nie w przypadku publicznie udokumentowanych zajęć. Udokumentowałem proces 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

Bardzo dobra wskazówka. Uratował mnie przed importowaniem przerażającej (i ukochanej) biblioteki Bouncycastle. Bloat Begone! –

+11

Czy istnieje sposób, aby to zrobić, nie wzywając do sun.security.x509. *? Biorąc pod uwagę, że w rzeczywistości nie jest to coś, czego powinieneś użyć. –

+0

Doskonały. Zaoszczędził mi dużo pracy. Kod jest ładny i czysty. Edytuję w kodzie, aby upewnić się, że nie zniknie, a bloga nie działa. – 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!"); 
    } 
} 

lubiłem odpowiedź vbence, ale Ciągle otrzymuję następujący wyjątek:

java.security.cert.CertificateException: Temat klasa typu nieprawidłowy.

Po wielu próbach odkrycia była ważna klasa przedmiotu, dowiedziałem się, że X509CerInfo chciał instancji 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())); 

Więc linie 2 & 3 potrzebne, aby zmienić

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