Создание сертификата X509 на Java без BouncyCastle?

Можно ли разумно создать сертификат X509 в коде Java без использования классов Bouncy Castle X509V*CertificateGenerator?


person Yuliy    schedule 23.10.2009    source источник


Ответы (5)


Возможность подписывать сертификаты не является частью стандартной библиотеки или расширения Java.

Большая часть кода, необходимого для того, чтобы сделать это самостоятельно, является частью ядра. Существуют классы для кодирования и декодирования имен X.500, расширений сертификатов X.509, открытых ключей для различных алгоритмов и, конечно же, для фактического выполнения цифровой подписи.

Реализовать это самостоятельно нетривиально, но это определенно выполнимо. Я, наверное, потратил 4 или 5 полных дней на первый раз, когда я сделал рабочий прототип для подписи сертификата. Для меня это было фантастическим упражнением по обучению, но трудно оправдать эти расходы, когда есть полезные библиотеки, доступные бесплатно.

person erickson    schedule 23.10.2009
comment
Это все еще верно по состоянию на 2017 год? - person user674669; 31.10.2017
comment
Нет, фрагмент кода использует устаревшую криптографию (MD5withRSA или SHA1withRSA в Javadoc) для подписи сертификата. Вместо этого я бы рекомендовал использовать SHA256withRSA, SHA384withRSA, SHA512withRSA или SHA256withECDSA, SHA384withECDSA и SHA512withECDSA в зависимости от уровня безопасности, который вы разрабатываете. - person Daniel Gartmann; 23.10.2018
comment
@DanielGartmann Непонятно, о каком фрагменте кода вы говорите. Вы не отвечаете предыдущему комментатору? - person erickson; 23.10.2018
comment
@erickson да, я, очевидно, отвечаю на вопрос user674669. Под фрагментом кода я имел в виду код, отображаемый в первых двух ответах ниже (в серой области, содержащей исходный код). - person Daniel Gartmann; 24.10.2018
comment
@DanielGartmann Комментатор спрашивает, верен ли мой ответ в 2017 году. Я думаю, что это так; Вы не можете подписать сертификаты из коробки, вы должны собрать его самостоятельно. Итак, если вы говорите, что это неправильно, можете ли вы объяснить, почему? Но ваш второй комментарий больше похож на то, что вы говорите, что другие ответы неверны в том, как они применяют предоставленные API (и с этим я согласен). - person erickson; 25.10.2018
comment
Итак, в JCA (Java Criptography Arquitecture) нет класса, который позволял бы это, верно? - person Carlitos Way; 18.12.2019
comment
@CarlitosWay Верно. Для этого нет поддерживаемого API. - person erickson; 18.12.2019

Да, но не с публично задокументированными классами. Я задокументировал этот процесс в этой статье.

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;
}   
person Mike B    schedule 30.03.2011
comment
Очень хороший совет. Спас меня от импорта ужасной (и всеми любимой) библиотеки Bouncycastle. Вздутие исчезло! - person Robert Foss; 08.07.2011
comment
Есть ли способ сделать это, НЕ вызывая вызова на sun.security.x509. *? Учитывая, что на самом деле это не то, что вы должны использовать. - person Markus Jevring; 27.11.2011
comment
Отлично. Сэкономил мне много работы. Код красивый и чистый. Я редактирую код, чтобы убедиться, что он не исчезает, если блог не работает. - person Suma; 20.03.2012
comment
как добавить расширения? - person meno; 03.01.2013
comment
В JDK 8 это должно быть: info.set (X509CertInfo.SUBJECT, owner); info.set (X509CertInfo.ISSUER, владелец); - person Axel Fontaine; 23.10.2014
comment
Упомянутое изменение @AxelFontaine описано здесь bugs.openjdk.java.net/browse/JDK- 7198416 - person Bozho; 27.12.2015
comment
@meno Проверьте строку 2512 на github.com/openjdk-mirror/jdk7u-jdk/blob/master/src/share/ - person felipeptcho; 15.08.2017
comment
Как этот код устанавливает для BasicConstraint значение true для сертификата с собственной подписью? - person musterjunk; 29.03.2018
comment
Чтобы установить basicConstrains (флаг CA, длина пути), проверьте: docjar .com / docs / api / sun / security / x509 /. - person Sajed Almorsy; 18.11.2020

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!");
    }
}

Мне понравился ответ vbence, но я продолжал получать следующее исключение:

java.security.cert.CertificateException: недопустимый тип класса субъекта.

После множества попыток выяснить, является ли допустимым предметным классом, я обнаружил, что X509CerInfo нужен экземпляр 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()));

Итак, строки 2 и 3 нужно было изменить на

2 info.set(X509CertInfo.SUBJECT, owner);
3 info.set(X509CertInfo.ISSUER, owner);
person Clark Hobbie    schedule 24.06.2017

Все основные компоненты для создания самозаверяющего сертификата (подпись, кодировка X509 и т. Д.) Доступны в JRE. В отличие от BC, Sun JCE не предоставляет никаких публичных вызовов для подписания сертификата. Однако все функции доступны в Keytool. Для этого вы можете просто скопировать код из keytool. Вам нужно скопировать метод doSelfCert().

person ZZ Coder    schedule 23.10.2009
comment
К сожалению, Keytool использует для этого sun.* классов. Так что это не будет работать с каждой JRE. Однако вот исходный код - person Pith; 14.09.2015

Зависит от того, что именно вы хотите делать (и, вероятно, от вашего определения «Вменяемый»). Как указал ZZ Coder, вы можете создать самозаверяющий сертификат напрямую, скопировав keytool. Но я не верю, что вы можете создать объект запроса сертификата PKCS10 со стандартным JCE, что вам, вероятно, понадобится, если вы хотите создать стандартные EEC, подписанные CA.

person Von    schedule 10.01.2010
comment
Хм, а почему бы и нет? Keytool может преобразовать самоподписанный в csr, вам просто нужно скопировать этот код. - person eckes; 22.05.2015