1 /*
2 * $Id: JCESigner.java,v 1.28 2004/07/21 23:07:16 pelle Exp $
3 * $Log: JCESigner.java,v $
4 * Revision 1.28 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.27 2004/05/16 00:04:00 pelle
9 * Added SigningServer which encapsulates all the web serving functionality.
10 * Added IdentityPanel which contains an IdentityTree of Identities.
11 * Added AssetPanel
12 * Save now works and Add Personality as well.
13 *
14 * Revision 1.26 2004/05/14 23:47:02 pelle
15 * Moved PersonalSigner and OpenSignerDialog to neuclear-commons where they belong.
16 * The whole mechanism of opening keystores is pretty smooth right now.
17 * Currently working on saving, which doesnt quite work yet. I have added a save method to OpenSignerDialog, which
18 * should handle it.
19 *
20 * Revision 1.25 2004/04/20 23:32:05 pelle
21 * All unit tests (junit and cactus) work. The AssetControllerServlet is operational.
22 *
23 * Revision 1.24 2004/04/14 00:10:52 pelle
24 * Added a MessageLabel for handling errors, validation and info
25 * Save works well now.
26 * It's pretty much there I think.
27 *
28 * Revision 1.23 2004/04/13 17:32:07 pelle
29 * Now has save dialog
30 * Remembers passphrases
31 *
32 * Revision 1.22 2004/04/12 23:50:08 pelle
33 * implemented the queue and improved the DefaultSigner
34 *
35 * Revision 1.21 2004/04/09 22:56:45 pelle
36 * SwingAgent now manages key creation as well through the NewAliasDialog.
37 * Many small uservalidation features have also been added.
38 *
39 * Revision 1.20 2004/04/09 18:40:45 pelle
40 * BrowsableSigner now inherits Signer and PublicKeySource, which means implementations only need to implement BrowsableSigner now.
41 * Added NewAliasDialog, which isnt yet complete.
42 *
43 * Revision 1.19 2004/04/07 17:22:10 pelle
44 * Added support for the new improved interactive signing model. A new Agent is also available with SwingAgent.
45 * The XMLSig classes have also been updated to support this.
46 *
47 * Revision 1.18 2004/03/22 20:09:05 pelle
48 * Added simple ledger for unit testing and in memory use
49 *
50 * Revision 1.17 2004/01/20 17:38:58 pelle
51 * Further updates to unit tests
52 *
53 * Revision 1.16 2004/01/19 17:53:14 pelle
54 * Various clean ups
55 *
56 * Revision 1.15 2003/12/22 22:14:37 pelle
57 * Last minute cleanups and documentation prior to release 0.8.1
58 *
59 * Revision 1.14 2003/12/19 18:02:53 pelle
60 * Revamped a lot of exception handling throughout the framework, it has been simplified in most places:
61 * - For most cases the main exception to worry about now is InvalidNamedObjectException.
62 * - Most lowerlevel exception that cant be handled meaningful are now wrapped in the LowLevelException, a
63 * runtime exception.
64 * - Source and Store patterns each now have their own exceptions that generalizes the various physical
65 * exceptions that can happen in that area.
66 *
67 * Revision 1.13 2003/12/19 00:31:15 pelle
68 * Lots of usability changes through out all the passphrase agents and end user tools.
69 *
70 * Revision 1.12 2003/12/18 17:40:07 pelle
71 * You can now create keys that get stored with a X509 certificate in the keystore. These can be saved as well.
72 * IdentityCreator has been modified to allow creation of keys.
73 * Note The actual Creation of Certificates still have a problem that will be resolved later today.
74 *
75 * Revision 1.11 2003/12/16 21:09:22 pelle
76 * The Sample Web App is semi stable for now.
77 *
78 * Revision 1.10 2003/12/14 20:52:54 pelle
79 * Added ServletPassPhraseAgent which uses ThreadLocal to transfer the passphrase to the signer.
80 * Added ServletSignerFactory, which builds Signers for use within servlets based on parameters in the Servlets
81 * Init parameters in web.xml
82 * Updated SQLContext to use ThreadLocal
83 * Added jakarta cactus unit tests to neuclear-commons to test the 2 new features above.
84 * Added use of the new features in neuclear-commons to the servilets within neuclear-id and added
85 * configuration parameters in web.xml
86 *
87 * Revision 1.9 2003/12/10 23:55:45 pelle
88 * Did some cleaning up in the builders
89 * Fixed some stuff in IdentityCreator
90 * New maven goal to create executable jarapp
91 * We are close to 0.8 final of ID, 0.11 final of XMLSIG and 0.5 of commons.
92 * Will release shortly.
93 *
94 * Revision 1.8 2003/11/22 00:22:52 pelle
95 * All unit tests in commons, id and xmlsec now work.
96 * AssetController now successfully processes payments in the unit test.
97 * Payment Web App has working form that creates a TransferRequest presents it to the signer
98 * and forwards it to AssetControlServlet. (Which throws an XML Parser Exception) I think the XMLReaderServlet is bust.
99 *
100 * Revision 1.7 2003/11/21 04:43:41 pelle
101 * EncryptedFileStore now works. It uses the PBECipher with DES3 afair.
102 * Otherwise You will Finaliate.
103 * 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.
104 * This should hopefully make everything more stable (and secure).
105 *
106 * Revision 1.6 2003/11/19 23:32:50 pelle
107 * Signers now can generatekeys via the generateKey() method.
108 * Refactored the relationship between SignedNamedObject and NamedObjectBuilder a bit.
109 * SignedNamedObject now contains the full xml which is returned with getEncoded()
110 * This means that it is now possible to further receive on or process a SignedNamedObject, leaving
111 * NamedObjectBuilder for its original purposes of purely generating new Contracts.
112 * NamedObjectBuilder.sign() now returns a SignedNamedObject which is the prefered way of processing it.
113 * Updated all major interfaces that used the old model to use the new model.
114 *
115 * Revision 1.5 2003/11/18 15:07:18 pelle
116 * Changes to JCE Implementation
117 * Working on getting all tests working including store tests
118 *
119 * Revision 1.4 2003/11/18 00:01:02 pelle
120 * The simple signing web application for logging in and out is now working.
121 * There had been an issue in the canonicalizer when dealing with the embedded object of the SignatureRequest object.
122 *
123 * Revision 1.3 2003/11/13 23:26:17 pelle
124 * The signing service and web authentication application is now almost working.
125 *
126 * Revision 1.2 2003/11/12 23:47:50 pelle
127 * Much work done in creating good test environment.
128 * PaymentReceiverTest works, but needs a abit more work in its environment to succeed testing.
129 *
130 * Revision 1.1 2003/11/11 21:17:47 pelle
131 * Further vital reshuffling.
132 * org.neudist.crypto.* and org.neudist.utils.* have been moved to respective areas under org.neuclear.commons
133 * org.neuclear.signers.* as well as org.neuclear.passphraseagents have been moved under org.neuclear.commons.crypto as well.
134 * Did a bit of work on the Canonicalizer and changed a few other minor bits.
135 *
136 * Revision 1.3 2003/11/08 20:27:06 pelle
137 * Updated the Signer interface to return a key type to be used for XML SignatureInfo. Thus we now support DSA sigs yet again.
138 *
139 * Revision 1.2 2003/10/29 23:17:53 pelle
140 * Updated some javadocs
141 * Added a neuclear specific maven repository at:
142 * http://neuclear.org/maven/ and updated the properties files to reflect that.
143 *
144 * Revision 1.1 2003/10/29 21:16:28 pelle
145 * Refactored the whole signing process. Now we have an interface called Signer which is the old SignerStore.
146 * To use it you pass a byte array and an alias. The sign method then returns the signature.
147 * If a Signer needs a passphrase it uses a PassPhraseAgent to present a dialogue box, read it from a command line etc.
148 * This new Signer pattern allows us to use secure signing hardware such as N-Cipher in the future for server applications as well
149 * as SmartCards for end user applications.
150 *
151 * Revision 1.4 2003/10/28 23:44:03 pelle
152 * The GuiDialogAgent now works. It simply presents itself as a simple modal dialog box asking for a passphrase.
153 * The two Signer implementations both use it for the passphrase.
154 *
155 * Revision 1.3 2003/10/21 22:29:59 pelle
156 * Renamed NeudistException to NeuClearException and moved it to org.neuclear.commons where it makes more sense.
157 * Unhooked the XMLException in the xmlsig library from NeuClearException to make all of its exceptions an independent hierarchy.
158 * Obviously had to perform many changes throughout the code to support these changes.
159 *
160 * Revision 1.2 2003/02/20 13:26:41 pelle
161 * Adding all of the modification from Rams?s Morales ramses@computer.org to support DSASHA1 Signatures
162 * Thanks Rams?s good work.
163 * So this means there is now support for:
164 * - DSA KeyInfo blocks
165 * - DSA Key Generation within CryptoTools
166 * - Signing using DSASHA1
167 *
168 * Revision 1.1 2003/02/18 00:03:32 pelle
169 * Moved the Signer classes from neuclearframework into neuclear-xmlsig
170 *
171 * Revision 1.2 2003/02/09 00:15:55 pelle
172 * Fixed things so they now compile with r_0.7 of XMLSig
173 *
174 * Revision 1.1 2002/10/06 00:39:26 pelle
175 * I have now expanded support for different types of Signers.
176 * There is now a JCESigner which uses a JCE KeyStore for signing.
177 * I have refactored the SigningServlet a bit, eliminating most of the demo code.
178 * This has been moved into DemoSigningServlet.
179 * I have expanded the CommandLineSigner, so it now also has an option for specifying a default signing service.
180 * The default web application now contains two signers.
181 * - The Demo one is still at /Signer
182 * - There is a new one at /personal/Signer this uses the testkeys.ks for
183 * signing anything under neu://test
184 * Note neu://test now has a default interactive signer running on localhost.
185 * So to play with this you must install the webapp on your own local machine.
186 *
187 * Revision 1.2 2002/09/23 15:09:11 pelle
188 * Got the SimpleSigner working properly.
189 * I couldn't get SealedObjects working with BouncyCastle's Symmetric keys.
190 * Don't know what I was doing, so I reimplemented it. Encrypting
191 * and decrypting it my self.
192 *
193 * Revision 1.1 2002/09/21 23:11:16 pelle
194 * A bunch of clean ups. Got rid of as many hard coded URL's as I could.
195 *
196 * User: pelleb
197 * Date: Sep 20, 2002
198 * Time: 12:37:32 PM
199 */
200 package org.neuclear.commons.crypto.signers;
201
202 import org.neuclear.commons.LowLevelException;
203 import org.neuclear.commons.Utility;
204 import org.neuclear.commons.crypto.CryptoException;
205 import org.neuclear.commons.crypto.CryptoTools;
206 import org.neuclear.commons.crypto.passphraseagents.AlwaysTheSamePassphraseAgent;
207 import org.neuclear.commons.crypto.passphraseagents.InteractiveAgent;
208 import org.neuclear.commons.crypto.passphraseagents.PassPhraseAgent;
209 import org.neuclear.commons.crypto.passphraseagents.UserCancellationException;
210
211 import java.io.*;
212 import java.security.*;
213 import java.security.cert.Certificate;
214 import java.security.cert.CertificateException;
215 import java.security.interfaces.DSAPublicKey;
216 import java.security.interfaces.RSAPublicKey;
217 import java.util.Enumeration;
218 import java.util.Iterator;
219
220 /***
221 * Wrapper around JCE KeyStore
222 */
223 public class JCESigner implements BrowsableSigner {
224
225 /***
226 * Constructs a JCESigner with the agent providing the keystore passphrase.
227 *
228 * @param filename
229 * @param type
230 * @param provider
231 * @param agent
232 * @throws InvalidPassphraseException If the given passphrase is incorrect
233 * @throws UserCancellationException If the user choses to cancel the process in the passphrase agent, this should cancel the loading process
234 */
235 public JCESigner(final String filename, final String type, final String provider, final PassPhraseAgent agent) throws UserCancellationException, InvalidPassphraseException {
236 this(filename, createInputStream(filename), type, provider, agent);
237 this.filename = filename;
238 }
239
240 /***
241 * Constructs a JCESigner providing a initial passphrase in the parameters.
242 *
243 * @param filename
244 * @param type
245 * @param provider
246 * @param agent
247 * @param initialpassphrase
248 * @throws InvalidPassphraseException If the given passphrase is incorrect
249 */
250 public JCESigner(final String filename, final String type, final String provider, final PassPhraseAgent agent, final char[] initialpassphrase) throws InvalidPassphraseException {
251 this(filename, createInputStream(filename), type, provider, agent, initialpassphrase);
252 this.filename = filename;
253
254 }
255
256 /***
257 * The purpose of this method is to either return an InputStream or Null. The reason being that the Keystore accepts null
258 * to create a new KeyStore in memory.
259 *
260 * @param filename
261 * @return
262 */
263 private static InputStream createInputStream(final String filename) {
264 if (Utility.isEmpty(filename))
265 return null;
266 final File file = new File(filename);
267 if (!file.exists())
268 return null;
269 try {
270 return new FileInputStream(file);
271 } catch (FileNotFoundException e) {
272 System.err.println(e.getLocalizedMessage());
273 throw new LowLevelException(e);
274 }
275 }
276
277 /***
278 * Constructs a JCESigner using the agent to provide the initial passphrase
279 *
280 * @param name
281 * @param in
282 * @param type
283 * @param provider
284 * @param agent
285 * @throws InvalidPassphraseException If the given passphrase is incorrect
286 * @throws UserCancellationException If the user choses to cancel the process in the passphrase agent, this should cancel the loading process
287 */
288 protected JCESigner(final String name, final InputStream in, final String type, final String provider, final PassPhraseAgent agent) throws UserCancellationException, InvalidPassphraseException {
289 this(loadKeyStore(provider, type, in, agent, name), agent);
290 }
291
292 /***
293 * Constructs a JCESigner using the provided Initial passphrase to load the keystore
294 *
295 * @param name
296 * @param in
297 * @param type
298 * @param provider
299 * @param agent
300 * @param initpassphrase
301 * @throws InvalidPassphraseException If the given passphrase is incorrect
302 */
303 protected JCESigner(final String name, final InputStream in, final String type, final String provider, final PassPhraseAgent agent, final char[] initpassphrase) throws InvalidPassphraseException {
304 this(loadKeyStore(provider, type, in, initpassphrase), agent);
305 }
306
307 private static KeyStore loadKeyStore(final String provider, final String type, final InputStream in, final char[] passphrase) throws InvalidPassphraseException {
308 try {
309 return loadKeyStore(provider, type, in, new AlwaysTheSamePassphraseAgent(passphrase), "keystore");
310 } catch (UserCancellationException e) {
311 throw new LowLevelException(e);
312 }
313 }
314
315 private static KeyStore loadKeyStore(final String provider, final String type, final InputStream in, final PassPhraseAgent agent, final String name) throws InvalidPassphraseException, UserCancellationException {
316 // System.out.println("Loading JCESigner using passphrase: "+new String(passphrase));
317 try {
318 KeyStore ki = null;
319 if (provider == null)
320 ki = KeyStore.getInstance(type);
321 else
322 ki = KeyStore.getInstance(type, provider);
323 if (in != null)
324 ki.load(in, agent.getPassPhrase("Keystore password for: " + name));
325 else
326 ki.load(null, null);
327 return ki;
328 } catch (KeyStoreException e) {
329 throw new LowLevelException(e);
330 } catch (NoSuchProviderException e) {
331 throw new LowLevelException(e);
332 } catch (IOException e) {
333 System.err.println("Incorrect Passphrase");
334 throw new InvalidPassphraseException("entered passphrase was invalid");
335 } catch (NoSuchAlgorithmException e) {
336 throw new LowLevelException(e);
337 } catch (CertificateException e) {
338 throw new LowLevelException(e);
339 }
340 }
341
342 /***
343 * Creates a signer based on a fully loaded keystore
344 *
345 * @param ks
346 * @param agent
347 */
348 public JCESigner(final KeyStore ks, final PassPhraseAgent agent) {
349 this.agent = agent;
350 this.ks = ks;
351 cache = new KeyCache(ks);
352 try {
353 kpg = KeyPairGenerator.getInstance("RSA");
354 kpg.initialize(1024, SecureRandom.getInstance("SHA1PRNG"));
355 } catch (NoSuchAlgorithmException e) {
356 throw new LowLevelException(e);
357 }
358
359 }
360
361 private PrivateKey getKey(final String name, final char[] passphrase) throws UnrecoverableKeyException, NonExistingSignerException, NoSuchAlgorithmException, KeyStoreException {
362 try {
363 final PrivateKey key = (PrivateKey) cache.getKey(name, passphrase);
364 if (key == null)
365 throw new NonExistingSignerException("No keys for: " + name);
366 return key;
367 } catch (ClassCastException e) {
368 throw new NonExistingSignerException("Incorrect Key type found");
369 }
370
371 }
372
373 public final byte[] sign(final String name, final byte[] data) throws NonExistingSignerException, UserCancellationException {
374 return sign(name, data, false);
375 }
376
377 public final byte[] sign(final String name, final byte[] data, boolean incorrect) throws UserCancellationException, NonExistingSignerException {
378 try {
379 final char[] pass = getPassPhrase(name, incorrect);
380 return CryptoTools.sign(getKey(name, pass), data);
381 } catch (UnrecoverableKeyException e) {
382 System.err.println("Incorrect Passphrase Attemt on: " + name);
383 return sign(name, data, true);
384 } catch (NoSuchAlgorithmException e) {
385 throw new LowLevelException(e);
386 } catch (KeyStoreException e) {
387 // Could try to reload it here but I wont for now
388 throw new LowLevelException(e);
389 } catch (CryptoException e) {
390 throw new LowLevelException(e);
391 }
392 }
393
394 private char[] getPassPhrase(final String name, boolean incorrect) throws UserCancellationException {
395 if (incorrect && (agent instanceof InteractiveAgent))
396 ((InteractiveAgent) agent).getPassPhrase(name, true);
397 return agent.getPassPhrase(name);
398 }
399
400 public final boolean canSignFor(final String name) {
401 try {
402 return ks.containsAlias(name);
403 } catch (KeyStoreException e) {
404 throw new LowLevelException(e);
405 }
406 }
407
408 public final int getKeyType(final String name) {
409 try {
410 if (ks.isKeyEntry(name)) {
411 final PublicKey pk = getPublicKey(name);
412 if (pk instanceof RSAPublicKey)
413 return KEY_RSA;
414 if (pk instanceof DSAPublicKey)
415 return KEY_DSA;
416 return KEY_OTHER;
417 }
418 } catch (KeyStoreException e) {
419 throw new LowLevelException(e);
420 } catch (NonExistingSignerException e) {
421 return KEY_NONE;
422 }
423 return KEY_NONE; //To change body of implemented methods use Options | File Templates.
424 }
425
426
427 public final PublicKey generateKey(final String alias) throws UserCancellationException {
428 try {
429 final KeyPair kp = kpg.generateKeyPair();
430 ks.setKeyEntry(alias, kp.getPrivate(), agent.getPassPhrase(alias), new Certificate[]{CryptoTools.createCertificate(alias, kp)});
431 return kp.getPublic();
432 } catch (KeyStoreException e) {
433 throw new LowLevelException(e);
434 } catch (SignatureException e) {
435 throw new LowLevelException(e);
436 } catch (InvalidKeyException e) {
437 throw new LowLevelException(e);
438 }
439 }
440
441 public PublicKey generateKey() throws UserCancellationException {
442 try {
443 final KeyPair kp = kpg.generateKeyPair();
444 String alias = CryptoTools.encodeBase32(CryptoTools.digest(kp.getPublic().getEncoded()));
445 ks.setKeyEntry(alias, kp.getPrivate(), agent.getPassPhrase(alias), new Certificate[]{CryptoTools.createCertificate(alias, kp)});
446 return kp.getPublic();
447 } catch (KeyStoreException e) {
448 throw new LowLevelException(e);
449 } catch (SignatureException e) {
450 throw new LowLevelException(e);
451 } catch (InvalidKeyException e) {
452 throw new LowLevelException(e);
453 }
454 }
455
456 public final PublicKey getPublicKey(final String name) throws NonExistingSignerException {
457 try {
458 final Certificate certificate = ks.getCertificate(name);
459 if (certificate == null)
460 throw new NonExistingSignerException(name);
461
462 PublicKey pub = certificate.getPublicKey();
463 if (pub == null)
464 throw new NonExistingSignerException(name);
465 return pub;
466
467 } catch (KeyStoreException e) {
468 throw new LowLevelException(e);
469 }
470 }
471
472 public byte[] sign(byte data[], SetPublicKeyCallBack callback) throws UserCancellationException {
473 return ((InteractiveAgent) agent).sign(this, data, callback);
474 }
475
476 public byte[] sign(String name, char pass[], byte data[], SetPublicKeyCallBack callback) throws InvalidPassphraseException {
477 try {
478 final byte[] bytes = CryptoTools.sign(getKey(name, pass), data);
479 if (callback != null)
480 callback.setPublicKey(getPublicKey(name));
481 return bytes;
482 } catch (UnrecoverableKeyException e) {
483 throw new InvalidPassphraseException(name);
484 } catch (NoSuchAlgorithmException e) {
485 throw new LowLevelException(e);
486 } catch (KeyStoreException e) {
487 throw new LowLevelException(e);
488 } catch (CryptoException e) {
489 throw new LowLevelException(e);
490 }
491 }
492
493 public void createKeyPair(String alias, char passphrase[]) throws CryptoException {
494 try {
495 final KeyPair kp = kpg.generateKeyPair();
496 ks.setKeyEntry(alias, kp.getPrivate(), passphrase, new Certificate[]{CryptoTools.createCertificate(alias, kp)});
497 // if (!Utility.isEmpty(filename)) save();
498 } catch (KeyStoreException e) {
499 throw new LowLevelException(e);
500 } catch (SignatureException e) {
501 throw new LowLevelException(e);
502 } catch (InvalidKeyException e) {
503 throw new LowLevelException(e);
504 }
505 }
506
507 public void save() throws UserCancellationException {
508 try {
509 save(filename);
510 } catch (FileNotFoundException e) {
511 throw new LowLevelException(e);
512 }
513 }
514
515 public synchronized final void save(String filename) throws FileNotFoundException, UserCancellationException {
516 save(filename, agent.getPassPhrase(filename));
517 }
518
519 public synchronized final void save(String filename, char passphrase[]) throws FileNotFoundException {
520 if (Utility.isEmpty(filename))
521 throw new FileNotFoundException("no keystore filename");
522 try {
523 File ksfile = new File(filename);
524 ksfile.getParentFile().mkdirs();
525 System.out.println("Saving " + filename + " " + Thread.currentThread());
526 ks.store(new FileOutputStream(ksfile), passphrase);
527 } catch (Exception e) {
528 throw new LowLevelException(e);
529 }
530 }
531
532 public Iterator iterator() throws KeyStoreException {
533 final Enumeration enumerator = ks.aliases();
534 return new Iterator() {
535 public void remove() {
536
537 }
538
539 public boolean hasNext() {
540 return enumerator.hasMoreElements();
541 }
542
543 public Object next() {
544 return enumerator.nextElement();
545 }
546
547 };
548 }
549
550 private final KeyStore ks;
551 private final KeyCache cache;
552 private final PassPhraseAgent agent;
553 private final KeyPairGenerator kpg;
554 private String filename;
555 }
This page was automatically generated by Maven