/******************************************************************************* * Copyright 2020 Graz University of Technology * MOA ZS has been developed in a cooperation between EGIZ * and Graz University of Technology. * Licensed under the EUPL, Version 1.2 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/news/understanding-eupl-v12 * * 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. * * This product combines work with different licenses. See the "NOTICE" text * file for details on the various modules and licenses. * The "NOTICE" text file is part of the distribution. Any derivative works * that you distribute must include a readable copy of the "NOTICE" text file. *******************************************************************************/ package at.gv.egiz.moazs.backend; import at.gv.egiz.eid.authhandler.modules.sigverify.moasig.api.ISignatureVerificationService; import at.gv.egiz.eid.authhandler.modules.sigverify.moasig.api.data.IXMLSignatureVerificationResponse; import at.gv.egiz.eid.authhandler.modules.sigverify.moasig.exceptions.MOASigServiceException; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import java.nio.charset.StandardCharsets; import java.util.function.Consumer; import static at.gv.egiz.moazs.MoaZSException.moaZSException; import static java.lang.String.format; /** * @author Christof Rabensteiner * */ public class SignatureVerifier implements Consumer { private static final Logger log = LoggerFactory.getLogger(SignatureVerifier.class); private static final int OK_CODE = 0; private static final String MOASPSS_FAILED_ERROR_MSG = "MOA SPSS could not find the signature. "; private static final String SIGNATURE_CODE_ERROR_MSG = "Signature is not valid: Check code was %d. "; private static final String CERT_CODE_ERROR_MSG = "Certificate chain is not valid: Check code was %d. "; private static final String MANIFEST_CODE_ERROR_MSG = "Signature Manifest is not valid: Check code was %d. "; private static final String XMLMANIFEST_CODE_ERROR_MSG = "XmlDSIGManifest is not valid: Check code was %d. "; private static final String XML_SIGNATURE_RESPONSE_TEMPLATE = "XmlDsigSubjectName: %s; " + "SignatureManifestCheckCode: %s; " + "XmlDSIGManifestCheckCode: %s; " + "CertificateCheckCode: %s; " + "SignatureCheckCode: %s; " + "SigningDateTime: %s; " + "isXmlDSIGManigest: %s; " + "isPublicAuthority: %s; " + "isQualifiedCertificate: %s; " + "getPublicAuthorityCode: %s; "; private static final String MOASIG_SERVICE_ERROR_MSG = "MOA SPSS could not accept the XML signature. "; private final ISignatureVerificationService service; private final String trustProfile; private final boolean isManifestCheckActive; public SignatureVerifier(ISignatureVerificationService service, String trustProfile, boolean isManifestCheckActive) { this.service = service; this.trustProfile = trustProfile; this.isManifestCheckActive = isManifestCheckActive; } /** * Verifies the signature of a signed XML document. If the validation fails, it will throw an exception. * @param signedXMLdocument * @throws at.gv.egiz.moazs.MoaZSException */ @Override public void accept(byte[] signedXMLdocument) { debug(signedXMLdocument); try { var response = service.verifyXMLSignature(signedXMLdocument, trustProfile); debug(response); if (response == null) { throw moaZSException(MOASPSS_FAILED_ERROR_MSG); } var builder = new StringBuilder(); if (response.getSignatureCheckCode() != OK_CODE) { builder.append(format(SIGNATURE_CODE_ERROR_MSG, response.getSignatureCheckCode())); } if (response.getCertificateCheckCode() != OK_CODE) { builder.append(format(CERT_CODE_ERROR_MSG, response.getCertificateCheckCode())); } if (response.getSignatureManifestCheckCode() != OK_CODE) { var signatureManifestErrorMsg = format(MANIFEST_CODE_ERROR_MSG, response.getSignatureManifestCheckCode()); if (isManifestCheckActive) { builder.append(signatureManifestErrorMsg); } else { log.warn(signatureManifestErrorMsg); } } if (response.isXmlDSIGManigest() && response.getXmlDSIGManifestCheckCode() != OK_CODE) { var xmlDSIGManifestErrorMsg = format(XMLMANIFEST_CODE_ERROR_MSG, response.getXmlDSIGManifestCheckCode()); if (isManifestCheckActive) { builder.append(xmlDSIGManifestErrorMsg); } else { log.warn(xmlDSIGManifestErrorMsg); } } var msg = builder.toString(); if(!msg.isEmpty()) { throw moaZSException(msg); } } catch (MOASigServiceException e) { throw moaZSException(MOASIG_SERVICE_ERROR_MSG, e); } } private void debug(byte[] signedXMLdocument) { if (log.isDebugEnabled()) { log.info("Verifying the following byte[]: \n--------------------\n{}\n--------------------\n", new String(signedXMLdocument, StandardCharsets.UTF_8)); } } public static void debug(IXMLSignatureVerificationResponse response) { if (log.isDebugEnabled()) { var builder = new StringBuilder("Response: "); if (response == null) { builder.append("null"); } else { var objects = new Object[]{response.getXmlDsigSubjectName(), response.getSignatureManifestCheckCode(), response.getXmlDSIGManifestCheckCode(), response.getCertificateCheckCode(), response.getSignatureCheckCode(), response.getSigningDateTime(), response.isXmlDSIGManigest(), response.isPublicAuthority(), response.isQualifiedCertificate(), response.getPublicAuthorityCode()}; builder.append(String.format(XML_SIGNATURE_RESPONSE_TEMPLATE, objects)); } log.debug(builder.toString()); } } }