XmppDomainVerifier.java (5331B)
1 package eu.siacs.utils; 2 3 import org.bouncycastle.asn1.ASN1Primitive; 4 import org.bouncycastle.asn1.DERIA5String; 5 import org.bouncycastle.asn1.DERTaggedObject; 6 import org.bouncycastle.asn1.DERUTF8String; 7 import org.bouncycastle.asn1.DLSequence; 8 import org.bouncycastle.asn1.x500.RDN; 9 import org.bouncycastle.asn1.x500.X500Name; 10 import org.bouncycastle.asn1.x500.style.BCStyle; 11 import org.bouncycastle.asn1.x500.style.IETFUtils; 12 import org.bouncycastle.cert.jcajce.JcaX509CertificateHolder; 13 14 import java.io.IOException; 15 import java.security.cert.Certificate; 16 import java.security.cert.X509Certificate; 17 import java.util.ArrayList; 18 import java.util.Collection; 19 import java.util.List; 20 21 import javax.net.ssl.HostnameVerifier; 22 import javax.net.ssl.SSLSession; 23 24 public class XmppDomainVerifier implements HostnameVerifier { 25 26 private static XmppDomainVerifier instance = new XmppDomainVerifier(); 27 28 private final static String SRVName = "1.3.6.1.5.5.7.8.7"; 29 private final static String xmppAddr = "1.3.6.1.5.5.7.8.5"; 30 31 @Override 32 public boolean verify(String domain, SSLSession sslSession) { 33 try { 34 Certificate[] chain = sslSession.getPeerCertificates(); 35 if (chain.length == 0 || !(chain[0] instanceof X509Certificate)) { 36 return false; 37 } 38 X509Certificate certificate = (X509Certificate) chain[0]; 39 Collection<List<?>> alternativeNames = certificate.getSubjectAlternativeNames(); 40 List<String> xmppAddrs = new ArrayList<>(); 41 List<String> srvNames = new ArrayList<>(); 42 List<String> domains = new ArrayList<>(); 43 if (alternativeNames != null) { 44 for (List<?> san : alternativeNames) { 45 Integer type = (Integer) san.get(0); 46 if (type == 0) { 47 OtherName otherName = parseOtherName((byte[]) san.get(1)); 48 if (otherName != null) { 49 switch (otherName.oid) { 50 case SRVName: 51 srvNames.add(otherName.value); 52 break; 53 case xmppAddr: 54 xmppAddrs.add(otherName.value); 55 break; 56 } 57 } 58 } else if (type == 2) { 59 Object value = san.get(1); 60 if (value instanceof String) { 61 domains.add((String) value); 62 } 63 } 64 } 65 } 66 if (srvNames.size() == 0 && xmppAddrs.size() == 0 && domains.size() == 0) { 67 X500Name x500name = new JcaX509CertificateHolder(certificate).getSubject(); 68 RDN[] rdns = x500name.getRDNs(BCStyle.CN); 69 for (int i = 0; i < rdns.length; ++i) { 70 domains.add(IETFUtils.valueToString(x500name.getRDNs(BCStyle.CN)[i].getFirst().getValue())); 71 } 72 } 73 return xmppAddrs.contains(domain) || srvNames.contains("_xmpp-client." + domain) || matchDomain(domain, domains); 74 } catch (Exception e) { 75 return false; 76 } 77 } 78 79 private static OtherName parseOtherName(byte[] otherName) { 80 try { 81 ASN1Primitive asn1Primitive = ASN1Primitive.fromByteArray(otherName); 82 if (asn1Primitive instanceof DERTaggedObject) { 83 ASN1Primitive inner = ((DERTaggedObject) asn1Primitive).getObject(); 84 if (inner instanceof DLSequence) { 85 DLSequence sequence = (DLSequence) inner; 86 if (sequence.size() >= 2 && sequence.getObjectAt(1) instanceof DERTaggedObject) { 87 String oid = sequence.getObjectAt(0).toString(); 88 ASN1Primitive value = ((DERTaggedObject) sequence.getObjectAt(1)).getObject(); 89 if (value instanceof DERUTF8String) { 90 return new OtherName(oid, ((DERUTF8String) value).getString()); 91 } else if (value instanceof DERIA5String) { 92 return new OtherName(oid, ((DERIA5String) value).getString()); 93 } 94 } 95 } 96 } 97 return null; 98 } catch (IOException e) { 99 return null; 100 } 101 } 102 103 private static boolean matchDomain(String needle, List<String> haystack) { 104 for (String entry : haystack) { 105 if (entry.startsWith("*.")) { 106 int i = needle.indexOf('.'); 107 if (i != -1 && needle.substring(i).equals(entry.substring(1))) { 108 return true; 109 } 110 } else { 111 if (entry.equals(needle)) { 112 return true; 113 } 114 } 115 } 116 return false; 117 } 118 119 public static XmppDomainVerifier getInstance() { 120 return instance; 121 } 122 123 private static class OtherName { 124 private final String oid; 125 private final String value; 126 private OtherName(String oid, String value) { 127 this.oid = oid; 128 this.value = value; 129 } 130 } 131 }