All Downloads are FREE. Search and download functionalities are using the official Maven repository.

no.difi.oxalis.as2.inbound.As2InboundHandler Maven / Gradle / Ivy

/*
 * Copyright 2010-2017 Norwegian Agency for Public Management and eGovernment (Difi)
 *
 * Licensed under the EUPL, Version 1.1 or – as soon they
 * will be approved by the European Commission - subsequent
 * versions of the EUPL (the "Licence");
 *
 * You may not use this work except in compliance with the Licence.
 *
 * You may obtain a copy of the Licence at:
 *
 * https://joinup.ec.europa.eu/community/eupl/og_page/eupl
 *
 * Unless required by applicable law or agreed to in
 * writing, software distributed under the Licence is
 * distributed on an "AS IS" basis,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either
 * express or implied.
 * See the Licence for the specific language governing
 * permissions and limitations under the Licence.
 */

package no.difi.oxalis.as2.inbound;

import com.google.common.collect.ImmutableMap;
import com.google.common.io.ByteStreams;
import com.google.inject.Inject;
import no.difi.oxalis.api.lang.OxalisSecurityException;
import no.difi.oxalis.api.lang.OxalisTransmissionException;
import no.difi.oxalis.api.lang.TimestampException;
import no.difi.oxalis.api.lang.VerifierException;
import no.difi.oxalis.api.model.Direction;
import no.difi.oxalis.api.model.TransmissionIdentifier;
import no.difi.oxalis.api.persist.PersisterHandler;
import no.difi.oxalis.api.statistics.StatisticsService;
import no.difi.oxalis.api.timestamp.Timestamp;
import no.difi.oxalis.api.timestamp.TimestampProvider;
import no.difi.oxalis.api.transmission.TransmissionVerifier;
import no.difi.oxalis.as2.code.As2Header;
import no.difi.oxalis.as2.code.Disposition;
import no.difi.oxalis.as2.code.MdnHeader;
import no.difi.oxalis.as2.lang.OxalisAs2InboundException;
import no.difi.oxalis.as2.model.Mic;
import no.difi.oxalis.as2.util.*;
import no.difi.oxalis.commons.bouncycastle.BCHelper;
import no.difi.oxalis.commons.io.PeekingInputStream;
import no.difi.oxalis.commons.io.UnclosableInputStream;
import no.difi.vefa.peppol.common.code.Service;
import no.difi.vefa.peppol.common.model.Digest;
import no.difi.vefa.peppol.common.model.Header;
import no.difi.vefa.peppol.sbdh.SbdReader;
import no.difi.vefa.peppol.sbdh.lang.SbdhException;
import no.difi.vefa.peppol.security.api.CertificateValidator;
import no.difi.vefa.peppol.security.lang.PeppolSecurityException;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

import javax.mail.MessagingException;
import javax.mail.internet.InternetHeaders;
import javax.mail.internet.MimeMessage;
import java.io.ByteArrayOutputStream;
import java.io.IOException;
import java.io.InputStream;
import java.nio.file.Path;
import java.security.DigestInputStream;
import java.security.MessageDigest;
import java.security.NoSuchAlgorithmException;
import java.security.cert.X509Certificate;

/**
 * Main entry point for receiving AS2 messages.
 *
 * @author steinar
 * @author thore
 * @author erlend
 */
class As2InboundHandler {

    public static final Logger LOGGER = LoggerFactory.getLogger(As2InboundHandler.class);

    private final StatisticsService statisticsService;

    private final TimestampProvider timestampProvider;

    private final PersisterHandler persisterHandler;

    private final TransmissionVerifier transmissionVerifier;

    private final CertificateValidator certificateValidator;

    private final SMimeMessageFactory sMimeMessageFactory;

    @Inject
    public As2InboundHandler(StatisticsService statisticsService, TimestampProvider timestampProvider,
                             CertificateValidator certificateValidator, PersisterHandler persisterHandler,
                             TransmissionVerifier transmissionVerifier, SMimeMessageFactory sMimeMessageFactory) {
        this.statisticsService = statisticsService;
        this.timestampProvider = timestampProvider;
        this.certificateValidator = certificateValidator;

        this.persisterHandler = persisterHandler;
        this.transmissionVerifier = transmissionVerifier;

        this.sMimeMessageFactory = sMimeMessageFactory;
    }

