Improved parsing of subjectDNs. SMACK-181

git-svn-id: http://svn.igniterealtime.org/svn/repos/smack/trunk@5984 b35dd754-fafc-0310-a699-88a17e54d16e
This commit is contained in:
Gaston Dombiak 2006-11-08 00:57:39 +00:00 committed by gato
parent a31f93d9d3
commit 0e0f1701a4
1 changed files with 81 additions and 19 deletions

View File

@ -24,8 +24,11 @@ import javax.net.ssl.X509TrustManager;
import java.io.FileInputStream; import java.io.FileInputStream;
import java.security.*; import java.security.*;
import java.security.cert.CertificateException; import java.security.cert.CertificateException;
import java.security.cert.CertificateParsingException;
import java.security.cert.X509Certificate; import java.security.cert.X509Certificate;
import java.util.Date; import java.util.*;
import java.util.regex.Matcher;
import java.util.regex.Pattern;
/** /**
* Trust manager that checks all certificates presented by the server. This class * Trust manager that checks all certificates presented by the server. This class
@ -37,6 +40,8 @@ import java.util.Date;
*/ */
class ServerTrustManager implements X509TrustManager { class ServerTrustManager implements X509TrustManager {
private static Pattern cnPattern = Pattern.compile("(?i)(cn=)([^,]*)");
private ConnectionConfiguration configuration; private ConnectionConfiguration configuration;
/** /**
@ -74,7 +79,7 @@ class ServerTrustManager implements X509TrustManager {
int nSize = x509Certificates.length; int nSize = x509Certificates.length;
String peerIdentity = getPeerIdentity(x509Certificates[0]); List<String> peerIdentities = getPeerIdentity(x509Certificates[0]);
if (configuration.isVerifyChainEnabled()) { if (configuration.isVerifyChainEnabled()) {
// Working down the chain, for every certificate in the chain, // Working down the chain, for every certificate in the chain,
@ -94,12 +99,12 @@ class ServerTrustManager implements X509TrustManager {
} }
catch (GeneralSecurityException generalsecurityexception) { catch (GeneralSecurityException generalsecurityexception) {
throw new CertificateException( throw new CertificateException(
"signature verification failed of " + peerIdentity); "signature verification failed of " + peerIdentities);
} }
} }
else { else {
throw new CertificateException( throw new CertificateException(
"subject/issuer verification failed of " + peerIdentity); "subject/issuer verification failed of " + peerIdentities);
} }
} }
principalLast = principalSubject; principalLast = principalSubject;
@ -115,7 +120,7 @@ class ServerTrustManager implements X509TrustManager {
if (!trusted && nSize == 1 && configuration.isSelfSignedCertificateEnabled()) if (!trusted && nSize == 1 && configuration.isSelfSignedCertificateEnabled())
{ {
System.out.println("Accepting self-signed certificate of remote server: " + System.out.println("Accepting self-signed certificate of remote server: " +
peerIdentity); peerIdentities);
trusted = true; trusted = true;
} }
} }
@ -123,7 +128,7 @@ class ServerTrustManager implements X509TrustManager {
e.printStackTrace(); e.printStackTrace();
} }
if (!trusted) { if (!trusted) {
throw new CertificateException("root certificate not trusted of " + peerIdentity); throw new CertificateException("root certificate not trusted of " + peerIdentities);
} }
} }
@ -131,16 +136,16 @@ class ServerTrustManager implements X509TrustManager {
// Verify that the first certificate in the chain corresponds to // Verify that the first certificate in the chain corresponds to
// the server we desire to authenticate. // the server we desire to authenticate.
// Check if the certificate uses a wildcard indicating that subdomains are valid // Check if the certificate uses a wildcard indicating that subdomains are valid
if (peerIdentity.startsWith("*.")) { if (peerIdentities.size() == 1 && peerIdentities.get(0).startsWith("*.")) {
// Remove the wildcard // Remove the wildcard
peerIdentity = peerIdentity.substring(2); String peerIdentity = peerIdentities.get(0).replace("*.", "");
// Check if the requested subdomain matches the certified domain // Check if the requested subdomain matches the certified domain
if (!server.endsWith(peerIdentity)) { if (!server.endsWith(peerIdentity)) {
throw new CertificateException("target verification failed of " + peerIdentity); throw new CertificateException("target verification failed of " + peerIdentities);
} }
} }
else if (!server.equals(peerIdentity)) { else if (!peerIdentities.contains(server)) {
throw new CertificateException("target verification failed of " + peerIdentity); throw new CertificateException("target verification failed of " + peerIdentities);
} }
} }
@ -170,15 +175,72 @@ class ServerTrustManager implements X509TrustManager {
* @param x509Certificate the certificate the holds the identity of the remote server. * @param x509Certificate the certificate the holds the identity of the remote server.
* @return the identity of the remote server as defined in the specified certificate. * @return the identity of the remote server as defined in the specified certificate.
*/ */
public static String getPeerIdentity(X509Certificate x509Certificate) { public static List<String> getPeerIdentity(X509Certificate x509Certificate) {
Principal principalSubject = x509Certificate.getSubjectDN(); // Look the identity in the subjectAltName extension if available
// TODO Look the identity in the subjectAltName extension if available List<String> names = getSubjectAlternativeNames(x509Certificate);
String name = principalSubject.getName(); if (names.isEmpty()) {
if (name.startsWith("CN=")) { String name = x509Certificate.getSubjectDN().getName();
// Remove the CN= prefix Matcher matcher = cnPattern.matcher(name);
name = name.substring(3); if (matcher.find()) {
name = matcher.group(2);
}
// Create an array with the unique identity
names = new ArrayList<String>();
names.add(name);
} }
return name; return names;
}
/**
* Returns the JID representation of an XMPP entity contained as a SubjectAltName extension
* in the certificate. If none was found then return <tt>null</tt>.
*
* @param certificate the certificate presented by the remote entity.
* @return the JID representation of an XMPP entity contained as a SubjectAltName extension
* in the certificate. If none was found then return <tt>null</tt>.
*/
private static List<String> getSubjectAlternativeNames(X509Certificate certificate) {
List<String> identities = new ArrayList<String>();
try {
Collection<List<?>> altNames = certificate.getSubjectAlternativeNames();
// Check that the certificate includes the SubjectAltName extension
if (altNames == null) {
return Collections.emptyList();
}
// Use the type OtherName to search for the certified server name
/*for (List item : altNames) {
Integer type = (Integer) item.get(0);
if (type == 0) {
// Type OtherName found so return the associated value
try {
// Value is encoded using ASN.1 so decode it to get the server's identity
ASN1InputStream decoder = new ASN1InputStream((byte[]) item.toArray()[1]);
DEREncodable encoded = decoder.readObject();
encoded = ((DERSequence) encoded).getObjectAt(1);
encoded = ((DERTaggedObject) encoded).getObject();
encoded = ((DERTaggedObject) encoded).getObject();
String identity = ((DERUTF8String) encoded).getString();
// Add the decoded server name to the list of identities
identities.add(identity);
}
catch (UnsupportedEncodingException e) {
// Ignore
}
catch (IOException e) {
// Ignore
}
catch (Exception e) {
e.printStackTrace();
}
}
// Other types are not good for XMPP so ignore them
System.out.println("SubjectAltName of invalid type found: " + certificate);
}*/
}
catch (CertificateParsingException e) {
e.printStackTrace();
}
return identities;
} }
} }