1   /* $RCSfile: RSACipherImpl.java,v $
2    * $Revision: 1.5 $
3    * $Date: 2002/11/23 11:09:57 $
4    * $Author: uwe_guenther $
5    * $State: Exp $
6    *
7    * Created on December 1, 2001 1:36 PM
8    *
9    * Copyright (C) 2001 Uwe Guenther <uwe@cscc.de>
10   *
11   * This file is part of the jhbci JCE-ServiceProvider. The jhbci JCE-
12   * ServiceProvider is a library, written in JavaTM, that should be
13   * used in HBCI banking applications (clients and may be servers),
14   * to do cryptographic operations.
15   *
16   * The jhbci library is free software; you can redistribute it and/or
17   * modify it under the terms of the GNU Lesser General Public
18   * License as published by the Free Software Foundation; either
19   * version 2.1 of the License, or (at your option) any later version.
20   *
21   * The jhbci library is distributed in the hope that it will be useful,
22   * but WITHOUT ANY WARRANTY; without even the implied warranty of
23   * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
24   * Lesser General Public License for more details.
25   *
26   * You should have received a copy of the GNU Lesser General Public
27   * License along with this library; if not, write to the Free Software
28   * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
29   *
30   */
31  
32  package de.cscc.crypto.provider;
33  
34  import java.math.BigInteger;
35  import java.security.AlgorithmParameters;
36  import java.security.InvalidAlgorithmParameterException;
37  import java.security.InvalidKeyException;
38  import java.security.Key;
39  import java.security.NoSuchAlgorithmException;
40  import java.security.SecureRandom;
41  import java.security.interfaces.RSAKey;
42  import java.security.interfaces.RSAPrivateCrtKey;
43  import java.security.interfaces.RSAPrivateKey;
44  import java.security.interfaces.RSAPublicKey;
45  import java.security.spec.AlgorithmParameterSpec;
46  
47  import javax.crypto.BadPaddingException;
48  import javax.crypto.Cipher;
49  import javax.crypto.IllegalBlockSizeException;
50  import javax.crypto.NoSuchPaddingException;
51  import javax.crypto.ShortBufferException;
52  
53  import de.cscc.crypto.util.BigIntegerUtil;
54  
55  /**
56   * RSACipherImpl Class.
57   *
58   * @author  <a href=mailto:uwe@cscc.de>Uwe G&uuml;nther</a>
59   * @version $Revision: 1.5 $
60   */
61  final class RSACipherImpl {
62      
63      private SecureRandom random = new SecureRandom();
64      
65      private boolean initialized = false;
66      
67      private int opmode;
68      
69      /** RSAKey for the Cipher Object */
70      private RSAKey key;
71      
72      //not yet implemented (needed for update and doFinal)
73      private byte[] inputBuffer;
74      private int inputBufferCounter;
75      
76      RSACipherImpl() {}
77      
78      /**
79       * Returns a string representation of the object.
80       *
81       * @return  a string representation of the object.
82       */
83      public String toString() {
84          return "[RSA-Cipher]";
85      }
86      
87      /**
88       * Sets the mode of this cipher.
89       * @param mode the cipher mode.
90       * @throws NoSuchAlgorithmException if the requested cipher mode does not exist.
91       */
92      void setMode(String mode) throws NoSuchAlgorithmException {
93          throw new NoSuchAlgorithmException("No Feedback mode supported.");
94      }
95      
96      /**
97       * Sets the padding mechanism of this cipher.
98       *
99       * @param padding the padding mechanism.
100      * @throws NoSuchPaddingException if the requested padding mechanism does not exist.
101      */
102     void setPadding(String padding)
103     throws NoSuchPaddingException {
104         throw new NoSuchPaddingException("No Padding Supported.");
105     }
106     
107     /**
108      * Initializes this cipher with a key and a source of randomness.
109      *
110      * @param opmode the operation mode  of this cipher.
111      * This is one of the following:
112      * javax.crypto.Cipher.ENCRYPT_MODE
113      * javax.crypto.Cipher.DECRYPT_MODE
114      * javax.crypto.Cipher.WRAP_MODE
115      * javax.crypto.Cipher.UNWRAP_MODE
116      * @param key the key to encrypt or to decrypt.
117      * @param random the source of randomness.
118      * @throws InvalidKeyException if the given key is inappropriate for
119      * initializing this cipher, this means if the key does not match to
120      * the specific opmode.
121      * @throws NullPointerException if key is null.
122      */
123     void init(int opmode, Key key, SecureRandom random)
124     throws InvalidKeyException {
125         if (key == null) {
126             throw new NullPointerException("Prameter key is null.");
127         }
128         
129         //We use only keys of the JHBCI Provider, so if you want to
130         //use other keys,you have to convert them with the RSAKeyFactory.
131         //We do this, because other RSAKeys may be insecure.
132         if (opmode == Cipher.ENCRYPT_MODE) {
133             this.opmode = Cipher.ENCRYPT_MODE;
134             if (key instanceof RSAPublicKeyImpl) {
135                 this.key = (RSAKey) key;
136             } else {
137                 throw new InvalidKeyException(
138                 "Key does not match to opmode constant.");
139             }
140         } else if (opmode == Cipher.WRAP_MODE) {
141             this.opmode = Cipher.WRAP_MODE;
142             if (key instanceof RSAPublicKeyImpl) {
143                 this.key = (RSAKey) key;
144             } else {
145                 throw new InvalidKeyException(
146                 "Key does not match to opmode constant.");
147             }
148         } else if (opmode == Cipher.DECRYPT_MODE) {
149             this.opmode = Cipher.DECRYPT_MODE;
150             if (key instanceof RSAPrivateCrtKeyImpl) {
151                 this.key = (RSAKey) key;
152             } else if (key instanceof RSAPrivateKeyImpl) {
153                 this.key = (RSAKey) key;
154             } else {
155                 throw new InvalidKeyException(
156                 "Key does not match to opmode constant.");
157             }
158         } else if (opmode == Cipher.UNWRAP_MODE) {
159             this.opmode = Cipher.UNWRAP_MODE;
160             if (key instanceof RSAPrivateCrtKeyImpl) {
161                 this.key = (RSAKey) key;
162             } else if (key instanceof RSAPrivateKeyImpl) {
163                 this.key = (RSAKey) key;
164             } else {
165                 throw new InvalidKeyException(
166                 "Key does not match to opmode constant.");
167             }
168         } else {
169             throw new IllegalArgumentException("Illegal opmode constant.");
170         }
171         if (random != null) {
172             this.random = random;
173         }
174         this.initialized = true; //set to true before call engineGetBlockSize
175         
176         this.inputBuffer = new byte [getBlockSize()];
177         this.inputBufferCounter = 0;
178     }
179     
180     /**
181      * Initializes this cipher with a key, a set of algorithm parameters,
182      * and a source of randomness.
183      *
184      * @param opmode the operation mode  of this cipher.
185      * This is one of the following:
186      * javax.crypto.Cipher.ENCRYPT_MODE
187      * javax.crypto.Cipher.DECRYPT_MODE
188      * javax.crypto.Cipher.WRAP_MODE
189      * javax.crypto.Cipher.UNWRAP_MODE
190      * @param key the key to encrypt or to decrypt.
191      * @param params the algorithm paramter.
192      * @param random the source of randomness.
193      * @throws InvalidKeyException if the given key is inappropriate for
194      * initializing this cipher.
195      * @throws InvalidAlgorithmParameterException if params isn't null, because
196      * we don't support AlgorithmParameters.
197      */
198     void init(int opmode, Key key, AlgorithmParameters params,
199     SecureRandom random)
200     throws InvalidKeyException, InvalidAlgorithmParameterException {
201         if (params != null) {
202             throw new InvalidAlgorithmParameterException("Parameter params " +
203             "isn't null. We currently don't support AlgortihmParameters.");
204         }
205         init(opmode, key, random);
206     }
207     
208     /**
209      * Initializes this cipher with a key, a set of algorithm parameters,
210      * and a source of randomness.
211      *
212      * @param opmode the operation mode  of this cipher.
213      * This is one of the following:
214      * javax.crypto.Cipher.ENCRYPT_MODE
215      * javax.crypto.Cipher.DECRYPT_MODE
216      * javax.crypto.Cipher.WRAP_MODE
217      * javax.crypto.Cipher.UNWRAP_MODE
218      * @param key the key to encrypt or to decrypt.
219      * @param params the algorithm paramter.
220      * @param random the source of randomness.
221      * @throws InvalidKeyException if the given key is inappropriate for
222      * initializing this cipher.
223      * @throws InvalidAlgorithmParameterException if params isn't null, because
224      * we don't support AlgorithmParameterSpecs.
225      */
226     void init(int opmode, Key key, AlgorithmParameterSpec params,
227     SecureRandom random)
228     throws InvalidKeyException, InvalidAlgorithmParameterException {
229         if (params != null) {
230             throw new InvalidAlgorithmParameterException("Parameter params " +
231             "isn't null. We currently don't support AlgortihmParameterSpecs.");
232         }
233         init(opmode, key, random);
234     }
235     
236     /**
237      * Continues a multiple-part encryption or decryption operation
238      * (depending on how this cipher was initialized), processing another data
239      * part.
240      *
241      * @param input the input buffer.
242      * @param inputOffset the offset in input where the input starts.
243      * @param inputLen the input length.
244      * @throws IllegalStateException if the Cipher was not initialized, or you
245      * invoke this method in WRAP_MODE or UNWRAP_MODE.
246      * @return the new buffer with the result, or null if the
247      * underlying cipher is a block cipher and the input
248      * data is too short to result in a new block.
249      */
250     byte[] update(byte[] input, int inputOffset, int inputLen) {
251         if (this.initialized == false) {
252             throw new IllegalStateException("Cipher not initialized");
253         }
254         if (this.opmode == Cipher.ENCRYPT_MODE) {
255             //Put here code for encryption and remove the exception.
256             throw new UnsupportedOperationException(
257             "We support only Key wrap and Key unwrap.");            
258             
259         } else if (this.opmode == Cipher.DECRYPT_MODE) {
260             //Put here code for decryption and remove the exception.
261             throw new UnsupportedOperationException(
262             "We support only Key wrap and Key unwrap.");
263             
264         } else {
265             //Wrong opmode, we lose.
266             throw new IllegalStateException("Cipher has unappropriate " +
267             "opmode , to invoke this method.");        
268         }
269      }
270     
271     /**
272      * Continues a multiple-part encryption or decryption operation
273      * (depending on how this cipher was initialized), processing another data
274      * part.
275      *
276      * @param input the input buffer.
277      * @param inputOffset the offset in input where the input starts.
278      * @param inputLen the input length.
279      * @param output the buffer for the result.
280      * @param outputOffset the offset in output where the result is stored.
281      * @throws ShortBufferException if the given output buffer is too small to
282      * hold the result.
283      * @throws IllegalStateException if the Cipher was not initialized, or you
284      * invoke this method in WRAP_MODE or UNWRAP_MODE.
285      * @return the number of bytes stored in output
286      */
287     int update(byte[] input, int inputOffset, int inputLen,
288     byte[] output, int outputOffset)
289     throws ShortBufferException {
290         if (this.initialized == false) {
291             throw new IllegalStateException("Cipher not initialized");
292         }
293         if (this.opmode == Cipher.ENCRYPT_MODE) {
294             //Put here code for encryption and remove the exception.
295             throw new UnsupportedOperationException(
296             "We support only Key wrap and Key unwrap.");            
297             
298         } else if (this.opmode == Cipher.DECRYPT_MODE) {
299             //Put here code for decryption and remove the exception.
300             throw new UnsupportedOperationException(
301             "We support only Key wrap and Key unwrap.");
302             
303         } else {
304             //Wrong opmode, we lose.
305             throw new IllegalStateException("Cipher has unappropriate " +
306             "opmode , to invoke this method.");        
307         }
308     }
309     
310     
311     /**
312      * Encrypts or decrypts data in a single-part operation, or finishes a
313      * multiple-part operation.
314      *
315      * @param input the input buffer.
316      * @param inputOffset the offset in input where the input starts.
317      * @param inputLen the input length.
318      * @throws IllegalBlockSizeException if this cipher is a block cipher, no padding
319      * has been requested (only in encryption mode),
320      * and the total input length of the data processed
321      * by this cipher is not a multiple of block size.
322      * @throws BadPaddingException if this cipher is in decryption mode,
323      * and (un)padding has been requested,
324      * but the decrypted data is not bounded
325      * by the appropriate padding bytes.
326      * @throws IllegalStateException if the Cipher was not initialized, or you
327      * invoke this method in WRAP_MODE or UNWRAP_MODE.
328      * @return the new buffer with the finally result.
329      */
330     byte[] doFinal(byte[] input, int inputOffset, int inputLen)
331     throws IllegalBlockSizeException, BadPaddingException {
332         if (this.initialized == false) {
333             throw new IllegalStateException("Cipher not initialized");
334         }
335         if (this.opmode == Cipher.ENCRYPT_MODE) {
336             //Put here code for encryption and remove the exception.
337             throw new UnsupportedOperationException(
338             "We support only Key wrap and Key unwrap.");            
339             
340         } else if (this.opmode == Cipher.DECRYPT_MODE) {
341             //Put here code for decryption and remove the exception.
342             throw new UnsupportedOperationException(
343             "We support only Key wrap and Key unwrap.");
344             
345         } else {
346             //Wrong opmode, we lose.
347             throw new IllegalStateException("Cipher has unappropriate " +
348             "opmode , to invoke this method.");        
349         }
350     }
351     
352     
353     /**
354      * Encrypts or decrypts data in a single-part operation, or finishes a
355      * multiple-part operation.
356      *
357      * @param input the input buffer.
358      * @param inputOffset the offset in input where the input starts.
359      * @param inputLen the input length.
360      * @param output the buffer for the finally result.
361      * @param outputOffset the offset in output where the result is stored.
362      * @throws ShortBufferException if the given output buffer is too small to
363      * hold the result.
364      * @throws IllegalBlockSizeException if this cipher is a block cipher, no padding
365      * has been requested (only in encryption mode),
366      * and the total input length of the data processed
367      * by this cipher is not a multiple of block size.
368      * @throws BadPaddingException if this cipher is in decryption mode,
369      * and (un)padding has been requested,
370      * but the decrypted data is not bounded
371      * by the appropriate padding bytes.
372      * @throws IllegalStateException if the Cipher was not initialized, or you
373      * invoke this method in WRAP_MODE or UNWRAP_MODE.
374      * @return the number of bytes stored in output.
375      */
376     int doFinal(byte[] input, int inputOffset, int inputLen,
377     byte[] output, int outputOffset) throws ShortBufferException,
378     IllegalBlockSizeException, BadPaddingException {
379         if (this.initialized == false) {
380             throw new IllegalStateException("Cipher not initialized");
381         }
382         if (this.opmode == Cipher.ENCRYPT_MODE) {
383             //Put here code for encryption and remove the exception.
384             throw new UnsupportedOperationException(
385             "We support only Key wrap and Key unwrap.");            
386             
387         } else if (this.opmode == Cipher.DECRYPT_MODE) {
388             //Put here code for decryption and remove the exception.
389             throw new UnsupportedOperationException(
390             "We support only Key wrap and Key unwrap.");
391             
392         } else {
393             //Wrong opmode, we lose.
394             throw new IllegalStateException("Cipher has unappropriate " +
395             "opmode , to invoke this method.");        
396         }
397     }
398     
399     
400     /**
401      * Wrap a key.
402      *
403      * @param key the key to be wrapped.
404      * @throws NullpointerException if <code>key.getEncoded()</code> is null.
405      * This means the key does not support encoding.
406      * @throws IllegalBlockSizeException if the unsigned number representation
407      * of the encoded key is equal or greater than the modulus of the RSAKey.
408      * @throws InvalidKeyException if it is impossible or unsafe to wrap the
409      * key with this cipher (e.g., a hardware
410      * public key is being passed to a
411      * software-only cipher).
412      * @throws IllegalStateException if the Cipher was not initialized, or you
413      * invoke this method in ENCRYPT_MODE or DECRTYPT_MODE.
414      * @return the wrapped key.
415      */
416     byte[] wrap(Key key) throws IllegalBlockSizeException, InvalidKeyException {
417         if (this.initialized == false) {
418             throw new IllegalStateException("Cipher not initialized");
419         }
420         if (key == null) {
421             throw new NullPointerException("Prameter key is null.");
422         }
423         if (this.opmode == Cipher.WRAP_MODE) {
424             //DES1Key
425             if (key.getAlgorithm().equalsIgnoreCase("DES1Key")) {
426                 if (key.getFormat().equalsIgnoreCase("RAW")) {
427                     //throws NullPointerException or IllegalBlockSizeException
428                     return wrapKey(key);
429                 }
430                 //We are wrong...
431                 throw new InvalidKeyException("Key has inappropriate encoding.");
432             }
433             //DESede2Key
434             if (key.getAlgorithm().equalsIgnoreCase("DESede2Key")) {
435                 if(key.getFormat().equalsIgnoreCase("RAW")) {
436                     //throws NullPointerException or IllegalBlockSizeException
437                     return wrapKey(key);
438                 }
439                 //We are wrong...
440                 throw new InvalidKeyException("Key has inappropriate encoding.");
441             }
442             //DESede3Key
443             if (key.getAlgorithm().equalsIgnoreCase("DESede3Key")) {
444                 if(key.getFormat().equalsIgnoreCase("RAW")) {
445                     //throws NullPointerException or IllegalBlockSizeException
446                     return wrapKey(key);
447                 }
448                 //We are wrong...
449                 throw new InvalidKeyException("Key has inappropriate encoding.");
450             }
451             //We are wong... 
452             throw new InvalidKeyException("Key has inappropriate algorithm.");
453         }
454         //We are wrong... (default case - shouldn't really happen!)
455         //If we are ditching here, there are something wrong with initialization.
456         throw new IllegalStateException("Cipher contains an unappropriate " +
457         "opmode to invoke this method.");
458     }
459     
460     /**
461      * Wraps a key.
462      *
463      * @throws NullpointerException if <code>key.getEncoded()</code> is null.
464      * This means the key does not support encoding.
465      * @throws IllegalBlockSizeException if the unsigned number representation
466      * of the encoded key is equal or greater than the modulus of the RSAKey.
467      * @return the wrapped key as byte array with an unsigned number in binary
468      * representation.
469      */
470     private byte[] wrapKey(Key key) throws IllegalBlockSizeException {
471         byte[] encodedKey = key.getEncoded();
472         if (encodedKey == null) {
473             throw new NullPointerException("Key does not support encoding.");
474         }
475         //Convert the binary representation of the key in non negative number.
476         BigInteger keyAsNumber = new BigInteger(1, encodedKey);
477         BigInteger modulus = this.key.getModulus();
478         // keyAsNumber >= modulus
479         if (keyAsNumber.compareTo(modulus) >= 0) {
480             throw new IllegalBlockSizeException("Keys encoding data to large " +
481             "for the modulus.");
482         }
483         //Do RSA encryption: wrappedKey = keyAsNumber^publicExponent mod modulus
484         BigInteger publicExponent = ((RSAPublicKey) this.key).getPublicExponent();
485         return BigIntegerUtil.toUnsignedByteArray(
486         keyAsNumber.modPow(publicExponent, modulus));
487     }
488     
489     /**
490      * Unwrap a previously wrapped key.
491      *
492      * @param wrappedKey the wrapped key to unwrap.
493      * @param wrappedKeyAlgorithm the algorithm associated with the key wrapped
494      * key.
495      * @param wrappedKeyType the type of the wrapped key. This is one of
496      * javax.crypto.Cipher.SECRET_KEY,
497      * javax.crypto.Cipher.PRIVATE_KEY,
498      * javax.crypto.Cipher.PUBLIC_KEY.
499      * @throws InvalidKeyException if wrappedKey does not represent a wrapped
500      * key, or if the algorithm associated with the wrapped key is different
501      * from wrappedKeyAlgorithm and/or its key type is different from
502      * wrappedKeyType.
503      * @throws NoSuchAlgorithmException if no installed providers can create
504      * keys for the wrappedKeyAlgorithm.
505      * @throws IllegalStateException if the Cipher was not initialized.
506      * @return the unwrapped key.
507      */
508     Key unwrap(byte[] wrappedKey, String wrappedKeyAlgorithm, int wrappedKeyType)
509     throws InvalidKeyException, NoSuchAlgorithmException {
510         if (this.initialized == false) {
511             throw new IllegalStateException("Cipher not initialized");
512         }
513         if (wrappedKey == null) {
514             throw new NullPointerException("Prameter wrappedKey is null.");
515         }
516         if (wrappedKeyAlgorithm == null) {
517             throw new NullPointerException("Prameter wrappedKeyAlgorithm is " +
518             "null.");
519         }
520         if (wrappedKeyType != Cipher.SECRET_KEY) {
521             throw new InvalidKeyException("Unsupported wrapped key type.");
522         }
523         if (this.opmode == Cipher.UNWRAP_MODE) {
524             if (wrappedKeyType == Cipher.SECRET_KEY) {
525                 if (wrappedKeyAlgorithm.equalsIgnoreCase("DES1Key")) {
526                     byte[] unwrappedKey = unwrapKey(wrappedKey);
527                     //This check is addressed to the attack described by Dr. Ulrich Kuehn (many thanks!)
528                     if (unwrappedKey.length > 8) {
529                         throw new InvalidKeyException("Unwrapped DES1Key is longer than 64 bit.");
530                     }
531                     return new DES1KeySecretKeyImpl(unwrappedKey);
532                 } 
533                 if (wrappedKeyAlgorithm.equalsIgnoreCase("DESede2Key")) {
534                     byte[] unwrappedKey = unwrapKey(wrappedKey);
535                     //This check is addressed to the attack described by Dr. Ulrich Kuehn (many thanks!)
536                     if (unwrappedKey.length > 16) {
537                         throw new InvalidKeyException("Unwrapped DESede2Key is longer than 128 bit.");
538                     }                   
539                     return new DESede2KeySecretKeyImpl(unwrappedKey);
540                 } 
541                 if (wrappedKeyAlgorithm.equalsIgnoreCase("DESede3Key")) {
542                     byte[] unwrappedKey = unwrapKey(wrappedKey);
543                     //This check is addressed to the attack described by Dr. Ulrich Kuehn (many thanks!)
544                     if (unwrappedKey.length > 24) {
545                         throw new InvalidKeyException("Unwrapped DESede3Key is longer than 168 bit.");
546                     }                   
547                     return new DESede3KeySecretKeyImpl(unwrapKey(wrappedKey));
548                 } 
549                 //We are wrong...
550                 throw new NoSuchAlgorithmException("Requested wrapped key " + 
551                 "algorithm not supported by the requested wrapped key type.");
552             } if (wrappedKeyType == Cipher.PUBLIC_KEY) {
553                 //
554                 //put here code for Public Key unwrapping
555                 //
556                 //We don't support public key unwrapping
557                 throw new InvalidKeyException("Requested wrapped key type " +
558                 "not supported.");
559             } if (wrappedKeyType == Cipher.PRIVATE_KEY) {
560                 //
561                 //put here code for Public Key unwrapping
562                 //
563                 //We don't support private key unwrapping
564                 throw new InvalidKeyException("Requested wrapped key type " +
565                 "not supported.");
566             } 
567             //We are wrong...
568             throw new IllegalArgumentException("Cipher contains an unknown " +
569             "wrapped key type.");
570         }
571         //We are wrong... (default case - shouldn't really happen!)
572         //If we are ditching here, there are something wrong with initialization.        
573         throw new IllegalStateException("Cipher contains an unappropriate " +
574         "opmode to invoke this method.");
575     }
576     
577     
578     /**
579      * Unwrap (decrypt) a wrappedKey in a byte array.
580      *
581      * @param wrapped key the wrapped key in its binary representation.
582      * @throws InvalidKeyException if the unsigned BigInteger representation of
583      * the wrappedKey byte array is equals or greater than the modulus of the
584      * RSAKey.
585      * @throws IllegalStateException if the internal key of the Cipher
586      * is inappropriate (Should not happen).
587      * @return the unwrapped key in its binary representation.
588      */
589     private byte[] unwrapKey(byte[] wrappedKey) throws InvalidKeyException {
590         BigInteger wrappedKeyAsNumber = new BigInteger(1, wrappedKey);
591         BigInteger modulus = this.key.getModulus();
592         if (wrappedKeyAsNumber.compareTo(modulus) >= 0) {
593             throw new InvalidKeyException("Wrapped key data data to " +
594             "large for the modulus.");
595         }
596         //Chinese remainder theorem RSA decryption
597         if (this.key instanceof RSAPrivateCrtKey) {
598             BigInteger primeP =
599             ((RSAPrivateCrtKey) this.key).getPrimeP();
600             BigInteger primeQ =
601             ((RSAPrivateCrtKey) this.key).getPrimeQ();
602             BigInteger primeExponentP =
603             ((RSAPrivateCrtKey) this.key).getPrimeExponentP();
604             BigInteger primeExponentQ =
605             ((RSAPrivateCrtKey) this.key).getPrimeExponentQ();
606             BigInteger crtCoefficient =
607             ((RSAPrivateCrtKey) this.key).getCrtCoefficient();
608             
609             BigInteger messageOne =
610             wrappedKeyAsNumber.modPow(primeExponentP, primeP);
611             BigInteger messageTwo =
612             wrappedKeyAsNumber.modPow(primeExponentQ, primeQ);
613             BigInteger h =
614             messageOne.subtract(messageTwo).multiply(crtCoefficient).mod(primeP);
615             BigInteger message = primeQ.multiply(h).add(messageTwo);
616             
617             byte[] unWrappedKey = BigIntegerUtil.toUnsignedByteArray(message);
618             return unWrappedKey;
619         }
620         //original RSA decryption
621         if (this.key instanceof RSAPrivateKey) {
622             BigInteger privateExponent =
623             ((RSAPrivateKey) this.key).getPrivateExponent();
624             BigInteger message =
625             wrappedKeyAsNumber.modPow(privateExponent, modulus);
626             byte[] unWrappedKey = BigIntegerUtil.toUnsignedByteArray(message);
627             return unWrappedKey;
628         }
629         //We are wrong... (default case - shouldn't really happen!)
630         //If we are ditching here, there are something wrong with initialization.        
631         throw new IllegalStateException("Cipher contains inappropriate Key.");
632     }
633     
634     /**
635      * Returns the block size (in bytes) that can be processed while a
636      * multipart cipher operation.
637      *
638      * @throws IllegalStateException if the Cipher was not initialized or a
639      * opmode does not match.
640      * @return the input block size (in bytes).
641      */
642     int getBlockSize() {
643         //Input Block Size
644         if (this.initialized == false) {
645             throw new IllegalStateException("Cipher not initialized");
646         }
647         //Encryption
648         if (this.opmode == Cipher.ENCRYPT_MODE) {
649             return (this.key.getModulus().bitLength() - 1) / 8;
650         }
651         if (this.opmode == Cipher.WRAP_MODE) {
652             return (this.key.getModulus().bitLength() - 1) / 8;
653         }
654         //Decryption
655         if (this.opmode == Cipher.DECRYPT_MODE) {
656             return (this.key.getModulus().bitLength() + 7) / 8;
657         }
658         if (this.opmode == Cipher.UNWRAP_MODE) {
659             return (this.key.getModulus().bitLength() + 7) / 8;
660         }
661         //We are wrong...
662         throw new IllegalStateException("Unknown opmode.");    
663     }
664 
665     /**
666      * Returns the length in bytes that an output buffer would need to be in
667      * order to hold the result of the next update or doFinal operation, given
668      * the input length inputLen (in bytes).
669      *
670      * @param inputLen the input length in bytes.
671      * @throws IllegalStateException if the Cipher was not initialized or a
672      * opmode does not match.
673      * @return the reqired output buffer size in bytes.
674      */
675     int getOutputSize(int inputLen) {
676         /*
677          * In our case the output size is independend of the input length,
678          * because our ouput blocks have always a length that depends on the 
679          * modulus bit length.
680          */
681         
682         //Output Block Size
683         if (this.initialized == false) {
684             throw new IllegalStateException("Cipher not initialized");
685         }
686         //Encryption
687         if (this.opmode == Cipher.ENCRYPT_MODE) {
688             return (this.key.getModulus().bitLength() + 7) / 8;
689         }
690         if (this.opmode == Cipher.WRAP_MODE) {
691             return (this.key.getModulus().bitLength() + 7) / 8;
692         }
693         //Decryption
694         if (this.opmode == Cipher.DECRYPT_MODE) {
695             return (this.key.getModulus().bitLength() - 1) / 8;
696         }
697         if (this.opmode == Cipher.UNWRAP_MODE) {
698             return (this.key.getModulus().bitLength() - 1) / 8;
699         }
700         //We are wrong...
701         throw new IllegalStateException("Unknown opmode.");   
702     }
703 
704     /**
705      * Returns the key size of the given key object in bit. This method will be
706      * invoked trough the JCE before engineInit is invoked.
707      *
708      * @param key the key object.
709      * @throws InvalidKeyException if key is invalid.
710      * @return the key size of the given key object.
711      */
712     int getKeySize(Key key) throws InvalidKeyException {
713         //We check only keys of the JHBCI Provider, so if you want to
714         //use other keys,you have to convert them with the RSAKeyFactory.
715         //We do this, because other RSAKeys may be insecure.
716         if (key instanceof RSAPublicKeyImpl) {
717             return ((RSAKey) key).getModulus().bitLength();
718         }
719         if (key instanceof RSAPrivateKeyImpl) {
720             return ((RSAKey) key).getModulus().bitLength();
721         }
722         if (key instanceof RSAPrivateCrtKeyImpl) {
723             return ((RSAKey) key).getModulus().bitLength();
724         }
725         //We are wrong...
726         throw new InvalidKeyException("Inappropriate Key.");
727     }    
728     
729     /** 
730      * Returns the parameters used with this cipher.
731      *
732      * @throws IllegalStateException if the Cipher was not initialized.
733      * @return the parameters used with this cipher,
734      * or null if this cipher does not use
735      * any parameters.
736      */
737     AlgorithmParameters getParameters() {
738         if (this.initialized == false) {
739             throw new IllegalStateException("Cipher not initialized");
740         }
741         return null;
742     }
743     
744     /**
745      * Returns the initialization vector (IV) in a new buffer.
746      *
747      * @throws IllegalStateException if the Cipher was not initialized.
748      * @return the initialization vector in a new buffer,
749      * or null if the underlying algorithm does not
750      * use an IV, or if the IV has not yet been set.
751      */
752     byte[] getIV() {
753         if (this.initialized == false) {
754             throw new IllegalStateException("Cipher not initialized");
755         }
756         return null;
757     }
758 }
759