33import java .io .ByteArrayInputStream ;
44import java .io .IOException ;
55import java .net .InetAddress ;
6+ import java .net .UnknownHostException ;
67import java .security .cert .CertificateFactory ;
78import java .security .cert .X509Certificate ;
89import java .util .Collection ;
1314import java .util .logging .Logger ;
1415
1516import javax .net .ssl .SSLSession ;
17+ import javax .security .auth .x500 .X500Principal ;
1618
19+ import org .bouncycastle .asn1 .ASN1Primitive ;
20+ import org .bouncycastle .asn1 .ASN1String ;
1721import org .bouncycastle .asn1 .x500 .AttributeTypeAndValue ;
1822import org .bouncycastle .asn1 .x500 .RDN ;
1923import org .bouncycastle .asn1 .x500 .X500Name ;
2024import org .bouncycastle .asn1 .x500 .style .BCStyle ;
25+ import org .bouncycastle .asn1 .x509 .GeneralName ;
2126import org .bouncycastle .est .ESTException ;
27+ import org .bouncycastle .util .IPAddress ;
2228import org .bouncycastle .util .Strings ;
2329import org .bouncycastle .util .encoders .Hex ;
2430
@@ -77,6 +83,17 @@ public boolean verified(String name, SSLSession context)
7783 public boolean verify (String name , X509Certificate cert )
7884 throws IOException
7985 {
86+ if (name == null )
87+ {
88+ throw new NullPointerException ("'name' cannot be null" );
89+ }
90+
91+ boolean foundAnyDNSNames = false ;
92+
93+ boolean nameIsIPv4 = IPAddress .isValidIPv4 (name );
94+ boolean nameIsIPv6 = !nameIsIPv4 && IPAddress .isValidIPv6 (name );
95+ boolean nameIsIPAddress = nameIsIPv4 || nameIsIPv6 ;
96+
8097 //
8198 // Test against san.
8299 //
@@ -85,25 +102,59 @@ public boolean verify(String name, X509Certificate cert)
85102 Collection n = cert .getSubjectAlternativeNames ();
86103 if (n != null )
87104 {
105+ InetAddress nameInetAddress = null ;
106+
88107 for (Iterator it = n .iterator (); it .hasNext ();)
89108 {
90109 List l = (List )it .next ();
91- int type = ((Number )l .get (0 )).intValue ();
110+ int type = ((Integer )l .get (0 )).intValue ();
92111 switch (type )
93112 {
94- case 2 :
95- if (isValidNameMatch (name , l .get (1 ).toString (), knownSuffixes ))
113+ case GeneralName .dNSName :
114+ {
115+ if (!nameIsIPAddress &&
116+ isValidNameMatch (name , (String )l .get (1 ), knownSuffixes ))
96117 {
97118 return true ;
98119 }
120+ foundAnyDNSNames = true ;
99121 break ;
100- case 7 :
101- if (InetAddress .getByName (name ).equals (InetAddress .getByName (l .get (1 ).toString ())))
122+ }
123+ case GeneralName .iPAddress :
124+ {
125+ if (nameIsIPAddress )
102126 {
103- return true ;
127+ String ipAddress = (String )l .get (1 );
128+
129+ if (name .equalsIgnoreCase (ipAddress ))
130+ {
131+ return true ;
132+ }
133+
134+ // In case of IPv6 addresses, convert to InetAddress to handle abbreviated forms correctly
135+ if (nameIsIPv6 && IPAddress .isValidIPv6 (ipAddress ))
136+ {
137+ try
138+ {
139+ if (nameInetAddress == null )
140+ {
141+ nameInetAddress = InetAddress .getByName (name );
142+ }
143+ if (nameInetAddress .equals (InetAddress .getByName (ipAddress )))
144+ {
145+ return true ;
146+ }
147+ }
148+ catch (UnknownHostException e )
149+ {
150+ // Ignore
151+ }
152+ }
104153 }
105154 break ;
155+ }
106156 default :
157+ {
107158 // ignore, maybe log
108159 if (LOG .isLoggable (Level .INFO ))
109160 {
@@ -121,38 +172,42 @@ public boolean verify(String name, X509Certificate cert)
121172 LOG .log (Level .INFO , "ignoring type " + type + " value = " + value );
122173 }
123174 }
175+ }
124176 }
125-
126- //
127- // As we had subject alternative names, we must not attempt to match against the CN.
128- //
129-
130- return false ;
131177 }
132178 }
133179 catch (Exception ex )
134180 {
135181 throw new ESTException (ex .getMessage (), ex );
136182 }
137183
184+ // If we found any DNS names in the subject alternative names, we must not attempt to match against the CN.
185+ if (nameIsIPAddress || foundAnyDNSNames )
186+ {
187+ return false ;
188+ }
189+
190+ X500Principal subject = cert .getSubjectX500Principal ();
191+
138192 // can't match - would need to check subjectAltName
139- if (cert . getSubjectX500Principal () == null )
193+ if (subject == null )
140194 {
141195 return false ;
142196 }
143197
144198 // Common Name match only.
145- RDN [] rdNs = X500Name .getInstance (cert . getSubjectX500Principal () .getEncoded ()).getRDNs ();
146- for (int i = rdNs .length - 1 ; i >= 0 ; --i )
199+ RDN [] rdns = X500Name .getInstance (subject .getEncoded ()).getRDNs ();
200+ for (int i = rdns .length - 1 ; i >= 0 ; --i )
147201 {
148- RDN rdn = rdNs [i ];
149- AttributeTypeAndValue [] typesAndValues = rdn .getTypesAndValues ();
202+ AttributeTypeAndValue [] typesAndValues = rdns [i ].getTypesAndValues ();
150203 for (int j = 0 ; j != typesAndValues .length ; j ++)
151204 {
152- AttributeTypeAndValue atv = typesAndValues [j ];
153- if (atv . getType () .equals (BCStyle . CN ))
205+ AttributeTypeAndValue typeAndValue = typesAndValues [j ];
206+ if (BCStyle . CN .equals (typeAndValue . getType () ))
154207 {
155- return isValidNameMatch (name , atv .getValue ().toString (), knownSuffixes );
208+ ASN1Primitive commonName = typeAndValue .getValue ().toASN1Primitive ();
209+ return commonName instanceof ASN1String
210+ && isValidNameMatch (name , ((ASN1String )commonName ).getString (), knownSuffixes );
156211 }
157212 }
158213 }
0 commit comments