1   /* $RCSfile: DESBaseCipherImpl.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 January 2, 2002 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.security.AlgorithmParameters;
35  import java.security.InvalidAlgorithmParameterException;
36  import java.security.InvalidKeyException;
37  import java.security.InvalidParameterException;
38  import java.security.Key;
39  import java.security.NoSuchAlgorithmException;
40  import java.security.SecureRandom;
41  import java.security.spec.AlgorithmParameterSpec;
42  
43  import javax.crypto.BadPaddingException;
44  import javax.crypto.Cipher;
45  import javax.crypto.IllegalBlockSizeException;
46  import javax.crypto.NoSuchPaddingException;
47  import javax.crypto.SecretKey;
48  import javax.crypto.ShortBufferException;
49  
50  import de.cscc.crypto.provider.spec.DESOperationModeInitializationVectorSpec;
51  
52  /** 
53   * DESBaseCipherImpl Class.
54   *
55   * @author  <a href=mailto:uwe@cscc.de>Uwe G&uuml;nther</a>
56   * @version $Revision: 1.5 $
57   */
58  class DESBaseCipherImpl {
59      
60      /** 
61       * Internal opmode field. Takes values as
62       * Cipher.ENCRYPT_MODE, Cipher.DECRYPT_MODE,
63       * Cipher.WRAP_MODE, Cipher.UNWRAP_MODE.
64       */    
65      int opmode;
66      
67      /** 
68       * Holds a reference to a concret
69       * subclass of DESCoreBlockCipher. This is the
70       * algorithm object that does the cipher work.
71       *
72       */    
73      DESCoreBlockCipher cipher;
74      
75      /** 
76       * Holds a reference to a concret
77       * subclass of OperationMode. This is the
78       * algorithm object that does the operation
79       * mode work.
80       */    
81      OperationMode mode;
82      
83      /** 
84       * Holds a reference to a concret
85       * subclass of Padding. This is the
86       * algorithm object that does the padding work.
87       */    
88      Padding padding;
89  
90      /** 
91       * Contains the used cipher algorithm name,
92       * such as "DES1Key", "DESede2Key", "DESede3Key".
93       */    
94      String cipherAlgorithm;
95      
96      /** 
97       * Contains the used operation mode algorithm name,
98       * such as CBC or ECB.
99       */    
100     String modeAlgorithm = new String("CBC");
101     
102     /** 
103      * Contains the used padding algorithm name,
104      * such as ISO10126OctetPadding or NoPadding.
105      */    
106     String paddingAlgorithm = new String("ISO10126OctetPadding");
107     
108     /** 
109      * Don't create an instance from these class. Should only be invoke from a 
110      * subclass in the same package.
111      */    
112     DESBaseCipherImpl() {}
113     
114     /** 
115      * Returns a string representation of the object. 
116      * @return  a string representation of the object.
117      */
118     public String toString() {
119         
120         String returnValue ="";
121         
122         returnValue += "opmode: [" + this.opmode + "]";
123         returnValue += "cipher: [" + this.cipher + "]";
124         returnValue += "mode: [" + this.mode + "]";
125         returnValue += "padding: [" + this.padding + "]";
126         returnValue += "cipherAlgorithm: [" + this.cipherAlgorithm + "]";
127         returnValue += "modeAlgorithm: [" + this.modeAlgorithm + "]";
128         returnValue += "paddingAlgorithm: [" + this.paddingAlgorithm + "]";        
129         
130         return returnValue;
131     }      
132     
133     /** 
134      * Sets the mode of this cipher.
135      * @param mode the cipher mode.
136      * @throws NoSuchAlgorithmException if the requested cipher mode does not exist.
137      */
138     void setMode(String mode) throws NoSuchAlgorithmException {
139         if (mode.equalsIgnoreCase("ECB")) {
140             this.modeAlgorithm = "ECB";            
141             
142         } else if (mode.equalsIgnoreCase("CBC")) {
143             this.modeAlgorithm = "CBC";
144             
145         } else {
146             throw new NoSuchAlgorithmException ("Operation mode " + mode 
147                     + " not supported.");
148         }
149     }
150     
151     /** 
152      * Sets the padding mechanism of this cipher.
153      * @param padding the padding mechanism.
154      * @throws NoSuchPaddingException if the requested padding mechanism does not exist.
155      */
156     void setPadding(String padding) throws NoSuchPaddingException {
157         if (padding.equalsIgnoreCase("NoPadding")) {
158             this.paddingAlgorithm = "NoPadding";
159             
160         } else if (padding.equalsIgnoreCase("ISO10126OctetPadding")) {
161             this.paddingAlgorithm = "ISO10126OctetPadding";
162             
163         } else {
164             throw new NoSuchPaddingException("Padding mechanism " + padding 
165                     + " not supported.");
166         }
167     }
168     
169     /** 
170      * Check if given opmode is valid, if not
171      * it throws a InvalidParamterException.
172      * @param opmode the given opmode to check.
173      * @throws InvalidParameterException if the given opmode isn't valid.
174      */    
175     private void checkOpmode(int opmode) throws InvalidParameterException {
176         if (opmode != Cipher.ENCRYPT_MODE 
177                 && opmode != Cipher.DECRYPT_MODE 
178                 && opmode != Cipher.WRAP_MODE 
179                 && opmode != Cipher.UNWRAP_MODE) {
180                 
181             throw new InvalidParameterException("Wrong opmode: ENCRYPT_MODE, "
182                     + "DECRYPT_MODE, WRAP_MODE or UNWRAP_MODE expected.");
183         }
184         
185         return;
186     }
187     
188     /** 
189      * Check if given key is valid, if not
190      * it throws a InvalidParamterException.
191      * @param key the given key to check.
192      * @throws InvalidKeyException if the given key isn't valid.
193      */    
194     private void checkKey(Key key) throws InvalidKeyException {
195         if (key == null){
196             throw new InvalidKeyException("No key given.");
197         }
198         if ((key instanceof SecretKey) == false){
199             throw new InvalidKeyException(
200                     "Wrong Key: SecretKey expected.");
201         }
202         if (key.getAlgorithm().equalsIgnoreCase(this.cipherAlgorithm) == false){
203             throw new InvalidKeyException(
204                     "Wrong Algorithm: " + this.cipherAlgorithm + " required.");            
205         }
206         if (key.getFormat().equalsIgnoreCase("RAW") == false){
207             throw new InvalidKeyException("Wrong Format: RAW required.");                    
208         }
209         if (key.getEncoded().length != this.cipher.getKeyByteSize()){
210             throw new InvalidKeyException(
211                     "Wrong Length: " + 
212                     this.cipher.getKeyByteSize() + 
213                     " expected.");
214         }
215         
216         return;
217     }
218 
219     /** 
220      * Generates a new valid Iv from a given random source.
221      * @param random the random source.
222      * @return a valid Iv (byte[8]).
223      */    
224     private byte[] generateIv(SecureRandom random) {
225         if (random == null){
226             random = new SecureRandom();
227         }
228 
229         byte[] returnValue = new byte[8];
230         random.nextBytes(returnValue);
231         return returnValue;
232     }
233     
234     /** Initializes this cipher with a key and a source of randomness.
235      * @param opmode the operation mode  of this cipher.
236      * This is one of the following:
237      * javax.crypto.Cipher.ENCRYPT_MODE
238      * javax.crypto.Cipher.DECRYPT_MODE
239      * javax.crypto.Cipher.WRAP_MODE
240      * javax.crypto.Cipher.UNWRAP_MODE
241      * @param key the key to encrypt or to decrypt.
242      * @param random the source of randomness.
243      * @throws InvalidKeyException if the given key is inappropriate for 
244      * initializing this cipher, or if this cipher is being initialized
245      * for decryption and requires algorithm parameters that cannot be 
246      * determinated from the given key.
247      */
248     void init(int opmode, Key key, SecureRandom random) 
249             throws InvalidKeyException {
250         //opmode
251         checkOpmode(opmode);
252         this.opmode = opmode;
253 
254         //key
255         checkKey(key);
256         try{
257             if (this.cipherAlgorithm.equals("DES1Key")) {
258                 this.cipher =new DESCore1KeyBlockCipher(key.getEncoded());
259                 
260             } else if (this.cipherAlgorithm.equals("DESede2Key")) {
261                 this.cipher =new DESCoreEde2KeyBlockCipher(key.getEncoded());
262                 
263             } else if (this.cipherAlgorithm.equals("DESede3Key")) {
264                 this.cipher =new DESCoreEde3KeyBlockCipher(key.getEncoded());
265             }
266             
267         } catch (IllegalBlockSizeException cannothappen){
268             throw new Error("Can not happen.", cannothappen);
269         }
270 
271         //OperationMode
272         if (this.modeAlgorithm.equals("ECB")) {
273             this.mode = new OperationModeECB(this.cipher);
274             
275         } else if (this.modeAlgorithm.equals("CBC")) {
276             if (this.opmode == Cipher.ENCRYPT_MODE 
277                     || this.opmode == Cipher.WRAP_MODE) {
278                 this.mode = new OperationModeCBC(this.cipher);
279                 try{
280                     this.mode.setIv(generateIv(random));
281                 } catch (IllegalBlockSizeException cannothappen) {
282                     throw new Error("Can not happen.", cannothappen);
283                 }            
284             
285             } else if (this.opmode == Cipher.DECRYPT_MODE 
286                     || this.opmode == Cipher.UNWRAP_MODE) {
287 
288                 throw new InvalidKeyException("Need Iv for CBC DECRYPTION_MODE "
289                         + "or UNWRAP_MODE.");
290             }
291         }    
292 
293         //padding
294         if (this.paddingAlgorithm.equals("ISO10126OctetPadding")) {
295             this.padding = new PaddingISO10126OctetPadding(this.mode);
296             
297             
298         } else if (this.paddingAlgorithm.equals("NoPadding")){
299             this.padding = new PaddingNoPadding(this.mode);
300             
301         }              
302 
303     }
304     
305     /** 
306      * Initializes this cipher with a key, a set of algorithm parameters,
307      * and a source of randomness.
308      * @param opmode the operation mode  of this cipher.
309      * This is one of the following:
310      * javax.crypto.Cipher.ENCRYPT_MODE
311      * javax.crypto.Cipher.DECRYPT_MODE
312      * javax.crypto.Cipher.WRAP_MODE
313      * javax.crypto.Cipher.UNWRAP_MODE
314      * @param key the key to encrypt or to decrypt.
315      * @param params the algorithm paramter.
316      * @param random the source of randomness.
317      * @throws InvalidKeyException if the given key is inappropriate for initializing
318      * this cipher.
319      * @throws InvalidAlgorithmParameterException if the given algorithm parameters are inappropriate
320      * for this cipher, or if this cipher is being
321      * initialized for decryption and requires algorithm
322      * parameters and params is null.
323      */
324     void init(int opmode, Key key, 
325             AlgorithmParameters params, SecureRandom random) 
326             throws InvalidKeyException, InvalidAlgorithmParameterException {
327                 
328         throw new InvalidAlgorithmParameterException("Don't support "
329                 + "AlogrithmParameters, use AlgorithmParamterSpec.");
330     }
331     
332     /** 
333      * Initializes this cipher with a key, a set of algorithm parameters,
334      * and a source of randomness.
335      * @param opmode the operation mode  of this cipher.
336      * This is one of the following:
337      * javax.crypto.Cipher.ENCRYPT_MODE
338      * javax.crypto.Cipher.DECRYPT_MODE
339      * javax.crypto.Cipher.WRAP_MODE
340      * javax.crypto.Cipher.UNWRAP_MODE
341      * @param key the key to encrypt or to decrypt.
342      * @param params the algorithm paramter.
343      * @param random the source of randomness.
344      * @throws InvalidKeyException if the given key is inappropriate for initializing
345      * this cipher.
346      * @throws InvalidAlgorithmParameterException if the given algorithm parameters are inappropriate
347      * for this cipher, or if this cipher is being
348      * initialized for decryption and requires algorithm
349      * parameters and params is null.
350      */    
351     void init(int opmode, Key key, 
352             AlgorithmParameterSpec params, SecureRandom random) 
353             throws InvalidKeyException, InvalidAlgorithmParameterException {
354                 
355         //opmode
356         checkOpmode(opmode);
357         this.opmode = opmode;
358 
359         //key
360         checkKey(key);
361         try{
362             if (this.cipherAlgorithm.equals("DES1Key")) {
363                 this.cipher =new DESCore1KeyBlockCipher(key.getEncoded());
364                    
365             } else if (this.cipherAlgorithm.equals("DESede2Key")) {
366                 this.cipher =new DESCoreEde2KeyBlockCipher(key.getEncoded());
367                 
368             } else if (this.cipherAlgorithm.equals("DESede3Key")) {
369                 this.cipher =new DESCoreEde3KeyBlockCipher(key.getEncoded());
370             }
371             
372         } catch (IllegalBlockSizeException cannothappen) {
373             throw new Error("Can not happen.", cannothappen);
374         }
375         
376         //OperationMode
377         if (this.modeAlgorithm.equals("ECB")) {
378             this.mode = new OperationModeECB(this.cipher);
379             
380         } else if (this.modeAlgorithm.equals("CBC")) {
381             if (params == null) {
382                 if (this.opmode == Cipher.ENCRYPT_MODE || 
383                     this.opmode == Cipher.WRAP_MODE) {
384                     
385                     this.mode = new OperationModeCBC(this.cipher);
386                     
387                     try{
388                         this.mode.setIv(generateIv(random));
389                     } catch (IllegalBlockSizeException cannothappen) {
390                         throw new Error("Can not happen.", cannothappen);
391                     }            
392                     
393                 } else if (this.opmode == Cipher.DECRYPT_MODE || 
394                            this.opmode == Cipher.UNWRAP_MODE) {
395                     
396                     throw new InvalidAlgorithmParameterException("No algorithm "
397                     + "paramter spec given.");
398                 }
399                 
400             } else if (params instanceof DESOperationModeInitializationVectorSpec){
401                
402                 this.mode = new OperationModeCBC(this.cipher);
403                 
404                 try{
405                     this.mode.setIv(
406                     ((DESOperationModeInitializationVectorSpec) params).getIv());
407                 } catch (IllegalBlockSizeException cannothappen) {
408                     throw new Error("Can not happen.", cannothappen);
409                 }
410                                     
411             } else {
412                 
413                 throw new InvalidAlgorithmParameterException("Wrong Type: "
414                 + "de.cscc.crypto.spec.DESOperationModeInitialization"
415                 + "VectorSpec expected.");            
416             }
417         }    
418         
419         //Padding
420         if (this.paddingAlgorithm.equals("ISO10126OctetPadding")) {
421             this.padding = new PaddingISO10126OctetPadding(this.mode);
422             
423         } else if (this.paddingAlgorithm.equals("NoPadding")) {
424             this.padding = new PaddingNoPadding(this.mode);
425             
426         }    
427     }
428     
429     /** 
430      * Continues a multiple-part encryption or decryption operation
431      * (depending on how this cipher was initialized), processing another data
432      * part.
433      * @param input the input buffer.
434      * @param inputOffset the offset in input where the input starts.
435      * @param inputLen the input length.
436      * @return the new buffer with the result, or null if the
437      * underlying cipher is a block cipher and the input
438      * data is too short to result in a new block.
439      */
440     byte[] update(byte[] input, int inputOffset, int inputLen) {
441         if (this.opmode == Cipher.ENCRYPT_MODE) {
442             return this.padding.updateEncryption(input, inputOffset, inputLen);
443         }
444         if (this.opmode == Cipher.DECRYPT_MODE) {
445             return this.padding.updateDecryption(input, inputOffset, inputLen);
446         }
447         
448         //If the cipher initialized for WRAP_MODE or UNWRAP_MODE do nothing.
449         return new byte[0];        
450     }
451     
452     /** 
453      * Continues a multiple-part encryption or decryption operation
454      * (depending on how this cipher was initialized), processing another data
455      * part.
456      * @param input the input buffer.
457      * @param inputOffset the offset in input where the input starts.
458      * @param inputLen the input length.
459      * @param output the buffer for the result.
460      * @param outputOffset the offset in output where the result is stored.
461      * @throws ShortBufferException if the given output buffer is too small to
462      * hold the result.
463      * @return the number of bytes stored in output
464      */    
465     int update(byte[] input, int inputOffset, int inputLen, 
466             byte[] output, int outputOffset) throws ShortBufferException {
467         if (this.opmode == Cipher.ENCRYPT_MODE) {
468             return this.padding.updateEncryption(input,  inputOffset, inputLen,
469                     output, outputOffset);
470         }
471         if (this.opmode == Cipher.DECRYPT_MODE) {
472             return this.padding.updateDecryption(input,  inputOffset, inputLen,
473                     output, outputOffset);
474         }
475         
476         //If the cipher initialized for WRAP_MODE or UNWRAP_MODE do nothing.
477         return 0;
478     }
479     
480     
481     /** Encrypts or decrypts data in a single-part operation, or finishes a
482      * multiple-part operation.
483      * @param input the input buffer.
484      * @param inputOffset the offset in input where the input starts.
485      * @param inputLen the input length.
486      * @throws IllegalBlockSizeException if this cipher is a block cipher, no padding
487      * has been requested (only in encryption mode),
488      * and the total input length of the data processed
489      * by this cipher is not a multiple of block size.
490      * @throws BadPaddingException if this cipher is in decryption mode,
491      * and (un)padding has been requested,
492      * but the decrypted data is not bounded
493      * by the appropriate padding bytes.
494      * @return the new buffer with the finally result.
495      */
496     byte[] doFinal(byte[] input, int inputOffset, int inputLen) 
497             throws IllegalBlockSizeException, BadPaddingException {
498         if (this.opmode == Cipher.ENCRYPT_MODE) {
499             return this.padding.doFinalEncryption(input, inputOffset, inputLen);
500         }
501         if (this.opmode == Cipher.DECRYPT_MODE) {
502             return this.padding.doFinalDecryption(input, inputOffset, inputLen);
503         }
504         
505         //If the cipher initialized for WRAP_MODE or UNWRAP_MODE do nothing.
506         return new byte[0];
507     }
508     
509     
510     /** 
511      * Encrypts or decrypts data in a single-part operation, or finishes a
512      * multiple-part operation.
513      * @param input the input buffer.
514      * @param inputOffset the offset in input where the input starts.
515      * @param inputLen the input length.
516      * @param output the buffer for the finally result.
517      * @param outputOffset the offset in output where the result is stored.
518      * @throws ShortBufferException if the given output buffer is too small to
519      * hold the result.
520      * @throws IllegalBlockSizeException if this cipher is a block cipher, no padding
521      * has been requested (only in encryption mode),
522      * and the total input length of the data processed
523      * by this cipher is not a multiple of block size.
524      * @throws BadPaddingException if this cipher is in decryption mode,
525      * and (un)padding has been requested,
526      * but the decrypted data is not bounded
527      * by the appropriate padding bytes.
528      * @return the number of bytes stored in output.
529      */    
530     int doFinal(byte[] input, int inputOffset, int inputLen, 
531             byte[] output, int outputOffset) 
532             throws ShortBufferException, IllegalBlockSizeException, 
533             BadPaddingException {
534         if (this.opmode == Cipher.ENCRYPT_MODE) {
535             return this.padding.doFinalEncryption(input,  inputOffset, inputLen,
536                     output, outputOffset);
537         }
538         if (this.opmode == Cipher.DECRYPT_MODE) {
539             return this.padding.doFinalDecryption(input,  inputOffset, inputLen,
540                     output, outputOffset);
541         }
542         
543         //If the cipher initialized for WRAP_MODE or UNWRAP_MODE do nothing.
544         return 0;
545     }
546     
547     /** 
548      * Returns the length in bytes that an output buffer would need to be in
549      * order to hold the result of the next update() or doFinal() operation, given
550      * the input length inputLen (in bytes).
551      * @param inputLen the input length in bytes.
552      * @return the reqired output buffer size in bytes.
553      */
554     int getOutputSize(int inputLen) {
555         if (this.opmode == Cipher.ENCRYPT_MODE) {
556             return this.padding.getDoFinalEncryptionOutputSize(inputLen);
557         }
558         if (this.opmode == Cipher.DECRYPT_MODE) {
559             return this.padding.getDoFinalDecryptionOutputSize(inputLen);
560         }
561         if (this.opmode == Cipher.WRAP_MODE) {
562             return 0;
563         }        
564         if (this.opmode == Cipher.UNWRAP_MODE) {
565             return 0;
566         }                
567         
568         //This case will never be true, because the JCE catches this case.
569         return 0;
570     }
571     
572     /** 
573      * Returns the parameters used with this cipher.
574      * @return the parameters used with this cipher,
575      * or null if this cipher does not use
576      * any parameters.
577      */
578     AlgorithmParameters getParameters() {
579         return null;
580     }
581     
582     /** 
583      * Returns the initialization vector (IV) in a new buffer.
584      * @return the initialization vector in a new buffer,
585      * or null if the underlying algorithm does not
586      * use an IV, or if the IV has not yet been set.
587      */
588     byte[] getIV() {
589         return this.mode.getIv();
590     }
591     
592     /** 
593      * Returns the block size (in bytes).
594      * @return the block size (in bytes), or 0 if the
595      * underlying algorithm is not a block cipher
596      */
597     int getBlockSize() {
598         return this.cipher.getBlockSize();
599     }
600     
601     
602     /** 
603      * Returns the key size of the given key object.
604      * @param key the key object.
605      * @throws InvalidKeyException if key is invalid.
606      * @return the key size of the given key object.
607      */
608     int getKeySize(Key key) throws InvalidKeyException {
609         checkKey(key);
610         
611         return this.cipher.getKeyBitSize();
612     }
613     
614     /** 
615      * Unwrap a previously wrapped key.
616      * @param wrappedKey the wrapped key to unwrap.
617      * @param wrappedKeyAlgorithm the algorithm associated with the key wrapped key.
618      * @param wrappedKeyType the type of the wrapped key. This is one of
619      * javax.crypto.Cipher.SECRET_KEY,
620      * javax.crypto.Cipher.PRIVATE_KEY,
621      * javax.crypto.Cipher.PUBLIC_KEY.
622      * @throws InvalidKeyException if wrappedKey does not represent a wrapped key,
623      * or if the algorithm associated with the wrapped
624      * key is different from wrappedKeyAlgorithm and/or
625      * its key type is different from wrappedKeyType.
626      * @throws NoSuchAlgorithmException if no installed providers can create
627      * keys for the wrappedKeyAlgorithm.
628      * @return the unwrapped key.
629      */
630     Key unwrap(byte[] wrappedKey, String wrappedKeyAlgorithm, 
631             int wrappedKeyType) 
632             throws InvalidKeyException, NoSuchAlgorithmException {
633         throw new UnsupportedOperationException("Don't support key unwrapping.");
634     }
635     
636     /** 
637      * Wrap a key.
638      * @param key the key to be wrapped.
639      * @throws IllegalBlockSizeException if this cipher is a block cipher,
640      * no padding has been requested, and the
641      * length of the encoding of the key to be
642      * wrapped is not a multiple of the block size.
643      * @throws InvalidKeyException if it is impossible or unsafe to wrap the
644      * key with this cipher (e.g., a hardware
645      * public key is being passed to a
646      * software-only cipher).
647      * @return the wrapped key.
648      */
649     byte[] wrap(Key key) 
650             throws IllegalBlockSizeException, InvalidKeyException {
651         throw new UnsupportedOperationException("Don't support key wrapping.");
652     }
653 }
654