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

ru.i_novus.common.sign.util.FileSignatureVerifier Maven / Gradle / Ivy

There is a newer version: 2.0.0
Show newest version
package ru.i_novus.common.sign.util;

/*-
 * -----------------------------------------------------------------
 * common-sign-gost
 * -----------------------------------------------------------------
 * Copyright (C) 2018 - 2019 I-Novus LLC
 * -----------------------------------------------------------------
 * Licensed 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.
 * -----------------------------------------------------------------
 */

import lombok.extern.slf4j.Slf4j;
import javax.activation.DataHandler;
import java.io.ByteArrayInputStream;
import java.io.IOException;
import java.io.InputStream;
import java.io.UncheckedIOException;
import java.security.*;
import java.security.cert.X509Certificate;
import java.util.Arrays;
import java.util.Optional;

import org.bouncycastle.asn1.ASN1ObjectIdentifier;
import org.bouncycastle.asn1.DEROctetString;
import org.bouncycastle.cert.X509CertificateHolder;
import org.bouncycastle.cms.CMSException;
import org.bouncycastle.cms.CMSSignedData;
import org.bouncycastle.cms.SignerInformation;
import org.bouncycastle.cms.SignerInformationStore;
import ru.i_novus.common.sign.api.SignAlgorithmType;

/**
 * Утилита для верификации подписи файла
 */
@Slf4j
public class FileSignatureVerifier {

    private static final int BUFFER_SIZE = 4096;

    private FileSignatureVerifier() {
        // не позволяет создать экземпляр класса, класс утилитный
    }

    /**
     * Верифицирует значение Digest, использованного при подписи
     * @param dataHandler          интерфейс для получения бинарных данных исходного файла
     * @param signedDataByteArray  бинарные данные подписи файла
     * @return результат верификации Digest, использованного при подписи. true - значение Digest верно, false - Digest не прошел проверку
     *
     * @throws CMSException
     */
    public static boolean verifyDigest(DataHandler dataHandler, byte[] signedDataByteArray) throws CMSException {

        byte[] data;
        try {
            data = org.bouncycastle.util.io.Streams.readAll(dataHandler.getInputStream());
        } catch (IOException e) {
            throw new UncheckedIOException("Cannot read data from DataHandler", e);
        }

        CMSSignedData signedData = new CMSSignedData(signedDataByteArray);

        X509Certificate x509Certificate = getX509Certificate(signedData)
                .orElseThrow(() -> new IllegalStateException("Certificate was not received from signed data"));

        SignerInformation signerInformation = getSignerInformation(signedData)
                .orElseThrow(() -> new IllegalStateException("Signature metadata was not received from signed data"));

        SignAlgorithmType signAlgorithmType = SignAlgorithmType.findByCertificate(x509Certificate);

        final byte[] argDigestedData = CryptoUtil.getFileDigest(data, signAlgorithmType);

        ASN1ObjectIdentifier asn1ObjectIdentifier = org.bouncycastle.asn1.pkcs.PKCSObjectIdentifiers.pkcs_9_at_messageDigest;

        org.bouncycastle.asn1.cms.Attribute attribute = signerInformation.getSignedAttributes().get(asn1ObjectIdentifier);

        DEROctetString oct = (DEROctetString) attribute.getAttributeValues()[0];

        byte[] signedDigestedData = oct.getOctets();

        return Arrays.equals(argDigestedData, signedDigestedData);
    }

    /**
     * Верифицирует подпись файла
     * @param signedDataByteArray бинарные данные подписи файла
     * @return результат верификации подписи. true - подпись верна, false - подпись не прошла проверку
     * @throws CMSException
     * @throws GeneralSecurityException
     * @throws IOException
     */
    public static boolean verifyPKCS7Signature(byte[] signedDataByteArray) throws CMSException, GeneralSecurityException, IOException {

        CMSSignedData signedData = new CMSSignedData(signedDataByteArray);

        X509Certificate x509Certificate = getX509Certificate(signedData)
                .orElseThrow(() -> new IllegalStateException("Certificate was not received from signed data"));

        SignerInformation signerInformation = getSignerInformation(signedData)
                .orElseThrow(() -> new IllegalStateException("Signature metadata was not received from signed data"));

        byte[] signatureAsByteArray = signerInformation.getSignature();

        SignAlgorithmType signAlgorithmType = SignAlgorithmType.findByCertificate(x509Certificate);

        boolean signatureIsVerified;

        try (InputStream inputStream = new ByteArrayInputStream(signerInformation.getEncodedSignedAttributes())) {

            Signature signature = CryptoUtil.getSignatureInstance(signAlgorithmType);
            signature.initVerify(x509Certificate);

            byte[] localBuffer = new byte[BUFFER_SIZE];
            for (int readBytesCount; (readBytesCount = inputStream.read(localBuffer)) > 0; ) {
                signature.update(localBuffer, 0, readBytesCount);
            }

            signatureIsVerified = signature.verify(signatureAsByteArray);
        }

        return signatureIsVerified;
    }

    /**
     * Получает метаданные подписи из объекта CMSSignedData
     * @param signedData объектное представление подписи
     * @return метаданные подписи
     */
    private static Optional getSignerInformation(CMSSignedData signedData) {
        SignerInformationStore signerInformationStore = signedData.getSignerInfos();
        return signerInformationStore.getSigners().stream().findFirst();
    }

    /**
     * Получает метаданные сертификата ЭП из объекта CMSSignedData
     * @param signedData объектное представление подписи
     * @return метаданные сертификата ЭП
     */
    private static Optional getX509Certificate(CMSSignedData signedData) {
        Optional x509CertificateHolder = signedData.getCertificates().getMatches(null).stream().findFirst();
        return x509CertificateHolder.map(certificateHolder -> CryptoFormatConverter.getInstance().getCertificateFromHolder(certificateHolder));
    }
}




© 2015 - 2024 Weber Informatics LLC | Privacy Policy