1   /* $RCSfile: ISO9796Part1WithRSASignatureImpl.java,v $
2    * $Revision: 1.3 $
3    * $Date: 2002/11/23 11:09:56 $
4    * $Author: uwe_guenther $
5    * $State: Exp $
6    *
7    * Created on October 20, 2002 3:27 PM
8    *
9    * Copyright (C) 2002 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.PrivateKey;
38  import java.security.PublicKey;
39  import java.security.SecureRandom;
40  import java.security.SignatureException;
41  import java.security.interfaces.RSAKey;
42  import java.security.interfaces.RSAPrivateKey;
43  import java.security.interfaces.RSAPublicKey;
44  import java.security.spec.AlgorithmParameterSpec;
45  
46  /** 
47   * ISO9796Part1WithRSASignatureImpl Class.
48   *
49   * @author  <a href=mailto:uwe@cscc.de>Uwe G&uuml;nther</a>
50   *
51   * @version $Revision: 1.3 $
52   */
53  final class ISO9796Part1WithRSASignatureImpl implements Cloneable {
54   
55      /** The ISO9796Part1WithRSASignatureByteBuffer message buffer object. */
56      private ISO9796Part1WithRSASignatureByteBuffer mb = 
57      new ISO9796Part1WithRSASignatureByteBuffer();
58      
59      /** The RIPEMD160 ISO9796-1:1991 codec object. */
60      private ISO9796Part1RSACodec codec = new ISO9796Part1RSACodec();
61      
62      /** 
63       * The length of the signature in bits according to the modulus of the 
64       * RSA key.
65       */
66      private int ks;
67      
68      /** The object state variable. */
69      private State state = State.UNINITIALIZED;
70      
71      
72      /** Creates new ISO9796Part1WithRSASignatureImpl.  */
73      public ISO9796Part1WithRSASignatureImpl() {
74      }
75  
76      /**
77       * Creates and returns a copy of this object.  
78       *
79       * @return a clone of this instance.
80       * @throws CloneNotSupportedException  if the object's class does not
81       * support the <code>Cloneable</code> interface. Subclasses
82       * that override the <code>clone</code> method can also
83       * throw this exception to indicate that an instance cannot
84       * be cloned.
85       */
86      public Object clone() throws CloneNotSupportedException {
87          ISO9796Part1WithRSASignatureImpl result =
88          (ISO9796Part1WithRSASignatureImpl) super.clone();
89          result.mb = (ISO9796Part1WithRSASignatureByteBuffer) this.mb.clone();
90          result.codec = (ISO9796Part1RSACodec) this.codec.clone();
91          return result;
92      }
93      
94      /**
95       * Indicates whether some other object is "equal to" this one.
96       *
97       * @param   obj   the reference object with which to compare.
98       * @return  <code>true</code> if this object is the same as the obj
99       *          argument; <code>false</code> otherwise.
100      * @see     #hashCode()
101      * @see     java.util.Hashtable
102      */
103     public boolean equals(Object obj) {
104         //Only for performance.
105         if (this == obj) {
106             return true;
107         } 
108         
109         //If obj == null then instanceof returns false, see JLS 15.20.2
110         if (!(obj instanceof ISO9796Part1WithRSASignatureImpl)) {
111             return false;
112         }
113         
114         ISO9796Part1WithRSASignatureImpl other = 
115         (ISO9796Part1WithRSASignatureImpl)obj;
116         
117         return this.mb.equals(other.mb) &&
118                this.codec.equals(other.codec) &&
119                this.ks == other.ks &&
120                this.state == other.state;
121     }
122     
123     /**
124      * Returns a hash code value for the object. 
125      *
126      * @return  a hash code value for this object.
127      * @see     java.lang.Object#equals(java.lang.Object)
128      * @see     java.util.Hashtable
129      */
130     public int hashCode() {
131         int result = 17;
132         result = 37*result + this.mb.hashCode();
133         result = 37*result + this.codec.hashCode();
134         result = 37*result + this.ks;
135         result = 37*result + this.state.hashCode();
136         return result;
137     }
138     
139     /**
140      * Returns a string representation of the object. 
141      *
142      * @return  a string representation of the object.
143      */
144     public String toString() {
145         return "[ISO9796-1WithRSASignatureImpl - state: " + this.state + "]";
146     }
147     
148     /**
149      * Initializes this signature object with the specified
150      * private key for signing operations.
151      *
152      * @param privateKey the private key of the identity whose signature
153      * will be generated.
154      *
155      * @throws InvalidKeyException if the key is improperly encoded, parameters
156      * are missing, and so on.
157      */
158     void initSign(PrivateKey privateKey) throws InvalidKeyException {
159         if (privateKey == null) {
160             throw new NullPointerException("Parameter privateKey is null.");
161         }
162         if (privateKey instanceof RSAPrivateKey) {
163             this.ks = ((RSAKey) privateKey).getModulus().bitLength() - 1;
164         } else {
165             throw new InvalidKeyException("Inappropriate Key.");
166         }        
167         this.codec.initEncode(privateKey);
168         this.state = State.SIGN;
169         this.mb.reset();
170     }
171     
172     /**
173      * Initializes this signature object with the specified
174      * private key and source of randomness for signing operations.
175      *
176      * <p>This concrete method has been added to this previously-defined
177      * abstract class. (For backwards compatibility, it cannot be abstract.)
178      *
179      * @param privateKey the private key of the identity whose signature
180      * will be generated.
181      * @param random the source of randomness
182      *
183      * @throws InvalidKeyException if the key is improperly encoded, parameters 
184      * are missing, and so on.
185      */
186     void initSign(PrivateKey privateKey, SecureRandom random) 
187     throws InvalidKeyException {
188         initSign(privateKey);
189     }
190     
191     /**
192      * Initializes this signature object with the specified
193      * public key for verification operations.
194      *
195      * @param publicKey the public key of the identity whose signature is
196      * going to be verified.
197      *
198      * @throws InvalidKeyException if the key is improperly
199      * encoded, parameters are missing, and so on.
200      */
201     void initVerify(PublicKey publicKey) throws InvalidKeyException {
202         if (publicKey == null) {
203             throw new NullPointerException("Parameter publicKey is null.");
204         }
205         if (publicKey instanceof RSAPublicKey) {
206             this.ks = ((RSAKey) publicKey).getModulus().bitLength() - 1;
207         } else {
208             throw new InvalidKeyException("Inappropriate Key.");
209         }
210         this.codec.initDecode(publicKey);
211         this.state = State.VERIFY;
212         this.mb.reset();        
213     }
214     
215     /**
216      * Updates the data to be signed or verified
217      * using the specified byte.
218      *
219      * @param value the byte to use for the update.
220      *
221      * @throws SignatureException if the engine is not initialized
222      * properly.
223      */
224     void update(byte value) throws SignatureException {
225         if (this.state == State.UNINITIALIZED) {
226             throw new SignatureException("Signature is not initialized.");
227         }
228         this.mb.add(value);
229     }
230     
231     /**
232      * Updates the data to be signed or verified, using the
233      * specified array of bytes, starting at the specified offset.
234      *
235      * @param values the array of bytes
236      * @param offset the offset to start from in the array of bytes
237      * @param len the number of bytes to use, starting at offset
238      *
239      * @throws SignatureException if the engine is not initialized
240      * properly
241      * @throws NullPointerException if outbuf is null.
242      * @throws IllegalArgumentException if offset or len is negative, or the 
243      * sum of offset and len is greater than length of the values array.
244      */
245     void update(byte[] values, int offset, int len) throws SignatureException {
246         if (this.state == State.UNINITIALIZED) {
247             throw new SignatureException("Signature is not initialized.");
248         }
249         if (values == null) {
250             throw new NullPointerException("Parameter values is null.");
251         }
252         if (offset < 0) {
253             throw new IllegalArgumentException("Parameter offset is less " +
254             "than zero.");
255         }
256         if (len < 0) {
257             throw new IllegalArgumentException("Parameter len is less " +
258             "than zero.");        
259         }
260         if (offset + len > values.length) {
261             throw new IllegalArgumentException("Parameter offset + len is " +
262             "greater than outbuf.length .");
263         }        
264         this.mb.add(values, offset, len);
265     }
266     
267     /**
268      * Returns the signature bytes of all the data
269      * updated so far.
270      * The format of the signature depends on the underlying
271      * signature scheme.
272      *
273      * @return the signature bytes of the signing operation's result.
274      *
275      * @throws SignatureException if the engine is not initialized properly.
276      */
277     byte[] sign() throws SignatureException {
278         if (this.state != State.SIGN) {
279             throw new SignatureException("Signature is not in SIGN state.");
280         }  
281         if (this.mb.isEmpty()) {
282             throw new SignatureException("You have to add at least one byte " + 
283                     "to sign a message.");
284         }
285         
286         try {
287             
288             ISO9796Part1BitString signature =
289             this.codec.encodeMessage(this.mb.fetch(), false);
290 
291             return signature.getBitString();
292             
293         } finally {
294             //If something goes wrong we should reset the mb without fail!
295             //Also, if nothing goes wrong we should reset the mb too.
296             this.mb.reset();
297         }
298     }
299     
300     /**
301      * Finishes this signature operation and stores the resulting signature
302      * bytes in the provided buffer <code>outbuf</code>, starting at
303      * <code>offset</code>.
304      * The format of the signature depends on the underlying
305      * signature scheme.
306      *
307      * <p>The signature implementation is reset to its initial state
308      * (the state it was in after a call to one of the
309      * <code>engineInitSign</code> methods)
310      * and can be reused to generate further signatures with the same private
311      * key.
312      *
313      * This method should be abstract, but we leave it concrete for
314      * binary compatibility.  Knowledgeable providers should override this
315      * method.
316      *
317      * @param outbuf buffer for the signature result.
318      *
319      * @param offset offset into <code>outbuf</code> where the signature is
320      * stored.
321      *
322      * @param len number of bytes within <code>outbuf</code> allotted for the
323      * signature.
324      * Both this default implementation and the SUN provider do not
325      * return partial digests. If the value of this parameter is less
326      * than the actual signature length, this method will throw a
327      * SignatureException.
328      * This parameter is ignored if its value is greater than or equal to
329      * the actual signature length.
330      *
331      * @return the number of bytes placed into <code>outbuf</code>
332      *
333      * @throws SignatureException if an error occurs or <code>len</code>
334      * is less than the actual signature length.
335      * @throws NullPointerException if outbuf is null.
336      * @throws IllegalArgumentException if offset or len is negative, or the 
337      * sum of offset and len is greater than length of the outbuf array.
338      */
339     int sign(byte[] outbuf, int offset, int len) throws SignatureException {
340         if (this.state != State.SIGN) {
341             throw new SignatureException("Signature is not in SIGN state.");
342         }        
343         if (outbuf == null) {
344             throw new NullPointerException("Parameter outbuf is null.");
345         }
346         if (offset < 0) {
347             throw new IllegalArgumentException("Parameter offset is less " +
348             "than zero.");
349         }
350         if (len < 0) {
351             throw new IllegalArgumentException("Parameter len is less " +
352             "than zero.");        
353         }
354         if (offset + len > outbuf.length) {
355             throw new IllegalArgumentException("Parameter offset + len is " +
356             "greater than outbuf.length .");
357         }
358         if (((ks + 7) / 8) > len) {
359             throw new SignatureException("Existing space in outbuf is less "
360             + "than the signature length would be.");        
361         }
362 
363         //Calculate the Signature with sign()
364         byte[] result = sign();
365         
366         //Copy the Signature into the outbuf starting at offset.
367         System.arraycopy(result, 0, outbuf, offset, result.length);
368         
369         //Return the number of bytes placed into outbuf.
370         return result.length;
371     }    
372     
373     /**
374      * Verifies the passed-in signature.
375      *
376      * @param sigBytes the signature bytes to be verified.
377      *
378      * @return true if the signature was verified, false if not.
379      *
380      * @throws SignatureException if the engine is not initialized
381      * properly, or the passed-in signature is improperly encoded or
382      * of the wrong type, etc.
383      */
384     boolean verify(byte[] sigBytes) throws SignatureException {
385         if (this.state != State.VERIFY) {
386             throw new SignatureException("Signature is not in VERIFY state.");
387         }
388         if (sigBytes == null) {
389             throw new NullPointerException("Parameter signBytes is null.");
390         }
391         if (this.mb.isEmpty()) {
392             throw new SignatureException("You have to add at least one byte " + 
393                     "to verify a message.");
394         }        
395         
396         try {
397             ISO9796Part1BitString sigma = 
398             new ISO9796Part1BitString (sigBytes, this.ks);
399         
400             ISO9796Part1BitString recoveredMessageDigest = 
401             this.codec.decodeSignature(sigma);
402 
403             if (recoveredMessageDigest == null) {
404                 return false;
405                 
406             } else { //(message != null)
407                 if (recoveredMessageDigest.equals(this.mb.fetch())) {
408                     return true;
409                 } else {
410                     return false;
411                 }
412             }
413         } finally {
414             //If something goes wrong we should reset the mb without fail!
415             //Also, if nothing goes wrong we should reset the mb too.
416             this.mb.reset();
417         }        
418     }
419     
420     /**
421      * Verifies the passed-in signature in the specified array
422      * of bytes, starting at the specified offset.
423      *
424      * <p> Note: Subclasses should overwrite the default implementation.
425      *
426      *
427      * @param sigBytes the signature bytes to be verified.
428      * @param offset the offset to start from in the array of bytes.
429      * @param leng the number of bytes to use, starting at offset.
430      *
431      * @return true if the signature was verified, false if not.
432      *
433      * @throws SignatureException if the engine is not initialized
434      * properly, or the passed-in signature is improperly encoded or
435      * of the wrong type, etc.
436      */
437     boolean verify(byte[] sigBytes, int offset, int len) 
438     throws SignatureException {
439         if (this.state != State.VERIFY) {
440             throw new SignatureException("Signature is not in VERIFY state.");
441         }        
442         if (sigBytes == null) {
443             throw new NullPointerException("Parameter signBytes is null.");
444         }
445         if (offset < 0) {
446             throw new IllegalArgumentException("Parameter offset is less " +
447             "than zero.");
448         }
449         if (len < 0) {
450             throw new IllegalArgumentException("Parameter len is less " +
451             "than zero.");        
452         }
453         if (offset + len > sigBytes.length) {
454             throw new IllegalArgumentException("Parameter offset + len is " +
455             "greater than outbuf.length .");
456         }      
457         if (((ks + 7) / 8) != len) {
458             throw new SignatureException("Signature in sigBytes is not " +
459             ((ks + 7) / 8) + "bytes long.");        
460         }        
461         
462         //Create a temporaly byte array that holds the 'sigBytes'.
463         byte[] tempSigBytes = new byte[len];
464         
465         //Copy the 'sigBytes' to 'tempSigBytes'.
466         System.arraycopy(sigBytes, offset, tempSigBytes, 0, len);
467         
468         //Invoke the verify(byte[]) method with the 'tempSigBytesArray'.
469         return verify(tempSigBytes);
470     }
471     
472     /**
473      * <p>This method is overridden by providers to initialize
474      * this signature engine with the specified parameter set.
475      *
476      * @param params the parameters
477      *
478      * @exception UnsupportedOperationException if this method is not
479      * overridden by a provider
480      *
481      * @throws InvalidAlgorithmParameterException if this method is
482      * overridden by a provider and the the given parameters
483      * are inappropriate for this signature engine
484      */
485     void setParameter(AlgorithmParameterSpec params) 
486     throws InvalidAlgorithmParameterException {
487         throw new UnsupportedOperationException("AlgorithmParameters are not " +
488         "supported for this signature.");        
489     }
490     
491     /**
492      * <p>This method is overridden by providers to return the
493      * parameters used with this signature engine, or null
494      * if this signature engine does not use any parameters.
495      *
496      * <p>The returned parameters may be the same that were used to initialize
497      * this signature engine, or may contain a combination of default and
498      * randomly generated parameter values used by the underlying signature
499      * implementation if this signature engine requires algorithm parameters
500      * but was not initialized with any.
501      *
502      * @return the parameters used with this signature engine, or null if this
503      * signature engine does not use any parameters
504      *
505      * @throws UnsupportedOperationException if this method is
506      * not overridden by a provider
507      */
508     AlgorithmParameters getParameters() {
509         throw new UnsupportedOperationException("AlgorithmParameters are not " +
510         "supported for this signature.");
511     }
512     
513     /** Type safe enum for the enclosing class object state. */
514     private final static class State {
515         /** Name odf the state. */
516         private final String name;
517         
518         /** Constructor need only to called within this class. */
519         private State(String name) {
520             this.name = name;
521         }
522         
523         /**
524          * Returns a string representation of the object. 
525          *
526          * @return  a string representation of the object.
527          */
528         public String toString() {
529             return this.name;
530         }
531         
532         /** State constante if the enclosing class isn't initialized. */
533         private static final State UNINITIALIZED = 
534         new State("Uninitialized");
535         
536         /** State constante if the enclossing class is in Sign mode. */
537         private static final State SIGN = new State("Sign");
538         
539         /** State constante if the enclossing class is in Verify mode. */        
540         private static final State VERIFY = new State("Verify");
541     }     
542     
543 }
544