1 /*
2 * $Id: SimpleSigner.java,v 1.11 2004/07/21 23:07:16 pelle Exp $
3 * $Log: SimpleSigner.java,v $
4 * Revision 1.11 2004/07/21 23:07:16 pelle
5 * Updated the Signer interface with a new generateKey() method, which doesn't take any parameters.
6 * It stores the generated key using the Base32 encoded SHA1 digest as it's alias.
7 *
8 * Revision 1.10 2004/01/19 17:53:14 pelle
9 * Various clean ups
10 *
11 * Revision 1.9 2003/12/19 18:02:53 pelle
12 * Revamped a lot of exception handling throughout the framework, it has been simplified in most places:
13 * - For most cases the main exception to worry about now is InvalidNamedObjectException.
14 * - Most lowerlevel exception that cant be handled meaningful are now wrapped in the LowLevelException, a
15 * runtime exception.
16 * - Source and Store patterns each now have their own exceptions that generalizes the various physical
17 * exceptions that can happen in that area.
18 *
19 * Revision 1.8 2003/12/19 00:31:15 pelle
20 * Lots of usability changes through out all the passphrase agents and end user tools.
21 *
22 * Revision 1.7 2003/12/18 17:40:07 pelle
23 * You can now create keys that get stored with a X509 certificate in the keystore. These can be saved as well.
24 * IdentityCreator has been modified to allow creation of keys.
25 * Note The actual Creation of Certificates still have a problem that will be resolved later today.
26 *
27 * Revision 1.6 2003/12/16 23:16:40 pelle
28 * Work done on the SigningServlet. The two phase web model is now only an option.
29 * Allowing much quicker signing, using the GuiDialogueAgent.
30 * The screen has also been cleaned up and displays the xml to be signed.
31 * The GuiDialogueAgent now optionally remembers passphrases and has a checkbox to support this.
32 * The PassPhraseAgent's now have a UserCancellationException, which allows the agent to tell the application if the user specifically
33 * cancels the signing process.
34 *
35 * Revision 1.5 2003/12/10 23:55:45 pelle
36 * Did some cleaning up in the builders
37 * Fixed some stuff in IdentityCreator
38 * New maven goal to create executable jarapp
39 * We are close to 0.8 final of ID, 0.11 final of XMLSIG and 0.5 of commons.
40 * Will release shortly.
41 *
42 * Revision 1.4 2003/11/21 04:43:41 pelle
43 * EncryptedFileStore now works. It uses the PBECipher with DES3 afair.
44 * Otherwise You will Finaliate.
45 * Anything that can be final has been made final throughout everyting. We've used IDEA's Inspector tool to find all instance of variables that could be final.
46 * This should hopefully make everything more stable (and secure).
47 *
48 * Revision 1.3 2003/11/19 23:32:50 pelle
49 * Signers now can generatekeys via the generateKey() method.
50 * Refactored the relationship between SignedNamedObject and NamedObjectBuilder a bit.
51 * SignedNamedObject now contains the full xml which is returned with getEncoded()
52 * This means that it is now possible to further receive on or process a SignedNamedObject, leaving
53 * NamedObjectBuilder for its original purposes of purely generating new Contracts.
54 * NamedObjectBuilder.sign() now returns a SignedNamedObject which is the prefered way of processing it.
55 * Updated all major interfaces that used the old model to use the new model.
56 *
57 * Revision 1.2 2003/11/12 18:54:42 pelle
58 * Updated SimpleSignerStoreTest to use a StoredPassPhraseAgent eliminating the popup during testing.
59 * Created SigningBenchmark for running comparative performance benchmarks on various key algorithms.
60 *
61 * Revision 1.1 2003/11/11 21:17:47 pelle
62 * Further vital reshuffling.
63 * org.neudist.crypto.* and org.neudist.utils.* have been moved to respective areas under org.neuclear.commons
64 * org.neuclear.signers.* as well as org.neuclear.passphraseagents have been moved under org.neuclear.commons.crypto as well.
65 * Did a bit of work on the Canonicalizer and changed a few other minor bits.
66 *
67 * Revision 1.3 2003/11/08 20:27:06 pelle
68 * Updated the Signer interface to return a key type to be used for XML SignatureInfo. Thus we now support DSA sigs yet again.
69 *
70 * Revision 1.2 2003/10/29 23:17:53 pelle
71 * Updated some javadocs
72 * Added a neuclear specific maven repository at:
73 * http://neuclear.org/maven/ and updated the properties files to reflect that.
74 *
75 * Revision 1.1 2003/10/29 21:16:28 pelle
76 * Refactored the whole signing process. Now we have an interface called Signer which is the old SignerStore.
77 * To use it you pass a byte array and an alias. The sign method then returns the signature.
78 * If a Signer needs a passphrase it uses a PassPhraseAgent to present a dialogue box, read it from a command line etc.
79 * This new Signer pattern allows us to use secure signing hardware such as N-Cipher in the future for server applications as well
80 * as SmartCards for end user applications.
81 *
82 * Revision 1.4 2003/10/28 23:44:03 pelle
83 * The GuiDialogAgent now works. It simply presents itself as a simple modal dialog box asking for a passphrase.
84 * The two Signer implementations both use it for the passphrase.
85 *
86 * Revision 1.3 2003/10/21 22:29:59 pelle
87 * Renamed NeudistException to NeuClearException and moved it to org.neuclear.commons where it makes more sense.
88 * Unhooked the XMLException in the xmlsig library from NeuClearException to make all of its exceptions an independent hierarchy.
89 * Obviously had to perform many changes throughout the code to support these changes.
90 *
91 * Revision 1.2 2003/02/20 13:26:41 pelle
92 * Adding all of the modification from Rams?s Morales ramses@computer.org to support DSASHA1 Signatures
93 * Thanks Rams?s good work.
94 * So this means there is now support for:
95 * - DSA KeyInfo blocks
96 * - DSA Key Generation within CryptoTools
97 * - Signing using DSASHA1
98 *
99 * Revision 1.1 2003/02/18 00:03:32 pelle
100 * Moved the Signer classes from neuclearframework into neuclear-xmlsig
101 *
102 * Revision 1.5 2003/02/10 22:30:13 pelle
103 * Got rid of even further dependencies. In Particular OSCore
104 *
105 * Revision 1.4 2003/02/09 00:15:55 pelle
106 * Fixed things so they now compile with r_0.7 of XMLSig
107 *
108 * Revision 1.3 2002/10/06 00:39:26 pelle
109 * I have now expanded support for different types of Signers.
110 * There is now a JCESigner which uses a JCE KeyStore for signing.
111 * I have refactored the SigningServlet a bit, eliminating most of the demo code.
112 * This has been moved into DemoSigningServlet.
113 * I have expanded the CommandLineSigner, so it now also has an option for specifying a default signing service.
114 * The default web application now contains two signers.
115 * - The Demo one is still at /Signer
116 * - There is a new one at /personal/Signer this uses the testkeys.ks for
117 * signing anything under neu://test
118 * Note neu://test now has a default interactive signer running on localhost.
119 * So to play with this you must install the webapp on your own local machine.
120 *
121 * Revision 1.2 2002/09/23 15:09:11 pelle
122 * Got the SimpleSigner working properly.
123 * I couldn't get SealedObjects working with BouncyCastle's Symmetric keys.
124 * Don't know what I was doing, so I reimplemented it. Encrypting
125 * and decrypting it my self.
126 *
127 * Revision 1.1 2002/09/21 23:11:16 pelle
128 * A bunch of clean ups. Got rid of as many hard coded URL's as I could.
129 *
130 * User: pelleb
131 * Date: Sep 20, 2002
132 * Time: 12:37:32 PM
133 */
134 package org.neuclear.commons.crypto.signers;
135
136 import org.neuclear.commons.LowLevelException;
137 import org.neuclear.commons.crypto.CryptoException;
138 import org.neuclear.commons.crypto.CryptoTools;
139 import org.neuclear.commons.crypto.passphraseagents.PassPhraseAgent;
140 import org.neuclear.commons.crypto.passphraseagents.UserCancellationException;
141
142 import javax.crypto.Cipher;
143 import javax.crypto.CipherInputStream;
144 import javax.crypto.CipherOutputStream;
145 import java.io.*;
146 import java.security.*;
147 import java.security.spec.KeySpec;
148 import java.security.spec.PKCS8EncodedKeySpec;
149 import java.util.HashMap;
150 import java.util.Map;
151
152 /***
153 * Simple memory based implementation of Signer.
154 * Currently it doesnt even use the passphrase. However it does do a SHA1 digest on the name first.
155 */
156 public final class SimpleSigner implements Signer {
157
158 public SimpleSigner(final String file, final PassPhraseAgent agent) {
159 this.agent = agent;
160 try {
161 signerFile = new File(file);
162 if (signerFile.exists()) {
163 System.out.println("NeuClear: Loading KeyStore");
164 final FileInputStream in = new FileInputStream(signerFile);
165 final ObjectInputStream s = new ObjectInputStream(in);
166 ks = (HashMap) s.readObject();
167 } else
168 ks = new HashMap();
169 kf = KeyFactory.getInstance("RSA");
170 kpg = KeyPairGenerator.getInstance("RSA");
171 kpg.initialize(1024, SecureRandom.getInstance("SHA1PRNG"));
172
173 } catch (IOException e) {
174 throw new LowLevelException(e);
175 } catch (ClassNotFoundException e) {
176 throw new LowLevelException(e);
177 } catch (NoSuchAlgorithmException e) {
178 throw new LowLevelException(e);
179 }
180 }
181
182 private PrivateKey getKey(final String name, final char[] passphrase) throws InvalidPassphraseException, NonExistingSignerException {
183 System.out.println("NEUDIST: UnSealing key " + name + " ...");
184 final byte[] encrypted = (byte[]) ks.get(getDigestedName(name));
185 if (encrypted == null)
186 throw new NonExistingSignerException("Signer " + name + "doesnt exist in this Store");
187 final ByteArrayInputStream bis = new ByteArrayInputStream(encrypted);
188 byte keyBytes[] = new byte[0];
189 try {
190 final Cipher c = CryptoTools.makePBECipher(Cipher.DECRYPT_MODE, passphrase);
191 final CipherInputStream cin = new CipherInputStream(bis, c);
192 final DataInputStream din = new DataInputStream(cin);
193 //byte keyBytes[]=new byte[c.getOutputSize(encrypted.length)];
194 if (din.readInt() != 11870) //This is just a quick check to see if the passphrase worked
195 throw new InvalidPassphraseException("Passphrase Didnt Match");
196
197 final int i = din.readInt();
198 // Sanity Check
199 if (i > 5000)
200 throw new InvalidPassphraseException("Returned key is too big");
201 keyBytes = new byte[i];
202 din.readFully(keyBytes, 0, keyBytes.length);
203 din.close();
204 final KeySpec spec = new PKCS8EncodedKeySpec(keyBytes);
205 return kf.generatePrivate(spec);
206 } catch (GeneralSecurityException e) {
207 throw new InvalidPassphraseException(e.getLocalizedMessage());
208 } catch (IOException e) {
209 throw new LowLevelException(e);
210 }
211 }
212
213 /***
214 * Adds the given key to the store. Uses the PassPhrase
215 * agent to ask for PassPhrase.
216 *
217 * @param name The name to store it as
218 * @param key The PrivateKey itself.
219 */
220
221 public final void addKey(final String name, final PrivateKey key) throws UserCancellationException {
222 addKey(name, agent.getPassPhrase(name), key);
223 }
224
225 /***
226 * Adds the given key to the store.
227 *
228 * @param name The name to store it as
229 * @param passphrase The passphrase to encrypt the key
230 * @param key The PrivateKey itself.
231 */
232
233 public final void addKey(final String name, final char[] passphrase, final PrivateKey key) {
234 System.out.println("NeuClear: Sealing key: " + name + " in format " + key.getFormat());
235 try {
236 final ByteArrayOutputStream bOut = new ByteArrayOutputStream();
237 DataOutputStream dOut = new DataOutputStream(bOut);
238 final Cipher c = CryptoTools.makePBECipher(Cipher.ENCRYPT_MODE, passphrase);
239 final CipherOutputStream cOut = new CipherOutputStream(dOut, c);
240 dOut = new DataOutputStream(cOut);
241 dOut.writeInt(11870);//This is just a quick check to see if the passphrase worked
242 final byte[] keyBytes = key.getEncoded(); //I'm assuming this is PKCS8, If not tough dooda
243 dOut.writeInt(keyBytes.length);
244 dOut.write(keyBytes);
245 dOut.close();
246 final byte[] encrypted = bOut.toByteArray();
247 ks.put(getDigestedName(name), encrypted);
248 } catch (GeneralSecurityException e) {
249 throw new LowLevelException(e);
250 } catch (IOException e) {
251 throw new LowLevelException(e);
252 }
253 }
254
255 public final boolean canSignFor(final String name) {
256 return ks.containsKey(getDigestedName(name));
257 }
258
259 /***
260 * Checks the key type of the given alias
261 *
262 * @param name
263 * @return KEY_NONE,KEY_RSA,KEY_DSA
264 */
265 public final int getKeyType(final String name) {
266 return (canSignFor(name)) ? KEY_RSA : KEY_NONE; // We always use RSA here
267 }
268
269 static final protected String getDigestedName(final String name) {
270 return new String(CryptoTools.digest(name.getBytes()));
271 }
272
273 public final void save() {
274 if (signerFile.getParent() != null)
275 signerFile.getParentFile().mkdirs();
276
277 try {
278 final FileOutputStream f = new FileOutputStream(signerFile);
279 final ObjectOutput s = new ObjectOutputStream(f);
280 s.writeObject(ks);
281 s.flush();
282 } catch (IOException e) {
283 throw new LowLevelException(e);
284 }
285
286 }
287
288 /***
289 * Signs the data with the privatekey of the given name
290 *
291 * @param name Alias of private key to be used within KeyStore
292 * @param data Data to be signed
293 * @return The signature
294 * @throws UserCancellationException
295 */
296 public final byte[] sign(final String name, final byte[] data) throws UserCancellationException {
297 return sign(name, data, false);
298 }
299
300 private final byte[] sign(final String name, final byte[] data, boolean incorrect) throws UserCancellationException {
301 try {
302 return CryptoTools.sign(getKey(name, agent.getPassPhrase(name, incorrect)), data);
303 } catch (CryptoException e) {
304 return sign(name, data, true);
305 }
306 }
307
308 public final PublicKey generateKey(final String alias) throws UserCancellationException {
309 final KeyPair kp = kpg.generateKeyPair();
310 addKey(alias, agent.getPassPhrase(alias), kp.getPrivate());
311 return kp.getPublic();
312 }
313
314 public PublicKey generateKey() throws UserCancellationException {
315 final KeyPair kp = kpg.generateKeyPair();
316 String alias = CryptoTools.encodeBase32(CryptoTools.digest(kp.getPublic().getEncoded()));
317 addKey(alias, agent.getPassPhrase(alias), kp.getPrivate());
318 return kp.getPublic();
319 }
320
321 private KeyFactory kf;
322 private Map ks;
323
324 private final File signerFile;
325 private final PassPhraseAgent agent;
326 private final KeyPairGenerator kpg;
327 }
This page was automatically generated by Maven