bcgit / bc-java

Bouncy Castle Java Distribution (Mirror)
https://www.bouncycastle.org/java.html
MIT License
2.32k stars 1.14k forks source link

IllegalArgumentException: Unknown object id - DNQ - passed to distinguished name #1622

Open ralfhauser opened 7 months ago

ralfhauser commented 7 months ago

A qualified Italian QES cert seems to throw this error

java.lang.IllegalArgumentException: Unknown object id - DNQ - passed to distinguished name
        at org.bouncycastle.asn1.x500.style.IETFUtils.decodeAttrName(Unknown Source)
        at org.bouncycastle.asn1.x500.style.BCStyle.attrNameToOID(Unknown Source)
        at org.bouncycastle.asn1.x500.style.IETFUtils.rDNsFromString(Unknown Source)
        at org.bouncycastle.asn1.x500.style.BCStyle.fromString(Unknown Source)

dnq.pem.txt

    at org.bouncycastle.asn1.x500.X500Name.<init>(Unknown Source)
    at org.bouncycastle.asn1.x500.X500Name.<init>(Unknown Source)`

Are they wrong ?


Edited for clarity by @cipherboy.

cipherboy commented 7 months ago

@ralfhauser Can you give sample code of what you're attempting to do?

It appears our code expects the identifier dnqualifier rather than dnq for this value, if you're using the RFC 4519 class rather than the BCStyle class.

This matches OpenSSL's output for this certificate:

$ openssl x509 -in ~/Downloads/dnq.pem.txt -text
Certificate:
    Data:
        Version: 3 (0x2)
        Serial Number: 4112325213882364794 (0x3911e9fe7657337a)
        Signature Algorithm: sha256WithRSAEncryption
        Issuer: C = IT, O = Namirial S.p.A./02046570426, OU = Certification Authority, CN = Namirial CA Firma Qualificata
        Validity
            Not Before: Dec 19 10:35:00 2022 GMT
            Not After : Dec 17 23:00:00 2025 GMT
        Subject: C = IT, SN = VINATTIERI, GN = GIACOMO, serialNumber = TINIT-VNTGCM75A21D612F, CN = VINATTIERI GIACOMO, dnQualifier = LOVG2022121631199701

... snip ...

However, it does appear to be missing from our BCStyle class... so it could be that it might not work with BCStyle and dnQualifier either, in which case we can fix it. :-)

Are you aware of a reference calling this dnq over dnqualifier?

ralfhauser commented 7 months ago

Hi Alexander, Thanks for the quick reply, I just do X500Principal java.security.cert.X509Certificate.getSubjectX500Principal() an then I get the IllegalArgumentException

cipherboy commented 7 months ago

@ralfhauser Interesting... What version of BC are you running?

I get the following:

Code ```java import java.io.ByteArrayInputStream; import java.io.StringReader; import java.security.Security; import java.security.cert.Certificate; import java.security.cert.CertificateFactory; import java.security.cert.X509Certificate; import javax.security.auth.x500.X500Principal; import org.bouncycastle.asn1.x500.X500Name; import org.bouncycastle.cert.X509CertificateHolder; import org.bouncycastle.openssl.PEMParser; import org.bouncycastle.jce.provider.BouncyCastleProvider; public class Main { public static void main(String[] args) throws Exception { Security.addProvider(new BouncyCastleProvider()); StringReader sr = new StringReader("-----BEGIN CERTIFICATE-----\nMIIF+jCCBOKgAwIBAgIIORHp/nZXM3owDQYJKoZIhvcNAQELBQAwfTELMAkGA1UE\nBhMCSVQxJDAiBgNVBAoMG05hbWlyaWFsIFMucC5BLi8wMjA0NjU3MDQyNjEgMB4G\nA1UECwwXQ2VydGlmaWNhdGlvbiBBdXRob3JpdHkxJjAkBgNVBAMMHU5hbWlyaWFs\nIENBIEZpcm1hIFF1YWxpZmljYXRhMB4XDTIyMTIxOTEwMzUwMFoXDTI1MTIxNzIz\nMDAwMFowgZExCzAJBgNVBAYTAklUMRMwEQYDVQQEDApWSU5BVFRJRVJJMRAwDgYD\nVQQqDAdHSUFDT01PMR8wHQYDVQQFExZUSU5JVC1WTlRHQ003NUEyMUQ2MTJGMRsw\nGQYDVQQDDBJWSU5BVFRJRVJJIEdJQUNPTU8xHTAbBgNVBC4TFExPVkcyMDIyMTIx\nNjMxMTk5NzAxMIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEAhcdxxuHh\nxhimZ5OWvGS1JPiDAxzwUr1f5TMEyzEdr0MqkG+DCcl0dmLhChya9j4WxaSgVkDP\n+YF8p+Ki/bRaQo/7UrA6Jr3q1Q3IPTrk2py5dsrhh9BlYjCBnNvkpZFqJyrPjn0B\ncyr3AgPslpqoMr6Hwy8z1OjqbpI24rv14XpIOfYBDb7ZO/mcVWEKR99Szbo+zI5b\n1k0mfNx9YErOiNoNCnzVz/n5cKKTSK8j/MQfznALyTALJNvoMVgf+iUVYOnDtloT\nWnnE7ro2KmHxjhrn0Sy7mLDe6LiE7rdlsZ35H5KuPJ8JHKaxbliu6ZHl4vyn5N+j\nc/Y6yw2OhybD/QIDAQABo4ICZzCCAmMwgZwGCCsGAQUFBwEBBIGPMIGMMFEGCCsG\nAQUFBzAChkVodHRwczovL2RvY3MubmFtaXJpYWx0c3AuY29tL2RvY3VtZW50cy9O\nYW1pcmlhbENBRmlybWFRdWFsaWZpY2F0YS5jcnQwNwYIKwYBBQUHMAGGK2h0dHA6\nLy9vY3NwLm5hbWlyaWFsdHNwLmNvbS9vY3NwL2NlcnRzdGF0dXMwHQYDVR0OBBYE\nFMT24uLpEFwHzv+KAGTrpaBG47OdMB8GA1UdIwQYMBaAFGP97eaMYkdIz+oJQXN2\nEeJkYnsQMIHNBggrBgEFBQcBAwSBwDCBvTAIBgYEAI5GAQEwCwYGBACORgEDAgEU\nMAgGBgQAjkYBBDATBgYEAI5GAQYwCQYHBACORgEGATCBhAYGBACORgEFMHowOxY1\naHR0cHM6Ly9kb2NzLm5hbWlyaWFsdHNwLmNvbS9kb2N1bWVudHMvUERTL1BEU19l\nbi5wZGYTAmVuMDsWNWh0dHBzOi8vZG9jcy5uYW1pcmlhbHRzcC5jb20vZG9jdW1l\nbnRzL1BEUy9QRFNfaXQucGRmEwJpdDBaBgNVHSAEUzBRMAkGBwQAi+xAAQIwOgYL\nKwYBBAGCmmsBAQIwKzApBggrBgEFBQcCARYdaHR0cHM6Ly9kb2NzLm5hbWlyaWFs\ndHNwLmNvbS8wCAYGBACPegECMEYGA1UdHwQ/MD0wO6A5oDeGNWh0dHA6Ly9jcmwu\nbmFtaXJpYWx0c3AuY29tL0Zpcm1hQ2VydGFRdWFsaWZpY2F0YTEuY3JsMA4GA1Ud\nDwEB/wQEAwIGQDANBgkqhkiG9w0BAQsFAAOCAQEASrkvsTg+7AJCvU9+KYqnjrcl\n5L8VZhKrDWFHAeQOGK+vBoRuDXqmrmuaSaiJbKGY6A7VL4SSV1SeJYAIKR4FdyDR\nr6XH5Hcw5b8k5IjcFU9a5oUMbRv7ie2A6vluRoHV9njDcBasKoAs3a/iYtptq2t3\n0TEXdadj8NPlCYZsZaptBUhIZGnpKmYye8UP/g98Sy955PxEhMiqfL53iwp4FdBJ\n/Asx0acMTwVB1sok+LxNAnLAufhj6xvMaXPLoJ8E23OlrgOle9baRKhKO+TfNamw\nton3fJUp2/dvGc5GEvp9gEN0sQSksPLd3y8ZePsauWWLkO0H89ykEnnKeUYOUA==\n-----END CERTIFICATE-----\n"); PEMParser p = new PEMParser(sr); Object o = p.readObject(); System.out.println("Got: " + o); X509CertificateHolder bcCert = (X509CertificateHolder)o; System.out.println("BC Cert: " + bcCert); X500Name bcSubj = bcCert.getSubject(); System.out.println("BC Subject: " + bcSubj); byte[] data = bcCert.getEncoded(); CertificateFactory cf = CertificateFactory.getInstance("X509", "BC"); Certificate jCert = cf.generateCertificate(new ByteArrayInputStream(data)); System.out.println("Java Cert:" + jCert); X509Certificate xCert = (X509Certificate)jCert; System.out.println("X509 Java Cert:" + xCert); X500Principal xPrinc = xCert.getSubjectX500Principal(); System.out.println("X500 Principal: " + xPrinc); } } ```
Terminal Output ``` Got: org.bouncycastle.cert.X509CertificateHolder@9b27e087 BC Cert: org.bouncycastle.cert.X509CertificateHolder@9b27e087 BC Subject: C=IT,SURNAME=VINATTIERI,GIVENNAME=GIACOMO,SERIALNUMBER=TINIT-VNTGCM75A21D612F,CN=VINATTIERI GIACOMO,DN=LOVG2022121631199701 Java Cert: [0] Version: 3 SerialNumber: 4112325213882364794 IssuerDN: C=IT,O=Namirial S.p.A./02046570426,OU=Certification Authority,CN=Namirial CA Firma Qualificata Start Date: Mon Dec 19 05:35:00 EST 2022 Final Date: Wed Dec 17 18:00:00 EST 2025 SubjectDN: C=IT,SURNAME=VINATTIERI,GIVENNAME=GIACOMO,SERIALNUMBER=TINIT-VNTGCM75A21D612F,CN=VINATTIERI GIACOMO,DN=LOVG2022121631199701 Public Key: RSA Public Key [64:4c:b7:6e:53:05:e9:0a:11:1e:33:72:e2:06:18:79:84:72:e5:b2],[56:66:d1:a4] modulus: 85c771c6e1e1c618a6679396bc64b524f883031cf052bd5fe53304cb311daf432a906f8309c9747662e10a1c9af63e16c5a4a05640cff9817ca7e2a2fdb45a428ffb52b03a26bdead50dc83d3ae4da9cb976cae187d0656230819cdbe4a5916a272acf8e7d01732af70203ec969aa832be87c32f33d4e8ea6e9236e2bbf5e17a4839f6010dbed93bf99c55610a47df52cdba3ecc8e5bd64d267cdc7d604ace88da0d0a7cd5cff9f970a29348af23fcc41fce700bc9300b24dbe831581ffa251560e9c3b65a135a79c4eeba362a61f18e1ae7d12cbb98b0dee8b884eeb765b19df91f92ae3c9f091ca6b16e58aee991e5e2fca7e4dfa373f63acb0d8e8726c3fd public exponent: 10001 Signature Algorithm: SHA256WITHRSA Signature: 4ab92fb1383eec0242bd4f7e298aa78eb725e4bf 156612ab0d614701e40e18afaf06846e0d7aa6ae 6b9a49a8896ca198e80ed52f849257549e258008 291e057720d1afa5c7e47730e5bf24e488dc154f 5ae6850c6d1bfb89ed80eaf96e4681d5f678c370 16ac2a802cddafe262da6dab6b77d1311775a763 f0d3e509866c65aa6d0548486469e92a66327bc5 0ffe0f7c4b2f79e4fc4484c8aa7cbe778b0a7815 d049fc0b31d1a70c4f0541d6ca24f8bc4d0272c0 b9f863eb1bcc6973cba09f04db73a5ae03a57bd6 da44a84a3be4df35a9b0b689f77c9529dbf76f19 ce4612fa7d804374b104a4b0f2dddf2f1978fb1a b9658b90ed07f3dca41279ca79460e50 Extensions: critical(false) 1.3.6.1.5.5.7.1.1 value = Sequence Sequence ObjectIdentifier(1.3.6.1.5.5.7.48.2) Tagged [CONTEXT 6] IMPLICIT DER Octet String[69] Sequence ObjectIdentifier(1.3.6.1.5.5.7.48.1) Tagged [CONTEXT 6] IMPLICIT DER Octet String[43] critical(false) 2.5.29.14 value = DER Octet String[20] critical(false) 2.5.29.35 value = Sequence Tagged [CONTEXT 0] IMPLICIT DER Octet String[20] critical(false) 1.3.6.1.5.5.7.1.3 value = Sequence Sequence ObjectIdentifier(0.4.0.1862.1.1) Sequence ObjectIdentifier(0.4.0.1862.1.3) Integer(20) Sequence ObjectIdentifier(0.4.0.1862.1.4) Sequence ObjectIdentifier(0.4.0.1862.1.6) Sequence ObjectIdentifier(0.4.0.1862.1.6.1) Sequence ObjectIdentifier(0.4.0.1862.1.5) Sequence Sequence IA5String(https://docs.namirialtsp.com/documents/PDS/PDS_en.pdf) PrintableString(en) Sequence IA5String(https://docs.namirialtsp.com/documents/PDS/PDS_it.pdf) PrintableString(it) critical(false) 2.5.29.32 value = Sequence Sequence ObjectIdentifier(0.4.0.194112.1.2) Sequence ObjectIdentifier(1.3.6.1.4.1.36203.1.1.2) Sequence Sequence ObjectIdentifier(1.3.6.1.5.5.7.2.1) IA5String(https://docs.namirialtsp.com/) Sequence ObjectIdentifier(0.4.0.2042.1.2) critical(false) 2.5.29.31 value = Sequence Sequence Tagged [CONTEXT 0] Tagged [CONTEXT 0] Tagged [CONTEXT 6] IMPLICIT DER Octet String[53] critical(true) KeyUsage: 0x40 X509 Java Cert: [0] Version: 3 SerialNumber: 4112325213882364794 IssuerDN: C=IT,O=Namirial S.p.A./02046570426,OU=Certification Authority,CN=Namirial CA Firma Qualificata Start Date: Mon Dec 19 05:35:00 EST 2022 Final Date: Wed Dec 17 18:00:00 EST 2025 SubjectDN: C=IT,SURNAME=VINATTIERI,GIVENNAME=GIACOMO,SERIALNUMBER=TINIT-VNTGCM75A21D612F,CN=VINATTIERI GIACOMO,DN=LOVG2022121631199701 Public Key: RSA Public Key [64:4c:b7:6e:53:05:e9:0a:11:1e:33:72:e2:06:18:79:84:72:e5:b2],[56:66:d1:a4] modulus: 85c771c6e1e1c618a6679396bc64b524f883031cf052bd5fe53304cb311daf432a906f8309c9747662e10a1c9af63e16c5a4a05640cff9817ca7e2a2fdb45a428ffb52b03a26bdead50dc83d3ae4da9cb976cae187d0656230819cdbe4a5916a272acf8e7d01732af70203ec969aa832be87c32f33d4e8ea6e9236e2bbf5e17a4839f6010dbed93bf99c55610a47df52cdba3ecc8e5bd64d267cdc7d604ace88da0d0a7cd5cff9f970a29348af23fcc41fce700bc9300b24dbe831581ffa251560e9c3b65a135a79c4eeba362a61f18e1ae7d12cbb98b0dee8b884eeb765b19df91f92ae3c9f091ca6b16e58aee991e5e2fca7e4dfa373f63acb0d8e8726c3fd public exponent: 10001 Signature Algorithm: SHA256WITHRSA Signature: 4ab92fb1383eec0242bd4f7e298aa78eb725e4bf 156612ab0d614701e40e18afaf06846e0d7aa6ae 6b9a49a8896ca198e80ed52f849257549e258008 291e057720d1afa5c7e47730e5bf24e488dc154f 5ae6850c6d1bfb89ed80eaf96e4681d5f678c370 16ac2a802cddafe262da6dab6b77d1311775a763 f0d3e509866c65aa6d0548486469e92a66327bc5 0ffe0f7c4b2f79e4fc4484c8aa7cbe778b0a7815 d049fc0b31d1a70c4f0541d6ca24f8bc4d0272c0 b9f863eb1bcc6973cba09f04db73a5ae03a57bd6 da44a84a3be4df35a9b0b689f77c9529dbf76f19 ce4612fa7d804374b104a4b0f2dddf2f1978fb1a b9658b90ed07f3dca41279ca79460e50 Extensions: critical(false) 1.3.6.1.5.5.7.1.1 value = Sequence Sequence ObjectIdentifier(1.3.6.1.5.5.7.48.2) Tagged [CONTEXT 6] IMPLICIT DER Octet String[69] Sequence ObjectIdentifier(1.3.6.1.5.5.7.48.1) Tagged [CONTEXT 6] IMPLICIT DER Octet String[43] critical(false) 2.5.29.14 value = DER Octet String[20] critical(false) 2.5.29.35 value = Sequence Tagged [CONTEXT 0] IMPLICIT DER Octet String[20] critical(false) 1.3.6.1.5.5.7.1.3 value = Sequence Sequence ObjectIdentifier(0.4.0.1862.1.1) Sequence ObjectIdentifier(0.4.0.1862.1.3) Integer(20) Sequence ObjectIdentifier(0.4.0.1862.1.4) Sequence ObjectIdentifier(0.4.0.1862.1.6) Sequence ObjectIdentifier(0.4.0.1862.1.6.1) Sequence ObjectIdentifier(0.4.0.1862.1.5) Sequence Sequence IA5String(https://docs.namirialtsp.com/documents/PDS/PDS_en.pdf) PrintableString(en) Sequence IA5String(https://docs.namirialtsp.com/documents/PDS/PDS_it.pdf) PrintableString(it) critical(false) 2.5.29.32 value = Sequence Sequence ObjectIdentifier(0.4.0.194112.1.2) Sequence ObjectIdentifier(1.3.6.1.4.1.36203.1.1.2) Sequence Sequence ObjectIdentifier(1.3.6.1.5.5.7.2.1) IA5String(https://docs.namirialtsp.com/) Sequence ObjectIdentifier(0.4.0.2042.1.2) critical(false) 2.5.29.31 value = Sequence Sequence Tagged [CONTEXT 0] Tagged [CONTEXT 0] Tagged [CONTEXT 6] IMPLICIT DER Octet String[53] critical(true) KeyUsage: 0x40 X500 Principal: DNQ=LOVG2022121631199701, CN=VINATTIERI GIACOMO, SERIALNUMBER=TINIT-VNTGCM75A21D612F, GIVENNAME=GIACOMO, SURNAME=VINATTIERI, C=IT ```

but I will admit I'm on the as-of-yet unreleased BC 1.78. However, it doesn't look like this code has changed in a while. I went through PEMParser because I wanted to see what the internal X509CertificateHolder would output, which seems OK, but then went back to DER for the CertificateFactory parsing to get the java.security.cert.X509Certificate from the BC provider.

ralfhauser commented 7 months ago

bc*-jdk18on-171.jar

ralfhauser commented 7 months ago

if you add one line to your code, it is reproducible

`... System.out.println("X500 Principal: " + xPrinc);

X500Name subDN = new X500Name(xPrinc.toString());`

cipherboy commented 7 months ago

@ralfhauser Have you seen the alternative X500Name constructor?

public X500Name(X500NameStyle style,
                java.lang.String dirName)

This would let you parse DQNs directly in this format:

https://github.com/bcgit/bc-java/blob/5b1360854d85fd27b75720015be68f9e172db013/core/src/test/java/org/bouncycastle/asn1/test/X500NameTest.java#L60

https://github.com/bcgit/bc-java/blob/5b1360854d85fd27b75720015be68f9e172db013/core/src/test/java/org/bouncycastle/asn1/test/X500NameTest.java#L693-L703

https://github.com/bcgit/bc-java/blob/5b1360854d85fd27b75720015be68f9e172db013/core/src/test/java/org/bouncycastle/asn1/test/X500NameTest.java#L184-L187

If you're willing to modify your application code, I think this will work the best, and follows what was originally intended (w.r.t. custom names for OIDs). Let me know what you think!

ralfhauser commented 7 months ago

Why can't the simple constructor not handle this ? Or why is this DNQStyle only in the test class and not in X500Name.java as

private static X500NameStyle defaultStyle = DNQStyle.INSTANCE; //BCStyle.INSTANCE;

as it extends BCStyle , it seems to backward compatible (except for the IllegalArgumentException probably almost nobody is keen on seeing ?)

cipherboy commented 7 months ago

@ralfhauser Perhaps @dghgit can weigh in...

My understanding is a style class allows for overriding our understanding/parsing of string attributes into proper RDN sequences. You might prefer DNQ= in the string form, others might prefer dnQualifier=, which the default aligns with the RFC4519 style guide. Others still might want to parse completely custom OIDs (e.g., my favorite 1.2.3.4.5.6.7.8.9 should be parsed as favoriteSport=!). With a broad enough set of OIDs, you're bound to get overlap names to multiple OIDs (see e.g., uid=: is this a 2.5.4.45 or a 0.9.2342.19200300.100.1.1? Probably depends on the context!).

A custom style parser gives you the flexibility to set the string form+identifiers of RDN attributes you want to see/understand/parse (and extending BCStyle should hopefully give you), while letting others set the forms of attributes they want to see, without stepping on too many toes along the way. :-)

