
com.helger.peppol.httpclient.SMPHttpResponseHandlerSigned Maven / Gradle / Ivy
/**
* Copyright (C) 2015-2019 Philip Helger (www.helger.com)
* philip[at]helger[dot]com
*
* The Original Code is Copyright The PEPPOL project (http://www.peppol.eu)
*
* This Source Code Form is subject to the terms of the Mozilla Public
* License, v. 2.0. If a copy of the MPL was not distributed with this
* file, You can obtain one at http://mozilla.org/MPL/2.0/.
*/
package com.helger.peppol.httpclient;
import java.io.IOException;
import java.io.InputStream;
import java.util.Iterator;
import javax.annotation.Nonnull;
import javax.annotation.WillNotClose;
import javax.xml.crypto.MarshalException;
import javax.xml.crypto.dsig.Reference;
import javax.xml.crypto.dsig.XMLSignature;
import javax.xml.crypto.dsig.XMLSignatureException;
import javax.xml.crypto.dsig.XMLSignatureFactory;
import javax.xml.crypto.dsig.dom.DOMValidateContext;
import org.apache.http.HttpEntity;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.w3c.dom.Document;
import org.w3c.dom.NodeList;
import com.helger.commons.ValueEnforcer;
import com.helger.commons.collection.ArrayHelper;
import com.helger.commons.io.stream.NonBlockingByteArrayInputStream;
import com.helger.commons.io.stream.StreamHelper;
import com.helger.jaxb.GenericJAXBMarshaller;
import com.helger.peppol.smpclient.SMPClientConfiguration;
import com.helger.peppol.smpclient.exception.SMPClientBadResponseException;
import com.helger.security.keystore.EKeyStoreType;
import com.helger.xml.serialize.read.DOMReader;
/**
* This is the Apache HTTP client response handler to verify signed HTTP
* response messages.
*
* Note: this class is also licensed under Apache 2 license, as it was not part
* of the original implementation
*
*
* @author Philip Helger
* @param
* The type of object to be handled.
*/
public class SMPHttpResponseHandlerSigned extends AbstractSMPResponseHandler
{
public static final boolean DEFAULT_CHECK_CERTIFICATE = true;
private static final Logger LOGGER = LoggerFactory.getLogger (SMPHttpResponseHandlerSigned.class);
private final GenericJAXBMarshaller m_aMarshaller;
private boolean m_bCheckCertificate = DEFAULT_CHECK_CERTIFICATE;
public SMPHttpResponseHandlerSigned (@Nonnull final GenericJAXBMarshaller aMarshaller)
{
m_aMarshaller = ValueEnforcer.notNull (aMarshaller, "Marshaller");
}
/**
* Check the certificate retrieved from a signed SMP response? This may be
* helpful for debugging and testing of SMP client connections!
*
* @param bCheckCertificate
* true
to enable SMP response checking (on by default) or
* false
to disable it.
* @return this for chaining
* @since 5.2.1
*/
@Nonnull
public SMPHttpResponseHandlerSigned setCheckCertificate (final boolean bCheckCertificate)
{
m_bCheckCertificate = bCheckCertificate;
return this;
}
/**
* @return true
if SMP client response certificate checking is
* enabled, false
if it is disabled. By default this
* check is enabled (see {@link #DEFAULT_CHECK_CERTIFICATE}).
* @since 5.2.1
*/
public boolean isCheckCertificate ()
{
return m_bCheckCertificate;
}
private static boolean _checkSignature (@Nonnull @WillNotClose final InputStream aEntityInputStream) throws MarshalException,
XMLSignatureException
{
// Get response from servlet
final Document aDocument = DOMReader.readXMLDOM (aEntityInputStream);
// We make sure that the XML is a Signed. If not, we don't have to check
// any certificates.
// Find Signature element.
final NodeList aNodeList = aDocument.getElementsByTagNameNS (XMLSignature.XMLNS, "Signature");
if (aNodeList == null || aNodeList.getLength () == 0)
throw new IllegalArgumentException ("Element not found in SMP XML response");
final EKeyStoreType eTruststoreType = SMPClientConfiguration.getTrustStoreType ();
final String sTruststorePath = SMPClientConfiguration.getTrustStorePath ();
final String sTrustStorePassword = SMPClientConfiguration.getTrustStorePassword ();
final TrustStoreBasedX509KeySelector aKeySelector = new TrustStoreBasedX509KeySelector (eTruststoreType,
sTruststorePath,
sTrustStorePassword);
// Create a DOMValidateContext and specify a KeySelector
// and document context.
final DOMValidateContext aValidateContext = new DOMValidateContext (aKeySelector, aNodeList.item (0));
final XMLSignatureFactory aSignatureFactory = XMLSignatureFactory.getInstance ("DOM");
// Unmarshal the XMLSignature.
final XMLSignature aSignature = aSignatureFactory.unmarshalXMLSignature (aValidateContext);
// Validate the XMLSignature.
final boolean bCoreValid = aSignature.validate (aValidateContext);
if (!bCoreValid)
{
// This code block is for debugging purposes only - it has no semantical
// influence
LOGGER.info ("Signature failed core validation");
final boolean bSignatureValueValid = aSignature.getSignatureValue ().validate (aValidateContext);
if (LOGGER.isInfoEnabled ())
LOGGER.info (" Signature value valid: " + bSignatureValueValid);
if (!bSignatureValueValid)
{
// Check the validation status of each Reference.
int nIndex = 0;
final Iterator > i = aSignature.getSignedInfo ().getReferences ().iterator ();
while (i.hasNext ())
{
final boolean bRefValid = ((Reference) i.next ()).validate (aValidateContext);
if (LOGGER.isInfoEnabled ())
LOGGER.info (" Reference[" + nIndex + "] validity status: " + (bRefValid ? "valid" : "NOT valid!"));
++nIndex;
}
}
}
return bCoreValid;
}
@Override
@Nonnull
public T handleEntity (@Nonnull final HttpEntity aEntity) throws SMPClientBadResponseException, IOException
{
// Get complete response as one big byte buffer
final byte [] aResponseBytes = StreamHelper.getAllBytes (aEntity.getContent ());
if (ArrayHelper.isEmpty (aResponseBytes))
throw new SMPClientBadResponseException ("Could not read SMP server response content");
if (m_bCheckCertificate)
{
try (final InputStream aIS = new NonBlockingByteArrayInputStream (aResponseBytes))
{
// Check the signature
if (!_checkSignature (aIS))
throw new SMPClientBadResponseException ("Signature returned from SMP server was not valid");
}
catch (final Exception ex)
{
throw new SMPClientBadResponseException ("Error in validating signature returned from SMP server", ex);
}
}
else
LOGGER.error ("SMP response certificate checking is disabled. This should not happen in production systems!");
// Finally convert to domain object
final T ret = m_aMarshaller.read (aResponseBytes);
if (ret == null)
throw new SMPClientBadResponseException ("Malformed XML document returned from SMP server");
return ret;
}
}
© 2015 - 2025 Weber Informatics LLC | Privacy Policy