diff options
author | Shuyi Chen <shuyichen@google.com> | 2013-05-22 14:51:55 -0700 |
---|---|---|
committer | Shuyi Chen <shuyichen@google.com> | 2013-05-22 17:19:30 -0700 |
commit | d7955ce24d294fb2014c59d11fca184471056f44 (patch) | |
tree | e260500b0b7639127038495d46a0ad6dcbb6d96c /src/org/jivesoftware/smack/ServerTrustManager.java | |
parent | 8f4ce9ea0de51fee918bffe19c434612d6bbb2d7 (diff) | |
download | smack-jb-mr2.0.0-release.tar.gz |
Add android smack source.HEADandroid-wear-5.0.0_r1android-sdk-4.4.2_r1.0.1android-sdk-4.4.2_r1android-l-preview_r2android-cts-5.1_r9android-cts-5.1_r8android-cts-5.1_r7android-cts-5.1_r6android-cts-5.1_r5android-cts-5.1_r4android-cts-5.1_r3android-cts-5.1_r28android-cts-5.1_r27android-cts-5.1_r26android-cts-5.1_r25android-cts-5.1_r24android-cts-5.1_r23android-cts-5.1_r22android-cts-5.1_r21android-cts-5.1_r20android-cts-5.1_r2android-cts-5.1_r19android-cts-5.1_r18android-cts-5.1_r17android-cts-5.1_r16android-cts-5.1_r15android-cts-5.1_r14android-cts-5.1_r13android-cts-5.1_r10android-cts-5.1_r1android-cts-5.0_r9android-cts-5.0_r8android-cts-5.0_r7android-cts-5.0_r6android-cts-5.0_r5android-cts-5.0_r4android-cts-5.0_r3android-cts-4.4_r4android-cts-4.4_r1android-5.1.1_r9android-5.1.1_r8android-5.1.1_r7android-5.1.1_r6android-5.1.1_r5android-5.1.1_r4android-5.1.1_r38android-5.1.1_r37android-5.1.1_r36android-5.1.1_r35android-5.1.1_r34android-5.1.1_r33android-5.1.1_r30android-5.1.1_r3android-5.1.1_r29android-5.1.1_r28android-5.1.1_r26android-5.1.1_r25android-5.1.1_r24android-5.1.1_r23android-5.1.1_r22android-5.1.1_r20android-5.1.1_r2android-5.1.1_r19android-5.1.1_r18android-5.1.1_r17android-5.1.1_r16android-5.1.1_r15android-5.1.1_r14android-5.1.1_r13android-5.1.1_r12android-5.1.1_r10android-5.1.1_r1android-5.1.0_r5android-5.1.0_r4android-5.1.0_r3android-5.1.0_r1android-5.0.2_r3android-5.0.2_r1android-5.0.1_r1android-5.0.0_r7android-5.0.0_r6android-5.0.0_r5.1android-5.0.0_r5android-5.0.0_r4android-5.0.0_r3android-5.0.0_r2android-5.0.0_r1android-4.4w_r1android-4.4_r1.2.0.1android-4.4_r1.2android-4.4_r1.1.0.1android-4.4_r1.1android-4.4_r1.0.1android-4.4_r1android-4.4_r0.9android-4.4_r0.8android-4.4_r0.7android-4.4.4_r2.0.1android-4.4.4_r2android-4.4.4_r1.0.1android-4.4.4_r1android-4.4.3_r1.1.0.1android-4.4.3_r1.1android-4.4.3_r1.0.1android-4.4.3_r1android-4.4.2_r2.0.1android-4.4.2_r2android-4.4.2_r1.0.1android-4.4.2_r1android-4.4.1_r1.0.1android-4.4.1_r1android-4.3_r3.1android-4.3_r3android-4.3_r2.3android-4.3_r2.2android-4.3_r2.1android-4.3_r2android-4.3_r1.1android-4.3_r1android-4.3_r0.9.1android-4.3_r0.9android-4.3.1_r1tools_r22.2mastermainlollipop-wear-releaselollipop-releaselollipop-mr1-wfc-releaselollipop-mr1-releaselollipop-mr1-fi-releaselollipop-mr1-devlollipop-mr1-cts-releaselollipop-devlollipop-cts-releasel-previewkitkat-wearkitkat-releasekitkat-mr2.2-releasekitkat-mr2.1-releasekitkat-mr2-releasekitkat-mr1.1-releasekitkat-mr1-releasekitkat-devkitkat-cts-releasekitkat-cts-devjb-mr2.0.0-releasejb-mr2.0-releasejb-mr2-releasejb-mr2-devidea133-weekly-releaseidea133
Change-Id: I49ce97136c17173c4ae3965c694af6e7bc49897d
Diffstat (limited to 'src/org/jivesoftware/smack/ServerTrustManager.java')
-rw-r--r-- | src/org/jivesoftware/smack/ServerTrustManager.java | 331 |
1 files changed, 331 insertions, 0 deletions
diff --git a/src/org/jivesoftware/smack/ServerTrustManager.java b/src/org/jivesoftware/smack/ServerTrustManager.java new file mode 100644 index 0000000..63da3e7 --- /dev/null +++ b/src/org/jivesoftware/smack/ServerTrustManager.java @@ -0,0 +1,331 @@ +/** + * $RCSfile$ + * $Revision$ + * $Date$ + * + * Copyright 2003-2005 Jive Software. + * + * All rights reserved. Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package org.jivesoftware.smack; + +import javax.net.ssl.X509TrustManager; + +import java.io.FileInputStream; +import java.io.InputStream; +import java.io.IOException; +import java.security.*; +import java.security.cert.CertificateException; +import java.security.cert.CertificateParsingException; +import java.security.cert.X509Certificate; +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 + * is used during TLS negotiation. It is possible to disable/enable some or all checkings + * by configuring the {@link ConnectionConfiguration}. The truststore file that contains + * knows and trusted CA root certificates can also be configure in {@link ConnectionConfiguration}. + * + * @author Gaston Dombiak + */ +class ServerTrustManager implements X509TrustManager { + + private static Pattern cnPattern = Pattern.compile("(?i)(cn=)([^,]*)"); + + private ConnectionConfiguration configuration; + + /** + * Holds the domain of the remote server we are trying to connect + */ + private String server; + private KeyStore trustStore; + + private static Map<KeyStoreOptions, KeyStore> stores = new HashMap<KeyStoreOptions, KeyStore>(); + + public ServerTrustManager(String server, ConnectionConfiguration configuration) { + this.configuration = configuration; + this.server = server; + + InputStream in = null; + synchronized (stores) { + KeyStoreOptions options = new KeyStoreOptions(configuration.getTruststoreType(), + configuration.getTruststorePath(), configuration.getTruststorePassword()); + if (stores.containsKey(options)) { + trustStore = stores.get(options); + } else { + try { + trustStore = KeyStore.getInstance(options.getType()); + in = new FileInputStream(options.getPath()); + trustStore.load(in, options.getPassword().toCharArray()); + } catch (Exception e) { + trustStore = null; + e.printStackTrace(); + } finally { + if (in != null) { + try { + in.close(); + } catch (IOException ioe) { + // Ignore. + } + } + } + stores.put(options, trustStore); + } + if (trustStore == null) + // Disable root CA checking + configuration.setVerifyRootCAEnabled(false); + } + } + + public X509Certificate[] getAcceptedIssuers() { + return new X509Certificate[0]; + } + + public void checkClientTrusted(X509Certificate[] arg0, String arg1) + throws CertificateException { + } + + public void checkServerTrusted(X509Certificate[] x509Certificates, String arg1) + throws CertificateException { + + int nSize = x509Certificates.length; + + List<String> peerIdentities = getPeerIdentity(x509Certificates[0]); + + if (configuration.isVerifyChainEnabled()) { + // Working down the chain, for every certificate in the chain, + // verify that the subject of the certificate is the issuer of the + // next certificate in the chain. + Principal principalLast = null; + for (int i = nSize -1; i >= 0 ; i--) { + X509Certificate x509certificate = x509Certificates[i]; + Principal principalIssuer = x509certificate.getIssuerDN(); + Principal principalSubject = x509certificate.getSubjectDN(); + if (principalLast != null) { + if (principalIssuer.equals(principalLast)) { + try { + PublicKey publickey = + x509Certificates[i + 1].getPublicKey(); + x509Certificates[i].verify(publickey); + } + catch (GeneralSecurityException generalsecurityexception) { + throw new CertificateException( + "signature verification failed of " + peerIdentities); + } + } + else { + throw new CertificateException( + "subject/issuer verification failed of " + peerIdentities); + } + } + principalLast = principalSubject; + } + } + + if (configuration.isVerifyRootCAEnabled()) { + // Verify that the the last certificate in the chain was issued + // by a third-party that the client trusts. + boolean trusted = false; + try { + trusted = trustStore.getCertificateAlias(x509Certificates[nSize - 1]) != null; + if (!trusted && nSize == 1 && configuration.isSelfSignedCertificateEnabled()) + { + System.out.println("Accepting self-signed certificate of remote server: " + + peerIdentities); + trusted = true; + } + } + catch (KeyStoreException e) { + e.printStackTrace(); + } + if (!trusted) { + throw new CertificateException("root certificate not trusted of " + peerIdentities); + } + } + + if (configuration.isNotMatchingDomainCheckEnabled()) { + // Verify that the first certificate in the chain corresponds to + // the server we desire to authenticate. + // Check if the certificate uses a wildcard indicating that subdomains are valid + if (peerIdentities.size() == 1 && peerIdentities.get(0).startsWith("*.")) { + // Remove the wildcard + String peerIdentity = peerIdentities.get(0).replace("*.", ""); + // Check if the requested subdomain matches the certified domain + if (!server.endsWith(peerIdentity)) { + throw new CertificateException("target verification failed of " + peerIdentities); + } + } + else if (!peerIdentities.contains(server)) { + throw new CertificateException("target verification failed of " + peerIdentities); + } + } + + if (configuration.isExpiredCertificatesCheckEnabled()) { + // For every certificate in the chain, verify that the certificate + // is valid at the current time. + Date date = new Date(); + for (int i = 0; i < nSize; i++) { + try { + x509Certificates[i].checkValidity(date); + } + catch (GeneralSecurityException generalsecurityexception) { + throw new CertificateException("invalid date of " + server); + } + } + } + + } + + /** + * Returns the identity of the remote server as defined in the specified certificate. The + * identity is defined in the subjectDN of the certificate and it can also be defined in + * the subjectAltName extension of type "xmpp". When the extension is being used then the + * identity defined in the extension in going to be returned. Otherwise, the value stored in + * the subjectDN is returned. + * + * @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. + */ + public static List<String> getPeerIdentity(X509Certificate x509Certificate) { + // Look the identity in the subjectAltName extension if available + List<String> names = getSubjectAlternativeNames(x509Certificate); + if (names.isEmpty()) { + String name = x509Certificate.getSubjectDN().getName(); + Matcher matcher = cnPattern.matcher(name); + if (matcher.find()) { + name = matcher.group(2); + } + // Create an array with the unique identity + names = new ArrayList<String>(); + names.add(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; + } + + private static class KeyStoreOptions { + private final String type; + private final String path; + private final String password; + + public KeyStoreOptions(String type, String path, String password) { + super(); + this.type = type; + this.path = path; + this.password = password; + } + + public String getType() { + return type; + } + + public String getPath() { + return path; + } + + public String getPassword() { + return password; + } + + @Override + public int hashCode() { + final int prime = 31; + int result = 1; + result = prime * result + ((password == null) ? 0 : password.hashCode()); + result = prime * result + ((path == null) ? 0 : path.hashCode()); + result = prime * result + ((type == null) ? 0 : type.hashCode()); + return result; + } + + @Override + public boolean equals(Object obj) { + if (this == obj) + return true; + if (obj == null) + return false; + if (getClass() != obj.getClass()) + return false; + KeyStoreOptions other = (KeyStoreOptions) obj; + if (password == null) { + if (other.password != null) + return false; + } else if (!password.equals(other.password)) + return false; + if (path == null) { + if (other.path != null) + return false; + } else if (!path.equals(other.path)) + return false; + if (type == null) { + if (other.type != null) + return false; + } else if (!type.equals(other.type)) + return false; + return true; + } + } +} |