1   /* $RCSfile: RIPEMD160WithISO9796Part1AndRSASignatureImpl.java,v $
2    * $Revision: 1.4 $
3    * $Date: 2002/11/23 11:09:56 $
4    * $Author: uwe_guenther $
5    * $State: Exp $
6    *
7    * Created on December 20, 2001 6: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.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   * RIPEMD160WithISO9796Part1AndRSASignatureImpl Class.
48   *
49   * @author  <a href=mailto:uwe@cscc.de>Uwe G&uuml;nther</a>
50   *
51   * @version $Revision: 1.4 $
52   */
53  final class RIPEMD160WithISO9796Part1AndRSASignatureImpl implements Cloneable {
54   
55      /** The RIPEMD160 message digest object. */
56      private RIPEMD160MessageDigestImpl md = 
57      new RIPEMD160MessageDigestImpl();
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 RIPEMD160WithISO9796Part1AndRSASignatureImpl.  */
73      public RIPEMD160WithISO9796Part1AndRSASignatureImpl() {
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          RIPEMD160WithISO9796Part1AndRSASignatureImpl result =
88          (RIPEMD160WithISO9796Part1AndRSASignatureImpl) super.clone();
89          result.md = (RIPEMD160MessageDigestImpl) this.md.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 RIPEMD160WithISO9796Part1AndRSASignatureImpl)) {
111             return false;
112         }
113         
114         RIPEMD160WithISO9796Part1AndRSASignatureImpl other = 
115         (RIPEMD160WithISO9796Part1AndRSASignatureImpl)obj;
116         
117         return this.md.equals(other.md) &&
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.md.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 "[RIPEMD160WithISO9796-1AndRSA - 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.md.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.md.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.md.update(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.md.update(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         
282         try {
283             ISO9796Part1BitString messageDigest = 
284             new ISO9796Part1BitString(this.md.digest(), 160);
285         
286             ISO9796Part1BitString signature =
287             this.codec.encodeMessage(messageDigest, false);
288 
289             /*
290             if (signature.getBitLength() == this.ks) {
291                 return signature.getBitString();
292             } else {
293                 throw new SignatureException(
294                 "Calculated Signature has a wrong length.");
295             }
296             */
297             return signature.getBitString();
298             
299         } finally {
300             //If something goes wrong we should reset the md without fail!
301             //Also, if nothing goes wrong we should reset the md too.
302             this.md.reset();
303         }
304     }
305     
306     /**
307      * Finishes this signature operation and stores the resulting signature
308      * bytes in the provided buffer <code>outbuf</code>, starting at
309      * <code>offset</code>.
310      * The format of the signature depends on the underlying
311      * signature scheme.
312      *
313      * <p>The signature implementation is reset to its initial state
314      * (the state it was in after a call to one of the
315      * <code>engineInitSign</code> methods)
316      * and can be reused to generate further signatures with the same private
317      * key.
318      *
319      * This method should be abstract, but we leave it concrete for
320      * binary compatibility.  Knowledgeable providers should override this
321      * method.
322      *
323      * @param outbuf buffer for the signature result.
324      *
325      * @param offset offset into <code>outbuf</code> where the signature is
326      * stored.
327      *
328      * @param len number of bytes within <code>outbuf</code> allotted for the
329      * signature.
330      * Both this default implementation and the SUN provider do not
331      * return partial digests. If the value of this parameter is less
332      * than the actual signature length, this method will throw a
333      * SignatureException.
334      * This parameter is ignored if its value is greater than or equal to
335      * the actual signature length.
336      *
337      * @return the number of bytes placed into <code>outbuf</code>
338      *
339      * @throws SignatureException if an error occurs or <code>len</code>
340      * is less than the actual signature length.
341      * @throws NullPointerException if outbuf is null.
342      * @throws IllegalArgumentException if offset or len is negative, or the 
343      * sum of offset and len is greater than length of the outbuf array.
344      */
345     int sign(byte[] outbuf, int offset, int len) throws SignatureException {
346         if (this.state != State.SIGN) {
347             throw new SignatureException("Signature is not in SIGN state.");
348         }        
349         if (outbuf == null) {
350             throw new NullPointerException("Parameter outbuf is null.");
351         }
352         if (offset < 0) {
353             throw new IllegalArgumentException("Parameter offset is less " +
354             "than zero.");
355         }
356         if (len < 0) {
357             throw new IllegalArgumentException("Parameter len is less " +
358             "than zero.");        
359         }
360         if (offset + len > outbuf.length) {
361             throw new IllegalArgumentException("Parameter offset + len is " +
362             "greater than outbuf.length .");
363         }
364         if (((ks + 7) / 8) > len) {
365             throw new SignatureException("Existing space in outbuf is less "
366             + "than the signature length would be.");        
367         }
368 
369         //Calculate the Signature with sign()
370         byte[] result = sign();
371         
372         //Copy the Signature into the outbuf starting at offset.
373         System.arraycopy(result, 0, outbuf, offset, result.length);
374         
375         //Return the number of bytes placed into outbuf.
376         return result.length;
377     }    
378     
379     /**
380      * Verifies the passed-in signature.
381      *
382      * @param sigBytes the signature bytes to be verified.
383      *
384      * @return true if the signature was verified, false if not.
385      *
386      * @throws SignatureException if the engine is not initialized
387      * properly, or the passed-in signature is improperly encoded or
388      * of the wrong type, etc.
389      */
390     boolean verify(byte[] sigBytes) throws SignatureException {
391         if (this.state != State.VERIFY) {
392             throw new SignatureException("Signature is not in VERIFY state.");
393         }
394         if (sigBytes == null) {
395             throw new NullPointerException("Parameter signBytes is null.");
396         }
397         
398         try {
399             ISO9796Part1BitString sigma = 
400             new ISO9796Part1BitString (sigBytes, this.ks);
401         
402             ISO9796Part1BitString recoveredMessageDigest = 
403             this.codec.decodeSignature(sigma);
404 
405             if (recoveredMessageDigest == null) {
406                 return false;
407                 
408             } else { //(message != null)
409                 ISO9796Part1BitString messageDigest = 
410                 new ISO9796Part1BitString(this.md.digest(), 160);
411 
412                 if (recoveredMessageDigest.equals(messageDigest)) {
413                     return true;
414                 } else {
415                     return false;
416                 }
417             }
418         } finally {
419             //If something goes wrong we should reset the md without fail!
420             //Also, if nothing goes wrong we should reset the md too.
421             this.md.reset();
422         }        
423     }
424     
425     /**
426      * Verifies the passed-in signature in the specified array
427      * of bytes, starting at the specified offset.
428      *
429      * <p> Note: Subclasses should overwrite the default implementation.
430      *
431      *
432      * @param sigBytes the signature bytes to be verified.
433      * @param offset the offset to start from in the array of bytes.
434      * @param leng the number of bytes to use, starting at offset.
435      *
436      * @return true if the signature was verified, false if not.
437      *
438      * @throws SignatureException if the engine is not initialized
439      * properly, or the passed-in signature is improperly encoded or
440      * of the wrong type, etc.
441      */
442     boolean verify(byte[] sigBytes, int offset, int len) 
443     throws SignatureException {
444         if (this.state != State.VERIFY) {
445             throw new SignatureException("Signature is not in VERIFY state.");
446         }        
447         if (sigBytes == null) {
448             throw new NullPointerException("Parameter signBytes is null.");
449         }
450         if (offset < 0) {
451             throw new IllegalArgumentException("Parameter offset is less " +
452             "than zero.");
453         }
454         if (len < 0) {
455             throw new IllegalArgumentException("Parameter len is less " +
456             "than zero.");        
457         }
458         if (offset + len > sigBytes.length) {
459             throw new IllegalArgumentException("Parameter offset + len is " +
460             "greater than outbuf.length .");
461         }      
462         if (((ks + 7) / 8) != len) {
463             throw new SignatureException("Signature in sigBytes is not " +
464             ((ks + 7) / 8) + "bytes long.");        
465         }        
466         
467         //Create a temporaly byte array that holds the 'sigBytes'.
468         byte[] tempSigBytes = new byte[len];
469         
470         //Copy the 'sigBytes' to 'tempSigBytes'.
471         System.arraycopy(sigBytes, offset, tempSigBytes, 0, len);
472         
473         //Invoke the verify(byte[]) method with the 'tempSigBytesArray'.
474         return verify(tempSigBytes);
475     }
476     
477     /**
478      * <p>This method is overridden by providers to initialize
479      * this signature engine with the specified parameter set.
480      *
481      * @param params the parameters
482      *
483      * @exception UnsupportedOperationException if this method is not
484      * overridden by a provider
485      *
486      * @throws InvalidAlgorithmParameterException if this method is
487      * overridden by a provider and the the given parameters
488      * are inappropriate for this signature engine
489      */
490     void setParameter(AlgorithmParameterSpec params) 
491     throws InvalidAlgorithmParameterException {
492         throw new UnsupportedOperationException("AlgorithmParameters are not " +
493         "supported for this signature.");        
494     }
495     
496     /**
497      * <p>This method is overridden by providers to return the
498      * parameters used with this signature engine, or null
499      * if this signature engine does not use any parameters.
500      *
501      * <p>The returned parameters may be the same that were used to initialize
502      * this signature engine, or may contain a combination of default and
503      * randomly generated parameter values used by the underlying signature
504      * implementation if this signature engine requires algorithm parameters
505      * but was not initialized with any.
506      *
507      * @return the parameters used with this signature engine, or null if this
508      * signature engine does not use any parameters
509      *
510      * @throws UnsupportedOperationException if this method is
511      * not overridden by a provider
512      */
513     AlgorithmParameters getParameters() {
514         throw new UnsupportedOperationException("AlgorithmParameters are not " +
515         "supported for this signature.");
516     }
517     
518     /** Type safe enum for the enclosing class object state. */
519     private final static class State {
520         /** Name odf the state. */
521         private final String name;
522         
523         /** Constructor need only to called within this class. */
524         private State(String name) {
525             this.name = name;
526         }
527         
528         /**
529          * Returns a string representation of the object. 
530          *
531          * @return  a string representation of the object.
532          */
533         public String toString() {
534             return this.name;
535         }
536         
537         /** State constante if the enclosing class isn't initialized. */
538         private static final State UNINITIALIZED = 
539         new State("Uninitialized");
540         
541         /** State constante if the enclossing class is in Sign mode. */
542         private static final State SIGN = new State("Sign");
543         
544         /** State constante if the enclossing class is in Verify mode. */        
545         private static final State VERIFY = new State("Verify");
546     }     
547     
548 }
549