Не удается проанализировать subjectAlternativeNames в Java
Я пытаюсь разобрать дополнительные данные сертификата в java. Меня интересует раздел subjectAlternativeNames. Мой код:
CertificateFactory certFactory = CertificateFactory.getInstance("X.509")
certFactory.generateCertificate(ByteArrayInputStream(x509EntryChain.leafCertificate))
X509CertInfo
содержит java.io.IOException: invalid URI name:DNS:*.cetrel.lu, DNS:mail.cetrel.lu, DNS:www.cetrel.lu
. а такжеsubjectAlternativeNames
пустой.
Как его разобрать?
Меня интересует только extra_data
, не в leaf_input
. Я могу найти сертификат здесь: http://transparencyreport.google.com/https/certificates/
В DNS names
раздел пуст, похоже, Google тоже не может его разобрать.
Полный код для получения сертификата:
import org.apache.commons.codec.binary.Base64;
import java.io.ByteArrayInputStream;
import java.io.IOException;
import java.io.InputStream;
import sun.security.x509.CertificateExtensions;
import sun.security.x509.X509CertImpl;
import sun.security.x509.X509CertInfo;
import java.security.cert.CertificateException;
import java.security.cert.CertificateFactory;
public class CertParser {
public static void main(String[] args) throws CertificateException, IOException {
CertificateFactory certFactory = CertificateFactory.getInstance("X.509");
ByteArrayInputStream in = new ByteArrayInputStream(Base64.decodeBase64(
"AAAAAAFXsGq8HwAAAAW+MIIFujCCBKKgAwIBAgIDAZZpMA0GCSqGSIb3DQEBBQUAMEUxCzAJBgNVBAYTAkxVMRYwFAYDVQQKEw1MdXhUcnVzdCBTLkEuMR4wHAYDVQQDExVMdXhUcnVzdCBRdWFsaWZpZWQgQ0EwHhcNMTAwNDE1MDY0MzM0WhcNMTMwNDE1MDY0MzM0WjB8MQswCQYDVQQGEwJMVTERMA8GA1UEBxMITXVuc2JhY2gxFDASBgNVBAoTC0NFVFJFTCBTLkEuMRAwDgYDVQQLEwdJT1AtU1NTMRQwEgYDVQQDFAsqLmNldHJlbC5sdTEcMBoGCSqGSIb3DQEJARYNc3NzQGNldHJlbC5sdTCCASIwDQYJKoZIhvcNAQEBBQADggEPADCCAQoCggEBALhf+I7RshQlHMMWq/WPDLNxx+ODMd4Tn9ej14igvMEE+RYEagdMOZoeO8Bqz9qV2atzFHqz0D+Ad+cxuznBGKl4rhS9gUejgoAMox7car0+LSsv1NT4J0gAlnmH3BJlDMd9CihT0D/sRwMNfa8GYAvCuGDtWIvYb497RFy+2kmzk3cwCk3BgOO3MsT7iqhcn65Pd1Lq1vLjCCuQBoWLlcKk4uptPsyFKrHEh1/0ksY5evqBPxioVppoN+oay20RK36JzrzAl+vfpzq03WRlM2IgM0ItnesLqid9GqTUsOTq59i5aVX1EKlfgM5v7YCpYMLrJA+JBO3beR/4FSczfccCAwEAAaOCAnowggJ2MAwGA1UdEwEB/wQCMAAwYAYIKwYBBQUHAQEEVDBSMCMGCCsGAQUFBzABhhdodHRwOi8vb2NzcC5sdXh0cnVzdC5sdTArBggrBgEFBQcwAoYfaHR0cDovL2NhLmx1eHRydXN0Lmx1L0xUUUNBLmNydDBBBgNVHREEOjA4hjZETlM6Ki5jZXRyZWwubHUsIEROUzptYWlsLmNldHJlbC5sdSwgRE5TOnd3dy5jZXRyZWwubHUwggEABgNVHSAEgfgwgfUwgegGCCuBKwEBAgYBMIHbMIGtBggrBgEFBQcCAjCBoBqBnUx1eFRydXN0IFNlcnZlciBDZXJ0aWZpY2F0ZS4gTm90IHN1cHBvcnRlZCBieSBTU0NELCBLZXkgR2VuZXJhdGlvbiBieSBTdWJzY3JpYmVyLiBHVEMsIENQIGFuZCBDUFMgb24gaHR0cDovL3JlcG9zaXRvcnkubHV4dHJ1c3QubHUuIFNpZ25lZCBieSBhIFF1YWxpZmllZCBDQS4wKQYIKwYBBQUHAgEWHWh0dHA6Ly9yZXBvc2l0b3J5Lmx1eHRydXN0Lmx1MAgGBgQAj3oBAzARBglghkgBhvhCAQEEBAMCBeAwDgYDVR0PAQH/BAQDAgSwMCcGA1UdJQQgMB4GCCsGAQUFBwMBBggrBgEFBQcDAgYIKwYBBQUHAwQwHwYDVR0jBBgwFoAUjZCjB90aE3eZTJKrTUPeP80pZAUwMQYDVR0fBCowKDAmoCSgIoYgaHR0cDovL2NybC5sdXh0cnVzdC5sdS9MVFFDQS5jcmwwHQYDVR0OBBYEFLhOdzNJg4CSchxSsHbwKWMVm8xnMA0GCSqGSIb3DQEBBQUAA4IBAQBrQ9bYRA5O8a+4vIfVx5izH5x9yieKuQjpF4Etc4YUxfk3h5yZieWJuZGHygUjV5TCYDdhtVf6FFWkJ4FT5l6zDQeOLrEWqL12qcT4hGFN61mwjZO7kca8IHlqPPeqtYVg/Ssbpun+bjPOsGdvrvMulqNNTz5UeQuovc/VFaoHpYCQhezrQ6E6uQ684f6LFVIbsah6pT58wEnrf6xE1aRdSk27e3bF8wns3zOVsWE2wKck5pMS5DkGwjWli27Aqt6QQCyCKC7xqqxwL8GfnmXZNdn2iYYfSyr0I7rdqxa7FsuNFkEb8/PdZlyMxQP867YnucRCyLzzjCnRy1bOLnUeAAA="
));
readNumber(in, 1); // version
readNumber(in, 1); // type
readNumber(in, 8); // timestamp
readNumber(in, 2); // entry type
int length = (int) readNumber(in, 3);
byte[] x509 = readFixedLength(in, length);
X509CertImpl cert = (X509CertImpl) certFactory.generateCertificate(new ByteArrayInputStream(x509));
((CertificateExtensions)((X509CertInfo)cert.get("x509.info")).get("extensions")).getUnparseableExtensions(); // <-- want too be empty, but it's not!!!
}
static byte[] readFixedLength(InputStream inputStream, int dataLength) throws IOException {
byte[] toReturn = new byte[dataLength];
int bytesRead = inputStream.read(toReturn);
if (bytesRead < dataLength) {
throw new RuntimeException();
}
return toReturn;
}
static long readNumber(InputStream inputStream, int numBytes) throws IOException {
long toReturn = 0;
for (int i = 0; i < numBytes; i++) {
int valRead = inputStream.read();
if (valRead < 0) {
throw new RuntimeException();
}
toReturn = (toReturn << 8) | valRead;
}
return toReturn;
}
}
1 ответ
После того, как вы поделитесь своим кодом для доступа к сертификату, я смогу сохранить и проверить его.
Как уже было ясно из вопроса, проблемным расширением является альтернативное имя субъекта. Его значение указано (RFC 5280) как экземплярGeneralNames
который является SEQUENCE
из GeneralName
который является CHOICE
:
SubjectAltName ::= GeneralNames GeneralNames ::= SEQUENCE SIZE (1..MAX) OF GeneralName GeneralName ::= CHOICE { otherName [0] AnotherName, rfc822Name [1] IA5String, dNSName [2] IA5String, x400Address [3] ORAddress, directoryName [4] Name, ediPartyName [5] EDIPartyName, uniformResourceIdentifier [6] IA5String, iPAddress [7] OCTET STRING, registeredID [8] OBJECT IDENTIFIER }
Это дамп рассматриваемого расширения вашего сертификата:
<30 41>
676 65: . . . . SEQUENCE {
<06 03>
678 3: . . . . . OBJECT IDENTIFIER subjectAltName (2 5 29 17)
: . . . . . . (X.509 extension)
<04 3A>
683 58: . . . . . OCTET STRING, encapsulates {
<30 38>
685 56: . . . . . . SEQUENCE {
<86 36>
687 54: . . . . . . . [6]
: . . . . . . . . 'DNS:*.cetrel.lu, DNS:mail.cetrel.lu, DNS:www.cet'
: . . . . . . . . 'rel.lu'
: . . . . . . . }
: . . . . . . }
: . . . . . }
Значение помечено 6 ([6]
). Таким образомuniformResourceIdentifier
выбор используется. RFC 5280 в контексте альтернативных имен субъектов требует для значения этого выбора:
Имя НЕ ДОЛЖНО быть относительным URI, и оно ДОЛЖНО соответствовать синтаксису URI и правилам кодирования, указанным в [RFC3986]. Имя ДОЛЖНО включать как схему (например, "http" или "ftp"), так и специфичную для схемы часть.
Таким образом sun.security.x509.X509CertImpl
пытается проанализировать значение как URI. Это, очевидно, должно потерпеть неудачу, поскольку значение просто не является URI:
java.io.IOException: invalid URI name:DNS:*.cetrel.lu, DNS:mail.cetrel.lu, DNS:www.cetrel.lu
at sun.security.x509.URIName.<init>(URIName.java:109)
at sun.security.x509.URIName.<init>(URIName.java:96)
at sun.security.x509.GeneralName.<init>(GeneralName.java:122)
at sun.security.x509.GeneralName.<init>(GeneralName.java:76)
at sun.security.x509.GeneralNames.<init>(GeneralNames.java:68)
at sun.security.x509.SubjectAlternativeNameExtension.<init>(SubjectAlternativeNameExtension.java:141)
at sun.reflect.NativeConstructorAccessorImpl.newInstance0(Native Method)
at sun.reflect.NativeConstructorAccessorImpl.newInstance(NativeConstructorAccessorImpl.java:62)
at sun.reflect.DelegatingConstructorAccessorImpl.newInstance(DelegatingConstructorAccessorImpl.java:45)
at java.lang.reflect.Constructor.newInstance(Constructor.java:423)
at sun.security.x509.CertificateExtensions.parseExtension(CertificateExtensions.java:113)
at sun.security.x509.CertificateExtensions.init(CertificateExtensions.java:88)
at sun.security.x509.CertificateExtensions.<init>(CertificateExtensions.java:78)
at sun.security.x509.X509CertInfo.parse(X509CertInfo.java:702)
at sun.security.x509.X509CertInfo.<init>(X509CertInfo.java:167)
at sun.security.x509.X509CertImpl.parse(X509CertImpl.java:1804)
at sun.security.x509.X509CertImpl.<init>(X509CertImpl.java:195)
at sun.security.provider.X509Factory.engineGenerateCertificate(X509Factory.java:102)
at java.security.cert.CertificateFactory.generateCertificate(CertificateFactory.java:339)
Caused by: java.net.URISyntaxException: Illegal character in opaque part at index 16: DNS:*.cetrel.lu, DNS:mail.cetrel.lu, DNS:www.cetrel.lu
at java.net.URI$Parser.fail(URI.java:2848)
at java.net.URI$Parser.checkChars(URI.java:3021)
at java.net.URI$Parser.parse(URI.java:3058)
at java.net.URI.<init>(URI.java:588)
at sun.security.x509.URIName.<init>(URIName.java:107)
... 46 more
Если вы все же хотите получить доступ к значению, просто получите и проанализируйте его из UnparseableExtensions
карта у вас такая:
Map<String, Extension> unparseables = ((CertificateExtensions)((X509CertInfo)cert.get("x509.info")).get("extensions")).getUnparseableExtensions();
Extension extension = unparseables.get("2.5.29.17");
byte[] value = extension.getValue();
DerValue derValue = new DerValue(value);
while (derValue.data.available() > 0) {
DerValue encName = derValue.data.getDerValue();
if ((encName.tag & 0x1f) == 6) {
encName.resetTag(DerValue.tag_IA5String);
System.out.printf("IA5String value from URI GeneralName value: %s\n", encName.getIA5String());
}
}
за sun.security.x509.Extension
а также sun.security.util.DerValue
.
Выход:
IA5String value from URI GeneralName value: DNS:*.cetrel.lu, DNS:mail.cetrel.lu, DNS:www.cetrel.lu