Clover Coverage Report
Coverage timestamp: Sun Mar 23 2008 08:24:39 GMT
117   606   28   6.16
48   200   0.39   19
19     2.42  
1    
 
 
  DefaultSecurity       Line # 120 117 28 22.8% 0.22826087
 
  (1)
 
1    /*
2    * Copyright (c) 2000-2005, University of Salford
3    * All rights reserved.
4    *
5    * Redistribution and use in source and binary forms, with or without
6    * modification, are permitted provided that the following conditions are met:
7    *
8    * Redistributions of source code must retain the above copyright notice, this
9    * list of conditions and the following disclaimer.
10    *
11    * Redistributions in binary form must reproduce the above copyright notice,
12    * this list of conditions and the following disclaimer in the documentation
13    * and/or other materials provided with the distribution.
14    *
15    * Neither the name of the University of Salford nor the names of its
16    * contributors may be used to endorse or promote products derived from this
17    * software without specific prior written permission.
18    *
19    * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
20    * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
21    * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
22    * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE
23    * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
24    * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
25    * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
26    * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
27    * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
28    * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
29    * POSSIBILITY OF SUCH DAMAGE.
30    */
31    /*
32    * Copyright (c) 2006, University of Kent
33    * All rights reserved.
34    *
35    * Redistribution and use in source and binary forms, with or without
36    * modification, are permitted provided that the following conditions are met:
37    *
38    * Redistributions of source code must retain the above copyright notice, this
39    * list of conditions and the following disclaimer.
40    *
41    * Redistributions in binary form must reproduce the above copyright notice,
42    * this list of conditions and the following disclaimer in the documentation
43    * and/or other materials provided with the distribution.
44    *
45    * 1. Neither the name of the University of Kent nor the names of its
46    * contributors may be used to endorse or promote products derived from this
47    * software without specific prior written permission.
48    *
49    * 2. THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS
50    * IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO,
51    * THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
52    * PURPOSE ARE DISCLAIMED.
53    *
54    * 3. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE
55    * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
56    * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
57    * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
58    * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
59    * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
60    * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
61    * POSSIBILITY OF SUCH DAMAGE.
62    *
63    * 4. YOU AGREE THAT THE EXCLUSIONS IN PARAGRAPHS 2 AND 3 ABOVE ARE REASONABLE
64    * IN THE CIRCUMSTANCES. IN PARTICULAR, YOU ACKNOWLEDGE (1) THAT THIS
65    * SOFTWARE HAS BEEN MADE AVAILABLE TO YOU FREE OF CHARGE, (2) THAT THIS
66    * SOFTWARE IS NOT "PRODUCT" QUALITY, BUT HAS BEEN PRODUCED BY A RESEARCH
67    * GROUP WHO DESIRE TO MAKE THIS SOFTWARE FREELY AVAILABLE TO PEOPLE WHO WISH
68    * TO USE IT, AND (3) THAT BECAUSE THIS SOFTWARE IS NOT OF "PRODUCT" QUALITY
69    * IT IS INEVITABLE THAT THERE WILL BE BUGS AND ERRORS, AND POSSIBLY MORE
70    * SERIOUS FAULTS, IN THIS SOFTWARE.
71    *
72    * 5. This license is governed, except to the extent that local laws
73    * necessarily apply, by the laws of England and Wales.
74    */
75   
76    package issrg.security;
77   
78    import java.security.cert.X509Certificate;
79    import java.security.PrivateKey;
80   
81    import java.io.File;
82    import java.io.FileNotFoundException;
83   
84    import java.util.Map;
85    import java.util.Hashtable;
86    import java.util.Set;
87   
88    import javax.security.auth.login.LoginException;
89    import javax.security.auth.callback.TextOutputCallback;
90    import javax.security.auth.callback.PasswordCallback;
91   
92    import javax.security.auth.Subject;
93    import javax.security.auth.callback.Callback;
94    import javax.security.auth.callback.CallbackHandler;
95   
96    /**
97    * This is the default security class. It can sign and verify signatures on the
98    * given objects.
99    * The signing key should be provided as a part of PKCS#12 file (e.g. generated
100    * using
101    * openssl utility).
102    *
103    * <p>This default security supports:
104    * <ul>
105    * <br><li>signing, given the file with the private key and the password</li>
106    * <br><li>signature verification, given the PKC</li>
107    * <br><li>validation of the PKCs, given the root CA PKC, and given
108    * certification path is not
109    * deeper than 1 hop (i.e. root CA->end-user)</li> In this case the PKCs of
110    * end-users are retrieved from a given PKCRepository
111    * </ul>
112    *
113    * <p>Sample key generation using openssl:
114    * <p><code>openssl x509 -newkey rsa:1024 -out pkc.1 -keyout sk.1 -outform DER</code>
115    * <p>The above line will generate a self-signed PKC and will output a private
116    * RSA 1024-bit long
117    * key into sk.1 file. The PKC will be saved in pkc.1 file.
118    */
119   
 
