Skip to content

Commit ad3f727

Browse files
committed
Add hostname validation for JSSTrustManager
The JSSTrustManager has been modified to take an optional hostname attribute. If provided, it will be used to validate the SAN extension or the cert subject DN.
1 parent 89a6e63 commit ad3f727

File tree

1 file changed

+98
-0
lines changed

1 file changed

+98
-0
lines changed

base/src/main/java/org/mozilla/jss/provider/javax/crypto/JSSTrustManager.java

+98
Original file line numberDiff line numberDiff line change
@@ -27,14 +27,26 @@
2727
import java.util.Arrays;
2828
import java.util.Collection;
2929
import java.util.Enumeration;
30+
import java.util.HashSet;
3031
import java.util.List;
32+
import java.util.Set;
3133

3234
import javax.net.ssl.X509TrustManager;
3335
import javax.security.auth.x500.X500Principal;
3436

3537
import org.mozilla.jss.CryptoManager;
3638
import org.mozilla.jss.NotInitializedException;
3739
import org.mozilla.jss.netscape.security.util.Cert;
40+
import org.mozilla.jss.netscape.security.x509.CertificateSubjectName;
41+
import org.mozilla.jss.netscape.security.x509.DNSName;
42+
import org.mozilla.jss.netscape.security.x509.GeneralName;
43+
import org.mozilla.jss.netscape.security.x509.GeneralNameInterface;
44+
import org.mozilla.jss.netscape.security.x509.GeneralNames;
45+
import org.mozilla.jss.netscape.security.x509.PKIXExtensions;
46+
import org.mozilla.jss.netscape.security.x509.SubjectAlternativeNameExtension;
47+
import org.mozilla.jss.netscape.security.x509.X500Name;
48+
import org.mozilla.jss.netscape.security.x509.X509CertImpl;
49+
import org.mozilla.jss.netscape.security.x509.X509CertInfo;
3850
import org.mozilla.jss.pkcs11.PK11Cert;
3951
import org.mozilla.jss.ssl.SSLCertificateApprovalCallback;
4052
import org.mozilla.jss.ssl.SSLCertificateApprovalCallback.ValidityItem;
@@ -49,9 +61,18 @@ public class JSSTrustManager implements X509TrustManager {
4961
public static final String SERVER_AUTH_OID = "1.3.6.1.5.5.7.3.1";
5062
public static final String CLIENT_AUTH_OID = "1.3.6.1.5.5.7.3.2";
5163

64+
private String hostname;
5265
private boolean allowMissingExtendedKeyUsage = false;
5366
private SSLCertificateApprovalCallback callback;
5467

68+
public String getHostname() {
69+
return hostname;
70+
}
71+
72+
public void setHostname(String hostname) {
73+
this.hostname = hostname;
74+
}
75+
5576
public void configureAllowMissingExtendedKeyUsage(boolean allow) {
5677
allowMissingExtendedKeyUsage = allow;
5778
}
@@ -64,6 +85,81 @@ public void setCallback(SSLCertificateApprovalCallback certCallback) {
6485
this.callback = certCallback;
6586
}
6687

88+
public boolean isValidSAN(SubjectAlternativeNameExtension sanExt) throws Exception {
89+
90+
logger.debug("JSSTrustManager: Checking hostname in SAN extension");
91+
92+
if (sanExt == null) {
93+
return false;
94+
}
95+
96+
GeneralNames generalNames = sanExt.getGeneralNames();
97+
Set<String> dnsNames = new HashSet<>();
98+
99+
for (GeneralNameInterface generalName : generalNames) {
100+
101+
if (generalName instanceof GeneralName) {
102+
generalName = ((GeneralName) generalName).unwrap();
103+
}
104+
105+
if (generalName instanceof DNSName) {
106+
String dnsName = ((DNSName) generalName).getValue();
107+
logger.debug("JSSTrustManager: - dns: " + dnsName);
108+
dnsNames.add(dnsName.toLowerCase());
109+
continue;
110+
}
111+
}
112+
113+
// TODO: add support for wildcards
114+
return dnsNames.contains(hostname);
115+
}
116+
117+
public boolean isValidSubject(CertificateSubjectName subject) throws Exception {
118+
119+
logger.debug("JSSTrustManager: Checking hostname in subject");
120+
121+
X500Name dn = (X500Name) subject.get(CertificateSubjectName.DN_NAME);
122+
List<String> cns = dn.getAttributesForOid(X500Name.commonName_oid);
123+
124+
if (cns == null) {
125+
return false;
126+
}
127+
128+
for (String cn : cns) {
129+
logger.debug("JSSTrustManager: - cn: " + cn);
130+
}
131+
132+
// TODO: add support for wildcards
133+
return cns.contains(hostname);
134+
}
135+
136+
public void checkHostname(X509Certificate[] certChain, ValidityStatus status) throws Exception {
137+
138+
if (hostname == null) {
139+
return;
140+
}
141+
142+
// validating hostname on leaf cert only
143+
X509Certificate leafCert = certChain[certChain.length - 1];
144+
int depth = 0;
145+
146+
X509CertImpl certImpl = new X509CertImpl(leafCert.getEncoded());
147+
SubjectAlternativeNameExtension sanExt = (SubjectAlternativeNameExtension) certImpl.getExtension(PKIXExtensions.SubjectAlternativeName_Id.toString());
148+
149+
if (isValidSAN(sanExt)) {
150+
return;
151+
}
152+
153+
X509CertInfo info = certImpl.getInfo();
154+
CertificateSubjectName subject = (CertificateSubjectName) info.get(X509CertInfo.SUBJECT);
155+
156+
if (isValidSubject(subject)) {
157+
return;
158+
}
159+
160+
status.addReason(ValidityStatus.BAD_CERT_DOMAIN, leafCert, depth);
161+
}
162+
67163
public void checkCertChain(X509Certificate[] certChain, String keyUsage) throws Exception {
68164

69165
logger.debug("JSSTrustManager: checkCertChain(" + keyUsage + ")");
@@ -113,6 +209,8 @@ public void checkCertChain(X509Certificate[] certChain, String keyUsage) throws
113209

114210
public void checkCertChain(X509Certificate[] certChain, String keyUsage, ValidityStatus status) throws Exception {
115211

212+
checkHostname(certChain, status);
213+
116214
if (!isTrustedPeer(certChain)) {
117215
checkIssuerTrusted(certChain, status);
118216
}

0 commit comments

Comments
 (0)