View Javadoc

1   package org.eparapher.core.signature;
2   
3   import java.io.File;
4   import java.security.InvalidAlgorithmParameterException;
5   import java.security.NoSuchAlgorithmException;
6   import java.security.NoSuchProviderException;
7   import java.security.cert.CertStore;
8   import java.security.cert.CollectionCertStoreParameters;
9   import java.security.cert.X509Certificate;
10  import java.util.ArrayList;
11  
12  import org.apache.log4j.Logger;
13  import org.bouncycastle.asn1.cms.ContentInfo;
14  import org.bouncycastle.cms.CMSProcessable;
15  import org.bouncycastle.cms.CMSProcessableByteArray;
16  import org.bouncycastle.cms.CMSSignedData;
17  import org.bouncycastle.cms.CMSSignedDataGenerator;
18  import org.bouncycastle.cms.CMSSignedGenerator;
19  import org.bouncycastle.cms.SignerInformationStore;
20  import org.bouncycastle.x509.X509Store;
21  import org.eparapher.core.EParapherManager;
22  import org.eparapher.core.crypto.EPKeystoreManager;
23  import org.eparapher.core.crypto.keystore.IUserKeystore;
24  import org.eparapher.core.tools.FileUtil;
25  
26  
27  /**
28   * inspired from :
29   * http://p2p.wrox.com/book-beginning-cryptography-java/61612-bouncycastle-multiple-signatures.html
30   * http://www.codefund.com/71/verify-p7s-false-710519.shtm
31   * 
32   * @author Arnault MICHEL
33   *
34   */
35  public class CMSSigner {
36  
37  	private static Logger log = Logger.getLogger(CMSSigner.class);
38  	
39  	private IUserKeystore userpkandcert;
40  	private boolean addsignature;
41  
42  	public CMSSigner() {
43  		userpkandcert = EPKeystoreManager.getInstance().getUserkeystore();
44  	}
45  	
46  	public String sign( String original_file, CMSSignatureParameters cmssignparams ) {
47  		try {
48  			addsignature = false;
49  			String signed_file = manageFileBeforeSignature( original_file, cmssignparams.isDetached() );
50  			if (signed_file == null) {
51  				log.info("User cancel the CMS signature process");
52  				return null;
53  			}
54  			log.info("Generating " + (cmssignparams.isDetached()?"detached":"embedded") +" CMS signature of " +original_file + " in " + signed_file );
55  			
56  			// Loading file content to sign
57  			byte[] content_to_sign = FileUtil.readFile(original_file);
58  			
59  			// Loading certificates and CRLs
60  			CertStore certs = buildCertsAndCRLsListToInsertInCMS(cmssignparams);
61  			
62  			// Building the CMS Signature
63  			CMSSignedDataGenerator signGen = new CMSSignedDataGenerator();
64  			signGen.addSigner(userpkandcert.getPrivateKey(), userpkandcert.getX509Certificate(), cmssignparams.getSignatureDigestAlgOID());
65  			if (certs!=null)
66  				signGen.addCertificatesAndCRLs(certs);
67  			
68  			CMSSignedData signedData = null;
69  			CMSProcessable content=null;
70  
71  			//Load existing signature
72  			if (addsignature) {
73  				if (cmssignparams.isDetached())
74  					signedData = new CMSSignedData(FileUtil.readFile(signed_file));
75  				else
76  					signedData = new CMSSignedData(content_to_sign);
77  				
78  				if (signedData!=null){
79  					SignerInformationStore signers = signedData.getSignerInfos();
80  					ContentInfo ci = signedData.getContentInfo();
81  	
82  					CertStore existingCerts=signedData.getCertificatesAndCRLs("Collection", "BC");
83  					X509Store x509Store=signedData.getAttributeCertificates("Collection", "BC");
84  
85  					//add existing certs
86  					signGen.addCertificatesAndCRLs(existingCerts);
87  					//add existing certs attributes
88  					signGen.addAttributeCertificates(x509Store);
89  					//add existing signers
90  					signGen.addSigners(signers);
91  				}
92  			}
93  			// Load content to sign
94  			if ( addsignature && !cmssignparams.isDetached() )
95  				content = signedData.getSignedContent();
96  			else
97  				content = new CMSProcessableByteArray(content_to_sign);
98  
99  			// Generate CMS/PKCS#7 Signature
100 			if (EPKeystoreManager.isCAPICOMUsed() || EPKeystoreManager.isPKCS11Used()) {
101 				String provName = userpkandcert.getProviderName();
102 				signedData = signGen.generate(CMSSignedGenerator.DATA, content, !cmssignparams.isDetached(), provName);
103 			} else
104 				signedData = signGen.generate(CMSSignedGenerator.DATA, content, !cmssignparams.isDetached(), "BC");
105 			
106 			// Ecriture du buffer dans un fichier.
107 			FileUtil.writeToFile(signed_file, signedData.getEncoded());
108 			
109 			return signed_file;
110 		} catch (Exception e) {
111 			log.error(""+e.getLocalizedMessage(),e);
112 		}
113 
114 		return null;
115 	}
116 
117 	private CertStore buildCertsAndCRLsListToInsertInCMS( CMSSignatureParameters cmssignparams) {
118 		ArrayList<X509Certificate> certList = new ArrayList<X509Certificate>();
119 		certList.add(userpkandcert.getX509Certificate());
120 		// TODO : v0.2, implement CRL Retriever
121 		//if (cmssignparams.isInsertCRLs())
122 			//certList.add(getCRL);
123 		CertStore certstore = null;
124 		try {
125 			certstore = CertStore.getInstance("Collection", new CollectionCertStoreParameters(certList), "BC");
126 		} catch (InvalidAlgorithmParameterException e) {
127 			log.error(""+e.getLocalizedMessage(),e);
128 		} catch (NoSuchAlgorithmException e) {
129 			log.error(""+e.getLocalizedMessage(),e);
130 		} catch (NoSuchProviderException e) {
131 			log.error(""+e.getLocalizedMessage(),e);
132 		}
133 		return certstore;
134 	}
135 
136 	private String manageFileBeforeSignature(String original_file, boolean detached) {
137 
138 		String signed_file, p7sFile, p7mFile;
139 		
140 		// Generate p7s and p7m filename
141 		int dotpos     = original_file.toLowerCase().lastIndexOf(".");
142 		int fileseppos = original_file.lastIndexOf(File.separator);
143 		if (dotpos>=0 && dotpos>fileseppos) {
144 			p7sFile = original_file.substring(0,dotpos) + ".p7s";
145 			p7mFile = original_file.substring(0,dotpos) + ".p7m";
146 		} else {
147 			p7sFile = original_file + ".p7s";
148 			p7mFile = original_file + ".p7m";
149 		}
150 			
151 		// Throw an exception if file is empty
152 		//if (FileUtil.fileEmpty(original_file)) {
153 			//throw new SignatureException("Cannot sign empty file " + original_file);
154 		//}
155 		if (detached) {
156 			if (FileUtil.fileExists(p7sFile)) {
157 				//Want a confirmation notification when adding a new signature?
158 				//String confirmmsg = "An existing signature (*.p7s) has been found.\nPlease confirm that you want to generate a new signature";
159 				//if (EParapherManager.getInstance().getUI().askUserYesNo(confirmmsg)) {
160 					log.warn("Detached signature already exists : adding a new signature");
161 					addsignature = true;
162 				//} else {
163 					//return null;
164 				//}
165 			}
166 			signed_file = p7sFile;
167 		} else {
168 			if (FileUtil.fileExists(p7mFile)) {
169 				if (p7mFile.equals(original_file)) {
170 					//ask user that he wants to oversign/countersign ( == p7m(p7m) )
171 					//EParapherManager.getInstance().getUI().askUserYesNo("An existing signature has been found");
172 					log.info("The CMS attached signature file already exists : adding a new signature");
173 					addsignature = true;
174 				} else {
175 					//TODO : compare p7m(content) and original file content : first bytes then hashes
176 					//  and if hashes equals : ask if add signature.
177 					//  For the moment, ask user if overwrite and loose the other signature?
178 					String confirmmsg = "We detect that " + p7mFile + " exists.\nDo you want to overwrite it, and loose old sinatures?";
179 					if (EParapherManager.getInstance().getUI().askUserYesNo(confirmmsg)) {
180 						log.info("The existing p7m will be overwritten...");
181 					} else
182 						return null;
183 				}
184 			}
185 			signed_file = p7mFile;
186 		}
187 		
188 		return signed_file;
189 	}
190 
191 }