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();