My 2c -- but this was likely written for exactly this type of extensibility.

Whether or not we want to add it to the default constructor (and if we'll add it as dnq vs dnqualifier) probably depends on popularity of this attribute. Given existing RFCs state it should be called dnQualifier, I'm guessing its relatively unlikely we'll parse it as dnq, and may be unlikely we'll add dnQualifier either given it exists in the RFC4519Style class... But I will defer to @dghgit here again.

dghgit commented 7 months ago

Hi @ralfhauser, has been a while since this has come up.

So, the issue is it's not that anyone's right or wrong, and this isn't the first time we've found ourselves and Sun/Oracle using different symbols, IBM for example tend to treat this one as DNQUALIFIER, in our case it's always been "DN" (looking at RFC 4519 I have to admit IBM's probably the closest to correct, although we actually added the attribute before RFC 4519 came out, I'd guess based on either what OpenSSL was using at the time, or what Sun was using at the time, it was around 2005 though so while I was around, I'm afraid what and who in regards to this one are well lost in time).

What I've found in general is that avoiding converting directory names to strings until you actually have to hand one over to a human is the best idea, as the alternative is a path to madness.

The following will allow you to convert the directory name without issue:

    X509Certificate cer = (X509Certificate)certFact.generateCertificate(new FileInputStream("dnq.pem"));

    X500Name x500Name = X500Name.getInstance(cer.getSubjectX500Principal().getEncoded());

    System.err.println(x500Name);

Now, it you'd like that to print just as the Oracle one does, there's a choice between using the X500NameStyle class as @cipherboy suggests (which can be useful if you like life with X500Name) or simply doing the reverse at output time, i.e:

   System.err.println(new X500Principal(x500Name.getEncoded()));

It all depends how you want to play it. The one thing you can be confident of whatever decision you make you'll almost certainly run into someone that wants it to be done differently so I would recommend allowing for flexibility.

mikakoivisto commented 2 months ago

I just started running into the same issue. It seems it's Java (Amazon Corretto 17) that formats it as DNQ. We recently switched to getting the subject name from X509Certificate.getSubjectX500Principal().toString() and that produces the DNQ which then later fails as it is parse it with new X500Name(subject as string)

mikakoivisto commented 2 months ago

Fixed it by using X500Name.getInstance(cert.getSubjectX500Principal().getEncoded())