/*
 * Copyright 2018 Scytl Secure Electronic Voting SA
 *
 * All rights reserved
 *
 * See our extended copyright notice in *file 'Copyright.txt' which is part of this source code package
 */
package com.scytl.cryptolib.digital.envelope.service;

import static junitparams.JUnitParamsRunner.$;

import java.io.IOException;
import java.io.StringReader;
import java.nio.charset.StandardCharsets;
import java.security.KeyPair;
import java.security.PrivateKey;
import java.security.PublicKey;
import java.security.Security;

import org.bouncycastle.jce.provider.BouncyCastleProvider;
import org.junit.AfterClass;
import org.junit.Rule;
import org.junit.Test;
import org.junit.rules.ExpectedException;
import org.junit.runner.RunWith;

import com.fasterxml.jackson.databind.JsonNode;
import com.fasterxml.jackson.databind.ObjectMapper;
import com.scytl.cryptolib.api.exceptions.CryptoLibException;
import com.scytl.cryptolib.api.exceptions.GeneralCryptoLibException;
import com.scytl.cryptolib.asymmetric.utils.AsymmetricTestDataGenerator;
import com.scytl.cryptolib.digital.envelope.factory.CryptoDigitalEnvelope;
import com.scytl.cryptolib.primitives.primes.utils.PrimitivesTestDataGenerator;
import com.scytl.cryptolib.primitives.securerandom.constants.SecureRandomConstants;
import com.scytl.cryptolib.test.tools.bean.TestPrivateKey;
import com.scytl.cryptolib.test.tools.bean.TestPublicKey;
import com.scytl.cryptolib.test.tools.utils.CommonTestDataGenerator;

import junitparams.JUnitParamsRunner;
import junitparams.Parameters;

/**
 * Tests of digital envelope service API input validation.
 */
@RunWith(JUnitParamsRunner.class)
public class DigitalEnvelopeServiceValidationTest {

    private static final int MAXIMUM_NUMBER_ASYMMETRIC_KEY_PAIRS = 5;

    private static DigitalEnvelopeService _digitalEnvelopeServiceForDefaultPolicy;

    private static String _whiteSpaceString;

    private static byte[] _data;

    private static KeyPair[] _keyPairs;

    private static PublicKey[] _publicKeys;

    private static PrivateKey[] _privateKeys;

    private static CryptoDigitalEnvelope _digitalEnvelope;

    private static CryptoDigitalEnvelope _digitalEnvelopeFromMultiKeys;

    private static byte[] _emptyByteArray;

    private static PublicKey _nullContentPublicKey;

    private static PublicKey _emptyContentPublicKey;

    private static PrivateKey _nullContentPrivateKey;

    private static PrivateKey _emptyContentPrivateKey;

    private static byte[] _differentData;

    private static PublicKey _illegalSizePublicKey;

    private static PrivateKey _illegalSizePrivateKey;

    static {
        try {
            setUp();
        } catch (GeneralCryptoLibException e) {
            throw new CryptoLibException("Setup failed for class "
                + DigitalEnvelopeServiceValidationTest.class
                    .getSimpleName());
        }
    }

    public static void setUp() throws GeneralCryptoLibException {

        Security.addProvider(new BouncyCastleProvider());

        _digitalEnvelopeServiceForDefaultPolicy =
            new DigitalEnvelopeService();

        _whiteSpaceString = CommonTestDataGenerator
            .getWhiteSpaceString(CommonTestDataGenerator.getInt(1,
                SecureRandomConstants.MAXIMUM_GENERATED_STRING_LENGTH));

        _data = PrimitivesTestDataGenerator
            .getByteArray(CommonTestDataGenerator.getInt(1,
                SecureRandomConstants.MAXIMUM_GENERATED_BYTE_ARRAY_LENGTH));

        _keyPairs = AsymmetricTestDataGenerator
            .getKeyPairsForEncryption(CommonTestDataGenerator.getInt(2,
                MAXIMUM_NUMBER_ASYMMETRIC_KEY_PAIRS));
        _publicKeys =
            AsymmetricTestDataGenerator.extractPublicKeys(_keyPairs);
        _privateKeys =
            AsymmetricTestDataGenerator.extractPrivateKeys(_keyPairs);

        _digitalEnvelope = _digitalEnvelopeServiceForDefaultPolicy
            .createDigitalEnvelope(_data, _publicKeys[0]);
        _digitalEnvelopeFromMultiKeys =
            _digitalEnvelopeServiceForDefaultPolicy
                .createDigitalEnvelope(_data, _publicKeys);

        _emptyByteArray = new byte[0];

        _nullContentPublicKey = new TestPublicKey(null);
        _emptyContentPublicKey = new TestPublicKey(_emptyByteArray);

        _nullContentPrivateKey = new TestPrivateKey(null);
        _emptyContentPrivateKey = new TestPrivateKey(_emptyByteArray);

        _differentData =
            (new String(_data, StandardCharsets.UTF_8) + "X").getBytes();

        KeyPair keyPair = AsymmetricTestDataGenerator
            .getIllegaSizeKeyPairForEncryption();
        _illegalSizePublicKey = keyPair.getPublic();
        _illegalSizePrivateKey = keyPair.getPrivate();
    }

    @AfterClass
    public static void tearDown() {

        Security.removeProvider(BouncyCastleProvider.PROVIDER_NAME);
    }

    @Rule
    public ExpectedException thrown = ExpectedException.none();

