<commit_before>/****************************************************************************
 * Copyright (C) 2016 ecsec GmbH.
 * All rights reserved.
 * Contact: ecsec GmbH (<EMAIL>)
 *
 * This file is part of the Open eCard App.
 *
 * GNU General Public License Usage
 * This file may be used under the terms of the GNU General Public
 * License version 3.0 as published by the Free Software Foundation
 * and appearing in the file LICENSE.GPL included in the packaging of
 * this file. Please review the following information to ensure the
 * GNU General Public License version 3.0 requirements will be met:
 * http://www.gnu.org/copyleft/gpl.html.
 *
 * Other Usage
 * Alternatively, this file may be used in accordance with the terms
 * and conditions contained in a signed written agreement between
 * you and ecsec GmbH.
 *
 ***************************************************************************/

package org.openecard.addons.cg.impl;

import java.io.IOException;
import java.io.InputStream;
import java.nio.charset.StandardCharsets;
import java.security.InvalidAlgorithmParameterException;
import java.security.InvalidKeyException;
import java.security.KeyStore;
import java.security.KeyStoreException;
import java.security.NoSuchAlgorithmException;
import java.security.PrivateKey;
import java.security.SignatureException;
import java.security.UnrecoverableKeyException;
import java.security.cert.Certificate;
import java.security.cert.CertificateEncodingException;
import java.security.cert.CertificateException;
import java.security.cert.X509Certificate;
import java.util.Arrays;
import java.util.Base64;
import mockit.Mock;
import mockit.MockUp;
import org.openecard.bouncycastle.cert.jcajce.JcaCertStore;
import org.openecard.bouncycastle.cms.CMSException;
import org.openecard.bouncycastle.cms.CMSProcessableByteArray;
import org.openecard.bouncycastle.cms.CMSSignedData;
import org.openecard.bouncycastle.cms.CMSSignedDataGenerator;
import org.openecard.bouncycastle.cms.CMSSignerDigestMismatchException;
import org.openecard.bouncycastle.cms.CMSTypedData;
import org.openecard.bouncycastle.cms.jcajce.JcaSignerInfoGeneratorBuilder;
import org.openecard.bouncycastle.operator.ContentSigner;
import org.openecard.bouncycastle.operator.DigestCalculatorProvider;
import org.openecard.bouncycastle.operator.OperatorCreationException;
import org.openecard.bouncycastle.operator.jcajce.JcaContentSignerBuilder;
import org.openecard.bouncycastle.operator.jcajce.JcaDigestCalculatorProviderBuilder;
import org.openecard.bouncycastle.util.Store;
import org.openecard.common.util.StringUtils;
import org.testng.Assert;
import org.testng.annotations.BeforeClass;
import org.testng.annotations.Test;

/**
 *
 * @author <NAME>
 */
public class SignatureTest {

    private final String pass = "1";
    private final String caSignedAlias = "cms sign cert (cms intermediate ca)";
    private final String selfSignedAlias = "self signed";

    private KeyStore trustStore;
    private KeyStore signStore;

    @BeforeClass
    public void init() throws KeyStoreException, IOException, NoSuchAlgorithmException, CertificateException {
	InputStream trustStoreStream = SignatureTest.class.getResourceAsStream("/cms_root.jks");
	InputStream signStoreStream = SignatureTest.class.getResourceAsStream("/cms_sign.jks");

	trustStore = KeyStore.getInstance("JKS");
	trustStore.load(trustStoreStream, null);

	signStore = KeyStore.getInstance("JKS");
	signStore.load(signStoreStream, pass.toCharArray());
    }

