Coverage Report - ca.uhn.hl7v2.hoh.sign.BouncyCastleCmsMessageSigner
 
Classes in this File Line Coverage Branch Coverage Complexity
BouncyCastleCmsMessageSigner
79%
67/84
52%
20/38
6.25
 
 1  
 package ca.uhn.hl7v2.hoh.sign;
 2  
 
 3  
 import static ca.uhn.hl7v2.hoh.util.StringUtils.*;
 4  
 
 5  
 import java.security.GeneralSecurityException;
 6  
 import java.security.KeyStore;
 7  
 import java.security.KeyStoreException;
 8  
 import java.security.PrivateKey;
 9  
 import java.security.PublicKey;
 10  
 import java.security.Security;
 11  
 import java.security.cert.Certificate;
 12  
 import java.security.cert.X509Certificate;
 13  
 import java.util.ArrayList;
 14  
 import java.util.Iterator;
 15  
 import java.util.List;
 16  
 
 17  
 import org.bouncycastle.cert.jcajce.JcaCertStore;
 18  
 import org.bouncycastle.cms.CMSProcessable;
 19  
 import org.bouncycastle.cms.CMSProcessableByteArray;
 20  
 import org.bouncycastle.cms.CMSSignedData;
 21  
 import org.bouncycastle.cms.CMSSignedDataGenerator;
 22  
 import org.bouncycastle.cms.CMSSignerDigestMismatchException;
 23  
 import org.bouncycastle.cms.CMSTypedData;
 24  
 import org.bouncycastle.cms.SignerInformation;
 25  
 import org.bouncycastle.cms.SignerInformationStore;
 26  
 import org.bouncycastle.cms.SignerInformationVerifier;
 27  
 import org.bouncycastle.cms.jcajce.JcaSignerInfoGeneratorBuilder;
 28  
 import org.bouncycastle.cms.jcajce.JcaSimpleSignerInfoVerifierBuilder;
 29  
 import org.bouncycastle.jce.provider.BouncyCastleProvider;
 30  
 import org.bouncycastle.operator.ContentSigner;
 31  
 import org.bouncycastle.operator.jcajce.JcaContentSignerBuilder;
 32  
 import org.bouncycastle.operator.jcajce.JcaDigestCalculatorProviderBuilder;
 33  
 import org.bouncycastle.util.Store;
 34  
 
 35  
 import ca.uhn.hl7v2.hoh.util.repackage.Base64;
 36  
 
 37  
 public class BouncyCastleCmsMessageSigner implements ISigner {
 38  
 
 39  
         static final String MSG_KEY_IS_NOT_A_PRIVATE_KEY = "Key is not a private key: ";
 40  
         static final String MSG_KEY_IS_NOT_A_PUBLIC_KEY = "Key is not a public key: ";
 41  
         static final String MSG_KEYSTORE_DOES_NOT_CONTAIN_KEY_WITH_ALIAS = "Keystore does not contain key with alias: ";
 42  
 
 43  5
         private static final org.slf4j.Logger ourLog = org.slf4j.LoggerFactory.getLogger(BouncyCastleCmsMessageSigner.class);
 44  
 
 45  40
         private String myAlgorithm = "SHA512withRSA";
 46  
         private String myAliasPassword;
 47  
         private String myKeyAlias;
 48  
         private KeyStore myKeyStore;
 49  
         private PrivateKey myPrivateKey;
 50  
         private PublicKey myPublicKey;
 51  
 
 52  
         /**
 53  
          * Constructor
 54  
          */
 55  
         public BouncyCastleCmsMessageSigner() {
 56  40
                 super();
 57  40
         }
 58  
         
 59  
         private PrivateKey getPrivateKey() throws GeneralSecurityException, SignatureFailureException {
 60  30
                 if (myKeyStore == null) {
 61  0
                         throw new SignatureFailureException("Keystore is not set");
 62  
                 }
 63  30
                 if (isBlank(myKeyAlias)) {
 64  0
                         throw new SignatureFailureException("Key alias is not set");
 65  
                 }
 66  30
                 if (isBlank(myAliasPassword)) {
 67  0
                         throw new SignatureFailureException("Key alias password is not set");
 68  
                 }
 69  
 
 70  30
                 if (this.myPrivateKey == null) {
 71  
 
 72  25
                         myPrivateKey = (PrivateKey) myKeyStore.getKey(myKeyAlias, myAliasPassword.toCharArray());
 73  25
                         if (myPrivateKey == null) {
 74  5
                                 if (myKeyStore.containsAlias(myKeyAlias)) {
 75  5
                                         if (myKeyStore.isCertificateEntry(myKeyAlias)) {
 76  5
                                                 throw new SignatureFailureException(MSG_KEY_IS_NOT_A_PRIVATE_KEY + myKeyAlias);
 77  
                                         }
 78  
                                 } else {
 79  0
                                         throw new SignatureFailureException(MSG_KEYSTORE_DOES_NOT_CONTAIN_KEY_WITH_ALIAS + myKeyAlias);
 80  
                                 }
 81  
                         }
 82  
                 }
 83  25
                 return this.myPrivateKey;
 84  
         }
 85  
 
 86  
         private PublicKey getPublicKey() throws SignatureFailureException {
 87  15
                 if (myKeyStore == null) {
 88  0
                         throw new SignatureFailureException("Keystore is not set");
 89  
                 }
 90  15
                 if (isBlank(myKeyAlias)) {
 91  0
                         throw new SignatureFailureException("Key alias is not set");
 92  
                 }
 93  
 
 94  15
                 if (myPublicKey == null) {
 95  
                         try {
 96  15
                                 Certificate pubCert = myKeyStore.getCertificate(myKeyAlias);
 97  15
                                 myPublicKey = pubCert != null ? pubCert.getPublicKey() : null;
 98  15
                                 if (myPublicKey == null) {
 99  0
                                         if (myKeyStore.containsAlias(myKeyAlias)) {
 100  0
                                                 if (myKeyStore.isKeyEntry(myKeyAlias)) {
 101  0
                                                         throw new SignatureFailureException(MSG_KEY_IS_NOT_A_PUBLIC_KEY + myKeyAlias);
 102  
                                                 }
 103  
                                         } else {
 104  0
                                                 throw new SignatureFailureException(MSG_KEYSTORE_DOES_NOT_CONTAIN_KEY_WITH_ALIAS + myKeyAlias);
 105  
                                         }
 106  
                                 }
 107  0
                         } catch (KeyStoreException e) {
 108  0
                                 throw new SignatureFailureException("Failed to retrieve key with alias " + myKeyAlias + " from keystore", e);
 109  15
                         }
 110  
 
 111  
                 }
 112  15
                 return myPublicKey;
 113  
         }
 114  
 
 115  
         /**
 116  
          * @param theAliasPassword
 117  
          *            the aliasPassword to set
 118  
          */
 119  
         public void setAliasPassword(String theAliasPassword) {
 120  40
                 myAliasPassword = theAliasPassword;
 121  40
         }
 122  
 
 123  
         /**
 124  
          * @param theKeyAlias
 125  
          *            the keyAlias to set
 126  
          */
 127  
         public void setKeyAlias(String theKeyAlias) {
 128  40
                 myKeyAlias = theKeyAlias;
 129  40
         }
 130  
 
 131  
         /**
 132  
          * @param theKeyStore
 133  
          *            the keyStore to set
 134  
          */
 135  
         public void setKeyStore(KeyStore theKeyStore) {
 136  45
                 if (theKeyStore == null) {
 137  0
                         throw new NullPointerException("Keystore can not be null");
 138  
                 }
 139  45
                 myKeyStore = theKeyStore;
 140  45
         }
 141  
 
 142  
         /**
 143  
          * {@inheritDoc}
 144  
          */
 145  
         public String sign(byte[] theBytes) throws SignatureFailureException {
 146  
                 try {
 147  30
                         Security.addProvider(new BouncyCastleProvider());
 148  
 
 149  30
                         List<X509Certificate> certList = new ArrayList<X509Certificate>();
 150  30
                         CMSTypedData msg = new CMSProcessableByteArray(theBytes);
 151  
 
 152  30
                         X509Certificate signCert = (X509Certificate) myKeyStore.getCertificate(myKeyAlias);
 153  30
                         certList.add(signCert);
 154  
 
 155  30
                         Store certs = new JcaCertStore(certList);
 156  
 
 157  30
                         CMSSignedDataGenerator gen = new CMSSignedDataGenerator();
 158  30
                         ContentSigner sha1Signer = new JcaContentSignerBuilder(myAlgorithm).setProvider("BC").build(getPrivateKey());
 159  
 
 160  25
                         gen.addSignerInfoGenerator(new JcaSignerInfoGeneratorBuilder(new JcaDigestCalculatorProviderBuilder().setProvider("BC").build()).build(sha1Signer, signCert));
 161  
 
 162  25
                         gen.addCertificates(certs);
 163  
 
 164  25
                         CMSSignedData sigData = gen.generate(msg, false);
 165  25
                         return myAlgorithm + ' ' + Base64.encodeBase64String(sigData.getEncoded());
 166  
 //                        return Base64.encodeBase64String(sigData.getEncoded());
 167  
 
 168  5
                 } catch (Exception e) {
 169  5
                         throw new SignatureFailureException(e);
 170  
                 }
 171  
         }
 172  
 
 173  
         /**
 174  
          * {@inheritDoc}
 175  
          */
 176  
         public void verify(byte[] theBytes, String theSignature) throws SignatureVerificationException, SignatureFailureException {
 177  15
                 PublicKey pubKey = getPublicKey();
 178  
 
 179  
                 try {
 180  
 
 181  15
                         int spaceIndex = theSignature.indexOf(' ');
 182  15
                         if (spaceIndex == -1) {
 183  0
                                 throw new SignatureVerificationException("No algorithm found in signature block: " + theSignature);
 184  
                         }
 185  
 
 186  15
                         theSignature = theSignature.substring(spaceIndex + 1);
 187  
 
 188  15
                         CMSProcessable content = new CMSProcessableByteArray(theBytes);
 189  15
                         CMSSignedData s = new CMSSignedData(content, Base64.decodeBase64(theSignature));
 190  
 
 191  15
                         ourLog.debug("Verifying message against public key with alias[{}]", myKeyAlias);
 192  
 
 193  15
                         SignerInformationVerifier vib = new JcaSimpleSignerInfoVerifierBuilder().build(pubKey);
 194  
 
 195  15
                         SignerInformationStore signers = s.getSignerInfos();
 196  15
                         boolean verified = false;
 197  
 
 198  15
                         for (Iterator<?> i = signers.getSigners().iterator(); i.hasNext();) {
 199  15
                                 SignerInformation signer = (SignerInformation) i.next();
 200  
                                 try {
 201  
 
 202  15
                                         ourLog.debug("Signer: {}", signer.getSID());
 203  
 
 204  15
                                         if (signer.verify(vib)) {
 205  10
                                                 verified = true;
 206  
                                         }
 207  5
                                 } catch (CMSSignerDigestMismatchException e) {
 208  5
                                         throw new SignatureVerificationException(e);
 209  10
                                 }
 210  
 
 211  10
                         }
 212  
 
 213  10
                         if (verified == false) {
 214  0
                                 throw new SignatureVerificationException();
 215  
                         }
 216  
 
 217  5
                 } catch (SignatureVerificationException e) {
 218  5
                         throw e;
 219  0
                 } catch (Exception e) {
 220  0
                         throw new SignatureFailureException(e);
 221  10
                 }
 222  
 
 223  10
         }
 224  
 
 225  
 }