/** * 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.client; import java.io.ByteArrayInputStream; import java.math.BigInteger; import java.security.cert.CertificateEncodingException; import java.security.cert.CertificateException; import java.security.cert.CertificateFactory; import java.security.cert.X509Certificate; import java.util.ArrayList; import java.util.Collections; import java.util.List; import java.util.UUID; import javax.xml.bind.JAXBElement; import javax.xml.namespace.QName; import org.apache.cxf.xkms.exception.ExceptionMapper; import org.apache.cxf.xkms.exception.XKMSException; import org.apache.cxf.xkms.exception.XKMSLocateException; import org.apache.cxf.xkms.exception.XKMSValidateException; import org.apache.cxf.xkms.handlers.Applications; import org.apache.cxf.xkms.handlers.XKMSConstants; import org.apache.cxf.xkms.model.xkms.KeyBindingEnum; import org.apache.cxf.xkms.model.xkms.KeyUsageEnum; import org.apache.cxf.xkms.model.xkms.LocateRequestType; import org.apache.cxf.xkms.model.xkms.LocateResultType; import org.apache.cxf.xkms.model.xkms.MessageAbstractType; import org.apache.cxf.xkms.model.xkms.QueryKeyBindingType; import org.apache.cxf.xkms.model.xkms.StatusType; import org.apache.cxf.xkms.model.xkms.UseKeyWithType; import org.apache.cxf.xkms.model.xkms.ValidateRequestType; import org.apache.cxf.xkms.model.xkms.ValidateResultType; import org.apache.cxf.xkms.model.xmldsig.KeyInfoType; import org.apache.cxf.xkms.model.xmldsig.X509DataType; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import org.w3._2002._03.xkms_wsdl.XKMSPortType; public class XKMSInvoker { private static final Logger LOG = LoggerFactory.getLogger(XKMSInvoker.class); private static final org.apache.cxf.xkms.model.xmldsig.ObjectFactory DSIG_OF = new org.apache.cxf.xkms.model.xmldsig.ObjectFactory(); private static final org.apache.cxf.xkms.model.xkms.ObjectFactory XKMS_OF = new org.apache.cxf.xkms.model.xkms.ObjectFactory(); private static final String XKMS_LOCATE_INVALID_CERTIFICATE = "Cannot instantiate X509 certificate from XKMS response"; private static final String XKMS_VALIDATE_ERROR = "Certificate [%s] is not valid"; private final XKMSPortType xkmsConsumer; public XKMSInvoker(XKMSPortType xkmsConsumer) { this.xkmsConsumer = xkmsConsumer; } public X509Certificate getServiceCertificate(QName serviceName) { return getCertificateForId(Applications.SERVICE_NAME, serviceName.toString()); } public X509Certificate getCertificateForId(Applications application, String id) { List<X509AppId> ids = Collections.singletonList(new X509AppId(application, id)); return getCertificate(ids); } public X509Certificate getCertificateForIssuerSerial(String issuerDN, BigInteger serial) { List<X509AppId> ids = new ArrayList<>(); ids.add(new X509AppId(Applications.ISSUER, issuerDN)); ids.add(new X509AppId(Applications.SERIAL, serial.toString(16))); return getCertificate(ids); } public X509Certificate getCertificateForEndpoint(String endpoint) { List<X509AppId> ids = new ArrayList<>(); ids.add(new X509AppId(Applications.SERVICE_ENDPOINT, endpoint)); return getCertificate(ids); } public X509Certificate getCertificate(List<X509AppId> ids) { try { LocateRequestType locateRequestType = prepareLocateXKMSRequest(ids); LocateResultType locateResultType = xkmsConsumer.locate(locateRequestType); return parseLocateXKMSResponse(locateResultType, ids); } catch (RuntimeException e) { String msg = String .format("XKMS locate call fails for certificate: %s. Error: %s", ids, e.getMessage()); LOG.warn(msg, e); throw new XKMSLocateException(msg, e); } } public boolean validateCertificate(X509Certificate cert) { return checkCertificateValidity(cert, false); } public boolean validateDirectTrustCertificate(X509Certificate cert) { return checkCertificateValidity(cert, true); } protected boolean checkCertificateValidity(X509Certificate cert, boolean directTrust) { try { ValidateRequestType validateRequestType = prepareValidateXKMSRequest(cert); if (directTrust) { validateRequestType.getQueryKeyBinding().getKeyUsage() .add(KeyUsageEnum.HTTP_WWW_W_3_ORG_2002_03_XKMS_SIGNATURE); } ValidateResultType validateResultType = xkmsConsumer.validate(validateRequestType); String id = cert.getSubjectDN().getName(); CertificateValidationResult result = parseValidateXKMSResponse(validateResultType, id); if (!result.isValid()) { LOG.warn(String.format("Certificate %s is not valid: %s", cert.getSubjectDN(), result.getDescription())); } return result.isValid(); } catch (RuntimeException e) { String msg = String.format("XKMS validate call fails for certificate: %s. Error: %s", cert.getSubjectDN(), e.getMessage()); LOG.warn(msg, e); throw new XKMSValidateException(msg, e); } } protected LocateRequestType prepareLocateXKMSRequest(List<X509AppId> ids) { QueryKeyBindingType queryKeyBindingType = XKMS_OF .createQueryKeyBindingType(); for (X509AppId id : ids) { UseKeyWithType useKeyWithType = XKMS_OF.createUseKeyWithType(); useKeyWithType.setIdentifier(id.getId()); useKeyWithType.setApplication(id.getApplication().getUri()); queryKeyBindingType.getUseKeyWith().add(useKeyWithType); } LocateRequestType locateRequestType = XKMS_OF.createLocateRequestType(); locateRequestType.setQueryKeyBinding(queryKeyBindingType); setGenericRequestParams(locateRequestType); return locateRequestType; } @SuppressWarnings("unchecked") protected X509Certificate parseLocateXKMSResponse(LocateResultType locateResultType, List<X509AppId> ids) { XKMSException exception = ExceptionMapper.fromResponse(locateResultType); if (exception != null) { throw exception; } if (!locateResultType.getUnverifiedKeyBinding().iterator().hasNext()) { LOG.warn("X509Certificate is not found in XKMS for id: " + ids); return null; } KeyInfoType keyInfo = locateResultType.getUnverifiedKeyBinding() .iterator().next().getKeyInfo(); if (!keyInfo.getContent().iterator().hasNext()) { LOG.warn("X509Certificate is not found in XKMS for id: " + ids); return null; } JAXBElement<X509DataType> x509Data = (JAXBElement<X509DataType>)keyInfo .getContent().iterator().next(); JAXBElement<byte[]> certificate = (JAXBElement<byte[]>)x509Data .getValue().getX509IssuerSerialOrX509SKIOrX509SubjectName() .iterator().next(); try { CertificateFactory cf = CertificateFactory.getInstance("X.509"); return (X509Certificate)cf .generateCertificate(new ByteArrayInputStream(certificate .getValue())); } catch (CertificateException e) { throw new XKMSLocateException(XKMS_LOCATE_INVALID_CERTIFICATE, e); } } protected ValidateRequestType prepareValidateXKMSRequest( X509Certificate cert) { JAXBElement<byte[]> x509Cert; try { x509Cert = DSIG_OF.createX509DataTypeX509Certificate(cert .getEncoded()); } catch (CertificateEncodingException e) { throw new IllegalArgumentException(e); } X509DataType x509DataType = DSIG_OF.createX509DataType(); x509DataType.getX509IssuerSerialOrX509SKIOrX509SubjectName().add( x509Cert); JAXBElement<X509DataType> x509Data = DSIG_OF .createX509Data(x509DataType); KeyInfoType keyInfoType = DSIG_OF.createKeyInfoType(); keyInfoType.getContent().add(x509Data); QueryKeyBindingType queryKeyBindingType = XKMS_OF .createQueryKeyBindingType(); queryKeyBindingType.setKeyInfo(keyInfoType); ValidateRequestType validateRequestType = XKMS_OF .createValidateRequestType(); setGenericRequestParams(validateRequestType); validateRequestType.setQueryKeyBinding(queryKeyBindingType); // temporary validateRequestType.setId(cert.getSubjectDN().toString()); return validateRequestType; } protected CertificateValidationResult parseValidateXKMSResponse(ValidateResultType validateResultType, String id) { XKMSException exception = ExceptionMapper.fromResponse(validateResultType); if (exception != null) { throw exception; } StatusType status = validateResultType.getKeyBinding().iterator() .next().getStatus(); if (KeyBindingEnum.HTTP_WWW_W_3_ORG_2002_03_XKMS_VALID != status.getStatusValue()) { return new CertificateValidationResult(false, XKMS_VALIDATE_ERROR); } return new CertificateValidationResult(true, null); } public static class CertificateValidationResult { private final boolean valid; private final String description; public CertificateValidationResult(boolean valid, String description) { this.valid = valid; this.description = description; } public boolean isValid() { return valid; } public String getDescription() { return description; } } private void setGenericRequestParams(MessageAbstractType request) { request.setService(XKMSConstants.XKMS_ENDPOINT_NAME); request.setId(UUID.randomUUID().toString()); } }