    private CMSSignedData createSignature(String alias, byte[] challenge) throws KeyStoreException, NoSuchAlgorithmException,
	    UnrecoverableKeyException, InvalidKeyException, SignatureException, OperatorCreationException,
	    CertificateEncodingException, CMSException {
	PrivateKey privKey = (PrivateKey) signStore.getKey(alias, pass.toCharArray());
	X509Certificate cert = (X509Certificate) signStore.getCertificate(alias);
	Certificate[] certChain = (Certificate[]) signStore.getCertificateChain(alias);
	Store certs = new JcaCertStore(Arrays.asList(certChain));

//	Signature signature = Signature.getInstance("SHA256WithRSA");
//	signature.initSign(privKey);
//	signature.update(challenge);
//	byte[] signedBytes = signature.sign();

	CMSTypedData msg = new CMSProcessableByteArray(challenge);
	CMSSignedDataGenerator gen = new CMSSignedDataGenerator();
	ContentSigner signer = new JcaContentSignerBuilder("SHA256withRSA").build(privKey);
	DigestCalculatorProvider dgProv = new JcaDigestCalculatorProviderBuilder().build();
        gen.addSignerInfoGenerator(new JcaSignerInfoGeneratorBuilder(dgProv).build(signer, cert));
        gen.addCertificates(certs);
        CMSSignedData sigData = gen.generate(msg, false);
	return sigData;
    }

    @Test
    public void testValidSignature() throws KeyStoreException, NoSuchAlgorithmException, UnrecoverableKeyException,
	    InvalidKeyException, SignatureException, OperatorCreationException, CertificateEncodingException,
	    CMSException, IOException, CertificateException {
	byte[] challenge = "Hello World!".getBytes(StandardCharsets.UTF_8);

	CMSSignedData sigData = createSignature(caSignedAlias, challenge);
	byte[] sigBytes = sigData.getEncoded();

	SignatureVerifier validator = new SignatureVerifier(trustStore, challenge);
	try {
	    validator.validate(sigBytes);
	} catch (SignatureInvalid ex) {
	    Assert.fail("Invalid signature found, expected a valid one.");
	}
    }

    @Test
    public void testInvalidSignature() throws KeyStoreException, NoSuchAlgorithmException, UnrecoverableKeyException,
	    InvalidKeyException, SignatureException, OperatorCreationException, CertificateEncodingException,
	    CMSException, IOException, CertificateException {
	byte[] challenge = "Hello World!".getBytes(StandardCharsets.UTF_8);

	CMSSignedData sigData = createSignature(caSignedAlias, challenge);
	byte[] sigBytes = sigData.getEncoded();

	challenge[0] = 1; // flip a bit in the challenge and see if it still verifies
	SignatureVerifier validator = new SignatureVerifier(trustStore, challenge);
	try {
	    validator.validate(sigBytes);
	    Assert.fail("Invalid signature found, expecte a valid one.");
	} catch (SignatureInvalid ex) {
	}
    }

    @Test
    public void testInvalidPath() throws KeyStoreException, NoSuchAlgorithmException, UnrecoverableKeyException,
	    InvalidKeyException, SignatureException, OperatorCreationException, CertificateEncodingException,
	    CMSException, IOException, CertificateException, CMSSignerDigestMismatchException, InvalidAlgorithmParameterException {
	byte[] challenge = "Hello World!".getBytes(StandardCharsets.UTF_8);

	CMSSignedData sigData = createSignature(selfSignedAlias, challenge);
	byte[] sigBytes = sigData.getEncoded();

	SignatureVerifier validator = new SignatureVerifier(trustStore, challenge);
	try {
	    validator.validate(sigBytes);
	    Assert.fail("Invalid signature found, expecte a valid one.");
	} catch (SignatureInvalid ex) {
	}
    }

