/*
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
*
* 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.
*/
/**
* 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">
* <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
*/
private final static boolean DEBUG = false;
/**
* LDAP attribute identifiers.
*/
// Constants for various empty values
// cache related constants
private final static int LIFETIME;
"sun.security.certpath.ldap.cache.lifetime";
static {
new GetPropertyAction(PROP_LIFETIME));
if (s != null) {
} else {
}
}
/**
* The CertificateFactory used to decode certificates from
* their binary stored form.
*/
/**
* The JNDI directory context.
*/
/**
* Flag indicating whether we should prefetch CRLs.
*/
private boolean prefetchCRLs = false;
/**
* Creates a <code>CertStore</code> with the specified parameters.
* For this class, the parameters object must be an instance of
* <code>LDAPCertStoreParameters</code>.
*
* @param params the algorithm parameters
* @exception InvalidAlgorithmParameterException if params is not an
* instance of <code>LDAPCertStoreParameters</code>
*/
throws InvalidAlgorithmParameterException {
super(params);
if (!(params instanceof LDAPCertStoreParameters))
throw new InvalidAlgorithmParameterException(
"parameters must be LDAPCertStoreParameters");
// Create InitialDirContext needed to communicate with the server
// Create CertificateFactory for use later on
try {
} catch (CertificateException e) {
throw new InvalidAlgorithmParameterException(
"unable to create CertificateFactory for X.509");
}
if (LIFETIME == 0) {
} else if (LIFETIME < 0) {
} else {
}
}
/**
* Returns an LDAP CertStore. This method consults a cache of
*/
} else {
}
}
return lcs;
}
/**
* Create InitialDirContext.
*
* @param server Server DNS name hosting LDAP service
* @param port Port at which server listens for requests
* @throws InvalidAlgorithmParameterException if creation fails
*/
throws InvalidAlgorithmParameterException {
"com.sun.jndi.ldap.LdapCtxFactory");
try {
/*
* By default, follow referrals unless application has
* overridden property in an application resource file.
*/
}
} catch (NamingException e) {
+ "InvalidAlgorithmParameterException");
e.printStackTrace();
}
("unable to create InitialDirContext using supplied parameters");
throw (InvalidAlgorithmParameterException)ee;
}
}
/**
* Private class encapsulating the actual LDAP operations and cache
* handling. Use:
*
* LDAPRequest request = new LDAPRequest(dn);
* request.addRequestedAttribute(CROSS_CERT);
* request.addRequestedAttribute(CA_CERT);
* byte[][] crossValues = request.getValues(CROSS_CERT);
* byte[][] caValues = request.getValues(CA_CERT);
*
* At most one LDAP request is sent for each instance created. If all
* getValues() calls can be satisfied from the cache, no request
* is sent at all. If a request is sent, all requested attributes
* are always added to the cache irrespective of whether the getValues()
* method is called.
*/
private class LDAPRequest {
}
return name;
}
throw new IllegalStateException("Request already sent");
}
}
/**
* Gets one or more binary values from an attribute.
*
* @param name the location holding the attribute
* @param attrId the attribute identifier
* @return an array of binary values (byte arrays)
* @throws NamingException if a naming exception occurs
*/
+ cacheMisses);
}
cacheHits++;
return values;
}
cacheMisses++;
return values;
}
/**
* Get a map containing the values for this request. The first time
* this method is called on an object, the LDAP request is sent,
* the results parsed and added to a private map and also to the
* cache of this LDAPCertStore. Subsequent calls return the private
* map immediately.
*
* The map contains an entry for each requested attribute. The
* attribute name is the key, values are byte[][]. If there are no
* values for that attribute, values are byte[0][].
*
* @return the value Map
* @throws NamingException if a naming exception occurs
*/
return valueMap;
}
if (DEBUG) {
requests++;
}
}
try {
} catch (NameNotFoundException e) {
// name does not exist on this LDAP server
// treat same as not attributes found
}
}
return valueMap;
}
/**
* Add the values to the cache.
*/
}
/**
* Get the values for the given attribute. If the attribute is null
* or does not contain any values, a zero length byte array is
* returned. NOTE that it is assumed that all values are byte arrays.
*/
throws NamingException {
byte[][] values;
} else {
int i = 0;
+ "enum.next is a string!: " + obj);
}
}
}
}
return values;
}
}
/*
* Gets certificates from an attribute id and location in the LDAP
* directory. Returns a Collection containing only the Certificates that
* match the specified CertSelector.
*
* @param name the location holding the attribute
* @param id the attribute identifier
* @param sel a CertSelector that the Certificates must match
* @return a Collection of Certificates found
* @throws CertStoreException if an exception occurs
*/
/* fetch encoded certs from storage */
byte[][] encodedCert;
try {
} catch (NamingException namingEx) {
throw new CertStoreException(namingEx);
}
int n = encodedCert.length;
if (n == 0) {
}
/* decode certs and check if they satisfy selector */
for (int i = 0; i < n; i++) {
try {
}
} catch (CertificateException e) {
+ "exception while parsing cert, skipping the bad data: ");
}
}
}
return certs;
}
/*
* Gets certificate pairs from an attribute id and location in the LDAP
* directory.
*
* @param name the location holding the attribute
* @param id the attribute identifier
* @return a Collection of X509CertificatePairs found
* @throws CertStoreException if an exception occurs
*/
/* fetch the encoded cert pairs from storage */
byte[][] encodedCertPair;
try {
} catch (NamingException namingEx) {
throw new CertStoreException(namingEx);
}
int n = encodedCertPair.length;
if (n == 0) {
}
new ArrayList<X509CertificatePair>(n);
/* decode each cert pair and add it to the Collection */
for (int i = 0; i < n; i++) {
try {
} catch (CertificateException e) {
"LDAPCertStore.getCertPairs() encountered exception "
+ "while parsing cert, skipping the bad data: ");
}
}
}
return certPairs;
}
/*
* Looks at certificate pairs stored in the crossCertificatePair attribute
* at the specified location in the LDAP directory. Returns a Collection
* containing all Certificates stored in the forward component that match
* the forward CertSelector and all Certificates stored in the reverse
* component that match the reverse CertSelector.
* <p>
* If either forward or reverse is null, all certificates from the
* corresponding component will be rejected.
*
* @param name the location to look in
* @param forward the forward CertSelector (or null)
* @param reverse the reverse CertSelector (or null)
* @return a Collection of Certificates found
* @throws CertStoreException if an exception occurs
*/
throws CertStoreException {
// Get the cert pairs
// Find Certificates that match and put them in a list
new ArrayList<X509Certificate>();
}
}
}
}
}
return matchingCerts;
}
/**
* 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
*/
}
selector = new X509CertSelector();
}
if (!(selector instanceof X509CertSelector)) {
throw new CertStoreException("LDAPCertStore needs an X509CertSelector " +
"to find certs");
}
+ basicConstraints);
}
// basicConstraints:
// -2: only EE certs accepted
// -1: no check is done
// 0: any CA certificate accepted
// >1: certificate's basicConstraints extension pathlen must match
+ "subject is not null");
}
if (basicConstraints > -2) {
if (prefetchCRLs) {
}
}
if (basicConstraints < 0) {
}
if (basicConstraints > -2) {
+ "getMatchingCrossCerts(subject,xsel,null),certs.size(): "
}
+ "getCertificates(subject,CA_CERT,xsel),certs.size(): "
}
}
if (basicConstraints < 0) {
+ "getCertificates(subject,USER_CERT, xsel),certs.size(): "
}
}
} else {
("LDAPCertStore.engineGetCertificates() subject is null");
}
if (basicConstraints == -2) {
throw new CertStoreException("need subject to find EE certs");
}
throw new CertStoreException("need subject or issuer to find certs");
}
}
+ "getMatchingCrossCerts...");
}
if (prefetchCRLs) {
}
+ "getMatchingCrossCerts(issuer,null,xsel),certs.size(): "
}
+ "getCertificates(issuer,CA_CERT,xsel),certs.size(): "
}
}
}
return certs;
}
/*
* Gets CRLs from an attribute id and location in the LDAP directory.
* Returns a Collection containing only the CRLs that match the
* specified CRLSelector.
*
* @param name the location holding the attribute
* @param id the attribute identifier
* @param sel a CRLSelector that the CRLs must match
* @return a Collection of CRLs found
* @throws CertStoreException if an exception occurs
*/
/* fetch the encoded crls from storage */
byte[][] encodedCRL;
try {
} catch (NamingException namingEx) {
throw new CertStoreException(namingEx);
}
int n = encodedCRL.length;
if (n == 0) {
}
/* decode each crl and check if it matches selector */
for (int i = 0; i < n; i++) {
try {
}
} catch (CRLException e) {
+ " while parsing CRL, skipping the bad data: ");
}
}
}
return crls;
}
/**
* 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
*/
throws CertStoreException {
+ selector);
}
// Set up selector and collection to hold CRLs
selector = new X509CRLSelector();
}
if (!(selector instanceof X509CRLSelector)) {
throw new CertStoreException("need X509CRLSelector to find CRLs");
}
// Look in directory entry for issuer of cert we're checking.
if (certChecking != null) {
} else {
// But if we don't know which cert we're checking, try the directory
// entries of all acceptable CRL issuers
if (issuerNames == null) {
throw new CertStoreException("need issuerNames or certChecking to "
+ "find CRLs");
}
}
if (nameObject instanceof byte[]) {
try {
} catch (IllegalArgumentException e) {
continue;
}
} else {
}
// If all we want is CA certs, try to get the (probably shorter) ARL
if (prefetchCRLs) {
}
try {
// no ARLs found. We assume that means that there are
// no ARLs on this server at all and prefetch the CRLs.
prefetchCRLs = true;
} else {
}
} catch (CertStoreException e) {
+ "retrieving ARLs:" + e);
e.printStackTrace();
}
}
}
// Otherwise, get the CRL
// if certChecking is null, we don't know if we should look in ARL or CRL
// attribute, so check both for matching CRLs.
}
}
return crls;
}
// converts an LDAP URI into LDAPCertStoreParameters
return new SunLDAPCertStoreParameters();
} else {
return (port == -1
? new SunLDAPCertStoreParameters(host)
}
}
/*
* methods. This is necessary because the parameters are used as
* keys in the LDAPCertStore cache.
*/
private static class SunLDAPCertStoreParameters
extends LDAPCertStoreParameters {
super(serverName, port);
}
super(serverName);
}
super();
}
if (!(obj instanceof LDAPCertStoreParameters)) {
return false;
}
}
public int hashCode() {
if (hashCode == 0) {
int result = 17;
}
return hashCode;
}
}
/*
* This inner class wraps an existing X509CertSelector and adds
* additional criteria to match on when the certificate's subject is
* different than the LDAP Distinguished Name entry. The LDAPCertStore
* implementation uses the subject DN as the directory entry for
* looking up certificates. This can be problematic if the certificates
* that you want to fetch have a different subject DN than the entry
* where they are stored. You could set the selector's subject to the
* LDAP DN entry, but then the resulting match would fail to find the
* desired certificates because the subject DNs would not match. This
* class avoids that problem by introducing a certSubject which should
* be set to the certificate's subject DN when it is different than
* the LDAP DN.
*/
/**
* Creates an LDAPCertSelector.
*
* @param selector the X509CertSelector to wrap
* @param certSubject the subject DN of the certificate that you want
* to retrieve via LDAP
* @param ldapDN the LDAP DN where the certificate is stored
*/
this.certSubject = certSubject;
}
// we only override the get (accessor methods) since the set methods
// will not be invoked by the code that uses this LDAPCertSelector.
return selector.getCertificate();
}
return selector.getSerialNumber();
}
}
return selector.getIssuerAsString();
}
return selector.getIssuerAsBytes();
}
// return the ldap DN
return subject;
}
// return the ldap DN
}
// return the encoded ldap DN
return subject.getEncoded();
}
public byte[] getSubjectKeyIdentifier() {
return selector.getSubjectKeyIdentifier();
}
public byte[] getAuthorityKeyIdentifier() {
return selector.getAuthorityKeyIdentifier();
}
return selector.getCertificateValid();
}
return selector.getPrivateKeyValid();
}
return selector.getSubjectPublicKeyAlgID();
}
return selector.getSubjectPublicKey();
}
public boolean[] getKeyUsage() {
return selector.getKeyUsage();
}
return selector.getExtendedKeyUsage();
}
public boolean getMatchAllSubjectAltNames() {
return selector.getMatchAllSubjectAltNames();
}
return selector.getSubjectAlternativeNames();
}
public byte[] getNameConstraints() {
return selector.getNameConstraints();
}
public int getBasicConstraints() {
return selector.getBasicConstraints();
}
}
return selector.getPathToNames();
}
// temporarily set the subject criterion to the certSubject
// so that match will not reject the desired certificates
return match;
}
}
/**
* This class has the same purpose as LDAPCertSelector except it is for
* X.509 CRLs.
*/
/**
* Creates an LDAPCRLSelector.
*
* @param selector the X509CRLSelector to wrap
* @param certIssuers the issuer DNs of the CRLs that you want
* to retrieve via LDAP
* @param ldapDN the LDAP DN where the CRL is stored
*/
throws IOException {
this.certIssuers = certIssuers;
}
// we only override the get (accessor methods) since the set methods
// will not be invoked by the code that uses this LDAPCRLSelector.
// return the ldap DN
}
// return the ldap DN
}
}
}
return selector.getDateAndTime();
}
return selector.getCertificateChecking();
}
// temporarily set the issuer criterion to the certIssuers
// so that match will not reject the desired CRL
return match;
}
}
}