/* * Copyright (c) 2000, 2003, Oracle and/or its affiliates. All rights reserved. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * * This code is free software; you can redistribute it and/or modify it * under the terms of the GNU General Public License version 2 only, as * published by the Free Software Foundation. Oracle designates this * particular file as subject to the "Classpath" exception as provided * by Oracle in the LICENSE file that accompanied this code. * * This code is distributed in the hope that it will be useful, but WITHOUT * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License * version 2 for more details (a copy is included in the LICENSE file that * accompanied this code). * * You should have received a copy of the GNU General Public License version * 2 along with this work; if not, write to the Free Software Foundation, * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. * * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA * or visit www.oracle.com if you need additional information or have any * questions. */package com.sun.jndi.ldap.ext;import java.io.InputStream;import java.io.OutputStream;import java.io.BufferedInputStream;import java.io.BufferedOutputStream;import java.io.IOException;import java.net.Socket;import java.util.Collection;import java.util.Iterator;import java.util.List;import java.security.Principal;import java.security.cert.X509Certificate;import java.security.cert.CertificateException;import javax.net.ssl.SSLSession;import javax.net.ssl.SSLSocket;import javax.net.ssl.SSLSocketFactory;import javax.net.ssl.SSLPeerUnverifiedException;import javax.net.ssl.SSLContext;import javax.net.ssl.HostnameVerifier;import sun.security.util.HostnameChecker;import javax.naming.*;import javax.naming.ldap.*;import com.sun.jndi.ldap.Connection;/** * This class implements the LDAPv3 Extended Response for StartTLS as * defined in * <a href="http://www.ietf.org/rfc/rfc2830.txt">Lightweight Directory * Access Protocol (v3): Extension for Transport Layer Security</a> * * The object identifier for StartTLS is 1.3.6.1.4.1.1466.20037 * and no extended response value is defined. * *<p> * The Start TLS extended request and response are used to establish * a TLS connection over the existing LDAP connection associated with * the JNDI context on which <tt>extendedOperation()</tt> is invoked. * * @see StartTlsRequest * @author Vincent Ryan */final public class StartTlsResponseImpl extends StartTlsResponse { private static final boolean debug = false; /* * The dNSName type in a subjectAltName extension of an X.509 certificate */ private static final int DNSNAME_TYPE = 2; /* * The server's hostname. */ private transient String hostname = null; /* * The LDAP socket. */ private transient Connection ldapConnection = null; /* * The original input stream. */ private transient InputStream originalInputStream = null; /* * The original output stream. */ private transient OutputStream originalOutputStream = null; /* * The SSL socket. */ private transient SSLSocket sslSocket = null; /* * The SSL socket factories. */ private transient SSLSocketFactory defaultFactory = null; private transient SSLSocketFactory currentFactory = null; /* * The list of cipher suites to be enabled. */ private transient String[] suites = null; /* * The hostname verifier callback. */ private transient HostnameVerifier verifier = null; /* * The flag to indicate that the TLS connection is closed. */ private transient boolean isClosed = true; private static final long serialVersionUID = -1126624615143411328L; // public no-arg constructor required by JDK's Service Provider API. public StartTlsResponseImpl() {} /** * Overrides the default list of cipher suites enabled for use on the * TLS connection. The cipher suites must have already been listed by * <tt>SSLSocketFactory.getSupportedCipherSuites()</tt> as being supported. * Even if a suite has been enabled, it still might not be used because * the peer does not support it, or because the requisite certificates * (and private keys) are not available. * * @param suites The non-null list of names of all the cipher suites to * enable. * @see #negotiate */ public void setEnabledCipherSuites(String[] suites) { this.suites = suites; } /** * Overrides the default hostname verifier used by <tt>negotiate()</tt> * after the TLS handshake has completed. If * <tt>setHostnameVerifier()</tt> has not been called before * <tt>negotiate()</tt> is invoked, <tt>negotiate()</tt> * will perform a simple case ignore match. If called after * <tt>negotiate()</tt>, this method does not do anything. * * @param verifier The non-null hostname verifier callback. * @see #negotiate */ public void setHostnameVerifier(HostnameVerifier verifier) { this.verifier = verifier; } /** * Negotiates a TLS session using the default SSL socket factory. * <p> * This method is equivalent to <tt>negotiate(null)</tt>. * * @return The negotiated SSL session * @throw IOException If an IO error was encountered while establishing * the TLS session. * @see #setEnabledCipherSuites * @see #setHostnameVerifier */ public SSLSession negotiate() throws IOException { return negotiate(null); } /** * Negotiates a TLS session using an SSL socket factory. * <p> * Creates an SSL socket using the supplied SSL socket factory and * attaches it to the existing connection. Performs the TLS handshake * and returns the negotiated session information. * <p> * If cipher suites have been set via <tt>setEnabledCipherSuites</tt> * then they are enabled before the TLS handshake begins. * <p> * Hostname verification is performed after the TLS handshake completes. * The default check performs a case insensitive match of the server's * hostname against that in the server's certificate. The server's * hostname is extracted from the subjectAltName in the server's * certificate (if present). Otherwise the value of the common name * attribute of the subject name is used. If a callback has * been set via <tt>setHostnameVerifier</tt> then that verifier is used if * the default check fails. * <p> * If an error occurs then the SSL socket is closed and an IOException * is thrown. The underlying connection remains intact. * * @param factory The possibly null SSL socket factory to use. * If null, the default SSL socket factory is used. * @return The negotiated SSL session * @throw IOException If an IO error was encountered while establishing * the TLS session. * @see #setEnabledCipherSuites * @see #setHostnameVerifier */ public SSLSession negotiate(SSLSocketFactory factory) throws IOException { if (isClosed && sslSocket != null) { throw new IOException("TLS connection is closed."); } if (factory == null) { factory = getDefaultFactory(); } if (debug) { System.out.println("StartTLS: About to start handshake"); } SSLSession sslSession = startHandshake(factory).getSession(); if (debug) { System.out.println("StartTLS: Completed handshake"); } SSLPeerUnverifiedException verifExcep = null; try { if (verify(hostname, sslSession)) { isClosed = false; return sslSession; } } catch (SSLPeerUnverifiedException e) { // Save to return the cause verifExcep = e; } if ((verifier != null) && verifier.verify(hostname, sslSession)) { isClosed = false; return sslSession; } // Verification failed close(); sslSession.invalidate(); if (verifExcep == null) { verifExcep = new SSLPeerUnverifiedException( "hostname of the server '" + hostname + "' does not match the hostname in the " + "server's certificate."); } throw verifExcep; } /** * Closes the TLS connection gracefully and reverts back to the underlying * connection. * * @throw IOException If an IO error was encountered while closing the * TLS connection */ public void close() throws IOException { if (isClosed) { return; } if (debug) { System.out.println("StartTLS: replacing SSL " + "streams with originals"); } // Replace SSL streams with the original streams ldapConnection.replaceStreams( originalInputStream, originalOutputStream); if (debug) { System.out.println("StartTLS: closing SSL Socket"); } sslSocket.close(); isClosed = true; } /** * Sets the connection for TLS to use. The TLS connection will be attached * to this connection. * * @param ldapConnection The non-null connection to use. * @param hostname The server's hostname. If null, the hostname used to * open the connection will be used instead. */ public void setConnection(Connection ldapConnection, String hostname) { this.ldapConnection = ldapConnection; this.hostname = (hostname != null) ? hostname : ldapConnection.host; originalInputStream = ldapConnection.inStream; originalOutputStream = ldapConnection.outStream; } /* * Returns the default SSL socket factory. * * @return The default SSL socket factory. * @throw IOException If TLS is not supported. */ private SSLSocketFactory getDefaultFactory() throws IOException { if (defaultFactory != null) { return defaultFactory; } return (defaultFactory = (SSLSocketFactory) SSLSocketFactory.getDefault()); } /* * Start the TLS handshake and manipulate the input and output streams. * * @param factory The SSL socket factory to use. * @return The SSL socket. * @throw IOException If an exception occurred while performing the * TLS handshake. */ private SSLSocket startHandshake(SSLSocketFactory factory) throws IOException { if (ldapConnection == null) { throw new IllegalStateException("LDAP connection has not been set." + " TLS requires an existing LDAP connection."); } if (factory != currentFactory) { // Create SSL socket layered over the existing connection sslSocket = (SSLSocket) factory.createSocket(ldapConnection.sock, ldapConnection.host, ldapConnection.port, false); currentFactory = factory; if (debug) { System.out.println("StartTLS: Created socket : " + sslSocket); } } if (suites != null) { sslSocket.setEnabledCipherSuites(suites); if (debug) { System.out.println("StartTLS: Enabled cipher suites"); } } // Connection must be quite for handshake to proceed try { if (debug) { System.out.println( "StartTLS: Calling sslSocket.startHandshake"); } sslSocket.startHandshake(); if (debug) { System.out.println( "StartTLS: + Finished sslSocket.startHandshake"); } // Replace original streams with the new SSL streams ldapConnection.replaceStreams(sslSocket.getInputStream(), sslSocket.getOutputStream()); if (debug) { System.out.println("StartTLS: Replaced IO Streams"); } } catch (IOException e) { if (debug) { System.out.println("StartTLS: Got IO error during handshake"); e.printStackTrace(); } sslSocket.close(); isClosed = true; throw e; // pass up exception } return sslSocket; } /* * Verifies that the hostname in the server's certificate matches the * hostname of the server. * The server's first certificate is examined. If it has a subjectAltName * that contains a dNSName then that is used as the server's hostname. * The server's hostname may contain a wildcard for its left-most name part. * Otherwise, if the certificate has no subjectAltName then the value of * the common name attribute of the subject name is used. * * @param hostname The hostname of the server. * @param session the SSLSession used on the connection to host. * @return true if the hostname is verified, false otherwise. */ private boolean verify(String hostname, SSLSession session) throws SSLPeerUnverifiedException { java.security.cert.Certificate[] certs = null; // if IPv6 strip off the "[]" if (hostname != null && hostname.startsWith("[") && hostname.endsWith("]")) { hostname = hostname.substring(1, hostname.length() - 1); } try { HostnameChecker checker = HostnameChecker.getInstance( HostnameChecker.TYPE_LDAP); // Use ciphersuite to determine whether Kerberos is active. if (session.getCipherSuite().startsWith("TLS_KRB5")) { Principal principal = getPeerPrincipal(session); if (!checker.match(hostname, principal)) { throw new SSLPeerUnverifiedException( "hostname of the kerberos principal:" + principal + " does not match the hostname:" + hostname); } } else { // X.509 // get the subject's certificate certs = session.getPeerCertificates(); X509Certificate peerCert; if (certs[0] instanceof java.security.cert.X509Certificate) { peerCert = (java.security.cert.X509Certificate) certs[0]; } else { throw new SSLPeerUnverifiedException( "Received a non X509Certificate from the server"); } checker.match(hostname, peerCert); } // no exception means verification passed return true; } catch (SSLPeerUnverifiedException e) { /* * The application may enable an anonymous SSL cipher suite, and * hostname verification is not done for anonymous ciphers */ String cipher = session.getCipherSuite(); if (cipher != null && (cipher.indexOf("_anon_") != -1)) { return true; } throw e; } catch (CertificateException e) { /* * Pass up the cause of the failure */ throw(SSLPeerUnverifiedException) new SSLPeerUnverifiedException("hostname of the server '" + hostname + "' does not match the hostname in the " + "server's certificate.").initCause(e); } } /* * Get the peer principal from the session */ private static Principal getPeerPrincipal(SSLSession session) throws SSLPeerUnverifiedException { Principal principal; try { principal = session.getPeerPrincipal(); } catch (AbstractMethodError e) { // if the JSSE provider does not support it, return null, since // we need it only for Kerberos. principal = null; } return principal; }}
/*
* Copyright (c) 2000, 2003, Oracle and/or its affiliates. All rights reserved.
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
*
* This code is free software; you can redistribute it and/or modify it
* under the terms of the GNU General Public License version 2 only, as
* published by the Free Software Foundation. Oracle designates this
* particular file as subject to the "Classpath" exception as provided
* by Oracle in the LICENSE file that accompanied this code.
*
* This code is distributed in the hope that it will be useful, but WITHOUT
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
* version 2 for more details (a copy is included in the LICENSE file that
* accompanied this code).
*
* You should have received a copy of the GNU General Public License version
* 2 along with this work; if not, write to the Free Software Foundation,
* Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
*
* Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
* or visit www.oracle.com if you need additional information or have any
* questions.
*/
package com.sun.jndi.ldap.ext;
import java.io.InputStream;
import java.io.OutputStream;
import java.io.BufferedInputStream;
import java.io.BufferedOutputStream;
import java.io.IOException;
import java.net.Socket;
import java.util.Collection;
import java.util.Iterator;
import java.util.List;
import java.security.Principal;
import java.security.cert.X509Certificate;
import java.security.cert.CertificateException;
import javax.net.ssl.SSLSession;
import javax.net.ssl.SSLSocket;
import javax.net.ssl.SSLSocketFactory;
import javax.net.ssl.SSLPeerUnverifiedException;
import javax.net.ssl.SSLContext;
import javax.net.ssl.HostnameVerifier;
import sun.security.util.HostnameChecker;
import javax.naming.*;
import javax.naming.ldap.*;
import com.sun.jndi.ldap.Connection;
/**
* This class implements the LDAPv3 Extended Response for StartTLS as
* defined in
* <a href="http://www.ietf.org/rfc/rfc2830.txt">Lightweight Directory
* Access Protocol (v3): Extension for Transport Layer Security</a>
*
* The object identifier for StartTLS is 1.3.6.1.4.1.1466.20037
* and no extended response value is defined.
*
*<p>
* The Start TLS extended request and response are used to establish
* a TLS connection over the existing LDAP connection associated with
* the JNDI context on which <tt>extendedOperation()</tt> is invoked.
*
* @see StartTlsRequest
* @author Vincent Ryan
*/
final public class StartTlsResponseImpl extends StartTlsResponse {
private static final boolean debug = false;
/*
* The dNSName type in a subjectAltName extension of an X.509 certificate
*/
private static final int DNSNAME_TYPE = 2;
/*
* The server's hostname.
*/
private transient String hostname = null;
/*
* The LDAP socket.
*/
private transient Connection ldapConnection = null;
/*
* The original input stream.
*/
private transient InputStream originalInputStream = null;
/*
* The original output stream.
*/
private transient OutputStream originalOutputStream = null;
/*
* The SSL socket.
*/
private transient SSLSocket sslSocket = null;
/*
* The SSL socket factories.
*/
private transient SSLSocketFactory defaultFactory = null;
private transient SSLSocketFactory currentFactory = null;
/*
* The list of cipher suites to be enabled.
*/
private transient String[] suites = null;
/*
* The hostname verifier callback.
*/
private transient HostnameVerifier verifier = null;
/*
* The flag to indicate that the TLS connection is closed.
*/
private transient boolean isClosed = true;
private static final long serialVersionUID = -1126624615143411328L;
// public no-arg constructor required by JDK's Service Provider API.
public StartTlsResponseImpl() {}
/**
* Overrides the default list of cipher suites enabled for use on the
* TLS connection. The cipher suites must have already been listed by
* <tt>SSLSocketFactory.getSupportedCipherSuites()</tt> as being supported.
* Even if a suite has been enabled, it still might not be used because
* the peer does not support it, or because the requisite certificates
* (and private keys) are not available.
*
* @param suites The non-null list of names of all the cipher suites to
* enable.
* @see #negotiate
*/
public void setEnabledCipherSuites(String[] suites) {
this.suites = suites;
}
/**
* Overrides the default hostname verifier used by <tt>negotiate()</tt>
* after the TLS handshake has completed. If
* <tt>setHostnameVerifier()</tt> has not been called before
* <tt>negotiate()</tt> is invoked, <tt>negotiate()</tt>
* will perform a simple case ignore match. If called after
* <tt>negotiate()</tt>, this method does not do anything.
*
* @param verifier The non-null hostname verifier callback.
* @see #negotiate
*/
public void setHostnameVerifier(HostnameVerifier verifier) {
this.verifier = verifier;
}
/**
* Negotiates a TLS session using the default SSL socket factory.
* <p>
* This method is equivalent to <tt>negotiate(null)</tt>.
*
* @return The negotiated SSL session
* @throw IOException If an IO error was encountered while establishing
* the TLS session.
* @see #setEnabledCipherSuites
* @see #setHostnameVerifier
*/
public SSLSession negotiate() throws IOException {
return negotiate(null);
}
/**
* Negotiates a TLS session using an SSL socket factory.
* <p>
* Creates an SSL socket using the supplied SSL socket factory and
* attaches it to the existing connection. Performs the TLS handshake
* and returns the negotiated session information.
* <p>
* If cipher suites have been set via <tt>setEnabledCipherSuites</tt>
* then they are enabled before the TLS handshake begins.
* <p>
* Hostname verification is performed after the TLS handshake completes.
* The default check performs a case insensitive match of the server's
* hostname against that in the server's certificate. The server's
* hostname is extracted from the subjectAltName in the server's
* certificate (if present). Otherwise the value of the common name
* attribute of the subject name is used. If a callback has
* been set via <tt>setHostnameVerifier</tt> then that verifier is used if
* the default check fails.
* <p>
* If an error occurs then the SSL socket is closed and an IOException
* is thrown. The underlying connection remains intact.
*
* @param factory The possibly null SSL socket factory to use.
* If null, the default SSL socket factory is used.
* @return The negotiated SSL session
* @throw IOException If an IO error was encountered while establishing
* the TLS session.
* @see #setEnabledCipherSuites
* @see #setHostnameVerifier
*/
public SSLSession negotiate(SSLSocketFactory factory) throws IOException {
if (isClosed && sslSocket != null) {
throw new IOException("TLS connection is closed.");
}
if (factory == null) {
factory = getDefaultFactory();
}
if (debug) {
System.out.println("StartTLS: About to start handshake");
}
SSLSession sslSession = startHandshake(factory).getSession();
if (debug) {
System.out.println("StartTLS: Completed handshake");
}
SSLPeerUnverifiedException verifExcep = null;
try {
if (verify(hostname, sslSession)) {
isClosed = false;
return sslSession;
}
} catch (SSLPeerUnverifiedException e) {
// Save to return the cause
verifExcep = e;
}
if ((verifier != null) &&
verifier.verify(hostname, sslSession)) {
isClosed = false;
return sslSession;
}
// Verification failed
close();
sslSession.invalidate();
if (verifExcep == null) {
verifExcep = new SSLPeerUnverifiedException(
"hostname of the server '" + hostname +
"' does not match the hostname in the " +
"server's certificate.");
}
throw verifExcep;
}
/**
* Closes the TLS connection gracefully and reverts back to the underlying
* connection.
*
* @throw IOException If an IO error was encountered while closing the
* TLS connection
*/
public void close() throws IOException {
if (isClosed) {
return;
}
if (debug) {
System.out.println("StartTLS: replacing SSL " +
"streams with originals");
}
// Replace SSL streams with the original streams
ldapConnection.replaceStreams(
originalInputStream, originalOutputStream);
if (debug) {
System.out.println("StartTLS: closing SSL Socket");
}
sslSocket.close();
isClosed = true;
}
/**
* Sets the connection for TLS to use. The TLS connection will be attached
* to this connection.
*
* @param ldapConnection The non-null connection to use.
* @param hostname The server's hostname. If null, the hostname used to
* open the connection will be used instead.
*/
public void setConnection(Connection ldapConnection, String hostname) {
this.ldapConnection = ldapConnection;
this.hostname = (hostname != null) ? hostname : ldapConnection.host;
originalInputStream = ldapConnection.inStream;
originalOutputStream = ldapConnection.outStream;
}
/*
* Returns the default SSL socket factory.
*
* @return The default SSL socket factory.
* @throw IOException If TLS is not supported.
*/
private SSLSocketFactory getDefaultFactory() throws IOException {
if (defaultFactory != null) {
return defaultFactory;
}
return (defaultFactory =
(SSLSocketFactory) SSLSocketFactory.getDefault());
}
/*
* Start the TLS handshake and manipulate the input and output streams.
*
* @param factory The SSL socket factory to use.
* @return The SSL socket.
* @throw IOException If an exception occurred while performing the
* TLS handshake.
*/
private SSLSocket startHandshake(SSLSocketFactory factory)
throws IOException {
if (ldapConnection == null) {
throw new IllegalStateException("LDAP connection has not been set."
+ " TLS requires an existing LDAP connection.");
}
if (factory != currentFactory) {
// Create SSL socket layered over the existing connection
sslSocket = (SSLSocket) factory.createSocket(ldapConnection.sock,
ldapConnection.host, ldapConnection.port, false);
currentFactory = factory;
if (debug) {
System.out.println("StartTLS: Created socket : " + sslSocket);
}
}
if (suites != null) {
sslSocket.setEnabledCipherSuites(suites);
if (debug) {
System.out.println("StartTLS: Enabled cipher suites");
}
}
// Connection must be quite for handshake to proceed
try {
if (debug) {
System.out.println(
"StartTLS: Calling sslSocket.startHandshake");
}
sslSocket.startHandshake();
if (debug) {
System.out.println(
"StartTLS: + Finished sslSocket.startHandshake");
}
// Replace original streams with the new SSL streams
ldapConnection.replaceStreams(sslSocket.getInputStream(),
sslSocket.getOutputStream());
if (debug) {
System.out.println("StartTLS: Replaced IO Streams");
}
} catch (IOException e) {
if (debug) {
System.out.println("StartTLS: Got IO error during handshake");
e.printStackTrace();
}
sslSocket.close();
isClosed = true;
throw e; // pass up exception
}
return sslSocket;
}
/*
* Verifies that the hostname in the server's certificate matches the
* hostname of the server.
* The server's first certificate is examined. If it has a subjectAltName
* that contains a dNSName then that is used as the server's hostname.
* The server's hostname may contain a wildcard for its left-most name part.
* Otherwise, if the certificate has no subjectAltName then the value of
* the common name attribute of the subject name is used.
*
* @param hostname The hostname of the server.
* @param session the SSLSession used on the connection to host.
* @return true if the hostname is verified, false otherwise.
*/
private boolean verify(String hostname, SSLSession session)
throws SSLPeerUnverifiedException {
java.security.cert.Certificate[] certs = null;
// if IPv6 strip off the "[]"
if (hostname != null && hostname.startsWith("[") &&
hostname.endsWith("]")) {
hostname = hostname.substring(1, hostname.length() - 1);
}
try {
HostnameChecker checker = HostnameChecker.getInstance(
HostnameChecker.TYPE_LDAP);
// Use ciphersuite to determine whether Kerberos is active.
if (session.getCipherSuite().startsWith("TLS_KRB5")) {
Principal principal = getPeerPrincipal(session);
if (!checker.match(hostname, principal)) {
throw new SSLPeerUnverifiedException(
"hostname of the kerberos principal:" + principal +
" does not match the hostname:" + hostname);
}
} else { // X.509
// get the subject's certificate
certs = session.getPeerCertificates();
X509Certificate peerCert;
if (certs[0] instanceof java.security.cert.X509Certificate) {
peerCert = (java.security.cert.X509Certificate) certs[0];
} else {
throw new SSLPeerUnverifiedException(
"Received a non X509Certificate from the server");
}
checker.match(hostname, peerCert);
}
// no exception means verification passed
return true;
} catch (SSLPeerUnverifiedException e) {
/*
* The application may enable an anonymous SSL cipher suite, and
* hostname verification is not done for anonymous ciphers
*/
String cipher = session.getCipherSuite();
if (cipher != null && (cipher.indexOf("_anon_") != -1)) {
return true;
}
throw e;
} catch (CertificateException e) {
/*
* Pass up the cause of the failure
*/
throw(SSLPeerUnverifiedException)
new SSLPeerUnverifiedException("hostname of the server '" +
hostname +
"' does not match the hostname in the " +
"server's certificate.").initCause(e);
}
}
/*
* Get the peer principal from the session
*/
private static Principal getPeerPrincipal(SSLSession session)
throws SSLPeerUnverifiedException {
Principal principal;
try {
principal = session.getPeerPrincipal();
} catch (AbstractMethodError e) {
// if the JSSE provider does not support it, return null, since
// we need it only for Kerberos.
principal = null;
}
return principal;
}
}