We upgraded from Tomcat 9.0.60 to 9.0.62 and the Mutual-TLS failed. Logging from Tomcat 9.0.60 (M-TLS Works) 01 org.apache.catalina.authenticator.AuthenticatorBase.invoke Security checking request POST /speer/soap/services/somefunctionality 02 org.apache.catalina.realm.RealmBase.findSecurityConstraints Checking constraint 'SecurityConstraint[MijnvfRealm]' against POST /services/somefunctionality --> true 03 org.apache.catalina.realm.RealmBase.findSecurityConstraints Checking constraint 'SecurityConstraint[MijnvfRealm]' against POST /services/somefunctionality --> true 04 org.apache.catalina.authenticator.AuthenticatorBase.invoke Calling hasUserDataPermission() 05 org.apache.catalina.realm.RealmBase.hasUserDataPermission User data constraint already satisfied 06 org.apache.catalina.authenticator.AuthenticatorBase.invoke Calling authenticate() 07 org.apache.catalina.realm.CombinedRealm.authenticate Attempting to authenticate user [CN=cn, O=o, L=l, ST=st, C=c, SERIALNUMBER=00000001804415183000] with realm [org.apache.catalina.realm.UserDatabaseRealm] 08 org.apache.catalina.realm.RealmBase.authenticate Authenticating client certificate chain 09 org.apache.catalina.realm.RealmBase.authenticate Checking validity for 'CN=cn, O=o, L=l, ST=st, C=c, SERIALNUMBER=00000001804415183000' 10 org.apache.catalina.realm.RealmBase.authenticate Checking validity for 'CN=cnCA, O=o, C=c' 11 org.apache.catalina.realm.RealmBase.getPrincipal Got user name from X509 certificate: [CN=cn, O=o, L=l, ST=st, C=c, SERIALNUMBER=00000001804415183000] 12 org.apache.catalina.realm.CombinedRealm.authenticate Authenticated user [CN=cn, O=o, L=l, ST=st, C=c, SERIALNUMBER=00000001804415183000] with realm [org.apache.catalina.realm.UserDatabaseRealm] 13 org.apache.catalina.authenticator.AuthenticatorBase.register Authenticated 'CN=cn, O=o, L=l, ST=st, C=c, SERIALNUMBER=00000001804415183000' with type 'CLIENT_CERT' 14 org.apache.catalina.authenticator.AuthenticatorBase.invoke Calling accessControl() 15 org.apache.catalina.realm.RealmBase.hasResourcePermission Checking roles GenericPrincipal[CN=cn, O=o, L=l, ST=st, C=c, SERIALNUMBER=00000001804415183000()] 16 org.apache.catalina.realm.RealmBase.hasRole Username [CN=cn, O=o, L=l, ST=st, C=c, SERIALNUMBER=00000001804415183000] has role [correctUser] 17 org.apache.catalina.realm.RealmBase.hasResourcePermission Role found: correctUser 18 org.apache.catalina.authenticator.AuthenticatorBase.invoke Successfully passed all security constraints Logging from Tomcat 9.0.62 (M-TLS fails, no/wrong user found) 01 org.apache.catalina.authenticator.AuthenticatorBase.invoke Security checking request POST /speer/soap/services/somefunctionality 02 org.apache.catalina.realm.RealmBase.findSecurityConstraints Checking constraint 'SecurityConstraint[MijnvfRealm]' against POST /services/somefunctionality --> true 03 org.apache.catalina.realm.RealmBase.findSecurityConstraints Checking constraint 'SecurityConstraint[MijnvfRealm]' against POST /services/somefunctionality --> true 04 org.apache.catalina.authenticator.AuthenticatorBase.invoke Calling hasUserDataPermission() 05 org.apache.catalina.realm.RealmBase.hasUserDataPermission User data constraint already satisfied 06 org.apache.catalina.authenticator.AuthenticatorBase.invoke Calling authenticate() 07 org.apache.catalina.realm.CombinedRealm.authenticate Attempting to authenticate user [CN=cn, O=o, L=l, ST=st, C=c, SERIALNUMBER=00000001804415183000] with realm [org.apache.catalina.realm.UserDatabaseRealm] 08 org.apache.catalina.realm.RealmBase.authenticate Authenticating client certificate chain 09 org.apache.catalina.realm.RealmBase.authenticate Checking validity for 'CN=cn, O=o, L=l, ST=st, C=c, SERIALNUMBER=00000001804415183000' 10 org.apache.catalina.realm.RealmBase.authenticate Checking validity for 'CN=cnCA, O=o, C=c' 11 org.apache.catalina.realm.RealmBase.getPrincipal Got user name from X509 certificate: [CN=cn, O=o, L=l, ST=st, C=c, OID.2.5.4.5=00000001804415183000] 12 org.apache.catalina.realm.CombinedRealm.authenticate Failed to authenticate user [CN=cn, O=o, L=l, ST=st, C=c, SERIALNUMBER=00000001804415183000] with realm [org.apache.catalina.realm.UserDatabaseRealm] 13 org.apache.catalina.authenticator.AuthenticatorBase.invoke Failed authenticate() test If you look at line 11, in both logging you can see that "OID.2.5.4.5" is used in Tomcat 9.0.62 and "SERIALNUMBER" in Tomcat 9.0.60. While in all other instances "SERIALNUMBER" is used. Because of this correct user can not be found. Possible workaround: is to add the "OID.2.5.4.5" version also to the "tomcat-users.xml" file (not tested yet, but I expect id to work). We are running Tomcat in Docker and are using the "tomcat:9-jdk11" container as base image. When we reverted to the container using Tomcat 9.0.60 it worked again. Possible suspect is release 9.0.61, and the change in Coyote, https://tomcat.apache.org/tomcat-9.0-doc/changelog.html
(In reply to Maikel from comment #0) > Possible suspect is release 9.0.61, and the change in Coyote, > https://tomcat.apache.org/tomcat-9.0-doc/changelog.html I recommend you investigate this further. The relevant change is not in the changelog I think: https://github.com/apache/tomcat/commit/38d2c138a102a793bce630056fbca7088b7e05a3 https://github.com/apache/tomcat/commit/b21268dcebc3d470430227978caa4f168a3346d4 Is this really equivalent in all cases ?
Although, I haven't analyzed recent changes, the problem you see is different representations of the ASN.1 encoded subject DN. Here (https://github.com/apache/tomcat/blob/431f08b66e27411decb52e1333dd886cc181a854/java/org/apache/catalina/realm/RealmBase.java#L454-L455) it uses https://docs.oracle.com/javase/8/docs/api/javax/security/cert/X509Certificate.html#getIssuerDN-- which does not describe the format which is applied, but X509SubjectDnRetriever uses RFC 1779 (https://github.com/apache/tomcat/blob/431f08b66e27411decb52e1333dd886cc181a854/java/org/apache/catalina/realm/X509SubjectDnRetriever.java#L31) which is totally outdated. Moreover, depending on the X.500 Principal format you select Java maintains an internal map which OIDs can be reasonably mapped from ASN.1 to a string. Especially 2.5.4.5 is a total mess. I have a certificate processing application at work where I apply a custom formatting to properly canonicalize RFC 2253 formatted output with all possible OIDs Java will not map by default. I assume the codebase in Tomcat needs to be analyzed and apply similar. (My custom approach bases on the way OpenSSL handles DNs)
(In reply to Remy Maucherat from comment #1) > (In reply to Maikel from comment #0) > > Possible suspect is release 9.0.61, and the change in Coyote, > > https://tomcat.apache.org/tomcat-9.0-doc/changelog.html > > I recommend you investigate this further. > > The relevant change is not in the changelog I think: > https://github.com/apache/tomcat/commit/38d2c138a102a793bce630056fbca7088b7e05a3 > https://github.com/apache/tomcat/commit/b21268dcebc3d470430227978caa4f168a3346d4 > Is this really equivalent in all cases ? To help investigate this: Note that an implementation of org.apache.catalina.realm.X509UsernameRetriever that was changed by those commits is configurable, with "X509UsernameRetrieverClassName" attribute on a Realm. https://tomcat.apache.org/tomcat-9.0-doc/config/realm.html So that you can configure your own.
Thanks for the comments. Overall I would need a test certificate to see what each different method does. I don't know why getName(X500Principal.RFC1779) was used in X509UsernameRetriever instead of getName(X500Principal.RFC2253) or simply getName() (which simply uses X500Principal.RFC2253). Alternately, you can try to test by using X509UsernameRetrieverClassName as Konstantin said (thanks, great tip !).
Thanks for the information, I did not know I could use X509UsernameRetrieverClassName to change the behavior. We where using the certificate functionality out of the box with only some changes in the config files. I now use the workaround by adding the "OID.2.5.4.5" version also in the "tomcat-users.xml" file. That works.
(In reply to Remy Maucherat from comment #1) > https://github.com/apache/tomcat/commit/ > b21268dcebc3d470430227978caa4f168a3346d4 My guess is that the above patch will fix this issue. Can you please provide a copy of the certificate and we can double-check the behavior of getSubjectDN() vs getX500Principal().getName() vs getX500Principal().getName(X500Principal.RFC1779)? Alternatively, we could provide you with a simple Java utility to look at the cert and print those values.
Actually, this ought to do the trick: import java.security.cert.Certificate; import java.security.cert.CertificateFactory; import java.security.cert.X509Certificate; import javax.security.auth.x500.X500Principal; public class CertInfo { public static void main(String[] args) throws Exception { CertificateFactory cf = CertificateFactory.getInstance("X.509"); Certificate cert = cf.generateCertificate(System.in); if(cert instanceof X509Certificate) { System.out.println("Certificate is X.509"); X509Certificate xc = (X509Certificate)cert; System.out.println("getSubjectDN: " + xc.getSubjectDN()); System.out.println("getSubjectX500Principal.getName: " + xc.getSubjectX500Principal().getName()); System.out.println("getSubjectX500Principal.getName(RFC1779): " + xc.getSubjectX500Principal().getName(X500Principal.RFC1779)); } } }
Using your test, I can see that getName(RFC1779) formatting matches getSubjectDN, and RFC2253 does not. Now, with a certificate with more stuff inside, I get something where getSubjectDN returns EMAILADDRESS= and all the getName ones replace that with OID.1.2.840.113549.1.9.1=, which reproduces the bug. Replacing getSubjectDN with getSubjectX500Principal is simply not equivalent, so resolving the deprecation will require more effort (see comment 2 ;) ).
However getSubjectX500Principal().toString() returns the same result as getSubjectDN().getName() (I did look at the JVM code to find options ;) ), so I will revert to using that since the idea was not to changes things.
The fix will be in Tomcat 10.1.0-M15, 10.0.21, 9.0.63, 8.5.79.
Thanks for the quick reply and fixing the issue.