    @Test
    @Parameters(method = "createDigitalEnvelopeService")
    public void testDigitalEnvelopeServiceCreationValidation(String path,
            String errorMsg) throws GeneralCryptoLibException {

        thrown.expect(GeneralCryptoLibException.class);
        thrown.expectMessage(errorMsg);
        new DigitalEnvelopeService(path);
    }

    public static Object[] createDigitalEnvelopeService() {

        return $($(null, "Properties file path is null."),
            $("", "Properties file path is blank."),
            $(_whiteSpaceString, "Properties file path is blank."));
    }

    @Test
    @Parameters(method = "createDigitalEnvelope")
    public void testDigitalEnvelopeCreationValidation(byte[] data,
            PublicKey publicKey, String errorMsg)
            throws GeneralCryptoLibException {

        thrown.expect(GeneralCryptoLibException.class);
        thrown.expectMessage(errorMsg);
        _digitalEnvelopeServiceForDefaultPolicy.createDigitalEnvelope(data,
            publicKey);
    }

    public static Object[] createDigitalEnvelope() {

        return $($(null, _publicKeys[0], "Data is null."),
            $(_emptyByteArray, _publicKeys[0], "Data is empty."),
            $(_data, null, "Public key is null."),
            $(_data, _nullContentPublicKey, "Public key content is null."),
            $(_data, _emptyContentPublicKey,
                "Public key content is empty."),
            $(_data, _illegalSizePublicKey,
                "Byte length of public key must be equal to byte length of corresponding key in cryptographic policy for digital envelope service: "));
    }

    @Test
    @Parameters(method = "createDigitalEnvelopeFromMultipleKeys")
    public void testDigitalEnvelopeCreationFromMultipleKeysValidation(
            byte[] data, PublicKey[] publicKeys, String errorMsg)
            throws GeneralCryptoLibException {

        thrown.expect(GeneralCryptoLibException.class);
        thrown.expectMessage(errorMsg);
        _digitalEnvelopeServiceForDefaultPolicy.createDigitalEnvelope(data,
            publicKeys);
    }

    public static Object[] createDigitalEnvelopeFromMultipleKeys() {

        PublicKey[] publicKeyArrayConsistingOfNullKey = new PublicKey[1];
        publicKeyArrayConsistingOfNullKey[0] = null;

        PublicKey[] publicKeyArrayConsistingOfNullContentKey =
            new PublicKey[1];
        publicKeyArrayConsistingOfNullContentKey[0] =
            _nullContentPublicKey;

        PublicKey[] publicKeyArrayConsistingOfEmptyContentKey =
            new PublicKey[1];
        publicKeyArrayConsistingOfEmptyContentKey[0] =
            _emptyContentPublicKey;

        PublicKey[] publicKeyArrayContainingNullKey = _publicKeys.clone();
        publicKeyArrayContainingNullKey[0] = null;

        PublicKey[] publicKeyArrayContainingNullContentKey =
            _publicKeys.clone();
        publicKeyArrayContainingNullContentKey[0] = _nullContentPublicKey;

        PublicKey[] publicKeyArrayContainingEmptyContentKey =
            _publicKeys.clone();
        publicKeyArrayContainingEmptyContentKey[0] =
            _emptyContentPublicKey;

        PublicKey[] publicKeyArrayContainingIllegalSizeKey =
            _publicKeys.clone();
        publicKeyArrayContainingIllegalSizeKey[0] = _illegalSizePublicKey;

        return $($(null, _publicKeys, "Data is null."),
            $(_emptyByteArray, _publicKeys, "Data is empty."),
            $(_data, null, "Public key array is null."),
            $(_data, new PublicKey[0], "Public key array is empty."),
            $(_data, publicKeyArrayConsistingOfNullKey,
                "Public key is null."),
            $(_data, publicKeyArrayConsistingOfNullContentKey,
                "Public key content is null."),
            $(_data, publicKeyArrayConsistingOfEmptyContentKey,
                "Public key content is empty."),
            $(_data, publicKeyArrayContainingNullKey,
                "A public key is null."),
            $(_data, publicKeyArrayContainingNullContentKey,
                "Content of a public key is null."),
            $(_data, publicKeyArrayContainingEmptyContentKey,
                "Content of a public key is empty."),
            $(_data, publicKeyArrayContainingIllegalSizeKey,
                "Byte length of public key must be equal to byte length of corresponding key in cryptographic policy for digital envelope service: "));
    }

    @Test
    @Parameters(method = "openSingleKeyDigitalEnvelope")
    public void testSingleKeyDigitalEnvelopeOpeningValidation(
            CryptoDigitalEnvelope digitalEnvelope, PrivateKey privateKey,
            String errorMsg) throws GeneralCryptoLibException {

        thrown.expect(GeneralCryptoLibException.class);
        thrown.expectMessage(errorMsg);
        _digitalEnvelopeServiceForDefaultPolicy
            .openDigitalEnvelope(digitalEnvelope, privateKey);
    }

    public static Object[] openSingleKeyDigitalEnvelope()
            throws GeneralCryptoLibException, IOException {

        CryptoDigitalEnvelope corruptedDigitalEnvelope =
            getCorruptedDigitalEnvelope(_publicKeys[0]);

        return $($(null, _privateKeys[0], "Digital envelope is null."),
            $(_digitalEnvelope, null, "Private key is null."),
            $(_digitalEnvelope, _nullContentPrivateKey,
                "Private key content is null."),
            $(_digitalEnvelope, _emptyContentPrivateKey,
                "Private key content is empty."),
            $(_digitalEnvelope, _privateKeys[1],
                "Private key provided to open digital envelope does not correspond to a public key used to generate it."),
            $(_digitalEnvelope, _illegalSizePrivateKey,
                "Byte length of private key must be equal to byte length of corresponding key in cryptographic policy for digital envelope service: "),
            $(corruptedDigitalEnvelope, _privateKeys[0],
                "The integrity of the digital envelope could not be verified."));
    }