    @Test
    public void testLuxTrustSignature() throws IOException, KeyStoreException, NoSuchAlgorithmException,
	    CertificateException, SignatureInvalid {
	System.setProperty("java.security.debug", "certpath");

	byte[] challenge = StringUtils.toByteArray("F8CB44221F3471FC492294B5F49DF9C0");
	String sigStr = "<KEY>;
	byte[] sig = Base64.getDecoder().decode(sigStr);

	// TODO: obtain real signature with reachable OCSP and/or CRL, then enable revocation check<commit_msg>Disable signature check due to missing luxtrust truststore<commit_after>/****************************************************************************
 * Copyright (C) 2016 ecsec GmbH.
 * All rights reserved.
 * Contact: ecsec GmbH (<EMAIL>)
 *
 * This file is part of the Open eCard App.
 *
 * GNU General Public License Usage
 * This file may be used under the terms of the GNU General Public
 * License version 3.0 as published by the Free Software Foundation
 * and appearing in the file LICENSE.GPL included in the packaging of
 * this file. Please review the following information to ensure the
 * GNU General Public License version 3.0 requirements will be met:
 * http://www.gnu.org/copyleft/gpl.html.
 *
 * Other Usage
 * Alternatively, this file may be used in accordance with the terms
 * and conditions contained in a signed written agreement between
 * you and ecsec GmbH.
 *
 ***************************************************************************/

package org.openecard.addons.cg.impl;

import java.io.IOException;
import java.io.InputStream;
import java.nio.charset.StandardCharsets;
import java.security.InvalidAlgorithmParameterException;
import java.security.InvalidKeyException;
import java.security.KeyStore;
import java.security.KeyStoreException;
import java.security.NoSuchAlgorithmException;
import java.security.PrivateKey;
import java.security.SignatureException;
import java.security.UnrecoverableKeyException;
import java.security.cert.Certificate;
import java.security.cert.CertificateEncodingException;
import java.security.cert.CertificateException;
import java.security.cert.X509Certificate;
import java.util.Arrays;
import java.util.Base64;
import mockit.Mock;
import mockit.MockUp;
import org.openecard.bouncycastle.cert.jcajce.JcaCertStore;
import org.openecard.bouncycastle.cms.CMSException;
import org.openecard.bouncycastle.cms.CMSProcessableByteArray;
import org.openecard.bouncycastle.cms.CMSSignedData;
import org.openecard.bouncycastle.cms.CMSSignedDataGenerator;
import org.openecard.bouncycastle.cms.CMSSignerDigestMismatchException;
import org.openecard.bouncycastle.cms.CMSTypedData;
import org.openecard.bouncycastle.cms.jcajce.JcaSignerInfoGeneratorBuilder;
import org.openecard.bouncycastle.operator.ContentSigner;
import org.openecard.bouncycastle.operator.DigestCalculatorProvider;
import org.openecard.bouncycastle.operator.OperatorCreationException;
import org.openecard.bouncycastle.operator.jcajce.JcaContentSignerBuilder;
import org.openecard.bouncycastle.operator.jcajce.JcaDigestCalculatorProviderBuilder;
import org.openecard.bouncycastle.util.Store;
import org.openecard.common.util.StringUtils;
import org.testng.Assert;
import org.testng.annotations.BeforeClass;
import org.testng.annotations.Test;

/**
 *
 * @author <NAME>
 */
public class SignatureTest {

    private final String pass = "1";
    private final String caSignedAlias = "cms sign cert (cms intermediate ca)";
    private final String selfSignedAlias = "self signed";

    private KeyStore trustStore;
    private KeyStore signStore;

    @BeforeClass
    public void init() throws KeyStoreException, IOException, NoSuchAlgorithmException, CertificateException {
	InputStream trustStoreStream = SignatureTest.class.getResourceAsStream("/cms_root.jks");
	InputStream signStoreStream = SignatureTest.class.getResourceAsStream("/cms_sign.jks");

	trustStore = KeyStore.getInstance("JKS");
	trustStore.load(trustStoreStream, null);

	signStore = KeyStore.getInstance("JKS");
	signStore.load(signStoreStream, pass.toCharArray());
    }

    private CMSSignedData createSignature(String alias, byte[] challenge) throws KeyStoreException, NoSuchAlgorithmException,
	    UnrecoverableKeyException, InvalidKeyException, SignatureException, OperatorCreationException,
	    CertificateEncodingException, CMSException {
	PrivateKey privKey = (PrivateKey) signStore.getKey(alias, pass.toCharArray());
	X509Certificate cert = (X509Certificate) signStore.getCertificate(alias);
	Certificate[] certChain = (Certificate[]) signStore.getCertificateChain(alias);
	Store certs = new JcaCertStore(Arrays.asList(certChain));

//	Signature signature = Signature.getInstance("SHA256WithRSA");
//	signature.initSign(privKey);
//	signature.update(challenge);
//	byte[] signedBytes = signature.sign();

	CMSTypedData msg = new CMSProcessableByteArray(challenge);
	CMSSignedDataGenerator gen = new CMSSignedDataGenerator();
	ContentSigner signer = new JcaContentSignerBuilder("SHA256withRSA").build(privKey);
	DigestCalculatorProvider dgProv = new JcaDigestCalculatorProviderBuilder().build();
        gen.addSignerInfoGenerator(new JcaSignerInfoGeneratorBuilder(dgProv).build(signer, cert));
        gen.addCertificates(certs);
        CMSSignedData sigData = gen.generate(msg, false);
	return sigData;
    }

    @Test
    public void testValidSignature() throws KeyStoreException, NoSuchAlgorithmException, UnrecoverableKeyException,
	    InvalidKeyException, SignatureException, OperatorCreationException, CertificateEncodingException,
	    CMSException, IOException, CertificateException {
	byte[] challenge = "Hello World!".getBytes(StandardCharsets.UTF_8);

	CMSSignedData sigData = createSignature(caSignedAlias, challenge);
	byte[] sigBytes = sigData.getEncoded();

	SignatureVerifier validator = new SignatureVerifier(trustStore, challenge);
	try {
	    validator.validate(sigBytes);
	} catch (SignatureInvalid ex) {
	    Assert.fail("Invalid signature found, expected a valid one.");
	}
    }

    @Test
    public void testInvalidSignature() throws KeyStoreException, NoSuchAlgorithmException, UnrecoverableKeyException,
	    InvalidKeyException, SignatureException, OperatorCreationException, CertificateEncodingException,
	    CMSException, IOException, CertificateException {
	byte[] challenge = "Hello World!".getBytes(StandardCharsets.UTF_8);

	CMSSignedData sigData = createSignature(caSignedAlias, challenge);
	byte[] sigBytes = sigData.getEncoded();

	challenge[0] = 1; // flip a bit in the challenge and see if it still verifies
	SignatureVerifier validator = new SignatureVerifier(trustStore, challenge);
	try {
	    validator.validate(sigBytes);
	    Assert.fail("Invalid signature found, expecte a valid one.");
	} catch (SignatureInvalid ex) {
	}
    }

    @Test
    public void testInvalidPath() throws KeyStoreException, NoSuchAlgorithmException, UnrecoverableKeyException,
	    InvalidKeyException, SignatureException, OperatorCreationException, CertificateEncodingException,
	    CMSException, IOException, CertificateException, CMSSignerDigestMismatchException, InvalidAlgorithmParameterException {
	byte[] challenge = "Hello World!".getBytes(StandardCharsets.UTF_8);

	CMSSignedData sigData = createSignature(selfSignedAlias, challenge);
	byte[] sigBytes = sigData.getEncoded();

	SignatureVerifier validator = new SignatureVerifier(trustStore, challenge);
	try {
	    validator.validate(sigBytes);
	    Assert.fail("Invalid signature found, expecte a valid one.");
	} catch (SignatureInvalid ex) {
	}
    }

    // disabled due to incompatible truststores
    @Test(enabled = false)
    public void testLuxTrustSignature() throws IOException, KeyStoreException, NoSuchAlgorithmException,
	    CertificateException, SignatureInvalid {
	System.setProperty("java.security.debug", "certpath");

	byte[] challenge = StringUtils.toByteArray("F8CB44221F3471FC492294B5F49DF9C0");
	String sigStr = "<KEY>;
	byte[] sig = Base64.getDecoder().decode(sigStr);

	// TODO: obtain real signature with reachable OCSP and/or CRL, then enable revocation check
{'fraction_non_alphanumeric': 0.1065399441712083, 'fraction_numerical': 0.007177987504984713, 'mean_word_length': 6.13127962085308, 'pattern_counts': {'":': 0, '<': 9, '<?xml version=': 0, '>': 9, 'https://': 0, 'lorem ipsum': 0, 'www.': 2, 'xml': 0}, 'pii_count': 0, 'substrings_counts': 0, 'word_list_counts': {'cursed_substrings.json': 0, 'profanity_word_list.json': 0, 'sexual_word_list.json': 0, 'zh_pornsignals.json': 0}}
{'dir': 'git-commits-cleaned', 'id': '6746915', 'n_tokens_mistral': 4333, 'n_tokens_neox': 4031, 'n_words': 980}