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

ca.uhn.hl7v2.hoh.sign.BouncyCastleCmsMessageSigner Maven / Gradle / Ivy

The newest version!
package ca.uhn.hl7v2.hoh.sign;

import static ca.uhn.hl7v2.hoh.util.StringUtils.*;

import java.security.GeneralSecurityException;
import java.security.KeyStore;
import java.security.KeyStoreException;
import java.security.PrivateKey;
import java.security.PublicKey;
import java.security.Security;
import java.security.cert.Certificate;
import java.security.cert.X509Certificate;
import java.util.ArrayList;
import java.util.List;

import org.bouncycastle.cert.jcajce.JcaCertStore;
import org.bouncycastle.cms.CMSProcessable;
import org.bouncycastle.cms.CMSProcessableByteArray;
import org.bouncycastle.cms.CMSSignedData;
import org.bouncycastle.cms.CMSSignedDataGenerator;
import org.bouncycastle.cms.CMSSignerDigestMismatchException;
import org.bouncycastle.cms.CMSTypedData;
import org.bouncycastle.cms.SignerInformation;
import org.bouncycastle.cms.SignerInformationStore;
import org.bouncycastle.cms.SignerInformationVerifier;
import org.bouncycastle.cms.jcajce.JcaSignerInfoGeneratorBuilder;
import org.bouncycastle.cms.jcajce.JcaSimpleSignerInfoVerifierBuilder;
import org.bouncycastle.jce.provider.BouncyCastleProvider;
import org.bouncycastle.operator.ContentSigner;
import org.bouncycastle.operator.jcajce.JcaContentSignerBuilder;
import org.bouncycastle.operator.jcajce.JcaDigestCalculatorProviderBuilder;
import org.bouncycastle.util.Store;

import ca.uhn.hl7v2.hoh.util.repackage.Base64;

public class BouncyCastleCmsMessageSigner implements ISigner {

	static final String MSG_KEY_IS_NOT_A_PRIVATE_KEY = "Key is not a private key: ";
	static final String MSG_KEY_IS_NOT_A_PUBLIC_KEY = "Key is not a public key: ";
	static final String MSG_KEYSTORE_DOES_NOT_CONTAIN_KEY_WITH_ALIAS = "Keystore does not contain key with alias: ";

	private static final org.slf4j.Logger ourLog = org.slf4j.LoggerFactory.getLogger(BouncyCastleCmsMessageSigner.class);

	private final String myAlgorithm = "SHA512withRSA";
	private String myAliasPassword;
	private String myKeyAlias;
	private KeyStore myKeyStore;
	private PrivateKey myPrivateKey;
	private PublicKey myPublicKey;

	/**
	 * Constructor
	 */
	public BouncyCastleCmsMessageSigner() {
		super();
	}
	
	private PrivateKey getPrivateKey() throws GeneralSecurityException, SignatureFailureException {
		if (myKeyStore == null) {
			throw new SignatureFailureException("Keystore is not set");
		}
		if (isBlank(myKeyAlias)) {
			throw new SignatureFailureException("Key alias is not set");
		}
		if (isBlank(myAliasPassword)) {
			throw new SignatureFailureException("Key alias password is not set");
		}

		if (this.myPrivateKey == null) {

			myPrivateKey = (PrivateKey) myKeyStore.getKey(myKeyAlias, myAliasPassword.toCharArray());
			if (myPrivateKey == null) {
				if (myKeyStore.containsAlias(myKeyAlias)) {
					if (myKeyStore.isCertificateEntry(myKeyAlias)) {
						throw new SignatureFailureException(MSG_KEY_IS_NOT_A_PRIVATE_KEY + myKeyAlias);
					}
				} else {
					throw new SignatureFailureException(MSG_KEYSTORE_DOES_NOT_CONTAIN_KEY_WITH_ALIAS + myKeyAlias);
				}
			}
		}
		return this.myPrivateKey;
	}

