/**
* Licensed to the Apache Software Foundation (ASF) under one
* or more contributor license agreements. See the NOTICE file
* distributed with this work for additional information
* regarding copyright ownership. The ASF licenses this file
* to you 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.apache.cxf.xkms.x509.repo.ldap;
import java.io.ByteArrayInputStream;
import java.security.cert.CRLException;
import java.security.cert.CertificateException;
import java.security.cert.CertificateFactory;
import java.security.cert.X509CRL;
import java.security.cert.X509Certificate;
import java.util.ArrayList;
import java.util.List;
import java.util.logging.Level;
import java.util.logging.Logger;
import java.util.regex.Matcher;
import javax.naming.NamingEnumeration;
import javax.naming.NamingException;
import javax.naming.directory.Attribute;
import javax.naming.directory.Attributes;
import javax.naming.directory.BasicAttribute;
import javax.naming.directory.BasicAttributes;
import javax.naming.directory.SearchResult;
import org.apache.cxf.common.logging.LogUtils;
import org.apache.cxf.xkms.handlers.Applications;
import org.apache.cxf.xkms.model.xkms.UseKeyWithType;
import org.apache.cxf.xkms.x509.repo.CertificateRepo;
public class LdapCertificateRepo implements CertificateRepo {
private static final Logger LOG = LogUtils.getL7dLogger(LdapCertificateRepo.class);
private static final String ATTR_OBJECT_CLASS = "objectClass";
private LdapSearch ldapSearch;
private String rootDN;
private CertificateFactory certificateFactory;
private final LdapSchemaConfig ldapConfig;
private final String filterUIDTemplate;
private final String filterIssuerSerialTemplate;
/**
*
* @param ldapSearch
* @param rootDN rootDN of the LDAP tree
* @param trustedAuthorityFilter
* @param intermediateFilter
* @param attrName
*/
public LdapCertificateRepo(LdapSearch ldapSearch, LdapSchemaConfig ldapConfig, String rootDN) {
this.ldapSearch = ldapSearch;
this.ldapSearch = ldapSearch;
this.ldapConfig = ldapConfig;
this.rootDN = rootDN;
try {
this.certificateFactory = CertificateFactory.getInstance("X.509");
} catch (CertificateException e) {
LOG.log(Level.SEVERE, e.getMessage(), e);
}
filterUIDTemplate = "(" + ldapConfig.getAttrUID() + "=%s)";
filterIssuerSerialTemplate = "(&(" + ldapConfig.getAttrIssuerID() + "=%s)(" + ldapConfig.getAttrSerialNumber()
+ "=%s))";
}
@Override
public List<X509Certificate> getTrustedCaCerts() {
return getCertificatesFromLdap(rootDN, ldapConfig.getTrustedAuthorityFilter(), ldapConfig.getAttrCrtBinary());
}
@Override
public List<X509Certificate> getCaCerts() {
return getCertificatesFromLdap(rootDN, ldapConfig.getIntermediateFilter(), ldapConfig.getAttrCrtBinary());
}
@Override
public List<X509CRL> getCRLs() {
return getCRLsFromLdap(rootDN, ldapConfig.getCrlFilter(), ldapConfig.getAttrCrlBinary());
}
private List<X509Certificate> getCertificatesFromLdap(String tmpRootDN, String tmpFilter, String tmpAttrName) {
try {
List<X509Certificate> certificates = new ArrayList<X509Certificate>();
NamingEnumeration<SearchResult> answer = ldapSearch.searchSubTree(tmpRootDN, tmpFilter);
while (answer.hasMore()) {
SearchResult sr = answer.next();
Attributes attrs = sr.getAttributes();
Attribute attribute = attrs.get(tmpAttrName);
if (attribute != null) {
CertificateFactory cf = CertificateFactory.getInstance("X.509");
X509Certificate certificate = (X509Certificate) cf.generateCertificate(new ByteArrayInputStream(
(byte[]) attribute.get()));
certificates.add(certificate);
}
}
return certificates;
} catch (CertificateException e) {
throw new RuntimeException(e.getMessage(), e);
} catch (NamingException e) {
throw new RuntimeException(e.getMessage(), e);
}
}
private List<X509CRL> getCRLsFromLdap(String tmpRootDN, String tmpFilter, String tmpAttrName) {
try {
List<X509CRL> crls = new ArrayList<X509CRL>();
NamingEnumeration<SearchResult> answer = ldapSearch.searchSubTree(tmpRootDN, tmpFilter);
while (answer.hasMore()) {
SearchResult sr = answer.next();
Attributes attrs = sr.getAttributes();
Attribute attribute = attrs.get(tmpAttrName);
if (attribute != null) {
CertificateFactory cf = CertificateFactory.getInstance("X.509");
X509CRL crl = (X509CRL) cf.generateCRL(new ByteArrayInputStream(
(byte[]) attribute.get()));
crls.add(crl);
}
}
return crls;
} catch (CertificateException e) {
throw new RuntimeException(e.getMessage(), e);
} catch (NamingException e) {
throw new RuntimeException(e.getMessage(), e);
} catch (CRLException e) {
throw new RuntimeException(e.getMessage(), e);
}
}
private void saveCertificate(X509Certificate cert, String dn) {
Attributes attribs = new BasicAttributes();
attribs.put(new BasicAttribute(ATTR_OBJECT_CLASS, ldapConfig.getCertObjectClass()));
attribs.put(new BasicAttribute(ldapConfig.getAttrUID(), cert.getSubjectX500Principal().getName()));
attribs.put(new BasicAttribute(ldapConfig.getAttrIssuerID(), cert.getIssuerX500Principal().getName()));
attribs.put(new BasicAttribute(ldapConfig.getAttrSerialNumber(), cert.getSerialNumber().toString(16)));
addConstantAttributes(ldapConfig.getConstAttrNamesCSV(), ldapConfig.getConstAttrValuesCSV(), attribs);
try {
attribs.put(new BasicAttribute(ldapConfig.getAttrCrtBinary(), cert.getEncoded()));
ldapSearch.bind(dn, attribs);
} catch (Exception e) {
throw new RuntimeException(e.getMessage(), e);
}
}
private void addConstantAttributes(String names, String values, Attributes attribs) {
String[] arrNames = names.split(",");
String[] arrValues = values.split(",");
if (arrNames.length != arrValues.length) {
throw new IllegalArgumentException(
String.format("Inconsintent constant attributes: %s; %s", names, values));
}
for (int i = 0; i < arrNames.length; i++) {
attribs.put(new BasicAttribute(arrNames[i], arrValues[i]));
}
}
@Override
public X509Certificate findBySubjectDn(String id) {
X509Certificate cert = null;
try {
String dn = id;
if ((rootDN != null) && !(rootDN.isEmpty())) {
dn = dn + "," + rootDN;
}
cert = getCertificateForDn(dn);
} catch (NamingException e) {
// Not found
}
// Try to find certificate by search for uid attribute
try {
cert = getCertificateForUIDAttr(id);
} catch (NamingException e) {
// Not found
}
return cert;
}
@Override
public X509Certificate findByServiceName(String serviceName) {
X509Certificate cert = null;
try {
String dn = getDnForServiceName(serviceName);
cert = getCertificateForDn(dn);
} catch (NamingException e) {
// Not found
}
// Try to find certificate by search for uid attribute
try {
String uidAttr = String.format(ldapConfig.getServiceCertUIDTemplate(), serviceName);
cert = getCertificateForUIDAttr(uidAttr);
} catch (NamingException e) {
// Not found
}
return cert;
}
private String getDnForServiceName(String serviceName) {
String escapedIdentifier = serviceName.replaceAll("\\/", Matcher.quoteReplacement("\\/"));
return String.format(ldapConfig.getServiceCertRDNTemplate(), escapedIdentifier) + "," + rootDN;
}
private X509Certificate getCertificateForDn(String dn) throws NamingException {
Attribute attr = ldapSearch.getAttribute(dn, ldapConfig.getAttrCrtBinary());
return getCert(attr);
}
private X509Certificate getCertificateForUIDAttr(String uid) throws NamingException {
String filter = String.format(filterUIDTemplate, uid);
Attribute attr = ldapSearch.findAttribute(rootDN, filter, ldapConfig.getAttrCrtBinary());
return getCert(attr);
}
@Override
public X509Certificate findByIssuerSerial(String issuer, String serial) {
if ((issuer == null) || (serial == null)) {
throw new IllegalArgumentException("Issuer and serial applications are expected in request");
}
String filter = String.format(filterIssuerSerialTemplate, issuer, serial);
try {
Attribute attr = ldapSearch.findAttribute(rootDN, filter, ldapConfig.getAttrCrtBinary());
return getCert(attr);
} catch (NamingException e) {
throw new RuntimeException(e.getMessage(), e);
}
}
private X509Certificate getCert(Attribute attr) {
if (attr == null) {
return null;
}
byte[] data;
try {
data = (byte[]) attr.get();
} catch (NamingException e) {
throw new RuntimeException(e.getMessage(), e);
}
if (data == null) {
return null;
}
try {
return (X509Certificate) certificateFactory.generateCertificate(new ByteArrayInputStream(data));
} catch (CertificateException e) {
throw new RuntimeException("Error deserializing certificate: " + e.getMessage(), e);
}
}
@Override
public void saveCertificate(X509Certificate cert, UseKeyWithType key) {
Applications application = Applications.fromUri(key.getApplication());
String dn;
if (application == Applications.PKIX) {
dn = key.getIdentifier() + "," + rootDN;
} else if (application == Applications.SERVICE_SOAP) {
dn = getDnForServiceName(key.getIdentifier());
} else {
throw new IllegalArgumentException("Unsupported Application " + application);
}
saveCertificate(cert, dn);
}
}