    @Test
    @Parameters(method = "openMultipleKeyDigitalEnvelope")
    public void testMultipleKeyDigitalEnvelopeOpeningValidation(
            CryptoDigitalEnvelope digitalEnvelope,
            PrivateKey[] privateKeys, String errorMsg)
            throws GeneralCryptoLibException {

        thrown.expect(GeneralCryptoLibException.class);
        thrown.expectMessage(errorMsg);
        openDigitalEnvelopeFromMultiKeys(digitalEnvelope, privateKeys);
    }

    public static Object[] openMultipleKeyDigitalEnvelope()
            throws GeneralCryptoLibException, IOException {

        PrivateKey[] privateKeyArrayContainingNullKey =
            _privateKeys.clone();
        privateKeyArrayContainingNullKey[0] = null;

        PrivateKey[] privateKeyArrayContainingNullContentElement =
            _privateKeys.clone();
        privateKeyArrayContainingNullContentElement[0] =
            _nullContentPrivateKey;

        PrivateKey[] privateKeyArrayContainingEmptyContentElement =
            _privateKeys.clone();
        privateKeyArrayContainingEmptyContentElement[0] =
            _emptyContentPrivateKey;

        PrivateKey[] privateKeyArrayContainingWrongKey = new PrivateKey[1];
        privateKeyArrayContainingWrongKey[0] = AsymmetricTestDataGenerator
            .getUniqueKeyPairForEncryption(_keyPairs).getPrivate();

        PrivateKey[] privateKeyArrayContainingIllegalSizeKey =
            _privateKeys.clone();
        privateKeyArrayContainingIllegalSizeKey[0] =
            _illegalSizePrivateKey;

        CryptoDigitalEnvelope corruptedDigitalEnvelope =
            getCorruptedDigitalEnvelope(_publicKeys);

        return $($(null, _privateKeys, "Digital envelope is null."),
            $(_digitalEnvelopeFromMultiKeys,
                privateKeyArrayContainingNullKey, "Private key is null."),
            $(_digitalEnvelopeFromMultiKeys,
                privateKeyArrayContainingNullContentElement,
                "Private key content is null."),
            $(_digitalEnvelopeFromMultiKeys,
                privateKeyArrayContainingEmptyContentElement,
                "Private key content is empty."),
            $(_digitalEnvelopeFromMultiKeys,
                privateKeyArrayContainingWrongKey,
                "Private key provided to open digital envelope does not correspond to a public key used to generate it."),
            $(_digitalEnvelopeFromMultiKeys,
                privateKeyArrayContainingIllegalSizeKey,
                "Byte length of private key must be equal to byte length of corresponding key in cryptographic policy for digital envelope service: "),
            $(corruptedDigitalEnvelope, _privateKeys,
                "The integrity of the digital envelope could not be verified."));
    }

    @Test
    @Parameters(method = "openDigitalEnvelopeFromJson")
    public void testDigitalEnvelopeOpeningFromJsonValidation(
            String jsonStr, String errorMsg)
            throws GeneralCryptoLibException {

        thrown.expect(GeneralCryptoLibException.class);
        thrown.expectMessage(errorMsg);
        CryptoDigitalEnvelope.fromJson(jsonStr);
    }

