001: package org.bouncycastle.jce.provider;
002:
003: import org.bouncycastle.asn1.ASN1InputStream;
004: import org.bouncycastle.asn1.x509.CertificatePair;
005: import org.bouncycastle.jce.X509LDAPCertStoreParameters;
006:
007: import javax.naming.Context;
008: import javax.naming.NamingEnumeration;
009: import javax.naming.NamingException;
010: import javax.naming.directory.Attribute;
011: import javax.naming.directory.DirContext;
012: import javax.naming.directory.InitialDirContext;
013: import javax.naming.directory.SearchControls;
014: import javax.naming.directory.SearchResult;
015: import javax.security.auth.x500.X500Principal;
016: import java.io.ByteArrayInputStream;
017: import java.io.IOException;
018: import java.security.InvalidAlgorithmParameterException;
019: import java.security.cert.CRL;
020: import java.security.cert.CRLSelector;
021: import java.security.cert.CertSelector;
022: import java.security.cert.CertStoreException;
023: import java.security.cert.CertStoreParameters;
024: import java.security.cert.CertStoreSpi;
025: import java.security.cert.Certificate;
026: import java.security.cert.CertificateFactory;
027: import java.security.cert.X509CRLSelector;
028: import java.security.cert.X509CertSelector;
029: import java.util.ArrayList;
030: import java.util.Collection;
031: import java.util.HashSet;
032: import java.util.Iterator;
033: import java.util.List;
034: import java.util.Properties;
035: import java.util.Set;
036:
037: /**
038: *
039: * This is a general purpose implementation to get X.509 certificates and CRLs
040: * from a LDAP location.
041: * <p>
042: * At first a search is performed in the ldap*AttributeNames of the
043: * {@link org.bouncycastle.jce.X509LDAPCertStoreParameters} with the given
044: * information of the subject (for all kind of certificates) or issuer (for
045: * CRLs), respectively, if a X509CertSelector is given with that details. For
046: * CRLs, CA certificates and cross certificates a coarse search is made only for
047: * entries with that content to get more possibly matchign results.
048: */
049: public class X509LDAPCertStoreSpi extends CertStoreSpi {
050: private X509LDAPCertStoreParameters params;
051:
052: public X509LDAPCertStoreSpi(CertStoreParameters params)
053: throws InvalidAlgorithmParameterException {
054: super (params);
055:
056: if (!(params instanceof X509LDAPCertStoreParameters)) {
057: throw new InvalidAlgorithmParameterException(
058: "org.bouncycastle.jce.provider.LDAPCertStoreSpi: parameter must be a LDAPCertStoreParameters object\n"
059: + params.toString());
060: }
061:
062: this .params = (X509LDAPCertStoreParameters) params;
063: }
064:
065: /**
066: * Initial Context Factory.
067: */
068: private static String LDAP_PROVIDER = "com.sun.jndi.ldap.LdapCtxFactory";
069:
070: /**
071: * Processing referrals..
072: */
073: private static String REFERRALS_IGNORE = "ignore";
074:
075: /**
076: * Security level to be used for LDAP connections.
077: */
078: private static final String SEARCH_SECURITY_LEVEL = "none";
079:
080: /**
081: * Package Prefix for loading URL context factories.
082: */
083: private static final String URL_CONTEXT_PREFIX = "com.sun.jndi.url";
084:
085: private DirContext connectLDAP() throws NamingException {
086: Properties props = new Properties();
087: props.setProperty(Context.INITIAL_CONTEXT_FACTORY,
088: LDAP_PROVIDER);
089: props.setProperty(Context.BATCHSIZE, "0");
090:
091: props.setProperty(Context.PROVIDER_URL, params.getLdapURL());
092: props.setProperty(Context.URL_PKG_PREFIXES, URL_CONTEXT_PREFIX);
093: props.setProperty(Context.REFERRAL, REFERRALS_IGNORE);
094: props.setProperty(Context.SECURITY_AUTHENTICATION,
095: SEARCH_SECURITY_LEVEL);
096:
097: DirContext ctx = new InitialDirContext(props);
098: return ctx;
099: }
100:
101: private String parseDN(String subject, String subjectAttributeName) {
102: String temp = subject;
103: int begin = temp.toLowerCase().indexOf(
104: subjectAttributeName.toLowerCase());
105: temp = temp.substring(begin + subjectAttributeName.length());
106: int end = temp.indexOf(',');
107: if (end == -1) {
108: end = temp.length();
109: }
110: while (temp.charAt(end - 1) == '\\') {
111: end = temp.indexOf(',', end + 1);
112: if (end == -1) {
113: end = temp.length();
114: }
115: }
116: temp = temp.substring(0, end);
117: begin = temp.indexOf('=');
118: temp = temp.substring(begin + 1);
119: if (temp.charAt(0) == ' ') {
120: temp = temp.substring(1);
121: }
122: if (temp.startsWith("\"")) {
123: temp = temp.substring(1);
124: }
125: if (temp.endsWith("\"")) {
126: temp = temp.substring(0, temp.length() - 1);
127: }
128: return temp;
129: }
130:
131: public Collection engineGetCertificates(CertSelector selector)
132: throws CertStoreException {
133: if (!(selector instanceof X509CertSelector)) {
134: throw new CertStoreException(
135: "selector is not a X509CertSelector");
136: }
137: X509CertSelector xselector = (X509CertSelector) selector;
138:
139: Set certSet = new HashSet();
140:
141: Set set = getEndCertificates(xselector);
142: set.addAll(getCACertificates(xselector));
143: set.addAll(getCrossCertificates(xselector));
144:
145: Iterator it = set.iterator();
146:
147: try {
148: CertificateFactory cf = CertificateFactory.getInstance(
149: "X.509", "BC");
150: while (it.hasNext()) {
151: byte[] bytes = (byte[]) it.next();
152: List bytesList = new ArrayList();
153: bytesList.add(bytes);
154: try {
155: CertificatePair pair = CertificatePair
156: .getInstance(new ASN1InputStream(bytes)
157: .readObject());
158: bytesList.clear();
159: if (pair.getForward() != null) {
160: bytesList.add(pair.getForward().getEncoded());
161: }
162: if (pair.getReverse() != null) {
163: bytesList.add(pair.getReverse().getEncoded());
164: }
165: } catch (IOException e) {
166:
167: } catch (IllegalArgumentException e) {
168:
169: }
170: for (Iterator it2 = bytesList.iterator(); it2.hasNext();) {
171: ByteArrayInputStream bIn = new ByteArrayInputStream(
172: (byte[]) it2.next());
173: try {
174: Certificate cert = cf.generateCertificate(bIn);
175: // System.out.println(((X509Certificate)
176: // cert).getSubjectX500Principal());
177: if (xselector.match(cert)) {
178: certSet.add(cert);
179: }
180: } catch (Exception e) {
181:
182: }
183: }
184: }
185: } catch (Exception e) {
186: throw new CertStoreException(
187: "certificate cannot be constructed from LDAP result: "
188: + e);
189: }
190:
191: return certSet;
192: }
193:
194: private Set certSubjectSerialSearch(X509CertSelector xselector,
195: String[] attrs, String attrName, String subjectAttributeName)
196: throws CertStoreException {
197: Set set = new HashSet();
198: try {
199: if (xselector.getSubjectAsBytes() != null
200: || xselector.getSubjectAsString() != null
201: || xselector.getCertificate() != null) {
202: String subject = null;
203: String serial = null;
204: if (xselector.getCertificate() != null) {
205: subject = xselector.getCertificate()
206: .getSubjectX500Principal().getName(
207: "RFC1779");
208: serial = xselector.getCertificate()
209: .getSerialNumber().toString();
210: } else {
211: if (xselector.getSubjectAsBytes() != null) {
212: subject = new X500Principal(xselector
213: .getSubjectAsBytes())
214: .getName("RFC1779");
215: } else {
216: subject = xselector.getSubjectAsString();
217: }
218: }
219: String attrValue = parseDN(subject,
220: subjectAttributeName);
221: set.addAll(search(attrName, "*" + attrValue + "*",
222: attrs));
223: if (serial != null
224: && params.getSearchForSerialNumberIn() != null) {
225: attrValue = serial;
226: attrName = params.getSearchForSerialNumberIn();
227: set.addAll(search(attrName, "*" + attrValue + "*",
228: attrs));
229: }
230: } else {
231: set.addAll(search(attrName, "*", attrs));
232: }
233: } catch (IOException e) {
234: throw new CertStoreException(
235: "exception processing selector: " + e);
236: }
237:
238: return set;
239: }
240:
241: private Set getEndCertificates(X509CertSelector xselector)
242: throws CertStoreException {
243: String[] attrs = { params.getUserCertificateAttribute() };
244: String attrName = params.getLdapUserCertificateAttributeName();
245: String subjectAttributeName = params
246: .getUserCertificateSubjectAttributeName();
247:
248: Set set = certSubjectSerialSearch(xselector, attrs, attrName,
249: subjectAttributeName);
250: return set;
251: }
252:
253: private Set getCACertificates(X509CertSelector xselector)
254: throws CertStoreException {
255: String[] attrs = { params.getCACertificateAttribute() };
256: String attrName = params.getLdapCACertificateAttributeName();
257: String subjectAttributeName = params
258: .getCACertificateSubjectAttributeName();
259: Set set = certSubjectSerialSearch(xselector, attrs, attrName,
260: subjectAttributeName);
261:
262: if (set.isEmpty()) {
263: set.addAll(search(null, "*", attrs));
264: }
265:
266: return set;
267: }
268:
269: private Set getCrossCertificates(X509CertSelector xselector)
270: throws CertStoreException {
271: String[] attrs = { params.getCrossCertificateAttribute() };
272: String attrName = params.getLdapCrossCertificateAttributeName();
273: String subjectAttributeName = params
274: .getCrossCertificateSubjectAttributeName();
275: Set set = certSubjectSerialSearch(xselector, attrs, attrName,
276: subjectAttributeName);
277:
278: if (set.isEmpty()) {
279: set.addAll(search(null, "*", attrs));
280: }
281:
282: return set;
283: }
284:
285: public Collection engineGetCRLs(CRLSelector selector)
286: throws CertStoreException {
287: String[] attrs = { params
288: .getCertificateRevocationListAttribute() };
289: if (!(selector instanceof X509CRLSelector)) {
290: throw new CertStoreException(
291: "selector is not a X509CRLSelector");
292: }
293: X509CRLSelector xselector = (X509CRLSelector) selector;
294:
295: Set crlSet = new HashSet();
296:
297: String attrName = params
298: .getLdapCertificateRevocationListAttributeName();
299: Set set = new HashSet();
300:
301: if (xselector.getIssuerNames() != null) {
302: for (Iterator it = xselector.getIssuerNames().iterator(); it
303: .hasNext();) {
304: Object o = it.next();
305: String attrValue = null;
306: if (o instanceof String) {
307: String issuerAttributeName = params
308: .getCertificateRevocationListIssuerAttributeName();
309: attrValue = parseDN((String) o, issuerAttributeName);
310: } else {
311: String issuerAttributeName = params
312: .getCertificateRevocationListIssuerAttributeName();
313: attrValue = parseDN(new X500Principal((byte[]) o)
314: .getName("RFC1779"), issuerAttributeName);
315: }
316: set.addAll(search(attrName, "*" + attrValue + "*",
317: attrs));
318: }
319: } else {
320: set.addAll(search(attrName, "*", attrs));
321: }
322: set.addAll(search(null, "*", attrs));
323: Iterator it = set.iterator();
324:
325: try {
326: CertificateFactory cf = CertificateFactory.getInstance(
327: "X.509", "BC");
328: while (it.hasNext()) {
329: CRL crl = cf.generateCRL(new ByteArrayInputStream(
330: (byte[]) it.next()));
331: if (xselector.match(crl)) {
332: crlSet.add(crl);
333: }
334: }
335: } catch (Exception e) {
336: throw new CertStoreException(
337: "CRL cannot be constructed from LDAP result " + e);
338: }
339:
340: return crlSet;
341: }
342:
343: /**
344: * Returns a Set of byte arrays with the certificate or CRL encodings.
345: *
346: * @param attributeName The attribute name to look for in the LDAP.
347: * @param attributeValue The value the attribute name must have.
348: * @param attrs The attributes in the LDAP which hold the certificate,
349: * certificate pair or CRL in a found entry.
350: * @return Set of byte arrays with the certificate encodings.
351: */
352: private Set search(String attributeName, String attributeValue,
353: String[] attrs) throws CertStoreException {
354: String filter = attributeName + "=" + attributeValue;
355: if (attributeName == null) {
356: filter = null;
357: }
358: DirContext ctx = null;
359: Set set = new HashSet();
360: try {
361:
362: ctx = connectLDAP();
363:
364: SearchControls constraints = new SearchControls();
365: constraints.setSearchScope(SearchControls.SUBTREE_SCOPE);
366: constraints.setCountLimit(0);
367: for (int i = 0; i < attrs.length; i++) {
368: String temp[] = new String[1];
369: temp[0] = attrs[i];
370: constraints.setReturningAttributes(temp);
371:
372: String filter2 = "(&(" + filter + ")(" + temp[0]
373: + "=*))";
374: if (filter == null) {
375: filter2 = "(" + temp[0] + "=*)";
376: }
377: NamingEnumeration results = ctx.search(params
378: .getBaseDN(), filter2, constraints);
379: while (results.hasMoreElements()) {
380: SearchResult sr = (SearchResult) results.next();
381: // should only be one attribute in the attribute set with
382: // one
383: // attribute value as byte array
384: NamingEnumeration enumeration = ((Attribute) (sr
385: .getAttributes().getAll().next())).getAll();
386: while (enumeration.hasMore()) {
387: Object o = enumeration.next();
388: set.add(o);
389: }
390: }
391: }
392: } catch (Exception e) {
393: throw new CertStoreException(
394: "Error getting results from LDAP directory " + e);
395:
396: } finally {
397: try {
398: if (null != ctx) {
399: ctx.close();
400: }
401: } catch (Exception e) {
402: }
403: }
404: return set;
405: }
406:
407: }
|