120    public abstract class DefaultSecurity extends DefaultVerifier implements Signer, SessionOriented {
121    java.security.PrivateKey secretKey=null;
122    java.security.Signature sig=null;
123   
124    /**
125    * Configuration variable for setting a default file to be used by the
126    * login method: "DefaultSecurity.DefaultFile".
127    */
128    public final static String DEFAULT_FILE_STRING="DefaultSecurity.DefaultFile";
129   
130    /**
131    * Configuration variable for setting the last file to be used by the
132    * login method: "DefaultSecurity.LastFile".
133    */
134    public final static String LAST_FILE_STRING="DefaultSecurity.LastFile";
135   
136    /**
137    * Configuration variable containing the prompt to be displayed right before
138    * the file selection and passwords: "DefaultSecurity.Prompt"
139    */
140    public final static String PROMPT_STRING="DefaultSecurity.Prompt";
141   
142    /**
143    * Configuration variable containing the title to be displayed at the
144    * beginning of the authentication process: "DefaultSecurity.Title"
145    */
146    public final static String TITLE_STRING="DefaultSecurity.Title";
147   
148    /**
149    * Configuration variable containing the number of password
150    * shares: "DefaultSecurity.PasswordShares"
151    */
152    public final static String PASSWORD_SHARES_INTEGER="DefaultSecurity.PasswordShares";
153   
154    /**
155    * Configuration variable containing the prompt to be displayed before each
156    * password share is input (the number will be appended automatically):
157    * "DefaultSecurity.PasswordSharesPrompt"
158    */
159    public final static String PASSWORD_SHARES_STRING="DefaultSecurity.PasswordSharesPrompt";
160   
161    public final static String DEFAULT_FILE="";
162   
163    /**
164    * This variable specifies what digest algorithm to use to generate digital
165    * signatures. This will be concatenated with "with" and the encryption
166    * algorithm of the private key. The default setting is "SHA1", so the
167    * signature algorithm is going to be "SHA1withRSA" or "SHA1withDSA",
168    * depending on the private key.
169    */
170    public static String DIGEST_ALGORITHM="SHA1";
171   
172    /**
173    * This variable specifies the digest algorithm used by the actual instance;
174    * the value is copied from DIGEST_ALGORITHM at instantiation time.
175    */
176    protected String digestAlgorithm=DIGEST_ALGORITHM;
177   
178    public static java.util.Map DSA_ALGORITHMS=new java.util.Hashtable();
179    public static java.util.Map RSA_ALGORITHMS=new java.util.Hashtable();
180   
181    /**
182    * This is a Map of Public Key cryptography algorithms supported. Each
183    * entry contains a Map of digest algorithm identifiers.
184    */
185    public static java.util.Map KEY_ALGORITHMS=new java.util.Hashtable();
186   
187    /**
188    * This is the OID of the SHA-1 with DSA signature algorithm.
189    */
190    public static final String SHA1_WITH_DSA_ALGORITHM="1.2.840.10040.4.3";
191   
192    /**
193    * This is the OID of the SHA-1 with RSA signature algorithm.
194    */
195    public static final String SHA1_WITH_RSA_ALGORITHM="1.2.840.113549.1.1.5";
196   
197    /**
198    * This is the OID of the MD5 with RSA signature algorithm.
199    */
200    public static final String MD5_WITH_RSA_ALGORITHM="1.2.840.113549.1.1.4";
201   
202    /**
203    * This is the OID of the MD4 with RSA signature algorithm.
204    */
205    public static final String MD4_WITH_RSA_ALGORITHM="1.2.840.113549.1.1.3";
206   
207    /**
208    * This is the OID of the MD2 with RSA signature algorithm.
209    */
210    public static final String MD2_WITH_RSA_ALGORITHM="1.2.840.113549.1.1.2";
211   
212    /**
213    *This is the name under which we can store and retrieve the password for
214    *PKCS12 signing key.
215    */
216    public static final String PASSWORD_STORED = "Password is stored: ";
217   
218    /**
219    *This is the name under which we can store and collect passwords
220    */
221    public static final String PASSWORDS = "Passwords";
222   
 
223  2 toggle static{
224  2 DSA_ALGORITHMS.put("SHA1", SHA1_WITH_DSA_ALGORITHM);
225   
226  2 RSA_ALGORITHMS.put("SHA1", SHA1_WITH_RSA_ALGORITHM);
227  2 RSA_ALGORITHMS.put("MD5", MD5_WITH_RSA_ALGORITHM);
228  2 RSA_ALGORITHMS.put("MD4", MD4_WITH_RSA_ALGORITHM);
229  2 RSA_ALGORITHMS.put("MD2", MD2_WITH_RSA_ALGORITHM);
230   
231  2 KEY_ALGORITHMS.put("DSA", DSA_ALGORITHMS);
232  2 KEY_ALGORITHMS.put("RSA", RSA_ALGORITHMS);
233    }
234   
 
235  4 toggle public DefaultSecurity() throws SecurityException {
236    }
237   
238    /*
239    * SIGNER METHODS
240    */
241   
242    /**
243    * The method returns the verification certificate of just signed in user. If
244    * no user has signed in,
245    * an exception is thrown.
246    */
 
247  1 toggle public java.security.cert.X509Certificate getVerificationCertificate() throws SecurityException {
248  1 assumeLoggedIn();
249  1 return getRootCAs()[0];
250    }
251   
252   
253    /**
254    * This method signs the data using the secret key of the logged in user. If
255    * no user has been logged in, a SecurityException is thrown.
256    *
257    * @param toBeSigned - the byte array for which a digital signature must be
258    * generated
259    *
260    * @return byte array of the digital signature
261    *
262    * @throws SecurityException if there was a problem when signing the data
263    */
 
264  23 toggle public byte [] sign(byte [] toBeSigned) throws SecurityException {
265  23 assumeLoggedIn();
266  23 try{
267  23 sig.initSign(secretKey);
268  23 sig.update(toBeSigned);
269  23 return sig.sign();
270    }catch (Exception e){
271  0 throw new SecurityException ("Error signing object", e);
272    }
273    }
274   
275    /**
276    * This method checks whether any user has logged in.
277    *
278    * @return true, if some user has logged in (login was called successfully,
279    * logout has not been called yet); false otherwise
280    */
 
281  25 toggle public boolean isLoggedIn(){
282  25 return secretKey!=null;
283    }
284   
285    /**
286    * This is a utility method that checks if any user has logged in, and throws
287    * a SecurityException, if that is not so.
288    *
289    * @throws SecurityException, if no user is logged in at this point in time
290    */
 
291  25 toggle private void assumeLoggedIn() throws SecurityException {
292  25 if (!isLoggedIn()){
293  0 throw new SecurityException("No user has been logged in");
294    }
295    }
296   
297    /**
298    * This method returns the PrivateKey of the logged in user.
299    *
300    * @return PrivateKey of the logged in user, or null if no user is logged in
301    */
 
302  3 toggle public java.security.PrivateKey getPrivateKey(){
303  3 return secretKey;
304    }
305   
306    /**
307    * This method sets the PrivateKey of the logged in user. This does not change
308    * the key stored on a device or in a keystore file.
309    *
310    * <p>Normally you should not use this method, if a user has been logged in.
311    *
312    * @param pk - the PrivateKey to use for signing and decryption for the logged
313    * in user
314    */
 
315  3 toggle public void setPrivateKey(java.security.PrivateKey pk){
316  3 secretKey=pk;
317    }
318   
319    /*
320    * Login Module methods and members
321    */
322    protected Subject subj;
323    protected CallbackHandler ch;
324    protected Map sharedState;
325    protected Map options;
326   
327    /**
328    * Logs the user out by removing his secret key. The rootCA remains the same,
329    * and can be used
330    * for signature verification purposes.
331    *
332    * @return true, as logging out always succeeds
333    */
 
334  0 toggle public boolean logout() {
335  0 if (subj!=null){
336  0 Set pubCreds = subj.getPublicCredentials();
337  0 Set privCreds = subj.getPrivateCredentials();
338  0 Set principals = subj.getPrincipals();
339   
340  0 if (getRootCAs()!=null && getRootCAs()[0]!=null){
341  0 principals.remove(getRootCAs()[0].getSubjectDN());
342  0 pubCreds.remove(getRootCAs()[0]);
343    }
344   
345  0 if (secretKey!=null) privCreds.remove(secretKey);
346   
347  0 privCreds.remove(this);
348    }
349   
350  0 secretKey=null;
351  0 return true;
352    }
353   
354    /**
355    * This method aborts the current log in process. This is equivalent to
356    * calling logout()
357    *
358    * @return true; always succeeds
359    */
 
360  0 toggle public boolean abort() {
361  0 return logout();
362    }
363   
364    /**
365    * This method confirms that the identity of the user must be accepted by the
366    * authentication mechanism, as per JAAS specification.
367    *
368    * @return true, as this method always succeeds
369    */
 
370  0 toggle public boolean commit() {
371  0 return true;
372    }
373   
374    /**
375    * This method prepares the Login Module for logging in the specified subject
376    * using the given callback handlers and the maps of configuration parameters.
377    * See JAAS specification for more details.
378    *
379    * @param subject - the Subject that should be populated with the data about
380    * the user that logs in
381    * @param callbackHandler - the CallbackHandler to be used to display the user
382    * input controls
383    * @param sharedState - the Map of settings shared between multiple
384    * LoginModule instances, or between the login attempts on the same
385    * LoginModule
386    * @param options - the options configuring the behaviour of this LoginModule
387    */
 
388  0 toggle public void initialize(Subject subject, CallbackHandler callbackHandler, Map sharedState, Map options) {
389  0 subj=subject;
390  0 ch=callbackHandler;
391  0 if (sharedState==null) sharedState=new Hashtable();
392  0 if (options==null) options=new Hashtable();
393   
394  0 this.sharedState=sharedState;
395  0 this.options=options;
396    }
397   
398    /**
399    * This method logs the user in by authenticating him against a file.
400    * This is done by prompting for the file name and the password to it.
401    * If these match, the user is logged in. The method uses JAAS CallbackHandler
402    * to display the prompts and collect the information from the user.
403    *
404    * <p>Upon successful authentication the Public Credentials of the Subject
405    * (provided via initialize) are set to contain the X.509 PKC of the user
406    * obtained from the file; the Private Credentials of the Subject are
407    * set to contain the PrivateKey from the file, and the reference to
408    * this DefaultSecurity object; the SubjectDN from the PKC is added to the
409    * set of Principals of the Subject.
410    *
411    * <p>The file is remembered in the sharedState map, and will be
412    * reused for subsequent invocations of this method.
413    *
414    * @returns true only if authentication succeeded; false, if the user cancels
415    * input
416    *
417    * @throws LoginException if the object is not in the right state, an
418    * exception occured during some operation, or if authentication failed
419    */
 
420  0 toggle public boolean login() throws LoginException {
421  0 if (isLoggedIn()){
422  0 throw new LoginException("Cannot log in. Logout first");
423    }
424   
425  0 if (subj==null){
426  0 throw new LoginException("Cannot log in. No Subject was provided");
427    }
428  0 if (ch==null){
429  0 throw new LoginException("Cannot log in. No CallbackHandler was provided");
430    }
431   
432  0 String pkcFile=(String)sharedState.get(LAST_FILE_STRING);
433  0 if (pkcFile==null) pkcFile=(String)options.get(DEFAULT_FILE_STRING);
434  0 if (pkcFile==null) pkcFile=DEFAULT_FILE;
435   
436  0 String title=(String)options.get(TITLE_STRING);
437  0 if (title==null) title="Log in";
438   
439  0 String prompt=(String)options.get(PROMPT_STRING);
440   
441  0 String passwordShares=(String)options.get(PASSWORD_SHARES_INTEGER);
442  0 if (passwordShares==null) passwordShares="0";
443  0 int passShares=1;
444   
445  0 try{
446  0 passShares=Integer.parseInt(passwordShares);
447    }catch(Exception e){
448  0 passShares=1; // ignore the invalid numbers
449    }
450  0 if (passShares<1) passShares=1;
451   
452  0 passwordShares=(String)options.get(PASSWORD_SHARES_STRING);
453  0 if (passwordShares==null) passwordShares="Password";
454   
455   
456  0 Callback allCallbacks[]=new Callback[passShares+2+(prompt==null?0:1)];
457  0 allCallbacks[0]=new TitleCallback(title);
458  0 int j=1;
459   
460  0 if (prompt!=null){
461  0 allCallbacks[j++]=new TextOutputCallback(TextOutputCallback.INFORMATION, prompt);
462    }
463   
464  0 FilenameCallback fc=getFilenameCallback(pkcFile);
465  0 PasswordCallback pc[]=new PasswordCallback[passShares];
466  0 allCallbacks[j++]=fc;
467  0 for (int i=0; i<passShares; i++){
468  0 pc[i]=new PasswordCallback(passwordShares+(passShares==1?"":(" "+(i+1))), false);
469  0 allCallbacks[j++]=pc[i];
470    }
471   
472  0 try{
473  0 ch.handle(allCallbacks);
474   
475    // now the data has been collected from the user inside the callbacks
476  0 String filename=fc.getFileName();
477  0 if (filename==null || pc[0].getPassword()==null) return false; // the user cancelled input
478   
479  0 sharedState.put(LAST_FILE_STRING, filename); // should it remember the filename only if successfully logged in?
480  0 char [][] ps=new char[pc.length][];
481  0 for (int i=0; i<ps.length; i++) ps[i]=pc[i].getPassword();
482   
483  0 login(filename, ps);
484    //the following 4 lines are added by Tuan Anh. Some apps
485    //need password(s)
486  0 Object passwordIsStored = sharedState.get(PASSWORD_STORED);
487  0 if (passwordIsStored != null) {
488  0 sharedState.put(PASSWORDS, ps);
489    }
490   
491  0 Set pubCreds = subj.getPublicCredentials();
492  0 Set privCreds = subj.getPrivateCredentials();
493  0 Set principals = subj.getPrincipals();
494   
495  0 pubCreds.add(getRootCAs()[0]);
496  0 privCreds.add(secretKey);
497  0 privCreds.add(this);
498  0 principals.add(getRootCAs()[0].getSubjectDN());
499    }catch(Exception se){
500  0 LoginException le=new LoginException("Failed to log in: "+se.getMessage());
501  0 le.setStackTrace(se.getStackTrace());
502   
503  0 throw le;
504    }
505   
506  0 return true;
507    }
508   
509    /**
510    * This method logs in using multiple password shares. The implementation
511    * may transform the multiple shares into a single password, or maybe the
512    * other
513    * way round - the single-password login method will split the single password
514    * into shares, depending on underlying security device.
515    */
 
516  0 toggle protected void login(String filename, char ps[][]) throws SecurityException {
517  0 login(filename, ps[0]); // in fact, should be a transformation of multiple password shares
518    }
519   
520    /**
521    * This method should be called by the login method to set the Signature
522    * implementation. The Signature is instantiated once, to save time during
523    * sign process.
524    *
525    * @param sig - the Signature implementation to be used by sign method
526    */
 
527  6 toggle protected void setSignature(java.security.Signature sig){
528  6 this.sig=sig;
529    }
530   
531    /**
532    * This method returns the current Signature implementation that will be used
533    * by sign method.
534    *
535    * @return Signature object that is used to sign the data, or null, if no
536    * Signature implementation was provided (e.g. no user is logged in)
537    */
 
538  6 toggle protected java.security.Signature getSignature(){
539  6 return sig;
540    }
541   
542    /**
543    * This method returns a FilenameCallback that prompts for
544    * implementation-specific type of files.
545    *
546    * @param filename - the default file to be prompted by CallbackHandler
547    *
548    * @return FilenameCallback to be used to prompt for files
549    */
550    protected abstract FilenameCallback getFilenameCallback(String filename);
551   
552    /**
553    * This method logs in using a single password.
554    *
555    * @param filename - the file containing the encrypted Private Key of the user
556    * @param password - the password to be used to decrypt the key
557    */
558    public abstract void login(String filename, char [] password) throws SecurityException;
559   
560    /**
561    * This method returns the signing Algorithm Identifier, which is a
562    * combination of the default hashing algorithm and the encryption algorithm
563    * of the Private Key.
564    *
565    * @return the AlgorithmID of the signing algorithm
566    *
567    * @throws SecurityException if no user was logged in, or the Algorithm ID is
568    * not known (e.g. obscure hashing algorithm has been set, or the Private
569    * Key cannot be used to generate digital signatures)
570    */
 
571  1 toggle public String getSigningAlgorithmID() throws SecurityException {
572  1 assumeLoggedIn();
573  1 java.util.Map m=(java.util.Map)KEY_ALGORITHMS.get(secretKey.getAlgorithm());
574  0 if (m==null) throw new SecurityException("Can't find any signature algorithm ID for "+secretKey.getAlgorithm());
575   
576  1 String s=(String)m.get(getDigestAlgorithm());
577  0 if (s==null) throw new SecurityException("Can't find signature algorithm ID for "+getDigestAlgorithm()+" with "+secretKey.getAlgorithm());
578   
579  1 return s;
580    }
581   
582    /**
583    * This method returns the default Digest Algorithm used by this object when
584    * signing. The supported methods are "SHA1" and "MD5". The default one is
585    * "SHA1", which is deemed stronger than "MD5", but you can change this
586    * by using setDigestAlgorithm.
587    *
588    * @return the Digest Algorithm used by this object to compute the hash of the
589    * to-be-signed data
590    */
 
591  4 toggle public String getDigestAlgorithm(){
592  4 return digestAlgorithm;
593    }
594   
595    /**
596    * This method sets the Digest Algorithm to be used to produce hash of the
597    * to-be-signed data. The methods that are currently supported are "MD5" and
598    * "SHA1". "SHA1" is used by default as it is widely believed to be more
599    * secure than "MD5".
600    *
601    * @param da - the name of the Digest Algorithm
602    */
 
603  0 toggle public void setDigestAlgorithm(String da){
604  0 digestAlgorithm=da;
605    }
606    }