Java не может загрузить сертификат формата «BEGIN TRUSTED CERTIFICATE» - PullRequest
1 голос
/ 06 апреля 2019

У меня есть сертификат CA, который генерируется openssl с «trustout», поэтому он начинается с «----- BEGIN TRUSTED CERTIFICATE -----», когда я пытался прочитать его с Java, выбрасывает исключения,Поддерживает ли Java этот формат сертификата?Если да, то как это прочитать?

public class TestReadCerts {

    public static void main(String[] args) {
        // TODO Auto-generated method stub
         String sslrootcertfile = "F:\\javaworkspace\\opensource\\certs\\ca.pem";

            FileInputStream fis=null;
            try {
              fis = new FileInputStream(sslrootcertfile); // NOSONAR
            } catch (FileNotFoundException ex) {
              ex.printStackTrace();
            }
            try {
              CertificateFactory cf = CertificateFactory.getInstance("X.509");

              Object[] certs = cf.generateCertificates(fis).toArray(new Certificate[]{});

            } catch (Exception e) {
              e.printStackTrace();
            } 
    }

}

Исключение: java.security.cert.CertificateException: невозможно инициализировать,

java.io.IOException: extra data given to DerValue constructor
    at java.base/sun.security.x509.X509CertImpl.<init>(X509CertImpl.java:191)
    at java.base/sun.security.provider.X509Factory.parseX509orPKCS7Cert(X509Factory.java:476)
    at java.base/sun.security.provider.X509Factory.engineGenerateCertificates(X509Factory.java:361)
    at java.base/java.security.cert.CertificateFactory.generateCertificates(CertificateFactory.java:478)
    at TestReadCerts.main(TestReadCerts.java:21)
Caused by: java.io.IOException: extra data given to DerValue constructor
    at java.base/sun.security.util.DerValue.init(DerValue.java:409)
    at java.base/sun.security.util.DerValue.<init>(DerValue.java:294)
    at java.base/sun.security.util.DerValue.<init>(DerValue.java:305)
    at java.base/sun.security.x509.X509CertImpl.<init>(X509CertImpl.java:188)
    ... 4 more

И сертификат такой, как показано ниже:

-----BEGIN TRUSTED CERTIFICATE-----
MIICATCCAWoCCQDjKSwZBsrQwTANBgkqhkiG9w0BAQsFADBFMQswCQYDVQQGEwJB
VTETMBEGA1UECAwKU29tZS1TdGF0ZTEhMB8GA1UECgwYSW50ZXJuZXQgV2lkZ2l0
cyBQdHkgTHRkMB4XDTE5MDQwMzE0NDcwMVoXDTIwMDQwMjE0NDcwMVowRTELMAkG
A1UEBhMCQVUxEzARBgNVBAgMClNvbWUtU3RhdGUxITAfBgNVBAoMGEludGVybmV0
IFdpZGdpdHMgUHR5IEx0ZDCBnzANBgkqhkiG9w0BAQEFAAOBjQAwgYkCgYEAyRCB
rbB/FqN6e9IAJ86WUUGxM+8vEyfQ7cn2HWca220NB/Ns3Q+QtvztSe48PUzn9w6s
MNOwsDW4+8lenPLd78J32lG59x1P1R1jpjL3GcjNTwuewW1jIsex8jALzfU9hJzO
prraO/6X+UbKbazXt6GiB7mOlUvneKsWuoGpF5MCAwEAATANBgkqhkiG9w0BAQsF
AAOBgQCN1UJF/FdT84bzEn1kmg77b+LCCrU11DsFg/s/ABvo5TKV+OmilBPj1vML
dbZ4GDQSaXKZAOyJiAp0S5BzHXlXz5YfX9sM4mfhaqZt736WAnKVSnzd55CjMlEk
GxW3TkRFL5cVm5my1UQs3Mfg4MC5QPaoer5kc+0UhMHmTlgyvTAMMAoGCCsGAQUF
BwMB
-----END TRUSTED CERTIFICATE-----

Ответы [ 2 ]

3 голосов
/ 06 апреля 2019

Тип PEM 'TRUSTED CERTIFICATE' является нестандартным форматом , специфичным для OpenSSL, который Java не может обработать из коробки.На самом деле он содержит стандартный сертификат X.509 в виде одного блока DER плюс другого блока данных доверия, определенного в OpenSSL DER.

Если у вас есть OpenSSL, самый простой способ - преобразовать его в стандартныйФормат «СЕРТИФИКАТ» с openssl x509 <in >out.Вы можете добавить -outform DER, как Мисанторп, но это не нужно;CertificateFactory может читать стандартный формат в DER или PEM, так как OpenSSL неточно их вызывает.

Если у вас есть или вы можете получить и использовать bcpkix и bcprov из https://www.BouncyCastle.org, они включают процедуры для обработки этогоФормат OpenSSL PEM (и многие другие):

        // assumes filename in args[0], adjust as needed
        Object both = new PEMParser(new FileReader(args[0])).readObject();
        // should close the FileReader, maybe using try-resources
        byte[] cert = ((X509TrustedCertificateBlock)both).getCertificateHolder().getEncoded();
        X509Certificate good = (X509Certificate) CertificateFactory.getInstance("X.509")
                .generateCertificate(new ByteArrayInputStream(cert));
        System.out.println (good.getSubjectX500Principal().getName());

В противном случае можно разложить DER вручную, но неуклюже и ненадежно:

        String in1 = new String(Files.readAllBytes(new File(args[0]).toPath()));
        byte[] both = Base64.getMimeDecoder().decode(in1.replaceAll("-----[A-Z ]*-----\\r?\\n",""));
        if( both[0]!=0x30 || both[1]!=(byte)0x82 ) throw new Exception("wrong!"); // or other handling
        byte[] cert = Arrays.copyOf(both, (both[2]<<8 | both[3]&0xFF) + 4);
        X509Certificate good = (X509Certificate) CertificateFactory.getInstance("X.509")
                .generateCertificate(new ByteArrayInputStream(cert));
        System.out.println (good.getSubjectX500Principal().getName());
2 голосов
/ 06 апреля 2019

Пожалуйста, ознакомьтесь с ответом dave_thompson_085, поскольку он указывает на некоторые неточности в моем понимании и, как следствие, на этот ответ


Ваша проблема пытается прочитать кодированный PEM сертификат с CertificateFactory, который ожидает кодирование DER, как указано в документации

В случае фабрики сертификатов для сертификатов X.509, Сертификат, предоставленный в inStream, должен быть в кодировке DER и может быть поставляется в двоичной или печатной (Base64) кодировке.

Я думаю, что самый быстрый способ прочитать сертификат - это преобразовать сертификат в соответствующую кодировку. Поскольку вы уже упомянули, что для создания сертификата используется openssl, вы можете закодировать свой текущий сертификат в DER

.
$ openssl x509 -in /F/javaworkspace/opensource/certs/ca.pem -outform DER -out /F/javaworkspace/opensource/certs/ca.der

(скорректируйте путь до ca.pem и ca.der при необходимости)

Наконец, не забудьте обновить sslrootcertfile переменную

String sslrootcertfile = "F:\\javaworkspace\\opensource\\certs\\ca.der";
Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...