KickJava   Java API By Example, From Geeks To Geeks.

Java > Open Source Codes > net > sourceforge > jcetaglib > lib > Hybrid


1 /*
2   Name: Hybrid.java
3   Licensing: LGPL
4
5   API: Sun (http://java.sun.com) JCE 1.2.2 API (cleanroom implementation by Bouncy Castle)
6   Provider: Bouncy Castle (http://www.bouncycastle.org)
7
8   Disclaimer:
9
10   COVERED CODE IS PROVIDED UNDER THIS LICENSE ON AN "AS IS" BASIS, WITHOUT WARRANTY OF ANY KIND,
11   EITHER EXPRESSED OR IMPLIED, INCLUDING, WITHOUT LIMITATION, WARRANTIES THAT THE COVERED CODE
12   IS FREE OF DEFECTS, MERCHANTABLE, FIT FOR A PARTICULAR PURPOSE OR NON-INFRINGING. THE ENTIRE
13   RISK AS TO THE QUALITY AND PERFORMANCE OF THE COVERED CODE IS WITH YOU. SHOULD ANY COVERED CODE
14   PROVE DEFECTIVE IN ANY RESPECT, YOU (NOT THE INITIAL DEVELOPER OR ANY OTHER CONTRIBUTOR)
15   ASSUME THE COST OF ANY NECESSARY SERVICING, REPAIR OR CORRECTION. THIS DISCLAIMER OF WARRANTY
16   CONSTITUTES AN ESSENTIAL PART OF THIS LICENSE. NO USE OF ANY COVERED CODE IS AUTHORIZED
17   HEREUNDER EXCEPT UNDER THIS DISCLAIMER.
18
19   (C) Copyright 2003 Gert Van Ham
20 */

21
22 package net.sourceforge.jcetaglib.lib;
23
24 import net.sourceforge.jcetaglib.exceptions.CryptoException;
25 import net.sourceforge.jcetaglib.exceptions.HeaderException;
26 import net.sourceforge.jcetaglib.exceptions.InvalidHMACException;
27 import net.sourceforge.jcetaglib.exceptions.InvalidSignatureException;
28 import net.sourceforge.jcetaglib.tools.*;
29 import org.bouncycastle.jce.provider.BouncyCastleProvider;
30 import org.bouncycastle.util.encoders.Base64;
31
32 import javax.crypto.Cipher;
33 import javax.crypto.KeyGenerator;
34 import javax.crypto.Mac;
35 import javax.crypto.SecretKey;
36 import javax.crypto.spec.IvParameterSpec;
37 import javax.crypto.spec.SecretKeySpec;
38 import java.io.*;
39 import java.security.*;
40 import java.security.cert.CertificateFactory JavaDoc;
41 import java.security.cert.X509Certificate JavaDoc;
42
43 /**
44  * Hybrid encryption & decryption routines for use with BouncyCastle JCE provider
45  *
46  * @author Gert Van Ham
47  * @author hamgert@users.sourceforge.net
48  * @author http://jcetaglib.sourceforge.net
49  * @version $Id: Hybrid.java,v 1.3 2004/04/15 07:28:25 hamgert Exp $
50  */