    /**
     * Receives an AS2 Message in the form of a map of headers together with the payload,
     * which is made available in an input stream
     * 

* If persisting message to the Message Repository fails, we have to return negative MDN. * * @param httpHeaders the http headers received * @param mimeMessage supplies the MIME message * @return MDN object to signal if everything is ok or if some error occurred while receiving */ public MimeMessage receive(InternetHeaders httpHeaders, MimeMessage mimeMessage) throws OxalisAs2InboundException { LOGGER.debug("Receiving message .."); try { SMimeReader sMimeReader = new SMimeReader(mimeMessage); // Get timestamp using signature as input Timestamp t2 = timestampProvider.generate(sMimeReader.getSignature(), Direction.IN); // Initiate MDN MdnBuilder mdnBuilder = MdnBuilder.newInstance(mimeMessage); mdnBuilder.addHeader(MdnHeader.DATE, t2.getDate()); // Extract Message-ID TransmissionIdentifier transmissionIdentifier = TransmissionIdentifier.fromHeader(httpHeaders.getHeader(As2Header.MESSAGE_ID)[0]); mdnBuilder.addHeader(MdnHeader.ORIGINAL_MESSAGE_ID, httpHeaders.getHeader(As2Header.MESSAGE_ID)[0]); // Extract signed digest and digest algorithm SMimeDigestMethod digestMethod = sMimeReader.getDigestMethod(); // Extract content headers byte[] headerBytes = sMimeReader.getBodyHeader(); mdnBuilder.addHeader(MdnHeader.ORIGINAL_CONTENT_HEADER, headerBytes); // Prepare calculation of digest MessageDigest messageDigest = BCHelper.getMessageDigest(digestMethod.getIdentifier()); InputStream digestInputStream = new DigestInputStream(sMimeReader.getBodyInputStream(), messageDigest); // Add header to calculation of digest messageDigest.update(headerBytes); // Prepare content for reading of SBDH PeekingInputStream peekingInputStream = new PeekingInputStream(digestInputStream); // Extract SBDH Header header; try (SbdReader sbdReader = SbdReader.newInstance(peekingInputStream)) { header = sbdReader.getHeader(); } // Perform validation of SBDH transmissionVerifier.verify(header, Direction.IN); // Extract "fresh" InputStream Path payloadPath; try (InputStream payloadInputStream = peekingInputStream.newInputStream()) { // Persist content payloadPath = persisterHandler.persist(transmissionIdentifier, header, new UnclosableInputStream(payloadInputStream)); // Exhaust InputStream ByteStreams.exhaust(payloadInputStream); } // Fetch calculated digest Digest calculatedDigest = Digest.of(digestMethod.getDigestMethod(), messageDigest.digest()); mdnBuilder.addHeader(MdnHeader.RECEIVED_CONTENT_MIC, new Mic(calculatedDigest)); // Validate signature using calculated digest X509Certificate signer = SMimeBC.verifySignature( ImmutableMap.of(digestMethod.getOid(), calculatedDigest.getValue()), sMimeReader.getSignature() ); // Validate certificate certificateValidator.validate(Service.AP, signer); // Create receipt (MDN) mdnBuilder.addHeader(MdnHeader.DISPOSITION, Disposition.PROCESSED); MimeMessage mdn = sMimeMessageFactory.createSignedMimeMessage(mdnBuilder.build(), digestMethod); // MimeMessage mdn = sMimeMessageFactory.createSignedMimeMessageNew(mdnBuilder.build(), calculatedDigest, digestMethod); mdn.setHeader(As2Header.AS2_VERSION, As2Header.VERSION); mdn.setHeader(As2Header.AS2_FROM, httpHeaders.getHeader(As2Header.AS2_TO)[0]); mdn.setHeader(As2Header.AS2_TO, httpHeaders.getHeader(As2Header.AS2_FROM)[0]); // Prepare MDN ByteArrayOutputStream mdnOutputStream = new ByteArrayOutputStream(); mdn.writeTo(mdnOutputStream); // Persist metadata As2InboundMetadata inboundMetadata = new As2InboundMetadata(transmissionIdentifier, header, t2, digestMethod.getTransportProfile(), calculatedDigest, signer, mdnOutputStream.toByteArray()); persisterHandler.persist(inboundMetadata, payloadPath); // Persist statistics statisticsService.persist(inboundMetadata); return mdn; } catch (SbdhException e) { throw new OxalisAs2InboundException(Disposition.UNSUPPORTED_FORMAT, e.getMessage(), e); } catch (NoSuchAlgorithmException e) { throw new OxalisAs2InboundException(Disposition.UNSUPPORTED_MIC_ALGORITHMS, e.getMessage(), e); } catch (VerifierException e) { throw new OxalisAs2InboundException(Disposition.fromVerifierException(e), e.getMessage(), e); } catch (PeppolSecurityException e) { throw new OxalisAs2InboundException(Disposition.AUTHENTICATION_FAILED, e.getMessage(), e); } catch (OxalisSecurityException e) { throw new OxalisAs2InboundException(Disposition.INTEGRITY_CHECK_FAILED, e.getMessage(), e); } catch (IOException | TimestampException | MessagingException | OxalisTransmissionException e) { throw new OxalisAs2InboundException(Disposition.UNEXPECTED_PROCESSING_ERROR, e.getMessage(), e); } } }





© 2015 - 2024 Weber Informatics LLC | Privacy Policy