    public static Object[] openDigitalEnvelopeFromJson() {

        String envelopeWithNullEncryptedDataBase64 =
            "{\"digitalEnvelope\":{\"encryptedDataBase64\":null,\"macBase64\":\"cRwqRBb6wPYr3k+D/HctKEY6Hs9IQcRyLrFLTJgaVoY=\",\"encryptedSecretKeyConcatsBase64\":[\"dyWTD7ynFneJGcaq6PWymkfNlTguXhiXIjrr5gvbkhem8DKFxHk3CUgnSBqIZAV7s6OebZ/aoB4dP4rxu80g7LBOgO6KDwvBBjzOXY0qVn0/WI8KDyyPjW675rlZkuPm2BuVpe5HXRMnTzoMWFl87bmNbo92CbmtBh57tuc1zeL24vucJ4V62qX9CPxq/9Y15CcYNxa5ekqbga+5Nm8wWpVR4D80voF9czNOZS3alC8TsdqboG01p4lAtTKTAz0aQq1Bo2m7enkOQVoOlnFHALWbozs2W+PBMErnNmEudQ57JQA/08bZvdNONW7/dPbwD+m7U+6ftHsN6As6+fevLdvqkXXZTjQ+6GEqe91Jv2qRSWyGIJ2HXyplsGYAPamwln/poH4cKJPLY24rHjbDCiWbcZ1aR1by4l/XeBFUaptSaVQQ3gh2QLnI6Qk=\"],\"publicKeysPem\":[\"-----BEGIN PUBLIC KEY-----MIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEAiXl2mH+c/LVbI4fVEb1HRq2FJlG0gLMWQjcK3YX0Vme9AISdwjPfpjsDYJzWdF8bm3nhs/xrsEZs0Fnp9Gt/uqkkVfjtV2f2H5wmKzVspelZvaMn1fN3t8tacSMInmS4XBWDGf5O+WTAVbjvTVhOSHCVTxsPQG0DzJGUuN4SzSFOB/5mkDGkbiw7kuV55i6nJmE75QkbWgjH+K9P/O2PoXRUVp0iQNegzYLZnUN0ND4AZ3djX889oeIoAJvc5wSoMRbJrFWNDjH+/YdHtJy1OMS7kmn0PM+rqZeMJn1xwclzzZKt+xbWFfKJ609hGXVmqpcDCXFvs3HBFveaNiSl9wIDAQAB-----END PUBLIC KEY-----\"]}}";
        String envelopeWithEmptyEncryptedDataBase64 =
            "{\"digitalEnvelope\":{\"encryptedDataBase64\":\"\",\"macBase64\":\"cRwqRBb6wPYr3k+D/HctKEY6Hs9IQcRyLrFLTJgaVoY=\",\"encryptedSecretKeyConcatsBase64\":[\"dyWTD7ynFneJGcaq6PWymkfNlTguXhiXIjrr5gvbkhem8DKFxHk3CUgnSBqIZAV7s6OebZ/aoB4dP4rxu80g7LBOgO6KDwvBBjzOXY0qVn0/WI8KDyyPjW675rlZkuPm2BuVpe5HXRMnTzoMWFl87bmNbo92CbmtBh57tuc1zeL24vucJ4V62qX9CPxq/9Y15CcYNxa5ekqbga+5Nm8wWpVR4D80voF9czNOZS3alC8TsdqboG01p4lAtTKTAz0aQq1Bo2m7enkOQVoOlnFHALWbozs2W+PBMErnNmEudQ57JQA/08bZvdNONW7/dPbwD+m7U+6ftHsN6As6+fevLdvqkXXZTjQ+6GEqe91Jv2qRSWyGIJ2HXyplsGYAPamwln/poH4cKJPLY24rHjbDCiWbcZ1aR1by4l/XeBFUaptSaVQQ3gh2QLnI6Qk=\"],\"publicKeysPem\":[\"-----BEGIN PUBLIC KEY-----MIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEAiXl2mH+c/LVbI4fVEb1HRq2FJlG0gLMWQjcK3YX0Vme9AISdwjPfpjsDYJzWdF8bm3nhs/xrsEZs0Fnp9Gt/uqkkVfjtV2f2H5wmKzVspelZvaMn1fN3t8tacSMInmS4XBWDGf5O+WTAVbjvTVhOSHCVTxsPQG0DzJGUuN4SzSFOB/5mkDGkbiw7kuV55i6nJmE75QkbWgjH+K9P/O2PoXRUVp0iQNegzYLZnUN0ND4AZ3djX889oeIoAJvc5wSoMRbJrFWNDjH+/YdHtJy1OMS7kmn0PM+rqZeMJn1xwclzzZKt+xbWFfKJ609hGXVmqpcDCXFvs3HBFveaNiSl9wIDAQAB-----END PUBLIC KEY-----\"]}}";
        String envelopeWithWhiteSpaceEncryptedDataBase64 =
            "{\"digitalEnvelope\":{\"encryptedDataBase64\":\""
                + _whiteSpaceString
                + "\",\"macBase64\":\"cRwqRBb6wPYr3k+D/HctKEY6Hs9IQcRyLrFLTJgaVoY=\",\"encryptedSecretKeyConcatsBase64\":[\"dyWTD7ynFneJGcaq6PWymkfNlTguXhiXIjrr5gvbkhem8DKFxHk3CUgnSBqIZAV7s6OebZ/aoB4dP4rxu80g7LBOgO6KDwvBBjzOXY0qVn0/WI8KDyyPjW675rlZkuPm2BuVpe5HXRMnTzoMWFl87bmNbo92CbmtBh57tuc1zeL24vucJ4V62qX9CPxq/9Y15CcYNxa5ekqbga+5Nm8wWpVR4D80voF9czNOZS3alC8TsdqboG01p4lAtTKTAz0aQq1Bo2m7enkOQVoOlnFHALWbozs2W+PBMErnNmEudQ57JQA/08bZvdNONW7/dPbwD+m7U+6ftHsN6As6+fevLdvqkXXZTjQ+6GEqe91Jv2qRSWyGIJ2HXyplsGYAPamwln/poH4cKJPLY24rHjbDCiWbcZ1aR1by4l/XeBFUaptSaVQQ3gh2QLnI6Qk=\"],\"publicKeysPem\":[\"-----BEGIN PUBLIC KEY-----MIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEAiXl2mH+c/LVbI4fVEb1HRq2FJlG0gLMWQjcK3YX0Vme9AISdwjPfpjsDYJzWdF8bm3nhs/xrsEZs0Fnp9Gt/uqkkVfjtV2f2H5wmKzVspelZvaMn1fN3t8tacSMInmS4XBWDGf5O+WTAVbjvTVhOSHCVTxsPQG0DzJGUuN4SzSFOB/5mkDGkbiw7kuV55i6nJmE75QkbWgjH+K9P/O2PoXRUVp0iQNegzYLZnUN0ND4AZ3djX889oeIoAJvc5wSoMRbJrFWNDjH+/YdHtJy1OMS7kmn0PM+rqZeMJn1xwclzzZKt+xbWFfKJ609hGXVmqpcDCXFvs3HBFveaNiSl9wIDAQAB-----END PUBLIC KEY-----\"]}}";
        String envelopeWithNullHmacBase64 =
            "{\"digitalEnvelope\":{\"encryptedDataBase64\":\"Mv55D72CCjuoHJEGVuDxeeLfRx7SzuKT4jBXQc9fMAglvnIYIqGYod3CyDCwj1dpBoDDu1xq/qkqtkwtKaSBPanP2Bjfjf6K1p3pOzQS48wSY5ialt5zmpdEKQXaKPC5mKIv1o7XUvCs2B9d\",\"macBase64\":null,\"encryptedSecretKeyConcatsBase64\":[\"dyWTD7ynFneJGcaq6PWymkfNlTguXhiXIjrr5gvbkhem8DKFxHk3CUgnSBqIZAV7s6OebZ/aoB4dP4rxu80g7LBOgO6KDwvBBjzOXY0qVn0/WI8KDyyPjW675rlZkuPm2BuVpe5HXRMnTzoMWFl87bmNbo92CbmtBh57tuc1zeL24vucJ4V62qX9CPxq/9Y15CcYNxa5ekqbga+5Nm8wWpVR4D80voF9czNOZS3alC8TsdqboG01p4lAtTKTAz0aQq1Bo2m7enkOQVoOlnFHALWbozs2W+PBMErnNmEudQ57JQA/08bZvdNONW7/dPbwD+m7U+6ftHsN6As6+fevLdvqkXXZTjQ+6GEqe91Jv2qRSWyGIJ2HXyplsGYAPamwln/poH4cKJPLY24rHjbDCiWbcZ1aR1by4l/XeBFUaptSaVQQ3gh2QLnI6Qk=\"],\"publicKeysPem\":[\"-----BEGIN PUBLIC KEY-----MIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEAiXl2mH+c/LVbI4fVEb1HRq2FJlG0gLMWQjcK3YX0Vme9AISdwjPfpjsDYJzWdF8bm3nhs/xrsEZs0Fnp9Gt/uqkkVfjtV2f2H5wmKzVspelZvaMn1fN3t8tacSMInmS4XBWDGf5O+WTAVbjvTVhOSHCVTxsPQG0DzJGUuN4SzSFOB/5mkDGkbiw7kuV55i6nJmE75QkbWgjH+K9P/O2PoXRUVp0iQNegzYLZnUN0ND4AZ3djX889oeIoAJvc5wSoMRbJrFWNDjH+/YdHtJy1OMS7kmn0PM+rqZeMJn1xwclzzZKt+xbWFfKJ609hGXVmqpcDCXFvs3HBFveaNiSl9wIDAQAB-----END PUBLIC KEY-----\"]}}";
        String envelopeWithEmptyHmacBase64 =
            "{\"digitalEnvelope\":{\"encryptedDataBase64\":\"Mv55D72CCjuoHJEGVuDxeeLfRx7SzuKT4jBXQc9fMAglvnIYIqGYod3CyDCwj1dpBoDDu1xq/qkqtkwtKaSBPanP2Bjfjf6K1p3pOzQS48wSY5ialt5zmpdEKQXaKPC5mKIv1o7XUvCs2B9d\",\"macBase64\":\"\",\"encryptedSecretKeyConcatsBase64\":[\"dyWTD7ynFneJGcaq6PWymkfNlTguXhiXIjrr5gvbkhem8DKFxHk3CUgnSBqIZAV7s6OebZ/aoB4dP4rxu80g7LBOgO6KDwvBBjzOXY0qVn0/WI8KDyyPjW675rlZkuPm2BuVpe5HXRMnTzoMWFl87bmNbo92CbmtBh57tuc1zeL24vucJ4V62qX9CPxq/9Y15CcYNxa5ekqbga+5Nm8wWpVR4D80voF9czNOZS3alC8TsdqboG01p4lAtTKTAz0aQq1Bo2m7enkOQVoOlnFHALWbozs2W+PBMErnNmEudQ57JQA/08bZvdNONW7/dPbwD+m7U+6ftHsN6As6+fevLdvqkXXZTjQ+6GEqe91Jv2qRSWyGIJ2HXyplsGYAPamwln/poH4cKJPLY24rHjbDCiWbcZ1aR1by4l/XeBFUaptSaVQQ3gh2QLnI6Qk=\"],\"publicKeysPem\":[\"-----BEGIN PUBLIC KEY-----MIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEAiXl2mH+c/LVbI4fVEb1HRq2FJlG0gLMWQjcK3YX0Vme9AISdwjPfpjsDYJzWdF8bm3nhs/xrsEZs0Fnp9Gt/uqkkVfjtV2f2H5wmKzVspelZvaMn1fN3t8tacSMInmS4XBWDGf5O+WTAVbjvTVhOSHCVTxsPQG0DzJGUuN4SzSFOB/5mkDGkbiw7kuV55i6nJmE75QkbWgjH+K9P/O2PoXRUVp0iQNegzYLZnUN0ND4AZ3djX889oeIoAJvc5wSoMRbJrFWNDjH+/YdHtJy1OMS7kmn0PM+rqZeMJn1xwclzzZKt+xbWFfKJ609hGXVmqpcDCXFvs3HBFveaNiSl9wIDAQAB-----END PUBLIC KEY-----\"]}}";
        String envelopeWithWhiteSpaceHmacBase64 =
            "{\"digitalEnvelope\":{\"encryptedDataBase64\":\"Mv55D72CCjuoHJEGVuDxeeLfRx7SzuKT4jBXQc9fMAglvnIYIqGYod3CyDCwj1dpBoDDu1xq/qkqtkwtKaSBPanP2Bjfjf6K1p3pOzQS48wSY5ialt5zmpdEKQXaKPC5mKIv1o7XUvCs2B9d\",\"macBase64\":\""
                + _whiteSpaceString
                + "\",\"encryptedSecretKeyConcatsBase64\":[\"dyWTD7ynFneJGcaq6PWymkfNlTguXhiXIjrr5gvbkhem8DKFxHk3CUgnSBqIZAV7s6OebZ/aoB4dP4rxu80g7LBOgO6KDwvBBjzOXY0qVn0/WI8KDyyPjW675rlZkuPm2BuVpe5HXRMnTzoMWFl87bmNbo92CbmtBh57tuc1zeL24vucJ4V62qX9CPxq/9Y15CcYNxa5ekqbga+5Nm8wWpVR4D80voF9czNOZS3alC8TsdqboG01p4lAtTKTAz0aQq1Bo2m7enkOQVoOlnFHALWbozs2W+PBMErnNmEudQ57JQA/08bZvdNONW7/dPbwD+m7U+6ftHsN6As6+fevLdvqkXXZTjQ+6GEqe91Jv2qRSWyGIJ2HXyplsGYAPamwln/poH4cKJPLY24rHjbDCiWbcZ1aR1by4l/XeBFUaptSaVQQ3gh2QLnI6Qk=\"],\"publicKeysPem\":[\"-----BEGIN PUBLIC KEY-----MIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEAiXl2mH+c/LVbI4fVEb1HRq2FJlG0gLMWQjcK3YX0Vme9AISdwjPfpjsDYJzWdF8bm3nhs/xrsEZs0Fnp9Gt/uqkkVfjtV2f2H5wmKzVspelZvaMn1fN3t8tacSMInmS4XBWDGf5O+WTAVbjvTVhOSHCVTxsPQG0DzJGUuN4SzSFOB/5mkDGkbiw7kuV55i6nJmE75QkbWgjH+K9P/O2PoXRUVp0iQNegzYLZnUN0ND4AZ3djX889oeIoAJvc5wSoMRbJrFWNDjH+/YdHtJy1OMS7kmn0PM+rqZeMJn1xwclzzZKt+xbWFfKJ609hGXVmqpcDCXFvs3HBFveaNiSl9wIDAQAB-----END PUBLIC KEY-----\"]}}";
        String envelopeWithNullEncyptedKeyList =
            "{\"digitalEnvelope\":{\"encryptedDataBase64\":\"Mv55D72CCjuoHJEGVuDxeeLfRx7SzuKT4jBXQc9fMAglvnIYIqGYod3CyDCwj1dpBoDDu1xq/qkqtkwtKaSBPanP2Bjfjf6K1p3pOzQS48wSY5ialt5zmpdEKQXaKPC5mKIv1o7XUvCs2B9d\",\"macBase64\":\"cRwqRBb6wPYr3k+D/HctKEY6Hs9IQcRyLrFLTJgaVoY=\",\"encryptedSecretKeyConcatsBase64\":null,\"publicKeysPem\":[\"-----BEGIN PUBLIC KEY-----MIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEAiXl2mH+c/LVbI4fVEb1HRq2FJlG0gLMWQjcK3YX0Vme9AISdwjPfpjsDYJzWdF8bm3nhs/xrsEZs0Fnp9Gt/uqkkVfjtV2f2H5wmKzVspelZvaMn1fN3t8tacSMInmS4XBWDGf5O+WTAVbjvTVhOSHCVTxsPQG0DzJGUuN4SzSFOB/5mkDGkbiw7kuV55i6nJmE75QkbWgjH+K9P/O2PoXRUVp0iQNegzYLZnUN0ND4AZ3djX889oeIoAJvc5wSoMRbJrFWNDjH+/YdHtJy1OMS7kmn0PM+rqZeMJn1xwclzzZKt+xbWFfKJ609hGXVmqpcDCXFvs3HBFveaNiSl9wIDAQAB-----END PUBLIC KEY-----\"]}}";
        String envelopeWithEmptyEncyptedKeyList =
            "{\"digitalEnvelope\":{\"encryptedDataBase64\":\"Mv55D72CCjuoHJEGVuDxeeLfRx7SzuKT4jBXQc9fMAglvnIYIqGYod3CyDCwj1dpBoDDu1xq/qkqtkwtKaSBPanP2Bjfjf6K1p3pOzQS48wSY5ialt5zmpdEKQXaKPC5mKIv1o7XUvCs2B9d\",\"macBase64\":\"cRwqRBb6wPYr3k+D/HctKEY6Hs9IQcRyLrFLTJgaVoY=\",\"encryptedSecretKeyConcatsBase64\":[],\"publicKeysPem\":[\"-----BEGIN PUBLIC KEY-----MIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEAiXl2mH+c/LVbI4fVEb1HRq2FJlG0gLMWQjcK3YX0Vme9AISdwjPfpjsDYJzWdF8bm3nhs/xrsEZs0Fnp9Gt/uqkkVfjtV2f2H5wmKzVspelZvaMn1fN3t8tacSMInmS4XBWDGf5O+WTAVbjvTVhOSHCVTxsPQG0DzJGUuN4SzSFOB/5mkDGkbiw7kuV55i6nJmE75QkbWgjH+K9P/O2PoXRUVp0iQNegzYLZnUN0ND4AZ3djX889oeIoAJvc5wSoMRbJrFWNDjH+/YdHtJy1OMS7kmn0PM+rqZeMJn1xwclzzZKt+xbWFfKJ609hGXVmqpcDCXFvs3HBFveaNiSl9wIDAQAB-----END PUBLIC KEY-----\"]}}";
        String envelopeWithEncyptedKeyListContainingNull =
            "{\"digitalEnvelope\":{\"encryptedDataBase64\":\"Mv55D72CCjuoHJEGVuDxeeLfRx7SzuKT4jBXQc9fMAglvnIYIqGYod3CyDCwj1dpBoDDu1xq/qkqtkwtKaSBPanP2Bjfjf6K1p3pOzQS48wSY5ialt5zmpdEKQXaKPC5mKIv1o7XUvCs2B9d\",\"macBase64\":\"cRwqRBb6wPYr3k+D/HctKEY6Hs9IQcRyLrFLTJgaVoY=\",\"encryptedSecretKeyConcatsBase64\":[null],\"publicKeysPem\":[\"-----BEGIN PUBLIC KEY-----MIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEAiXl2mH+c/LVbI4fVEb1HRq2FJlG0gLMWQjcK3YX0Vme9AISdwjPfpjsDYJzWdF8bm3nhs/xrsEZs0Fnp9Gt/uqkkVfjtV2f2H5wmKzVspelZvaMn1fN3t8tacSMInmS4XBWDGf5O+WTAVbjvTVhOSHCVTxsPQG0DzJGUuN4SzSFOB/5mkDGkbiw7kuV55i6nJmE75QkbWgjH+K9P/O2PoXRUVp0iQNegzYLZnUN0ND4AZ3djX889oeIoAJvc5wSoMRbJrFWNDjH+/YdHtJy1OMS7kmn0PM+rqZeMJn1xwclzzZKt+xbWFfKJ609hGXVmqpcDCXFvs3HBFveaNiSl9wIDAQAB-----END PUBLIC KEY-----\"]}}";
        String envelopeWithNullPublicKeyList =
            "{\"digitalEnvelope\":{\"encryptedDataBase64\":\"Mv55D72CCjuoHJEGVuDxeeLfRx7SzuKT4jBXQc9fMAglvnIYIqGYod3CyDCwj1dpBoDDu1xq/qkqtkwtKaSBPanP2Bjfjf6K1p3pOzQS48wSY5ialt5zmpdEKQXaKPC5mKIv1o7XUvCs2B9d\",\"macBase64\":\"cRwqRBb6wPYr3k+D/HctKEY6Hs9IQcRyLrFLTJgaVoY=\",\"encryptedSecretKeyConcatsBase64\":[\"dyWTD7ynFneJGcaq6PWymkfNlTguXhiXIjrr5gvbkhem8DKFxHk3CUgnSBqIZAV7s6OebZ/aoB4dP4rxu80g7LBOgO6KDwvBBjzOXY0qVn0/WI8KDyyPjW675rlZkuPm2BuVpe5HXRMnTzoMWFl87bmNbo92CbmtBh57tuc1zeL24vucJ4V62qX9CPxq/9Y15CcYNxa5ekqbga+5Nm8wWpVR4D80voF9czNOZS3alC8TsdqboG01p4lAtTKTAz0aQq1Bo2m7enkOQVoOlnFHALWbozs2W+PBMErnNmEudQ57JQA/08bZvdNONW7/dPbwD+m7U+6ftHsN6As6+fevLdvqkXXZTjQ+6GEqe91Jv2qRSWyGIJ2HXyplsGYAPamwln/poH4cKJPLY24rHjbDCiWbcZ1aR1by4l/XeBFUaptSaVQQ3gh2QLnI6Qk=\"],\"publicKeysPem\":null}}";
        String envelopeWithEmptyPublicKeyList =
            "{\"digitalEnvelope\":{\"encryptedDataBase64\":\"Mv55D72CCjuoHJEGVuDxeeLfRx7SzuKT4jBXQc9fMAglvnIYIqGYod3CyDCwj1dpBoDDu1xq/qkqtkwtKaSBPanP2Bjfjf6K1p3pOzQS48wSY5ialt5zmpdEKQXaKPC5mKIv1o7XUvCs2B9d\",\"macBase64\":\"cRwqRBb6wPYr3k+D/HctKEY6Hs9IQcRyLrFLTJgaVoY=\",\"encryptedSecretKeyConcatsBase64\":[\"dyWTD7ynFneJGcaq6PWymkfNlTguXhiXIjrr5gvbkhem8DKFxHk3CUgnSBqIZAV7s6OebZ/aoB4dP4rxu80g7LBOgO6KDwvBBjzOXY0qVn0/WI8KDyyPjW675rlZkuPm2BuVpe5HXRMnTzoMWFl87bmNbo92CbmtBh57tuc1zeL24vucJ4V62qX9CPxq/9Y15CcYNxa5ekqbga+5Nm8wWpVR4D80voF9czNOZS3alC8TsdqboG01p4lAtTKTAz0aQq1Bo2m7enkOQVoOlnFHALWbozs2W+PBMErnNmEudQ57JQA/08bZvdNONW7/dPbwD+m7U+6ftHsN6As6+fevLdvqkXXZTjQ+6GEqe91Jv2qRSWyGIJ2HXyplsGYAPamwln/poH4cKJPLY24rHjbDCiWbcZ1aR1by4l/XeBFUaptSaVQQ3gh2QLnI6Qk=\"],\"publicKeysPem\":[]}}";
        String envelopeWithPublicKeyListContainingNull =
            "{\"digitalEnvelope\":{\"encryptedDataBase64\":\"Mv55D72CCjuoHJEGVuDxeeLfRx7SzuKT4jBXQc9fMAglvnIYIqGYod3CyDCwj1dpBoDDu1xq/qkqtkwtKaSBPanP2Bjfjf6K1p3pOzQS48wSY5ialt5zmpdEKQXaKPC5mKIv1o7XUvCs2B9d\",\"macBase64\":\"cRwqRBb6wPYr3k+D/HctKEY6Hs9IQcRyLrFLTJgaVoY=\",\"encryptedSecretKeyConcatsBase64\":[\"dyWTD7ynFneJGcaq6PWymkfNlTguXhiXIjrr5gvbkhem8DKFxHk3CUgnSBqIZAV7s6OebZ/aoB4dP4rxu80g7LBOgO6KDwvBBjzOXY0qVn0/WI8KDyyPjW675rlZkuPm2BuVpe5HXRMnTzoMWFl87bmNbo92CbmtBh57tuc1zeL24vucJ4V62qX9CPxq/9Y15CcYNxa5ekqbga+5Nm8wWpVR4D80voF9czNOZS3alC8TsdqboG01p4lAtTKTAz0aQq1Bo2m7enkOQVoOlnFHALWbozs2W+PBMErnNmEudQ57JQA/08bZvdNONW7/dPbwD+m7U+6ftHsN6As6+fevLdvqkXXZTjQ+6GEqe91Jv2qRSWyGIJ2HXyplsGYAPamwln/poH4cKJPLY24rHjbDCiWbcZ1aR1by4l/XeBFUaptSaVQQ3gh2QLnI6Qk=\"],\"publicKeysPem\":[null]}}";

        return $(
            $(null,
                CryptoDigitalEnvelope.class.getSimpleName()
                    + " JSON string is null."),
            $("",
                CryptoDigitalEnvelope.class.getSimpleName()
                    + " JSON string is blank."),
            $(_whiteSpaceString,
                CryptoDigitalEnvelope.class.getSimpleName()
                    + " JSON string is blank."),
            $(envelopeWithNullEncryptedDataBase64,
                "Base64 encoding of symmetrically encrypted data is null."),
            $(envelopeWithEmptyEncryptedDataBase64,
                "Base64 encoding of symmetrically encrypted data is blank."),
            $(envelopeWithWhiteSpaceEncryptedDataBase64,
                "Base64 encoding of symmetrically encrypted data is blank."),
            $(envelopeWithNullHmacBase64,
                "Base64 encoding of HMAC of symmetrically encrypted data is null."),
            $(envelopeWithEmptyHmacBase64,
                "Base64 encoding of HMAC of symmetrically encrypted data is blank."),
            $(envelopeWithWhiteSpaceHmacBase64,
                "Base64 encoding of HMAC of symmetrically encrypted data is blank."),
            $(envelopeWithNullEncyptedKeyList,
                "List of Base64 encoded asymmetric encryptions of secret key concatenation is null."),
            $(envelopeWithEmptyEncyptedKeyList,
                "List of Base64 encoded asymmetric encryptions of secret key concatenation is empty."),
            $(envelopeWithEncyptedKeyListContainingNull,
                "List of Base64 encoded asymmetric encryptions of secret key concatenation contains one or more null elements."),
            $(envelopeWithNullPublicKeyList,
                "List of public keys used for asymmetric encryptions of secret key concatenation is null."),
            $(envelopeWithEmptyPublicKeyList,
                "List of public keys used for asymmetric encryptions of secret key concatenation is empty."),
            $(envelopeWithPublicKeyListContainingNull,
                "List of public keys used for asymmetric encryptions of secret key concatenation contains one or more null elements."));
    }