	private PublicKey getPublicKey() throws SignatureFailureException {
		if (myKeyStore == null) {
			throw new SignatureFailureException("Keystore is not set");
		}
		if (isBlank(myKeyAlias)) {
			throw new SignatureFailureException("Key alias is not set");
		}

		if (myPublicKey == null) {
			try {
				Certificate pubCert = myKeyStore.getCertificate(myKeyAlias);
				myPublicKey = pubCert != null ? pubCert.getPublicKey() : null;
				if (myPublicKey == null) {
					if (myKeyStore.containsAlias(myKeyAlias)) {
						if (myKeyStore.isKeyEntry(myKeyAlias)) {
							throw new SignatureFailureException(MSG_KEY_IS_NOT_A_PUBLIC_KEY + myKeyAlias);
						}
					} else {
						throw new SignatureFailureException(MSG_KEYSTORE_DOES_NOT_CONTAIN_KEY_WITH_ALIAS + myKeyAlias);
					}
				}
			} catch (KeyStoreException e) {
				throw new SignatureFailureException("Failed to retrieve key with alias " + myKeyAlias + " from keystore", e);
			}

		}
		return myPublicKey;
	}

	/**
	 * @param theAliasPassword
	 *            the aliasPassword to set
	 */
	public void setAliasPassword(String theAliasPassword) {
		myAliasPassword = theAliasPassword;
	}

	/**
	 * @param theKeyAlias
	 *            the keyAlias to set
	 */
	public void setKeyAlias(String theKeyAlias) {
		myKeyAlias = theKeyAlias;
	}

	/**
	 * @param theKeyStore
	 *            the keyStore to set
	 */
	public void setKeyStore(KeyStore theKeyStore) {
		if (theKeyStore == null) {
			throw new NullPointerException("Keystore can not be null");
		}
		myKeyStore = theKeyStore;
	}

	/**
	 * {@inheritDoc}
	 */
	public String sign(byte[] theBytes) throws SignatureFailureException {
		try {
			Security.addProvider(new BouncyCastleProvider());

			List certList = new ArrayList<>();
			CMSTypedData msg = new CMSProcessableByteArray(theBytes);

			X509Certificate signCert = (X509Certificate) myKeyStore.getCertificate(myKeyAlias);
			certList.add(signCert);

			Store certs = new JcaCertStore(certList);

			CMSSignedDataGenerator gen = new CMSSignedDataGenerator();
			ContentSigner sha1Signer = new JcaContentSignerBuilder(myAlgorithm).setProvider("BC").build(getPrivateKey());

			gen.addSignerInfoGenerator(new JcaSignerInfoGeneratorBuilder(new JcaDigestCalculatorProviderBuilder().setProvider("BC").build()).build(sha1Signer, signCert));

			gen.addCertificates(certs);

			CMSSignedData sigData = gen.generate(msg, false);
			return myAlgorithm + ' ' + Base64.encodeBase64String(sigData.getEncoded());
//			return Base64.encodeBase64String(sigData.getEncoded());

		} catch (Exception e) {
			throw new SignatureFailureException(e);
		}
	}

	/**
	 * {@inheritDoc}
	 */
	public void verify(byte[] theBytes, String theSignature) throws SignatureVerificationException, SignatureFailureException {
		PublicKey pubKey = getPublicKey();

		try {

			int spaceIndex = theSignature.indexOf(' ');
			if (spaceIndex == -1) {
				throw new SignatureVerificationException("No algorithm found in signature block: " + theSignature);
			}

			theSignature = theSignature.substring(spaceIndex + 1);

			CMSProcessable content = new CMSProcessableByteArray(theBytes);
			CMSSignedData s = new CMSSignedData(content, Base64.decodeBase64(theSignature));

			ourLog.debug("Verifying message against public key with alias[{}]", myKeyAlias);

			SignerInformationVerifier vib = new JcaSimpleSignerInfoVerifierBuilder().build(pubKey);

			SignerInformationStore signers = s.getSignerInfos();
			boolean verified = false;

			for (Object o : signers.getSigners()) {
				SignerInformation signer = (SignerInformation) o;
				try {

					ourLog.debug("Signer: {}", signer.getSID());

					if (signer.verify(vib)) {
						verified = true;
					}
				} catch (CMSSignerDigestMismatchException e) {
					throw new SignatureVerificationException(e);
				}

			}

			if (!verified) {
				throw new SignatureVerificationException();
			}

		} catch (SignatureVerificationException e) {
			throw e;
		} catch (Exception e) {
			throw new SignatureFailureException(e);
		}

	}

}




© 2015 - 2024 Weber Informatics LLC | Privacy Policy