51 public class Hybrid {
52     // buffersizes in bytes
53
private static int BUFFERSIZE_TEXT = 1204;
54     private static int BUFFERSIZE_FILE = 8192;
55
56     static final int FILE_HEADER = 0x7e01; // File header.
57
static final int DATA_BLOCK = 1; // Data block
58
static final int FINAL_DATA_BLOCK = 2; // Final data block.
59
static final int HMAC_BLOCK = 3; // HMAC block
60
static final int SIG_BLOCK = 3; // Signature block
61
static final int CERT_BLOCK = 4; // Certificate block.
62
static final int KEY_BLOCK = 16; // Keystore block
63
static final int IV_BLOCK = 17; // IV block
64
static final int LOCK_BLOCK = 18; // Locking block
65
static final int HMAC_KEY_BLOCK = 18;
66
67     /**
68      * Encrypts text with an HMAC
69      *
70      * @param text text to encrypt
71      * @param receiverKey the receiver's public key for encrypting the symmetric key
72      * @param algorithm encryption algorithm (e.g. "Rijndael")
73      * @param seed seed for SecureRandom (optional)
74      * @param strength the keysize in bits (e.g. 128)
75      * @param mode encryption mode (e.g. "CBC")
76      * @param padding padding scheme (e.g."PKCS7Padding")
77      * @return encrypted string in BASE64 format
78      * @throws CryptoException encryption errors
79      **/

80     public static StringBuffer JavaDoc encryptWithHMAC(StringBuffer JavaDoc text
81                                                , PublicKey receiverKey
82                                                , String JavaDoc algorithm
83                                                , byte[] seed
84                                                , int strength
85                                                , String JavaDoc mode
86                                                , String JavaDoc padding) throws CryptoException {
87
88         ByteArrayOutputStream bao = null;
89         DataOutputStream dao = null;
90
91         try {
92             bao = new ByteArrayOutputStream();
93             dao = new DataOutputStream(bao);
94
95             // encrypt text with HMAC
96
encryptWithHMAC(new ByteArrayInputStream(text.toString().getBytes()), dao, receiverKey, algorithm, seed, strength, mode, padding, BUFFERSIZE_TEXT);
97             return new StringBuffer JavaDoc(new String JavaDoc(Base64.encode(bao.toByteArray())));
98         } catch (IOException ioe) {
99             ioe.printStackTrace();
100             throw new CryptoException(ioe.getMessage());
101         } finally {
102             if (dao != null) {
103                 // close outputstream
104
try {
105                     dao.close();
106                 } catch (IOException e) {
107                     ;
108                 }
109             }
110         }
111     }
112
113     /**
114      * Encrypts file with an HMAC
115      *
116      * @param file file to encrypt
117      * @param newfile encrypted file
118      * @param receiverKey the receiver's public key for encrypting the symmetric key
119      * @param algorithm encryption algorithm (e.g. "Rijndael")
120      * @param seed seed for SecureRandom (optional)
121      * @param strength the keysize in bits (e.g. 128)
122      * @param mode encryption mode (e.g. "CBC")
123      * @param padding padding scheme (e.g."PKCS7Padding")
124      * @throws CryptoException encryption errors
125      * @throws IOException I/O errors
126      **/

127     public static void encryptFileWithHMAC(String JavaDoc file
128                                            , String JavaDoc newfile
129                                            , PublicKey receiverKey
130                                            , String JavaDoc algorithm
131                                            , byte[] seed
132                                            , int strength
133                                            , String JavaDoc mode
134                                            , String JavaDoc padding) throws CryptoException, IOException {
135
136         FileInputStream fis = null;
137         FileOutputStream fos = null;
138         DataOutputStream dao = null;
139
140         try {
141             fis = new FileInputStream(file);
142
143             fos = new FileOutputStream(newfile);
144             dao = new DataOutputStream(fos);
145
146             // encrypt file
147
encryptWithHMAC(fis, dao, receiverKey, algorithm, seed, strength, mode, padding, BUFFERSIZE_FILE);
148
149         } catch (IOException ioe) {
150             ioe.printStackTrace();
151             throw new IOException(ioe.getMessage());
152         } finally {
153             if (dao != null) {
154                 // close outputstream
155
try {
156                     dao.close();
157                 } catch (IOException e) {
158                     ;
159                 }
160             }
161             if (fis != null) {
162                 // close outputstream
163
try {
164                     fis.close();
165                 } catch (IOException e) {
166                     ;
167                 }
168             }
169         }
170     }
171
172     /**
173      * Encrypts any inputstream with an HMAC
174      *
175      * @param is inputstream to encrypt
176      * @param daos outputstream to store the encrypted data
177      * @param receiverKey the receiver's public key for encrypting the symmetric key
178      * @param algorithm encryption algorithm (e.g. "Rijndael")
179      * @param seed seed for SecureRandom (optional)
180      * @param strength the keysize in bits (e.g. 128)
181      * @param mode encryption mode (e.g. "CBC")
182      * @param padding padding scheme (e.g."PKCS7Padding")
183      * @param bufferlength buffer length in bytes
184      * @throws CryptoException encryption errors
185      * @throws IOException I/O errors
186      **/

187     public static void encryptWithHMAC(InputStream is
188                                        , DataOutputStream daos
189                                        , PublicKey receiverKey
190                                        , String JavaDoc algorithm
191                                        , byte[] seed
192                                        , int strength
193                                        , String JavaDoc mode
194                                        , String JavaDoc padding
195                                        , int bufferlength)
196             throws CryptoException, IOException {
197
198         MacOutputStream macStr = null;
199         DataOutputStream dataStr = null;
200
201         try {
202             Security.addProvider(new BouncyCastleProvider());
203
204             SecureRandom secRand = Seed.getSecureRandom(seed);
205
206             // Generate symmetric key
207
KeyGenerator keyGen = KeyGenerator.getInstance(algorithm, "BC");
208             keyGen.init(strength, secRand);
209             Key symKey = keyGen.generateKey();
210
211             // Instantiate Symmetric cipher for encryption.
212
Cipher outputCipher = Cipher.getInstance(algorithm + "/" + mode + "/" + padding, "BC");
213             outputCipher.init(Cipher.ENCRYPT_MODE, symKey, secRand);
214
215             // Get key and IV for cipher so that they can be later
216
// encrypted to build a header.
217
byte[] keyEnc = symKey.getEncoded();
218             byte[] keyIV = outputCipher.getIV();
219
220             // Setup HMAC
221
Mac mac = Mac.getInstance("HMACSHA1", "BC");
222
223             byte[] macKeyBytes = new byte[20]; // Set to correct length for HMACSHA1
224
secRand.nextBytes(macKeyBytes); // Fill with random data,
225

226             Key macKey = new SecretKeySpec(macKeyBytes, "HMACSHA1");
227             mac.init(macKey); // Initialize.
228

229             // Setup RSA to encrypt secrets.
230
Cipher rsaEng = Cipher.getInstance("RSA/None/OAEPPadding", "BC");
231             rsaEng.init(Cipher.ENCRYPT_MODE, receiverKey, secRand);
232
233             // Setup to process MAC
234
macStr = new MacOutputStream(daos, mac);
235             dataStr = new DataOutputStream(macStr);
236
237             // Form HEADER for the encrypted string
238
dataStr.writeShort(FILE_HEADER); // Write a file or string header.
239

240             // Write out a block for the key of the cipher.
241
dataStr.writeShort(KEY_BLOCK); // Block header.
242
byte[] tmp = rsaEng.doFinal(keyEnc); // Encrypt it with RSA.
243
dataStr.writeInt(tmp.length); // Write length.
244
dataStr.write(tmp); // Write data.
245
Clean.blank(tmp); // Erase tmp array.
246

247             // Write out IV block
248
dataStr.writeShort(IV_BLOCK); // Block header
249
tmp = rsaEng.doFinal(keyIV); // Encrypt with RSA.
250
dataStr.writeInt(tmp.length); // Write length.
251
dataStr.write(tmp); // Write data.
252
Clean.blank(tmp); // Erase tmp array.
253

254             // Write out key for HMAC.
255
dataStr.writeShort(HMAC_KEY_BLOCK); // Write header.
256
tmp = outputCipher.doFinal(macKey.getEncoded()); // Encrypt
257
dataStr.writeInt(tmp.length); // Write length.
258
dataStr.write(tmp); // Write data.
259
Clean.blank(tmp); // Erase tmp array.
260

261             // Reset cipher back to original
262
//outputCipher.init(Cipher.ENCRYPT_MODE,symKey,new IvParameterSpec(keyIV)); // initialize with aes_key.
263

264             // Encrypt the message
265
int l = 0; // Universal length variable.
266
byte[] buf = new byte[bufferlength]; // A buffer to work in.
267
byte[] out = null; // Output buffer.
268

269             // Read while length is > -1
270
while ((l = is.read(buf)) > -1) {
271                 out = outputCipher.update(buf, 0, l); // Encrypt data.
272
if (out != null) {
273                     dataStr.writeShort(DATA_BLOCK); // Write data block header.
274
dataStr.writeInt(out.length); // Write length.
275
dataStr.write(out); // Write encrypted data.
276
}
277             }
278
279             // This is the last block
280
out = outputCipher.doFinal(); // Do final encryption.
281
dataStr.writeShort(FINAL_DATA_BLOCK); // Write header.
282
dataStr.writeInt(out.length); // Write length.
283
dataStr.write(out); // Write Data.
284

285             Clean.blank(buf); // Clear buffer.
286
buf = null; // Set Null
287

288             // Write out MAC block
289
dataStr.writeShort(HMAC_BLOCK); // Write Header.
290
dataStr.flush(); // Flush it..
291

292             tmp = mac.doFinal(); // Get MAC code.
293
dataStr.writeInt(tmp.length); // Write length.
294
dataStr.write(tmp); // Write data.
295
Clean.blank(tmp); // Clear.
296
} catch (IOException ioe) {
297             ioe.printStackTrace();
298             throw new IOException(ioe.getMessage());
299         } catch (Exception JavaDoc ex) {
300             ex.printStackTrace();
301             throw new CryptoException(ex.getMessage());
302         } finally {
303             if (dataStr != null) {
304                 try {
305                     dataStr.close();
306                 } catch (IOException ioe) {
307                     ;
308                 }
309             }
310         }
311     }
312
313     /**
314      * Decrypts and verifies text with HMAC
315      *
316      * @param text the text to decrypt
317      * @param privKey the receiver's private key for decryption
318      * @param algorithm encryption algorithm (e.g. "Rijndael")
319      * @param mode encryption mode (e.g. "CBC")
320      * @param padding padding scheme (e.g."PKCS7Padding")
321      * @return deciphered text
322      * @throws HeaderException thrown when package header is broken
323      * @throws InvalidHMACException thrown when the HMAC code is invalid
324      * @throws CryptoException all encryption errors
325      */

326     public static StringBuffer JavaDoc decryptAndVerifyHMAC(StringBuffer JavaDoc text
327                                                     , PrivateKey privKey
328                                                     , String JavaDoc algorithm
329                                                     , String JavaDoc mode
330                                                     , String JavaDoc padding) throws HeaderException, InvalidHMACException, CryptoException {
331
332         ByteArrayOutputStream bao = null;
333         DataOutputStream dao = null;
334
335         try {
336             bao = new ByteArrayOutputStream();
337             dao = new DataOutputStream(bao);
338
339             // decrypt & verify
340
decryptAndVerifyHMAC(new ByteArrayInputStream(Base64.decode(text.toString())), dao, privKey, algorithm, mode, padding, BUFFERSIZE_TEXT);
341
342             return new StringBuffer JavaDoc(new String JavaDoc(bao.toByteArray()));
343         } catch (HeaderException he) {
344             throw new HeaderException(he.getMessage());
345         } catch (InvalidHMACException ihe) {
346             throw new InvalidHMACException(ihe.getMessage());
347         } catch (Exception JavaDoc ioe) {
348             ioe.printStackTrace();
349             throw new CryptoException(ioe.getMessage());
350         } finally {
351             if (dao != null) {
352                 // close outputstream
353
try {
354                     dao.close();
355                 } catch (IOException e) {
356                     ;
357                 }
358             }
359         }
360     }
361
362     /**
363      * Decrypts and verifies file with HMAC
364      *
365      * @param file the file to decrypt
366      * @param newfile the decrypted file
367      * @param privKey the receiver's private key for decryption
368      * @param algorithm encryption algorithm (e.g. "Rijndael")
369      * @param mode encryption mode (e.g. "CBC")
370      * @param padding padding scheme (e.g."PKCS7Padding")
371      * @throws IOException I/O errors
372      * @throws HeaderException thrown when package header is broken
373      * @throws InvalidHMACException thrown when the HMAC code is invalid
374      * @throws CryptoException all encryption errors
375      */

376     public static void decryptFileAndVerifyHMAC(String JavaDoc file
377                                                 , String JavaDoc newfile
378                                                 , PrivateKey privKey
379                                                 , String JavaDoc algorithm
380                                                 , String JavaDoc mode
381                                                 , String JavaDoc padding) throws CryptoException, HeaderException, InvalidHMACException, IOException {
382
383         SecureRandom secRand;
384         Mac mac = null;
385         Cipher cipher = null;
386         Cipher decHMAC = null;
387
388         SecretKey symKey = null; // Symmetric key.
389
SecretKey macKey = null; // MAC key.
390

391         byte[] keyIV = null; // AES IV
392
byte[] macCode = null; // MAC code
393

394         DataInputStream dataIn = null;
395         MacInputStream macStr = null;
396         DataInputStream dataStr = null;
397
398         FileOutputStream outStr = null;
399
400         try {
401             Security.addProvider(new BouncyCastleProvider());
402
403             secRand = SecureRandom.getInstance("SHA1PRNG", "SUN");
404
405             dataIn = new DataInputStream(new FileInputStream(file));
406
407             int l = 0; // Universal length variable.
408
boolean ena = false; // Enable flag.. (Set when file/string header found)
409
boolean stop = false; // Stop flag.
410

411             // Setup RSA to decrypt secrets.
412
Cipher rsaEng = Cipher.getInstance("RSA/None/OAEPPadding", "BC");
413             rsaEng.init(Cipher.DECRYPT_MODE, privKey, secRand); // Initialize cipher for decryption.
414

415             while (!stop) {
416                 try {
417                     int cmd = dataIn.readShort(); // Read in block header.
418

419                     if (cmd == FILE_HEADER) // File header.
420
{
421                         ena = true; // Flag file header found.
422
continue;
423                     }
424
425                     if (cmd == DATA_BLOCK) // Read in data block
426
{
427                         if (!ena) {
428                             throw new HeaderException("Broken header");
429                         }
430
431                         l = dataIn.readInt(); // Read length.
432
dataIn.skip(l); // Skip this data.
433
continue;
434                     }
435
436                     if (cmd == FINAL_DATA_BLOCK) // Final data block.
437
{
438                         if (!ena) {
439                             throw new HeaderException("Broken header");
440                         }
441                         l = dataIn.readInt(); // Read length
442
dataIn.skip(l); // Skip this data.
443
continue;
444                     }
445
446                     if (cmd == HMAC_BLOCK) // MAC block.
447
{
448                         if (!ena) {
449                             throw new HeaderException("Broken header");
450                         }
451                         l = dataIn.readInt(); // Read length.
452
macCode = new byte[l]; // Create new array (l) in size.
453
dataIn.readFully(macCode); // Read in signature.
454
continue;
455                     }
456
457                     if (cmd == KEY_BLOCK) // Read in key block.
458
{
459                         if (!ena) {
460                             throw new HeaderException("Broken header");
461                         }
462
463                         l = dataIn.readInt(); // Read length.
464
byte[] d = new byte[l]; // Create new array (l) in size.
465
dataIn.readFully(d); // read in data.
466

467                         // We must use a SecretKeySpec set up
468
// to convert the raw encoded key back into a SecretKey Object.
469
// The key is also decrypted before processing.
470

471                         symKey = new SecretKeySpec(rsaEng.doFinal(d), algorithm);
472                         continue;
473                     }
474
475                     if (cmd == IV_BLOCK) // Read in IV.
476
{
477                         if (!ena) {
478                             throw new HeaderException("Broken header");
479                         }
480                         l = dataIn.readInt(); // Read length
481
keyIV = new byte[l]; // Create new array for IV (l) in size.
482
dataIn.readFully(keyIV); // Read in IV.
483
keyIV = rsaEng.doFinal(keyIV); // Decrypt IV
484
continue;
485                     }
486
487                     if (cmd == HMAC_KEY_BLOCK) // Read lock.
488
{
489                         if (!ena) {
490                             throw new HeaderException("Broken header");
491                         }
492
493                         // Set up cipher to decrypt MAC
494
decHMAC = Cipher.getInstance(algorithm + "/" + mode + "/" + padding, "BC");
495                         decHMAC.init(Cipher.DECRYPT_MODE, symKey, new IvParameterSpec(keyIV));
496                         l = dataIn.readInt(); // Read Length
497
byte[] d = new byte[l]; // Create new byte array (l) in size.
498
dataIn.readFully(d); // Read
499
macKey = new SecretKeySpec(decHMAC.doFinal(d), "HMACSHA1"); // Decrypt lock.
500

501                         continue;
502                     }
503                 } catch (EOFException eof) {
504                     stop = true;
505                 }
506             }
507
508             mac = Mac.getInstance("HMACSHA1", "BC"); // Mac algorithm based on SHA1 message digest.
509
mac.init(macKey); // initialize it with the mac key.
510

511             // Set up input stream wrappers.
512
macStr = new MacInputStream(new FileInputStream(file), mac);
513             dataStr = new DataInputStream(macStr);
514
515             int cmd = 0; // variable to store block header.
516
byte[] buf = new byte[BUFFERSIZE_FILE]; // Buffer to work in.
517
l = 0; // Universal length variable.
518

519             do {
520                 cmd = dataStr.readShort(); // Read off block header/
521

522                 if (cmd == DATA_BLOCK) // Skip HMAC block.
523
{
524                     l = dataStr.readInt(); // Read length.
525
dataStr.read(buf, 0, l); // dummy read.
526
}
527
528                 if (cmd == FINAL_DATA_BLOCK) // Skip the KEY Block
529
{
530                     l = dataStr.readInt();
531                     dataStr.read(buf, 0, l); // dummy read
532
}
533
534
535                 if (cmd == KEY_BLOCK) // Skip the KEY Block
536
{
537                     l = dataStr.readInt();
538                     dataStr.read(buf, 0, l); // dummy read
539
}
540
541                 if (cmd == IV_BLOCK) // Skip the IV block.
542
{
543                     l = dataStr.readInt();
544                     dataStr.read(buf, 0, l); // dummy read
545
}
546
547                 if (cmd == HMAC_KEY_BLOCK) // Skip the IV block.
548
{
549                     l = dataStr.readInt();
550                     dataStr.read(buf, 0, l); // dummy read
551
}
552             } while (cmd != HMAC_BLOCK);
553
554             buf = mac.doFinal();
555             dataStr.close();
556
557             if (!MessageDigest.isEqual(buf, macCode)) {
558                 throw new InvalidHMACException("Invalid HMAC");
559             }
560
561             cipher = Cipher.getInstance(algorithm + "/" + mode + "/" + padding, "BC");
562             cipher.init(Cipher.DECRYPT_MODE, symKey, new IvParameterSpec(keyIV));
563
564             outStr = new FileOutputStream(newfile); // Where to save plain text.
565

566             // Input source file, via DataInputStream wrapper.
567
dataStr = new DataInputStream(new FileInputStream(file));
568
569             stop = false; // Loop breaker.
570
cmd = 0; // Variable to hold block header.
571
l = 0; // Universal length variable.
572
buf = new byte[BUFFERSIZE_FILE]; // A buffer to work in.
573
byte[] out = null; // Output buffer.
574

575             for (; ;) {
576                 cmd = dataStr.readShort(); // Read in block header.
577

578                 if (cmd == DATA_BLOCK) {
579                     l = dataStr.readInt(); // Get length of data.
580
dataStr.readFully(buf, 0, l); // Read data.
581
out = cipher.update(buf, 0, l);
582                     if (out != null) outStr.write(out);
583                 }
584
585                 if (cmd == FINAL_DATA_BLOCK) {
586                     l = dataStr.readInt(); // Length of data.
587
dataStr.readFully(buf, 0, l); // Read in data.
588
out = cipher.doFinal(buf, 0, l);
589                     if (out != null) outStr.write(out);
590                     break;
591                 }
592
593                 // The following blocks and their content are skipped.
594
if (cmd == KEY_BLOCK) // Skip the KEY Block
595
{
596                     l = dataStr.readInt();
597                     dataStr.skip(l);
598                 }
599
600                 if (cmd == IV_BLOCK) // Skip the IV block.
601
{
602                     l = dataStr.readInt();
603                     dataStr.skip(l);
604                 }
605
606                 if (cmd == HMAC_BLOCK) // Skip the IV block.
607
{
608                     l = dataStr.readInt();
609                     dataStr.skip(l);
610                 }
611
612                 if (cmd == HMAC_KEY_BLOCK) // Skip the IV block.
613
{
614                     l = dataStr.readInt();
615                     dataStr.skip(l);
616                 }
617             }
618         } catch (IOException ioe) {
619             ioe.printStackTrace();
620             throw new IOException(ioe.getMessage());
621         } catch (HeaderException he) {
622             he.printStackTrace();
623             throw new HeaderException(he.getMessage());
624         } catch (InvalidHMACException ihe) {
625             ihe.printStackTrace();
626             throw new InvalidHMACException(ihe.getMessage());
627         } catch (Exception JavaDoc ex) {
628             ex.printStackTrace();
629             throw new CryptoException(ex.getMessage());
630         } finally {
631             if (outStr != null) {
632                 // close outputstream
633
try {
634                     outStr.close();
635                 } catch (IOException e) {
636                     ;
637                 }
638             }
639             if (dataStr != null) {
640                 // close outputstream
641
try {
642                     dataStr.close();
643                 } catch (IOException e) {
644                     ;
645                 }
646             }
647         }
648     }
649
650     /**
651      * Decrypts and verifies an inputstream (which must support mark/reset) with HMAC
652      *
653      * @param is inputstream to decrypt (NOTE: the inputstream must support mark/reset because it must be read three times)
654      * @param daos outputstream to store the decrypted data
655      * @param privKey the receiver's private key for decryption
656      * @param algorithm encryption algorithm (e.g. "Rijndael")
657      * @param mode encryption mode (e.g. "CBC")
658      * @param padding padding scheme (e.g."PKCS7Padding")
659      * @param bufferlength buffer length in bytes
660      * @throws IOException I/O errors
661      * @throws HeaderException thrown when package header is broken
662      * @throws InvalidHMACException thrown when the HMAC code is invalid
663      * @throws CryptoException all encryption errors
664      */

665     public static void decryptAndVerifyHMAC(InputStream is
666                                             , DataOutputStream daos
667                                             , PrivateKey privKey
668                                             , String JavaDoc algorithm
669                                             , String JavaDoc mode
670                                             , String JavaDoc padding
671                                             , int bufferlength)
672             throws IOException, HeaderException, InvalidHMACException, CryptoException {
673
674         SecureRandom secRand;
675         Mac mac = null;
676         Cipher cipher = null;
677         Cipher decHMAC = null;
678
679         SecretKey symKey = null; // Symmetric key.
680
SecretKey macKey = null; // MAC key.
681

682         byte[] keyIV = null; // AES IV
683
byte[] macCode = null; // MAC code
684

685         DataInputStream dataIn = null;
686         MacInputStream macStr = null;
687         DataInputStream dataStr = null;
688
689         try {
690             Security.addProvider(new BouncyCastleProvider());
691
692             secRand = SecureRandom.getInstance("SHA1PRNG", "SUN");
693
694             dataIn = new DataInputStream(is);
695
696             int l = 0; // Universal length variable.
697
boolean ena = false; // Enable flag.. (Set when file/string header found)
698
boolean stop = false; // Stop flag.
699

700             // Setup RSA to decrypt secrets.
701
Cipher rsaEng = Cipher.getInstance("RSA/None/OAEPPadding", "BC");
702             rsaEng.init(Cipher.DECRYPT_MODE, privKey, secRand); // Initialize cipher for decryption.
703

704             while (!stop) {
705                 try {
706                     int cmd = dataIn.readShort(); // Read in block header.
707

708                     if (cmd == FILE_HEADER) // File header.
709
{
710                         ena = true; // Flag file header found.
711
continue;
712                     }
713
714                     if (cmd == DATA_BLOCK) // Read in data block
715
{
716                         if (!ena) {
717                             throw new HeaderException("Broken header");
718                         }
719
720                         l = dataIn.readInt(); // Read length.
721
dataIn.skip(l); // Skip this data.
722
continue;
723                     }
724
725                     if (cmd == FINAL_DATA_BLOCK) // Final data block.
726
{
727                         if (!ena) {
728                             throw new HeaderException("Broken header");
729                         }
730                         l = dataIn.readInt(); // Read length
731
dataIn.skip(l); // Skip this data.
732
continue;
733                     }
734
735                     if (cmd == HMAC_BLOCK) // MAC block.
736
{
737                         if (!ena) {
738                             throw new HeaderException("Broken header");
739                         }
740                         l = dataIn.readInt(); // Read length.
741
macCode = new byte[l]; // Create new array (l) in size.
742
dataIn.readFully(macCode); // Read in signature.
743
continue;
744                     }
745
746                     if (cmd == KEY_BLOCK) // Read in key block.
747
{
748                         if (!ena) {
749                             throw new HeaderException("Broken header");
750                         }
751
752                         l = dataIn.readInt(); // Read length.
753
byte[] d = new byte[l]; // Create new array (l) in size.
754
dataIn.readFully(d); // read in data.
755
// We must use a SecretKeySpec set up
756
// to convert the raw encoded key back into a SecretKey Object.
757
// The key is also decrypted before processing.
758

759                         symKey = new SecretKeySpec(rsaEng.doFinal(d), algorithm);
760                         continue;
761                     }
762
763                     if (cmd == IV_BLOCK) // Read in IV.
764
{
765                         if (!ena) {
766                             throw new HeaderException("Broken header");
767                         }
768                         l = dataIn.readInt(); // Read length
769
keyIV = new byte[l]; // Create new array for IV (l) in size.
770
dataIn.readFully(keyIV); // Read in IV.
771
keyIV = rsaEng.doFinal(keyIV); // Decrypt IV
772
continue;
773                     }
774
775                     if (cmd == HMAC_KEY_BLOCK) // Read lock.
776
{
777                         if (!ena) {
778                             throw new HeaderException("Broken header");
779                         }
780
781                         // Set up cipher to decrypt MAC
782
decHMAC = Cipher.getInstance(algorithm + "/" + mode + "/" + padding, "BC");
783                         decHMAC.init(Cipher.DECRYPT_MODE, symKey, new IvParameterSpec(keyIV));
784                         l = dataIn.readInt(); // Read Length
785
byte[] d = new byte[l]; // Create new byte array (l) in size.
786
dataIn.readFully(d); // Read
787
macKey = new SecretKeySpec(decHMAC.doFinal(d), "HMACSHA1"); // Decrypt lock.
788

789                         continue;
790                     }
791                 } catch (EOFException eof) {
792                     stop = true;
793                 }
794             }
795
796             mac = Mac.getInstance("HMACSHA1", "BC"); // Mac algorithm based on SHA1 message digest.
797
mac.init(macKey); // initialize it with the mac key.
798

799             // Set up input stream wrappers.
800
is.reset();
801             macStr = new MacInputStream(is, mac);
802             dataStr = new DataInputStream(macStr);
803
804             int cmd = 0; // variable to store block header.
805
byte[] buf = new byte[bufferlength]; // Buffer to work in.
806
l = 0; // Universal length variable.
807

808             do {
809                 cmd = dataStr.readShort(); // Read off block header/
810

811                 if (cmd == DATA_BLOCK) // Skip HMAC block.
812
{
813                     l = dataStr.readInt(); // Read length.
814
dataStr.read(buf, 0, l); // dummy read.
815
}
816
817                 if (cmd == FINAL_DATA_BLOCK) // Skip the KEY Block
818
{
819                     l = dataStr.readInt();
820                     dataStr.read(buf, 0, l); // dummy read
821
}
822
823
824                 if (cmd == KEY_BLOCK) // Skip the KEY Block
825
{
826                     l = dataStr.readInt();
827                     dataStr.read(buf, 0, l); // dummy read
828
}
829
830                 if (cmd == IV_BLOCK) // Skip the IV block.
831
{
832                     l = dataStr.readInt();
833                     dataStr.read(buf, 0, l); // dummy read
834
}
835
836                 if (cmd == HMAC_KEY_BLOCK) // Skip the IV block.
837
{
838                     l = dataStr.readInt();
839                     dataStr.read(buf, 0, l); // dummy read
840
}
841             } while (cmd != HMAC_BLOCK);
842
843             buf = mac.doFinal();
844             dataStr.close();
845
846             if (!MessageDigest.isEqual(buf, macCode)) {
847                 throw new InvalidHMACException("Invalid HMAC");
848             }
849
850             cipher = Cipher.getInstance(algorithm + "/" + mode + "/" + padding, "BC");
851             cipher.init(Cipher.DECRYPT_MODE, symKey, new IvParameterSpec(keyIV));
852
853             is.reset();
854             dataStr = new DataInputStream(is);
855
856             stop = false; // Loop breaker.
857
cmd = 0; // Variable to hold block header.
858
l = 0; // Universal length variable.
859
buf = new byte[bufferlength]; // A buffer to work in.
860
byte[] out = null; // Output buffer.
861

862             for (; ;) {
863                 cmd = dataStr.readShort(); // Read in block header.
864

865                 if (cmd == DATA_BLOCK) {
866                     l = dataStr.readInt(); // Get length of data.
867
dataStr.readFully(buf, 0, l); // Read data.
868
out = cipher.update(buf, 0, l);
869                     if (out != null) daos.write(out);
870                 }
871
872                 if (cmd == FINAL_DATA_BLOCK) {
873                     l = dataStr.readInt(); // Length of data.
874
dataStr.readFully(buf, 0, l); // Read in data.
875
out = cipher.doFinal(buf, 0, l);
876                     if (out != null) daos.write(out);
877                     break;
878                 }
879
880                 // The following blocks and their content are skipped.
881
if (cmd == KEY_BLOCK) // Skip the KEY Block
882
{
883                     l = dataStr.readInt();
884                     dataStr.skip(l);
885                 }
886
887                 if (cmd == IV_BLOCK) // Skip the IV block.
888
{
889                     l = dataStr.readInt();
890                     dataStr.skip(l);
891                 }
892
893                 if (cmd == HMAC_BLOCK) // Skip the IV block.
894
{
895                     l = dataStr.readInt();
896                     dataStr.skip(l);
897                 }
898
899                 if (cmd == HMAC_KEY_BLOCK) // Skip the IV block.
900
{
901                     l = dataStr.readInt();
902                     dataStr.skip(l);
903                 }
904             }
905         } catch (IOException ioe) {
906             ioe.printStackTrace();
907             throw new IOException(ioe.getMessage());
908         } catch (HeaderException he) {
909             he.printStackTrace();
910             throw new HeaderException(he.getMessage());
911         } catch (InvalidHMACException ihe) {
912             ihe.printStackTrace();
913             throw new InvalidHMACException(ihe.getMessage());
914         } catch (Exception JavaDoc ex) {
915             ex.printStackTrace();
916             throw new CryptoException(ex.getMessage());
917         }
918     }
919
920     /**
921      * Encrypt and sign a text
922      *
923      * @param text the text to encrypt and sign
924      * @param receiverKey the public key of the receiver
925      * @param signingKey the private key of the signer
926      * @param cert the signer's certificate
927      * @param signame the signature's algorithm (e.g."MD5withRSA")
928      * @param algorithm encryption algorithm (e.g. "Rijndael")
929      * @param seed for SecureRandom (optional)
930      * @param strength the keysize in bits (e.g. 128)
931      * @param mode encryption mode (e.g. "CBC")
932      * @param padding padding scheme (e.g."PKCS7Padding")
933      * @return ciphered and signed text
934      * @throws CryptoException encryption errors
935      */

936     public static StringBuffer JavaDoc encryptAndSign(StringBuffer JavaDoc text
937                                               , PublicKey receiverKey
938                                               , PrivateKey signingKey
939                                               , X509Certificate JavaDoc cert
940                                               , String JavaDoc signame
941                                               , String JavaDoc algorithm
942                                               , byte[] seed
943                                               , int strength
944                                               , String JavaDoc mode
945                                               , String JavaDoc padding) throws CryptoException {
946
947         ByteArrayOutputStream bao = null;
948         DataOutputStream dao = null;
949
950         try {
951             bao = new ByteArrayOutputStream();
952             dao = new DataOutputStream(bao);
953
954             // encrypt text with HMAC
955
encryptAndSign(new ByteArrayInputStream(text.toString().getBytes()), dao, receiverKey, signingKey, cert, signame, algorithm, seed, strength, mode, padding, BUFFERSIZE_TEXT);
956             return new StringBuffer JavaDoc(new String JavaDoc(Base64.encode(bao.toByteArray())));
957         } catch (IOException ioe) {
958             ioe.printStackTrace();
959             throw new CryptoException(ioe.getMessage());
960         } finally {
961             if (dao != null) {
962                 // close outputstream
963
try {
964                     dao.close();
965                 } catch (IOException e) {
966                     ;
967                 }
968             }
969         }
970     }
971
972     /**
973      * Encrypt and sign a file
974      *
975      * @param file file to encrypt
976      * @param newfile encrypted file
977      * @param receiverKey the public key of the receiver
978      * @param signingKey the private key of the signer
979      * @param cert the signer's certificate
980      * @param signame the signature's algorithm (e.g."MD5withRSA")
981      * @param algorithm encryption algorithm (e.g. "Rijndael")
982      * @param seed for SecureRandom (optional)
983      * @param strength the keysize in bits (e.g. 128)
984      * @param mode encryption mode (e.g. "CBC")
985      * @param padding padding scheme (e.g."PKCS7Padding")
986      * @throws CryptoException encryption errors
987      * @throws IOException I/O errors
988      */

989     public static void encryptFileAndSign(String JavaDoc file
990                                           , String JavaDoc newfile
991                                           , PublicKey receiverKey
992                                           , PrivateKey signingKey
993                                           , X509Certificate JavaDoc cert
994                                           , String JavaDoc signame
995                                           , String JavaDoc algorithm
996                                           , byte[] seed
997                                           , int strength
998                                           , String JavaDoc mode
999                                           , String JavaDoc padding)
1000            throws CryptoException, IOException {
1001
1002        FileInputStream fis = null;
1003        FileOutputStream fos = null;
1004        DataOutputStream dao = null;
1005
1006        try {
1007            fis = new FileInputStream(file);
1008
1009            fos = new FileOutputStream(newfile);
1010            dao = new DataOutputStream(fos);
1011
1012            // encrypt file
1013
encryptAndSign(fis, dao, receiverKey, signingKey, cert, signame, algorithm, seed, strength, mode, padding, BUFFERSIZE_FILE);
1014
1015        } catch (IOException ioe) {
1016            ioe.printStackTrace();
1017            throw new IOException(ioe.getMessage());
1018        } finally {
1019            if (dao != null) {
1020                // close outputstream
1021
try {
1022                    dao.close();
1023                } catch (IOException e) {
1024                    ;
1025                }
1026            }
1027            if (fis != null) {
1028                // close outputstream
1029
try {
1030                    fis.close();
1031                } catch (IOException e) {
1032                    ;
1033                }
1034            }
1035        }
1036    }
1037
1038    /**
1039     * Encrypt and sign any inputstream
1040     *
1041     * @param is inputstream to encrypt
1042     * @param daos outputstream to store the encrypted & signed data
1043     * @param receiverKey the public key of the receiver
1044     * @param signingKey the private key of the signer
1045     * @param cert the signer's certificate
1046     * @param signame the signature's algorithm (e.g."MD5withRSA")
1047     * @param algorithm encryption algorithm (e.g. "Rijndael")
1048     * @param seed for SecureRandom (optional)
1049     * @param strength the keysize in bits (e.g. 128)
1050     * @param mode encryption mode (e.g. "CBC")
1051     * @param padding padding scheme (e.g."PKCS7Padding")
1052     * @param bufferlength buffer length in bytes
1053     * @throws CryptoException encryption errors
1054     * @throws IOException I/O errors
1055     */

1056    public static void encryptAndSign(InputStream is
1057                                      , DataOutputStream daos
1058                                      , PublicKey receiverKey
1059                                      , PrivateKey signingKey
1060                                      , X509Certificate JavaDoc cert
1061                                      , String JavaDoc signame
1062                                      , String JavaDoc algorithm
1063                                      , byte[] seed
1064                                      , int strength
1065                                      , String JavaDoc mode
1066                                      , String JavaDoc padding
1067                                      , int bufferlength)
1068            throws CryptoException, IOException {
1069
1070        SecureRandom secRand = null;
1071        KeyGenerator keyGen = null;
1072        Key symKey = null;
1073        Cipher outputCipher = null;
1074
1075        SignatureOutputStream sigStr = null;
1076        DataOutputStream dataStr = null;
1077
1078        try {
1079            Security.addProvider(new BouncyCastleProvider());
1080
1081            secRand = Seed.getSecureRandom(seed);
1082
1083            // Generate symmetric key
1084
keyGen = KeyGenerator.getInstance(algorithm, "BC");
1085            keyGen.init(strength, secRand);
1086            symKey = keyGen.generateKey();
1087
1088            // Instantiate Symmetric cipher for encryption.
1089
outputCipher = Cipher.getInstance(algorithm + "/" + mode + "/" + padding, "BC");
1090            outputCipher.init(Cipher.ENCRYPT_MODE, symKey, secRand);
1091
1092            // Get key and IV for cipher so that they can be later
1093
// encrypted to build a header.
1094
byte[] keyEnc = symKey.getEncoded();
1095            byte[] keyIV = outputCipher.getIV();
1096
1097            byte[] lock = new byte[24];
1098
1099            secRand.nextBytes(lock);
1100
1101            // Setup Signature
1102
Signature sig = Signature.getInstance(signame, "BC");
1103            sig.initSign(signingKey, secRand); // Initialize with my private signing key.
1104
sig.update(lock); // put plain text of lock data into signature.
1105

1106            // Setup RSA to encrypt secrets.
1107
Cipher rsaEng = Cipher.getInstance("RSA/None/OAEPPadding", "BC");
1108            rsaEng.init(Cipher.ENCRYPT_MODE, receiverKey, secRand);
1109
1110            // Setup to process File.
1111
//FileInputStream inStr = new FileInputStream(filename); // Source of plain text.
1112
//FileOutputStream outStr = new FileOutputStream(newfilename); // Final output stream.
1113
sigStr = new SignatureOutputStream(daos, sig);
1114            dataStr = new DataOutputStream(sigStr);
1115
1116            // Form HEADER for the encrypted string
1117
dataStr.writeShort(FILE_HEADER); // Write a file or string header.
1118

1119            // Write out a block for the key of the cipher.
1120
dataStr.writeShort(KEY_BLOCK); // Block header.
1121
byte[] tmp = rsaEng.doFinal(keyEnc); // Encrypt it with RSA.
1122
dataStr.writeInt(tmp.length); // Write length.
1123
dataStr.write(tmp); // Write data.
1124
Clean.blank(tmp); // Erase tmp array.
1125

1126            // Write out IV block
1127
dataStr.writeShort(IV_BLOCK); // Block header
1128
tmp = rsaEng.doFinal(keyIV); // Encrypt with RSA.
1129
dataStr.writeInt(tmp.length); // Write length.
1130
dataStr.write(tmp); // Write data.
1131
Clean.blank(tmp); // Erase tmp array.
1132

1133            // Write out lock data for SIGNATURE.
1134
dataStr.writeShort(LOCK_BLOCK); // Write header.
1135
tmp = outputCipher.doFinal(lock); // Encrypt with AES.
1136
dataStr.writeInt(tmp.length); // Write length.
1137
dataStr.write(tmp); // Write data.
1138
Clean.blank(tmp); // Erase tmp array.
1139

1140            // Reset cipher back to original
1141
outputCipher.init(Cipher.ENCRYPT_MODE, symKey, new IvParameterSpec(keyIV)); // initialize with aes_key.
1142

1143            // Encrypt the message
1144
int l = 0; // Universal length variable.
1145
byte[] buf = new byte[bufferlength]; // A buffer to work in.
1146
byte[] out = null; // Output buffer.
1147

1148            // Read while length is > -1
1149

1150            while ((l = is.read(buf)) > -1) {
1151                out = outputCipher.update(buf, 0, l); // Encrypt data.
1152
if (out != null) {
1153                    dataStr.writeShort(DATA_BLOCK); // Write data block header.
1154
dataStr.writeInt(out.length); // Write length.
1155
dataStr.write(out); // Write encrypted data.
1156
}
1157            }
1158
1159            // This is the last block
1160
out = outputCipher.doFinal(); // Do final encryption.
1161
dataStr.writeShort(FINAL_DATA_BLOCK); // Write header.
1162
dataStr.writeInt(out.length); // Write length.
1163
dataStr.write(out); // Write Data.
1164

1165            Clean.blank(buf); // Clear buffer.
1166
buf = null; // Set Null
1167

1168            // Write out our certificate
1169
dataStr.writeShort(CERT_BLOCK); // Cert block header.
1170
tmp = cert.getEncoded(); // Get encoded in a byte array.
1171
dataStr.writeInt(tmp.length); // Write length.
1172
dataStr.write(tmp); // Write data.
1173

1174            // Write out signature block
1175
dataStr.writeShort(SIG_BLOCK); // Write Header.
1176
dataStr.flush(); // Flush it..
1177

1178            tmp = sig.sign(); // Get signature code.
1179
dataStr.writeInt(tmp.length); // Write length.
1180
dataStr.write(tmp); // Write data.
1181
Clean.blank(tmp); // Clear.
1182

1183            // Flush and close output.
1184
dataStr.flush();
1185            dataStr.close();
1186        } catch (IOException ioe) {
1187            ioe.printStackTrace();
1188            throw new IOException(ioe.getMessage());
1189        } catch (Exception JavaDoc ex) {
1190            ex.printStackTrace();
1191            throw new CryptoException(ex.getMessage());
1192        } finally {
1193            if (dataStr != null) {
1194                try {
1195                    dataStr.close();
1196                } catch (IOException ioe) {
1197                    ;
1198                }
1199            }
1200        }
1201    }
1202
1203    /**
1204     * decrypt and verify text signature
1205     *
1206     * @param text the text to decrypt and verify
1207     * @param privKey the private key of the receiver
1208     * @param signercert returns the signer's certificate
1209     * @param signame the signature's algorithm (e.g."MD5withRSA")
1210     * @param algorithm encryption algorithm (e.g. "Rijndael")
1211     * @param mode encryption mode (e.g. "CBC")
1212     * @param padding padding scheme (e.g."PKCS7Padding")
1213     * @return the plaintext
1214     * @throws HeaderException thrown when package header is broken
1215     * @throws InvalidSignatureException thrown when the signature is invalid
1216     * @throws CryptoException all encryption errors
1217     */

1218    public static StringBuffer JavaDoc decryptAndVerify(StringBuffer JavaDoc text
1219                                                , PrivateKey privKey
1220                                                , SignerCertificate signercert
1221                                                , String JavaDoc signame
1222                                                , String JavaDoc algorithm
1223                                                , String JavaDoc mode
1224                                                , String JavaDoc padding) throws HeaderException, InvalidSignatureException, CryptoException {
1225
1226        ByteArrayOutputStream bao = null;
1227        DataOutputStream dao = null;
1228
1229        try {
1230            bao = new ByteArrayOutputStream();
1231            dao = new DataOutputStream(bao);
1232
1233            // decrypt & verify
1234
decryptAndVerify(new ByteArrayInputStream(Base64.decode(text.toString())), dao, privKey, signercert, signame, algorithm, mode, padding, BUFFERSIZE_TEXT);
1235
1236            return new StringBuffer JavaDoc(new String JavaDoc(bao.toByteArray()));
1237        } catch (HeaderException he) {
1238            throw new HeaderException(he.getMessage());
1239        } catch (InvalidSignatureException ise) {
1240            throw new InvalidSignatureException(ise.getMessage());
1241        } catch (Exception JavaDoc ioe) {
1242            ioe.printStackTrace();
1243            throw new CryptoException(ioe.getMessage());
1244        } finally {
1245            if (dao != null) {
1246                // close outputstream
1247
try {
1248                    dao.close();
1249                } catch (IOException e) {
1250                    ;
1251                }
1252            }
1253        }
1254    }
1255
1256    /**
1257     * decrypt and verify inputstream signature (which must support mark/reset)
1258     *
1259     * @param is the inputstream to decrypt and verify (NOTE: the inputstream must support mark/reset because it must be read three times)
1260     * @param daos the outputstream containing the deciphered data
1261     * @param privKey the private key of the receiver
1262     * @param signercert returns the signer's certificate
1263     * @param signame the signature's algorithm (e.g."MD5withRSA")
1264     * @param algorithm encryption algorithm (e.g. "Rijndael")
1265     * @param mode encryption mode (e.g. "CBC")
1266     * @param padding padding scheme (e.g."PKCS7Padding")
1267     * @param bufferlength buffer length in bytes
1268     * @throws IOException I/O errors
1269     * @throws HeaderException thrown when package header is broken
1270     * @throws InvalidSignatureException thrown when the signature is invalid
1271     * @throws CryptoException all encryption errors
1272     */

1273    public static void decryptAndVerify(InputStream is
1274                                        , DataOutputStream daos
1275                                        , PrivateKey privKey
1276                                        , SignerCertificate signercert
1277                                        , String JavaDoc signame
1278                                        , String JavaDoc algorithm
1279                                        , String JavaDoc mode
1280                                        , String JavaDoc padding
1281                                        , int bufferlength)
1282            throws CryptoException, HeaderException, InvalidSignatureException, IOException {
1283
1284        byte[] lockData = null; // Lock data
1285
X509Certificate JavaDoc cert = null; // Senders Authentication Certificate.
1286
SecretKey symKey = null; // Symmetric key.
1287
byte[] keyIV = null; // AES IV
1288
byte[] sigCode = null; // Signature.
1289

1290        DataInputStream dataIn = null;
1291
1292        try {
1293            Security.addProvider(new BouncyCastleProvider());
1294
1295            SecureRandom secRand = SecureRandom.getInstance("SHA1PRNG", "SUN");
1296
1297            dataIn = new DataInputStream(is);
1298
1299            int l = 0; // Universal length variable.
1300
boolean ena = false; // Enable flag.. (Set when file/string header found)
1301
boolean stop = false; // Stop flag.
1302

1303            // Setup RSA to decrypt secrets.
1304
Cipher rsaEng = Cipher.getInstance("RSA/None/OAEPPadding", "BC");
1305            rsaEng.init(Cipher.DECRYPT_MODE, privKey, secRand); // Initialize cipher for decryption.
1306

1307            while (!stop) {
1308                try {
1309                    int cmd = dataIn.readShort(); // Read in block header.
1310

1311                    if (cmd == Hybrid.FILE_HEADER) // File header.
1312
{
1313                        ena = true; // Flag file header found.
1314
continue;
1315                    }
1316
1317                    if (cmd == Hybrid.DATA_BLOCK) // Read in data block
1318
{
1319                        if (!ena) {
1320                            throw new Exception JavaDoc("Broken header");
1321                        }
1322
1323                        l = dataIn.readInt(); // Read length.
1324
dataIn.skip(l); // Skip this data.
1325
continue;
1326                    }
1327
1328                    if (cmd == Hybrid.FINAL_DATA_BLOCK) // Final data block.
1329
{
1330                        if (!ena) {
1331                            throw new Exception JavaDoc("Broken header");
1332                        }
1333                        l = dataIn.readInt(); // Read length
1334
dataIn.skip(l); // Skip this data.
1335
continue;
1336                    }
1337
1338                    if (cmd == Hybrid.SIG_BLOCK) // Signature block.
1339
{
1340                        if (!ena) {
1341                            throw new Exception JavaDoc("Broken header");
1342                        }
1343                        l = dataIn.readInt(); // Read length.
1344
sigCode = new byte[l]; // Create new array (l) in size.
1345
dataIn.readFully(sigCode); // Read in signature.
1346
continue;
1347                    }
1348
1349                    if (cmd == Hybrid.KEY_BLOCK) // Read in key block.
1350
{
1351                        if (!ena) {
1352                            throw new Exception JavaDoc("Broken header");
1353                        }
1354
1355                        l = dataIn.readInt(); // Read length.
1356
byte[] d = new byte[l]; // Create new array (l) in size.
1357
dataIn.readFully(d); // read in data.
1358

1359                        // We must use a SecretKeySpec set up
1360
// to convert the raw encoded key back into a SecretKey Object.
1361
// The key is also decrypted before processing.
1362

1363                        symKey = new SecretKeySpec(rsaEng.doFinal(d), algorithm);
1364                        continue;
1365                    }
1366
1367                    if (cmd == Hybrid.IV_BLOCK) // Read in IV.
1368
{
1369                        if (!ena) {
1370                            throw new Exception JavaDoc("Broken header");
1371                        }
1372                        l = dataIn.readInt(); // Read length
1373
keyIV = new byte[l]; // Create new array for IV (l) in size.
1374
dataIn.readFully(keyIV); // Read in IV.
1375
keyIV = rsaEng.doFinal(keyIV); // Decrypt IV
1376
continue;
1377                    }
1378
1379                    if (cmd == Hybrid.LOCK_BLOCK) // Read lock.
1380
{
1381                        if (!ena) {
1382                            throw new Exception JavaDoc("Broken header");
1383                        }
1384
1385                        // Set up cipher to decrypt lock data.
1386
Cipher decLock = Cipher.getInstance(algorithm + "/" + mode + "/" + padding, "BC");
1387                        decLock.init(Cipher.DECRYPT_MODE, symKey, new IvParameterSpec(keyIV));
1388                        l = dataIn.readInt(); // Read Length
1389
byte[] d = new byte[l]; // Create new byte array (l) in size.
1390
dataIn.readFully(d); // Read lock.
1391
lockData = decLock.doFinal(d); // Decrypt lock.
1392

1393                        decLock = null; // Make null;
1394

1395                        continue;
1396                    }
1397
1398                    if (cmd == Hybrid.CERT_BLOCK) // Senders certificate block.
1399
{
1400                        if (!ena) {
1401                            throw new Exception JavaDoc("Broken header");
1402                        }
1403
1404                        l = dataIn.readInt(); // Read Length
1405

1406                        byte[] d = new byte[l]; // Create new array (l) in length.
1407

1408                        dataIn.readFully(d); // Read in certificate.
1409

1410                        CertificateFactory JavaDoc cf = CertificateFactory.getInstance("X.509");
1411                        cert = (X509Certificate JavaDoc) cf.generateCertificate(new ByteArrayInputStream(d));
1412                        continue;
1413                    }
1414                } catch (EOFException eof) {
1415                    stop = true;
1416                }
1417            }
1418
1419            // return the certificate signer
1420
signercert.setCert(cert);
1421
1422            Signature sig = Signature.getInstance(signame, "BC"); // Set up signature object.
1423

1424            sig.initVerify(cert); // Initialize with senders certificate.
1425
sig.update(lockData); // Update with lock data.
1426

1427            // Set up input stream wrappers.
1428
is.reset();
1429            SignatureInputStream sigStr = new SignatureInputStream(is, sig);
1430            DataInputStream dataStr = new DataInputStream(sigStr);
1431
1432            int cmd = 0; // variable to store block header.
1433
byte[] buf = new byte[bufferlength]; // Buffer to work in.
1434
l = 0; // Universal length variable.
1435

1436            for (; ;) {
1437                cmd = dataStr.readShort(); // Read off block header/
1438

1439                if (cmd == Hybrid.DATA_BLOCK) // Skip HMAC block.
1440
{
1441                    l = dataStr.readInt(); // Read length.
1442
dataStr.read(buf, 0, l); // dummy read.
1443
}
1444
1445                if (cmd == Hybrid.FINAL_DATA_BLOCK) // Skip the KEY Block
1446
{
1447                    l = dataStr.readInt();
1448                    dataStr.read(buf, 0, l); // dummy read
1449
}
1450
1451
1452                if (cmd == Hybrid.KEY_BLOCK) // Skip the KEY Block
1453
{
1454                    l = dataStr.readInt();
1455                    dataStr.read(buf, 0, l); // dummy read
1456
}
1457
1458                if (cmd == Hybrid.IV_BLOCK) // Skip the IV block.
1459
{
1460                    l = dataStr.readInt();
1461                    dataStr.read(buf, 0, l); // dummy read
1462
}
1463
1464                if (cmd == Hybrid.LOCK_BLOCK) // Skip the lock block.
1465
{
1466                    l = dataStr.readInt();
1467                    dataStr.read(buf, 0, l); // dummy read
1468
}
1469
1470                if (cmd == Hybrid.CERT_BLOCK) // Skip the cert block.
1471
{
1472                    l = dataStr.readInt();
1473                    dataStr.read(buf, 0, l);// dummy read
1474
}
1475
1476                if (cmd == Hybrid.SIG_BLOCK) {
1477                    break;
1478                }
1479            }
1480
1481            dataStr.close();
1482
1483            // throw an exception when the signature could not be verified or is invalid
1484
if (!sig.verify(sigCode)) {
1485                throw new InvalidSignatureException("Signature is invalid");
1486            }
1487
1488            Cipher cipher = Cipher.getInstance(algorithm + "/" + mode + "/" + padding, "BC");
1489            cipher.init(Cipher.DECRYPT_MODE, symKey, new IvParameterSpec(keyIV));
1490
1491            //FileOutputStream outStr = new FileOutputStream(newfilename); // Where to save
1492

1493            // Input source file, via DataInputStream wrapper.
1494
is.reset();
1495            dataStr = new DataInputStream(is);
1496
1497            stop = false; // Loop breaker.
1498
cmd = 0; // Variable to hold block header.
1499
l = 0; // Universal length variable.
1500
buf = new byte[bufferlength]; // A buffer to work in.
1501
byte[] out = null; // Output buffer.
1502

1503            for (; ;) {
1504                cmd = dataStr.readShort(); // Read in block header.
1505

1506                if (cmd == Hybrid.DATA_BLOCK) {
1507                    l = dataStr.readInt(); // Get length of data.
1508
dataStr.readFully(buf, 0, l); // Read data.
1509
out = cipher.update(buf, 0, l);
1510                    if (out != null) daos.write(out);
1511                }
1512
1513                if (cmd == Hybrid.FINAL_DATA_BLOCK) {
1514                    l = dataStr.readInt(); // Length of data.
1515
dataStr.readFully(buf, 0, l); // Read in data.
1516
out = cipher.doFinal(buf, 0, l);
1517                    if (out != null) daos.write(out);
1518                    break;
1519                }
1520
1521                // The following blocks and their content are skipped.
1522
if (cmd == Hybrid.KEY_BLOCK) // Skip the KEY Block
1523
{
1524                    l = dataStr.readInt();
1525                    dataStr.skip(l);
1526                }
1527
1528                if (cmd == Hybrid.IV_BLOCK) // Skip the IV block.
1529
{
1530                    l = dataStr.readInt();
1531                    dataStr.skip(l);
1532                }
1533
1534                if (cmd == Hybrid.LOCK_BLOCK) // Skip the IV block.
1535
{
1536                    l = dataStr.readInt();
1537                    dataStr.skip(l);
1538                }
1539
1540                if (cmd == Hybrid.CERT_BLOCK) // Skip the IV block.
1541
{
1542                    l = dataStr.readInt();
1543                    dataStr.skip(l);
1544                }
1545
1546                if (cmd == Hybrid.SIG_BLOCK) {
1547                    l = dataStr.readInt();
1548                    dataStr.skip(l);
1549                }
1550            }
1551
1552            Clean.blank(buf); // Erase buffer.
1553
buf = null; // Set null.
1554

1555            dataStr.close();
1556        } catch (IOException ioe) {
1557            ioe.printStackTrace();
1558            throw new IOException(ioe.getMessage());
1559        } catch (HeaderException he) {
1560            he.printStackTrace();
1561            throw new HeaderException(he.getMessage());
1562        } catch (InvalidSignatureException ise) {
1563            ise.printStackTrace();
1564            throw new InvalidSignatureException(ise.getMessage());
1565        } catch (Exception JavaDoc ex) {
1566            ex.printStackTrace();
1567            throw new CryptoException(ex.getMessage());
1568        }
1569    }
1570
1571    /**
1572     *
1573     *
1574     * @param file the file to decrypt and verify
1575     * @param newfile the decrypted file
1576     * @param privKey the private key of the receiver
1577     * @param signercert returns the signer's certificate
1578     * @param signame the signature's algorithm (e.g."MD5withRSA")
1579     * @param algorithm encryption algorithm (e.g. "Rijndael")
1580     * @param mode encryption mode (e.g. "CBC")
1581     * @param padding padding scheme (e.g."PKCS7Padding")
1582     * @throws IOException I/O errors
1583     * @throws HeaderException thrown when package header is broken
1584     * @throws InvalidSignatureException thrown when the signature is invalid
1585     * @throws CryptoException all encryption errors
1586     */

1587    public static void decryptFileAndVerify(String JavaDoc file
1588                                            , String JavaDoc newfile
1589                                            , PrivateKey privKey
1590                                            , SignerCertificate signercert
1591                                            , String JavaDoc signame
1592                                            , String JavaDoc algorithm
1593                                            , String JavaDoc mode
1594                                            , String JavaDoc padding)
1595            throws CryptoException, HeaderException, InvalidSignatureException, IOException {
1596
1597        byte[] lockData = null; // Lock data
1598
X509Certificate JavaDoc cert = null; // Senders Authentication Certificate.
1599
SecretKey symKey = null; // Symmetric key.
1600
byte[] keyIV = null; // AES IV
1601
byte[] sigCode = null; // Signature.
1602

1603        SignatureInputStream sigStr = null;
1604
1605        DataInputStream dataIn = null;
1606        FileOutputStream outStr = null;
1607        DataInputStream dataStr = null;
1608
1609        try {
1610            Security.addProvider(new BouncyCastleProvider());
1611
1612            SecureRandom secRand = SecureRandom.getInstance("SHA1PRNG", "SUN");
1613
1614            dataIn = new DataInputStream(new FileInputStream(file));
1615
1616            int l = 0; // Universal length variable.
1617
boolean ena = false; // Enable flag.. (Set when file/string header found)
1618
boolean stop = false; // Stop flag.
1619

1620            // Setup RSA to decrypt secrets.
1621
Cipher rsaEng = Cipher.getInstance("RSA/None/OAEPPadding", "BC");
1622            rsaEng.init(Cipher.DECRYPT_MODE, privKey, secRand); // Initialize cipher for decryption.
1623

1624            while (!stop) {
1625                try {
1626                    int cmd = dataIn.readShort(); // Read in block header.
1627

1628                    if (cmd == Hybrid.FILE_HEADER) // File header.
1629
{
1630                        ena = true; // Flag file header found.
1631
continue;
1632                    }
1633
1634                    if (cmd == Hybrid.DATA_BLOCK) // Read in data block
1635
{
1636                        if (!ena) {
1637                            throw new Exception JavaDoc("Broken header");
1638                        }
1639
1640                        l = dataIn.readInt(); // Read length.
1641
dataIn.skip(l); // Skip this data.
1642
continue;
1643                    }
1644
1645                    if (cmd == Hybrid.FINAL_DATA_BLOCK) // Final data block.
1646
{
1647                        if (!ena) {
1648                            throw new Exception JavaDoc("Broken header");
1649                        }
1650                        l = dataIn.readInt(); // Read length
1651
dataIn.skip(l); // Skip this data.
1652
continue;
1653                    }
1654
1655                    if (cmd == Hybrid.SIG_BLOCK) // Signature block.
1656
{
1657                        if (!ena) {
1658                            throw new Exception JavaDoc("Broken header");
1659                        }
1660                        l = dataIn.readInt(); // Read length.
1661
sigCode = new byte[l]; // Create new array (l) in size.
1662
dataIn.readFully(sigCode); // Read in signature.
1663
continue;
1664                    }
1665
1666                    if (cmd == Hybrid.KEY_BLOCK) // Read in key block.
1667
{
1668                        if (!ena) {
1669                            throw new Exception JavaDoc("Broken header");
1670                        }
1671
1672                        l = dataIn.readInt(); // Read length.
1673
byte[] d = new byte[l]; // Create new array (l) in size.
1674
dataIn.readFully(d); // read in data.
1675

1676                        // We must use a SecretKeySpec set up
1677
// to convert the raw encoded key back into a SecretKey Object.
1678
// The key is also decrypted before processing.
1679

1680                        symKey = new SecretKeySpec(rsaEng.doFinal(d), algorithm);
1681                        continue;
1682                    }
1683
1684                    if (cmd == Hybrid.IV_BLOCK) // Read in IV.
1685
{
1686                        if (!ena) {
1687                            throw new Exception JavaDoc("Broken header");
1688                        }
1689                        l = dataIn.readInt(); // Read length
1690
keyIV = new byte[l]; // Create new array for IV (l) in size.
1691
dataIn.readFully(keyIV); // Read in IV.
1692
keyIV = rsaEng.doFinal(keyIV); // Decrypt IV
1693
continue;
1694                    }
1695
1696                    if (cmd == Hybrid.LOCK_BLOCK) // Read lock.
1697
{
1698                        if (!ena) {
1699                            throw new Exception JavaDoc("Broken header");
1700                        }
1701
1702                        // Set up cipher to decrypt lock data.
1703
Cipher decLock = Cipher.getInstance(algorithm + "/" + mode + "/" + padding, "BC");
1704                        decLock.init(Cipher.DECRYPT_MODE, symKey, new IvParameterSpec(keyIV));
1705                        l = dataIn.readInt(); // Read Length
1706
byte[] d = new byte[l]; // Create new byte array (l) in size.
1707
dataIn.readFully(d); // Read lock.
1708
lockData = decLock.doFinal(d); // Decrypt lock.
1709

1710                        decLock = null; // Make null;
1711

1712                        continue;
1713                    }
1714
1715                    if (cmd == Hybrid.CERT_BLOCK) // Senders certificate block.
1716
{
1717                        if (!ena) {
1718                            throw new Exception JavaDoc("Broken header");
1719                        }
1720
1721                        l = dataIn.readInt(); // Read Length
1722

1723                        byte[] d = new byte[l]; // Create new array (l) in length.
1724

1725                        dataIn.readFully(d); // Read in certificate.
1726

1727                        CertificateFactory JavaDoc cf = CertificateFactory.getInstance("X.509");
1728                        cert = (X509Certificate JavaDoc) cf.generateCertificate(new ByteArrayInputStream(d));
1729                        continue;
1730                    }
1731                } catch (EOFException eof) {
1732                    stop = true;
1733                }
1734            }
1735
1736            // return the certificate signer
1737
signercert.setCert(cert);
1738
1739            Signature sig = Signature.getInstance(signame, "BC"); // Set up signature object.
1740

1741            sig.initVerify(cert); // Initialize with senders certificate.
1742
sig.update(lockData); // Update with lock data.
1743

1744            // Set up input stream wrappers.
1745
sigStr = new SignatureInputStream(new FileInputStream(file), sig);
1746            dataStr = new DataInputStream(sigStr);
1747
1748            int cmd = 0; // variable to store block header.
1749
byte[] buf = new byte[BUFFERSIZE_FILE]; // Buffer to work in.
1750
l = 0; // Universal length variable.
1751

1752            for (; ;) {
1753                cmd = dataStr.readShort(); // Read off block header/
1754

1755                if (cmd == Hybrid.DATA_BLOCK) // Skip HMAC block.
1756
{
1757                    l = dataStr.readInt(); // Read length.
1758
dataStr.read(buf, 0, l); // dummy read.
1759
}
1760
1761                if (cmd == Hybrid.FINAL_DATA_BLOCK) // Skip the KEY Block
1762
{
1763                    l = dataStr.readInt();
1764                    dataStr.read(buf, 0, l); // dummy read
1765
}
1766
1767
1768                if (cmd == Hybrid.KEY_BLOCK) // Skip the KEY Block
1769
{
1770                    l = dataStr.readInt();
1771                    dataStr.read(buf, 0, l); // dummy read
1772
}
1773
1774                if (cmd == Hybrid.IV_BLOCK) // Skip the IV block.
1775
{
1776                    l = dataStr.readInt();
1777                    dataStr.read(buf, 0, l); // dummy read
1778
}
1779
1780                if (cmd == Hybrid.LOCK_BLOCK) // Skip the lock block.
1781
{
1782                    l = dataStr.readInt();
1783                    dataStr.read(buf, 0, l); // dummy read
1784
}
1785
1786                if (cmd == Hybrid.CERT_BLOCK) // Skip the cert block.
1787
{
1788                    l = dataStr.readInt();
1789                    dataStr.read(buf, 0, l);// dummy read
1790
}
1791
1792                if (cmd == Hybrid.SIG_BLOCK) {
1793                    break;
1794                }
1795            }
1796
1797            dataStr.close();
1798
1799            // throw an exception when the signature could not be verified or is invalid
1800
if (!sig.verify(sigCode)) {
1801                throw new InvalidSignatureException("Signature is invalid");
1802            }
1803
1804            Cipher cipher = Cipher.getInstance(algorithm + "/" + mode + "/" + padding, "BC");
1805            cipher.init(Cipher.DECRYPT_MODE, symKey, new IvParameterSpec(keyIV));
1806
1807            outStr = new FileOutputStream(newfile); // Where to save
1808

1809            // Input source file, via DataInputStream wrapper.
1810
dataStr = new DataInputStream(new FileInputStream(file));
1811
1812            stop = false; // Loop breaker.
1813
cmd = 0; // Variable to hold block header.
1814
l = 0; // Universal length variable.
1815
buf = new byte[BUFFERSIZE_FILE]; // A buffer to work in.
1816
byte[] out = null; // Output buffer.
1817

1818            for (; ;) {
1819                cmd = dataStr.readShort(); // Read in block header.
1820

1821                if (cmd == Hybrid.DATA_BLOCK) {
1822                    l = dataStr.readInt(); // Get length of data.
1823
dataStr.readFully(buf, 0, l); // Read data.
1824
out = cipher.update(buf, 0, l);
1825                    if (out != null) outStr.write(out);
1826                }
1827
1828                if (cmd == Hybrid.FINAL_DATA_BLOCK) {
1829                    l = dataStr.readInt(); // Length of data.
1830
dataStr.readFully(buf, 0, l); // Read in data.
1831
out = cipher.doFinal(buf, 0, l);
1832                    if (out != null) outStr.write(out);
1833                    break;
1834                }
1835
1836                // The following blocks and their content are skipped.
1837
if (cmd == Hybrid.KEY_BLOCK) // Skip the KEY Block
1838
{
1839                    l = dataStr.readInt();
1840                    dataStr.skip(l);
1841                }
1842
1843                if (cmd == Hybrid.IV_BLOCK) // Skip the IV block.
1844
{
1845                    l = dataStr.readInt();
1846                    dataStr.skip(l);
1847                }
1848
1849                if (cmd == Hybrid.LOCK_BLOCK) // Skip the IV block.
1850
{
1851                    l = dataStr.readInt();
1852                    dataStr.skip(l);
1853                }
1854
1855                if (cmd == Hybrid.CERT_BLOCK) // Skip the IV block.
1856
{
1857                    l = dataStr.readInt();
1858                    dataStr.skip(l);
1859                }
1860
1861                if (cmd == Hybrid.SIG_BLOCK) {
1862                    l = dataStr.readInt();
1863                    dataStr.skip(l);
1864                }
1865            }
1866
1867            Clean.blank(buf); // Erase buffer.
1868
buf = null; // Set null.
1869

1870        } catch (IOException ioe) {
1871            ioe.printStackTrace();
1872            throw new IOException(ioe.getMessage());
1873        } catch (HeaderException he) {
1874            he.printStackTrace();
1875            throw new HeaderException(he.getMessage());
1876        } catch (InvalidSignatureException ise) {
1877            ise.printStackTrace();
1878            throw new InvalidSignatureException(ise.getMessage());
1879        } catch (Exception JavaDoc ex) {
1880            ex.printStackTrace();
1881            throw new CryptoException(ex.getMessage());
1882        } finally {
1883            if (outStr != null) {
1884                // close outputstream
1885
try {
1886                    outStr.close();
1887                } catch (IOException e) {
1888                    ;
1889                }
1890            }
1891            if (dataStr != null) {
1892                // close outputstream
1893
try {
1894                    dataStr.close();
1895                } catch (IOException e) {
1896                    ;
1897                }
1898            }
1899        }
1900    }
1901}
1902
Popular Tags