/*
* Copyright (c) 2000, 2017, 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 sun.security.provider.certpath.ldap;
import java.net.URI;
import java.security.*;
import java.security.cert.*;
import java.util.*;
import sun.security.util.Cache;
import sun.security.util.Debug;
A CertStore
that retrieves Certificates
and
CRL
s from an LDAP directory, using the PKIX LDAP V2 Schema
(RFC 2587):
http://www.ietf.org/rfc/rfc2587.txt.
Before calling the engineGetCertificates
or engineGetCRLs
methods, the
LDAPCertStore(CertStoreParameters)
constructor is called to create the CertStore
and establish the DNS name and port of the LDAP
server from which Certificate
s and CRL
s will be
retrieved.
Concurrent Access
As described in the javadoc for CertStoreSpi
, the
engineGetCertificates
and engineGetCRLs
methods
must be thread-safe. That is, multiple threads may concurrently
invoke these methods on a single LDAPCertStore
object
(or more than one) with no ill effects. This allows a
CertPathBuilder
to search for a CRL while simultaneously
searching for further certificates, for instance.
This is achieved by adding the synchronized
keyword to the
engineGetCertificates
and engineGetCRLs
methods.
This classes uses caching and requests multiple attributes at once to
minimize LDAP round trips. The cache is associated with the CertStore
instance. It uses soft references to hold the values to minimize impact
on footprint and currently has a maximum size of 750 attributes and a
30 second default lifetime.
We always request CA certificates, cross certificate pairs, and ARLs in
a single LDAP request when any one of them is needed. The reason is that
we typically need all of them anyway and requesting them in one go can
reduce the number of requests to a third. Even if we don't need them,
these attributes are typically small enough not to cause a noticeable
overhead. In addition, when the prefetchCRLs flag is true, we also request
the full CRLs. It is currently false initially but set to true once any
request for an ARL to the server returns an null value. The reason is
that CRLs could be rather large but are rarely used. This implementation
should improve performance in most cases.
Author: Steve Hanna, Andreas Sterbenz See Also: Since: 1.4
/**
* A <code>CertStore</code> that retrieves <code>Certificates</code> and
* <code>CRL</code>s from an LDAP directory, using the PKIX LDAP V2 Schema
* (RFC 2587):
* <a href="http://www.ietf.org/rfc/rfc2587.txt">
* http://www.ietf.org/rfc/rfc2587.txt</a>.
* <p>
* Before calling the {@link #engineGetCertificates engineGetCertificates} or
* {@link #engineGetCRLs engineGetCRLs} methods, the
* {@link #LDAPCertStore(CertStoreParameters)
* LDAPCertStore(CertStoreParameters)} constructor is called to create the
* <code>CertStore</code> and establish the DNS name and port of the LDAP
* server from which <code>Certificate</code>s and <code>CRL</code>s will be
* retrieved.
* <p>
* <b>Concurrent Access</b>
* <p>
* As described in the javadoc for <code>CertStoreSpi</code>, the
* <code>engineGetCertificates</code> and <code>engineGetCRLs</code> methods
* must be thread-safe. That is, multiple threads may concurrently
* invoke these methods on a single <code>LDAPCertStore</code> object
* (or more than one) with no ill effects. This allows a
* <code>CertPathBuilder</code> to search for a CRL while simultaneously
* searching for further certificates, for instance.
* <p>
* This is achieved by adding the <code>synchronized</code> keyword to the
* <code>engineGetCertificates</code> and <code>engineGetCRLs</code> methods.
* <p>
* This classes uses caching and requests multiple attributes at once to
* minimize LDAP round trips. The cache is associated with the CertStore
* instance. It uses soft references to hold the values to minimize impact
* on footprint and currently has a maximum size of 750 attributes and a
* 30 second default lifetime.
* <p>
* We always request CA certificates, cross certificate pairs, and ARLs in
* a single LDAP request when any one of them is needed. The reason is that
* we typically need all of them anyway and requesting them in one go can
* reduce the number of requests to a third. Even if we don't need them,
* these attributes are typically small enough not to cause a noticeable
* overhead. In addition, when the prefetchCRLs flag is true, we also request
* the full CRLs. It is currently false initially but set to true once any
* request for an ARL to the server returns an null value. The reason is
* that CRLs could be rather large but are rarely used. This implementation
* should improve performance in most cases.
*
* @see java.security.cert.CertStore
*
* @since 1.4
* @author Steve Hanna
* @author Andreas Sterbenz
*/
public final class LDAPCertStore extends CertStoreSpi {
private static final Debug debug = Debug.getInstance("certpath");
private String ldapDN;
private LDAPCertStoreImpl impl;
public LDAPCertStore(CertStoreParameters params)
throws InvalidAlgorithmParameterException {
super(params);
String serverName;
int port;
String dn = null;
if (params == null) {
throw new InvalidAlgorithmParameterException(
"Parameters required for LDAP certstore");
}
if (params instanceof LDAPCertStoreParameters) {
LDAPCertStoreParameters p = (LDAPCertStoreParameters) params;
serverName = p.getServerName();
port = p.getPort();
} else if (params instanceof URICertStoreParameters) {
URICertStoreParameters p = (URICertStoreParameters) params;
URI u = p.getURI();
if (!u.getScheme().equalsIgnoreCase("ldap")) {
throw new InvalidAlgorithmParameterException(
"Unsupported scheme '" + u.getScheme()
+ "', only LDAP URIs are supported "
+ "for LDAP certstore");
}
// Use the same default values as in LDAPCertStoreParameters
// if unspecified in URI
serverName = u.getHost();
if (serverName == null) {
serverName = "localhost";
}
port = u.getPort();
if (port == -1) {
port = 389;
}
dn = u.getPath();
if (dn != null && dn.charAt(0) == '/') {
dn = dn.substring(1);
}
} else {
throw new InvalidAlgorithmParameterException(
"Parameters must be either LDAPCertStoreParameters or "
+ "URICertStoreParameters, but instance of "
+ params.getClass().getName() + " passed");
}
SecurityManager security = System.getSecurityManager();
if (security != null) {
security.checkConnect(serverName, port);
}
Key k = new Key(serverName, port);
LDAPCertStoreImpl lci = certStoreCache.get(k);
if (lci == null) {
this.impl = new LDAPCertStoreImpl(serverName, port);
certStoreCache.put(k, impl);
} else {
this.impl = lci;
if (debug != null) {
debug.println("LDAPCertStore.getInstance: cache hit");
}
}
this.ldapDN = dn;
}
private static class Key {
volatile int hashCode;
String serverName;
int port;
Key(String serverName, int port) {
this.serverName = serverName;
this.port = port;
}
@Override
public boolean equals(Object obj) {
if (!(obj instanceof Key)) {
return false;
}
Key key = (Key) obj;
return (port == key.port &&
serverName.equalsIgnoreCase(key.serverName));
}
@Override
public int hashCode() {
if (hashCode == 0) {
int result = 17;
result = 37*result + port;
result = 37*result +
serverName.toLowerCase(Locale.ENGLISH).hashCode();
hashCode = result;
}
return hashCode;
}
}
Returns an LDAPCertStoreImpl object. This method consults a cache of
LDAPCertStoreImpl objects (shared per JVM) using the corresponding
LDAP server name and port info as a key.
/**
* Returns an LDAPCertStoreImpl object. This method consults a cache of
* LDAPCertStoreImpl objects (shared per JVM) using the corresponding
* LDAP server name and port info as a key.
*/
private static final Cache<Key, LDAPCertStoreImpl>
certStoreCache = Cache.newSoftMemoryCache(185);
// Exist solely for regression test for ensuring that caching is done
static synchronized LDAPCertStoreImpl getInstance(LDAPCertStoreParameters params)
throws NoSuchAlgorithmException, InvalidAlgorithmParameterException {
String serverName = params.getServerName();
int port = params.getPort();
Key k = new Key(serverName, port);
LDAPCertStoreImpl lci = certStoreCache.get(k);
if (lci == null) {
lci = new LDAPCertStoreImpl(serverName, port);
certStoreCache.put(k, lci);
} else {
if (debug != null) {
debug.println("LDAPCertStore.getInstance: cache hit");
}
}
return lci;
}
Returns a Collection
of Certificate
s that
match the specified selector. If no Certificate
s
match the selector, an empty Collection
will be returned.
It is not practical to search every entry in the LDAP database for
matching Certificate
s. Instead, the CertSelector
is examined in order to determine where matching Certificate
s
are likely to be found (according to the PKIX LDAPv2 schema, RFC 2587).
If the subject is specified, its directory entry is searched. If the
issuer is specified, its directory entry is searched. If neither the
subject nor the issuer are specified (or the selector is not an
X509CertSelector
), a CertStoreException
is
thrown.
Params: - selector – a
CertSelector
used to select which
Certificate
s should be returned.
Throws: - CertStoreException – if an exception occurs
Returns: a Collection
of Certificate
s that
match the specified selector
/**
* Returns a <code>Collection</code> of <code>Certificate</code>s that
* match the specified selector. If no <code>Certificate</code>s
* match the selector, an empty <code>Collection</code> will be returned.
* <p>
* It is not practical to search every entry in the LDAP database for
* matching <code>Certificate</code>s. Instead, the <code>CertSelector</code>
* is examined in order to determine where matching <code>Certificate</code>s
* are likely to be found (according to the PKIX LDAPv2 schema, RFC 2587).
* If the subject is specified, its directory entry is searched. If the
* issuer is specified, its directory entry is searched. If neither the
* subject nor the issuer are specified (or the selector is not an
* <code>X509CertSelector</code>), a <code>CertStoreException</code> is
* thrown.
*
* @param selector a <code>CertSelector</code> used to select which
* <code>Certificate</code>s should be returned.
* @return a <code>Collection</code> of <code>Certificate</code>s that
* match the specified selector
* @throws CertStoreException if an exception occurs
*/
@Override
public synchronized Collection<X509Certificate> engineGetCertificates
(CertSelector selector) throws CertStoreException {
if (debug != null) {
debug.println("LDAPCertStore.engineGetCertificates() selector: "
+ String.valueOf(selector));
}
if (selector == null) {
selector = new X509CertSelector();
} else if (!(selector instanceof X509CertSelector)) {
throw new CertStoreException("Need X509CertSelector to find certs, "
+ "but instance of " + selector.getClass().getName()
+ " passed");
}
return impl.getCertificates((X509CertSelector) selector, ldapDN);
}
Returns a Collection
of CRL
s that
match the specified selector. If no CRL
s
match the selector, an empty Collection
will be returned.
It is not practical to search every entry in the LDAP database for
matching CRL
s. Instead, the CRLSelector
is examined in order to determine where matching CRL
s
are likely to be found (according to the PKIX LDAPv2 schema, RFC 2587).
If issuerNames or certChecking are specified, the issuer's directory
entry is searched. If neither issuerNames or certChecking are specified
(or the selector is not an X509CRLSelector
), a
CertStoreException
is thrown.
Params: - selector – A
CRLSelector
used to select which
CRL
s should be returned. Specify null
to return all CRL
s.
Throws: - CertStoreException – if an exception occurs
Returns: A Collection
of CRL
s that
match the specified selector
/**
* Returns a <code>Collection</code> of <code>CRL</code>s that
* match the specified selector. If no <code>CRL</code>s
* match the selector, an empty <code>Collection</code> will be returned.
* <p>
* It is not practical to search every entry in the LDAP database for
* matching <code>CRL</code>s. Instead, the <code>CRLSelector</code>
* is examined in order to determine where matching <code>CRL</code>s
* are likely to be found (according to the PKIX LDAPv2 schema, RFC 2587).
* If issuerNames or certChecking are specified, the issuer's directory
* entry is searched. If neither issuerNames or certChecking are specified
* (or the selector is not an <code>X509CRLSelector</code>), a
* <code>CertStoreException</code> is thrown.
*
* @param selector A <code>CRLSelector</code> used to select which
* <code>CRL</code>s should be returned. Specify <code>null</code>
* to return all <code>CRL</code>s.
* @return A <code>Collection</code> of <code>CRL</code>s that
* match the specified selector
* @throws CertStoreException if an exception occurs
*/
@Override
public synchronized Collection<X509CRL> engineGetCRLs(CRLSelector selector)
throws CertStoreException {
if (debug != null) {
debug.println("LDAPCertStore.engineGetCRLs() selector: "
+ selector);
}
// Set up selector and collection to hold CRLs
if (selector == null) {
selector = new X509CRLSelector();
} else if (!(selector instanceof X509CRLSelector)) {
throw new CertStoreException("Need X509CRLSelector to find CRLs, "
+ "but instance of " + selector.getClass().getName()
+ " passed");
}
return impl.getCRLs((X509CRLSelector) selector, ldapDN);
}
}