    private static void openDigitalEnvelopeFromMultiKeys(
            final CryptoDigitalEnvelope digitalEnvelope,
            final PrivateKey[] privateKeys)
            throws GeneralCryptoLibException {

        for (PrivateKey privateKey : privateKeys) {
            _digitalEnvelopeServiceForDefaultPolicy
                .openDigitalEnvelope(digitalEnvelope, privateKey);
        }
    }

    private static CryptoDigitalEnvelope getCorruptedDigitalEnvelope(
            final PublicKey... publicKey)
            throws GeneralCryptoLibException, IOException {

        String encryptedDataBase64 =
            getEncryptedDataBase64(_digitalEnvelope);

        CryptoDigitalEnvelope anotherDigitalEnvelope =
            _digitalEnvelopeServiceForDefaultPolicy
                .createDigitalEnvelope(_differentData, publicKey);
        String anotherEncryptedDataBase64 =
            getEncryptedDataBase64(anotherDigitalEnvelope);

        String corruptedSerializedDigitalEnvelope =
            _digitalEnvelope.toJson().replace(encryptedDataBase64,
                anotherEncryptedDataBase64);

        return CryptoDigitalEnvelope
            .fromJson(corruptedSerializedDigitalEnvelope);
    }

    private static String getEncryptedDataBase64(
            final CryptoDigitalEnvelope digitalEnvelope)
            throws GeneralCryptoLibException, IOException {

        JsonNode rootNode = new ObjectMapper()
            .readTree(new StringReader(digitalEnvelope.toJson()));
        JsonNode digitalEnvelopeNode = rootNode.get("digitalEnvelope");
        JsonNode encryptedDataBase64Node =
            digitalEnvelopeNode.get("encryptedDataBase64");

        return encryptedDataBase64Node.asText();
    }
}
