001: /*
002: * @(#)JavaKeyStore.java 1.50 06/10/10
003: *
004: * Copyright 1990-2006 Sun Microsystems, Inc. All Rights Reserved.
005: * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER
006: *
007: * This program is free software; you can redistribute it and/or
008: * modify it under the terms of the GNU General Public License version
009: * 2 only, as published by the Free Software Foundation.
010: *
011: * This program is distributed in the hope that it will be useful, but
012: * WITHOUT ANY WARRANTY; without even the implied warranty of
013: * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
014: * General Public License version 2 for more details (a copy is
015: * included at /legal/license.txt).
016: *
017: * You should have received a copy of the GNU General Public License
018: * version 2 along with this work; if not, write to the Free Software
019: * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA
020: * 02110-1301 USA
021: *
022: * Please contact Sun Microsystems, Inc., 4150 Network Circle, Santa
023: * Clara, CA 95054 or visit www.sun.com if you need additional
024: * information or have any questions.
025: *
026: */
027:
028: package sun.security.provider;
029:
030: import java.io.*;
031: import java.security.DigestInputStream;
032: import java.security.DigestOutputStream;
033: import java.security.MessageDigest;
034: import java.security.NoSuchAlgorithmException;
035: import java.security.Key;
036: import java.security.PrivateKey;
037: import java.security.KeyStoreSpi;
038: import java.security.KeyStoreException;
039: import java.security.UnrecoverableKeyException;
040: import java.security.cert.Certificate;
041: import java.security.cert.CertificateFactory;
042: import java.security.cert.X509Certificate;
043: import java.security.cert.CertificateException;
044: import java.util.*;
045:
046: import sun.security.pkcs.EncryptedPrivateKeyInfo;
047:
048: /**
049: * This class provides the keystore implementation referred to as "JKS".
050: *
051: * @author Jan Luehe
052: * @author David Brownell
053: *
054: * @version 1.50, 10/10/06
055: *
056: * @see KeyProtector
057: * @see java.security.KeyStoreSpi
058: * @see KeyTool
059: *
060: * @since JDK1.2
061: */
062:
063: public final class JavaKeyStore extends KeyStoreSpi {
064:
065: public static final int MAGIC = 0xfeedfeed;
066: public static final int VERSION_1 = 0x01;
067: public static final int VERSION_2 = 0x02;
068:
069: // Private keys and their supporting certificate chains
070: class KeyEntry {
071: Date date; // the creation date of this entry
072: byte[] protectedPrivKey;
073: Certificate chain[];
074: };
075:
076: // Trusted certificates
077: class TrustedCertEntry {
078: Date date; // the creation date of this entry
079: Certificate cert;
080: };
081:
082: /**
083: * Private keys and certificates are stored in a hashtable.
084: * Hash entries are keyed by alias names.
085: */
086: private Hashtable entries = new Hashtable();
087:
088: /**
089: * Returns the key associated with the given alias, using the given
090: * password to recover it.
091: *
092: * @param alias the alias name
093: * @param password the password for recovering the key
094: *
095: * @return the requested key, or null if the given alias does not exist
096: * or does not identify a <i>key entry</i>.
097: *
098: * @exception NoSuchAlgorithmException if the algorithm for recovering the
099: * key cannot be found
100: * @exception UnrecoverableKeyException if the key cannot be recovered
101: * (e.g., the given password is wrong).
102: */
103: public Key engineGetKey(String alias, char[] password)
104: throws NoSuchAlgorithmException, UnrecoverableKeyException {
105: Object entry = entries.get(alias.toLowerCase());
106:
107: if (entry == null || !(entry instanceof KeyEntry)) {
108: return null;
109: }
110:
111: KeyProtector keyProtector = new KeyProtector(password);
112: byte[] encrBytes = ((KeyEntry) entry).protectedPrivKey;
113: EncryptedPrivateKeyInfo encrInfo;
114: byte[] plain;
115: try {
116: encrInfo = new EncryptedPrivateKeyInfo(encrBytes);
117: } catch (IOException ioe) {
118: throw new UnrecoverableKeyException(
119: "Private key not stored as " + "PKCS #8 "
120: + "EncryptedPrivateKeyInfo");
121: }
122: return keyProtector.recover(encrInfo);
123: }
124:
125: /**
126: * Returns the certificate chain associated with the given alias.
127: *
128: * @param alias the alias name
129: *
130: * @return the certificate chain (ordered with the user's certificate first
131: * and the root certificate authority last), or null if the given alias
132: * does not exist or does not contain a certificate chain (i.e., the given
133: * alias identifies either a <i>trusted certificate entry</i> or a
134: * <i>key entry</i> without a certificate chain).
135: */
136: public Certificate[] engineGetCertificateChain(String alias) {
137: Object entry = entries.get(alias.toLowerCase());
138:
139: if (entry != null && entry instanceof KeyEntry) {
140: if (((KeyEntry) entry).chain == null) {
141: return null;
142: } else {
143: return (Certificate[]) ((KeyEntry) entry).chain.clone();
144: }
145: } else {
146: return null;
147: }
148: }
149:
150: /**
151: * Returns the certificate associated with the given alias.
152: *
153: * <p>If the given alias name identifies a
154: * <i>trusted certificate entry</i>, the certificate associated with that
155: * entry is returned. If the given alias name identifies a
156: * <i>key entry</i>, the first element of the certificate chain of that
157: * entry is returned, or null if that entry does not have a certificate
158: * chain.
159: *
160: * @param alias the alias name
161: *
162: * @return the certificate, or null if the given alias does not exist or
163: * does not contain a certificate.
164: */
165: public Certificate engineGetCertificate(String alias) {
166: Object entry = entries.get(alias.toLowerCase());
167:
168: if (entry != null) {
169: if (entry instanceof TrustedCertEntry) {
170: return ((TrustedCertEntry) entry).cert;
171: } else {
172: if (((KeyEntry) entry).chain == null) {
173: return null;
174: } else {
175: return ((KeyEntry) entry).chain[0];
176: }
177: }
178: } else {
179: return null;
180: }
181: }
182:
183: /**
184: * Returns the creation date of the entry identified by the given alias.
185: *
186: * @param alias the alias name
187: *
188: * @return the creation date of this entry, or null if the given alias does
189: * not exist
190: */
191: public Date engineGetCreationDate(String alias) {
192: Object entry = entries.get(alias.toLowerCase());
193:
194: if (entry != null) {
195: if (entry instanceof TrustedCertEntry) {
196: return new Date(((TrustedCertEntry) entry).date
197: .getTime());
198: } else {
199: return new Date(((KeyEntry) entry).date.getTime());
200: }
201: } else {
202: return null;
203: }
204: }
205:
206: /**
207: * Assigns the given key to the given alias, protecting it with the given
208: * password.
209: *
210: * <p>If the given key is of type <code>java.security.PrivateKey</code>,
211: * it must be accompanied by a certificate chain certifying the
212: * corresponding public key.
213: *
214: * <p>If the given alias already exists, the keystore information
215: * associated with it is overridden by the given key (and possibly
216: * certificate chain).
217: *
218: * @param alias the alias name
219: * @param key the key to be associated with the alias
220: * @param password the password to protect the key
221: * @param chain the certificate chain for the corresponding public
222: * key (only required if the given key is of type
223: * <code>java.security.PrivateKey</code>).
224: *
225: * @exception KeyStoreException if the given key cannot be protected, or
226: * this operation fails for some other reason
227: */
228: public void engineSetKeyEntry(String alias, Key key,
229: char[] password, Certificate[] chain)
230: throws KeyStoreException {
231: KeyProtector keyProtector = null;
232:
233: try {
234: synchronized (entries) {
235: KeyEntry entry = new KeyEntry();
236: entry.date = new Date();
237:
238: // Protect the encoding of the key
239: keyProtector = new KeyProtector(password);
240: entry.protectedPrivKey = keyProtector.protect(key);
241:
242: // clone the chain
243: if ((chain != null) && (chain.length != 0)) {
244: entry.chain = (Certificate[]) chain.clone();
245: } else {
246: entry.chain = null;
247: }
248:
249: entries.put(alias.toLowerCase(), entry);
250: }
251: } catch (NoSuchAlgorithmException nsae) {
252: throw new KeyStoreException(
253: "Key protection algorithm not found");
254: } finally {
255: keyProtector = null;
256: }
257: }
258:
259: /**
260: * Assigns the given key (that has already been protected) to the given
261: * alias.
262: *
263: * <p>If the protected key is of type
264: * <code>java.security.PrivateKey</code>, it must be accompanied by a
265: * certificate chain certifying the corresponding public key. If the
266: * underlying keystore implementation is of type <code>jks</code>,
267: * <code>key</code> must be encoded as an
268: * <code>EncryptedPrivateKeyInfo</code> as defined in the PKCS #8 standard.
269: *
270: * <p>If the given alias already exists, the keystore information
271: * associated with it is overridden by the given key (and possibly
272: * certificate chain).
273: *
274: * @param alias the alias name
275: * @param key the key (in protected format) to be associated with the alias
276: * @param chain the certificate chain for the corresponding public
277: * key (only useful if the protected key is of type
278: * <code>java.security.PrivateKey</code>).
279: *
280: * @exception KeyStoreException if this operation fails.
281: */
282: public void engineSetKeyEntry(String alias, byte[] key,
283: Certificate[] chain) throws KeyStoreException {
284: synchronized (entries) {
285: // key must be encoded as EncryptedPrivateKeyInfo as defined in
286: // PKCS#8
287: try {
288: new EncryptedPrivateKeyInfo(key);
289: } catch (IOException ioe) {
290: throw new KeyStoreException("key is not encoded as "
291: + "EncryptedPrivateKeyInfo");
292: }
293:
294: KeyEntry entry = new KeyEntry();
295: entry.date = new Date();
296:
297: entry.protectedPrivKey = (byte[]) key.clone();
298: if ((chain != null) && (chain.length != 0)) {
299: entry.chain = (Certificate[]) chain.clone();
300: } else {
301: entry.chain = null;
302: }
303:
304: entries.put(alias.toLowerCase(), entry);
305: }
306: }
307:
308: /**
309: * Assigns the given certificate to the given alias.
310: *
311: * <p>If the given alias already exists in this keystore and identifies a
312: * <i>trusted certificate entry</i>, the certificate associated with it is
313: * overridden by the given certificate.
314: *
315: * @param alias the alias name
316: * @param cert the certificate
317: *
318: * @exception KeyStoreException if the given alias already exists and does
319: * not identify a <i>trusted certificate entry</i>, or this operation
320: * fails for some other reason.
321: */
322: public void engineSetCertificateEntry(String alias, Certificate cert)
323: throws KeyStoreException {
324: synchronized (entries) {
325:
326: Object entry = entries.get(alias.toLowerCase());
327: if ((entry != null) && (entry instanceof KeyEntry)) {
328: throw new KeyStoreException(
329: "Cannot overwrite own certificate");
330: }
331:
332: TrustedCertEntry trustedCertEntry = new TrustedCertEntry();
333: trustedCertEntry.cert = cert;
334: trustedCertEntry.date = new Date();
335: entries.put(alias.toLowerCase(), trustedCertEntry);
336: }
337: }
338:
339: /**
340: * Deletes the entry identified by the given alias from this keystore.
341: *
342: * @param alias the alias name
343: *
344: * @exception KeyStoreException if the entry cannot be removed.
345: */
346: public void engineDeleteEntry(String alias)
347: throws KeyStoreException {
348: synchronized (entries) {
349: entries.remove(alias.toLowerCase());
350: }
351: }
352:
353: /**
354: * Lists all the alias names of this keystore.
355: *
356: * @return enumeration of the alias names
357: */
358: public Enumeration engineAliases() {
359: return entries.keys();
360: }
361:
362: /**
363: * Checks if the given alias exists in this keystore.
364: *
365: * @param alias the alias name
366: *
367: * @return true if the alias exists, false otherwise
368: */
369: public boolean engineContainsAlias(String alias) {
370: return entries.containsKey(alias.toLowerCase());
371: }
372:
373: /**
374: * Retrieves the number of entries in this keystore.
375: *
376: * @return the number of entries in this keystore
377: */
378: public int engineSize() {
379: return entries.size();
380: }
381:
382: /**
383: * Returns true if the entry identified by the given alias is a
384: * <i>key entry</i>, and false otherwise.
385: *
386: * @return true if the entry identified by the given alias is a
387: * <i>key entry</i>, false otherwise.
388: */
389: public boolean engineIsKeyEntry(String alias) {
390: Object entry = entries.get(alias.toLowerCase());
391: if ((entry != null) && (entry instanceof KeyEntry)) {
392: return true;
393: } else {
394: return false;
395: }
396: }
397:
398: /**
399: * Returns true if the entry identified by the given alias is a
400: * <i>trusted certificate entry</i>, and false otherwise.
401: *
402: * @return true if the entry identified by the given alias is a
403: * <i>trusted certificate entry</i>, false otherwise.
404: */
405: public boolean engineIsCertificateEntry(String alias) {
406: Object entry = entries.get(alias.toLowerCase());
407: if ((entry != null) && (entry instanceof TrustedCertEntry)) {
408: return true;
409: } else {
410: return false;
411: }
412: }
413:
414: /**
415: * Returns the (alias) name of the first keystore entry whose certificate
416: * matches the given certificate.
417: *
418: * <p>This method attempts to match the given certificate with each
419: * keystore entry. If the entry being considered
420: * is a <i>trusted certificate entry</i>, the given certificate is
421: * compared to that entry's certificate. If the entry being considered is
422: * a <i>key entry</i>, the given certificate is compared to the first
423: * element of that entry's certificate chain (if a chain exists).
424: *
425: * @param cert the certificate to match with.
426: *
427: * @return the (alias) name of the first entry with matching certificate,
428: * or null if no such entry exists in this keystore.
429: */
430: public String engineGetCertificateAlias(Certificate cert) {
431: Certificate certElem;
432:
433: for (Enumeration e = entries.keys(); e.hasMoreElements();) {
434: String alias = (String) e.nextElement();
435: Object entry = entries.get(alias);
436: if (entry instanceof TrustedCertEntry) {
437: certElem = ((TrustedCertEntry) entry).cert;
438: } else if (((KeyEntry) entry).chain != null) {
439: certElem = ((KeyEntry) entry).chain[0];
440: } else {
441: continue;
442: }
443: if (certElem.equals(cert)) {
444: return alias;
445: }
446: }
447: return null;
448: }
449:
450: /**
451: * Stores this keystore to the given output stream, and protects its
452: * integrity with the given password.
453: *
454: * @param stream the output stream to which this keystore is written.
455: * @param password the password to generate the keystore integrity check
456: *
457: * @exception IOException if there was an I/O problem with data
458: * @exception NoSuchAlgorithmException if the appropriate data integrity
459: * algorithm could not be found
460: * @exception CertificateException if any of the certificates included in
461: * the keystore data could not be stored
462: */
463: public void engineStore(OutputStream stream, char[] password)
464: throws IOException, NoSuchAlgorithmException,
465: CertificateException {
466: synchronized (entries) {
467: /*
468: * KEYSTORE FORMAT:
469: *
470: * Magic number (big-endian integer),
471: * Version of this file format (big-endian integer),
472: *
473: * Count (big-endian integer),
474: * followed by "count" instances of either:
475: *
476: * {
477: * tag=1 (big-endian integer),
478: * alias (UTF string)
479: * timestamp
480: * encrypted private-key info according to PKCS #8
481: * (integer length followed by encoding)
482: * cert chain (integer count, then certs; for each cert,
483: * integer length followed by encoding)
484: * }
485: *
486: * or:
487: *
488: * {
489: * tag=2 (big-endian integer)
490: * alias (UTF string)
491: * timestamp
492: * cert (integer length followed by encoding)
493: * }
494: *
495: * ended by a keyed SHA1 hash (bytes only) of
496: * { password + whitener + preceding body }
497: */
498:
499: // password is mandatory when storing
500: if (password == null) {
501: throw new IllegalArgumentException(
502: "password can't be null");
503: }
504:
505: byte[] encoded; // the certificate encoding
506:
507: MessageDigest md = getPreKeyedHash(password);
508: DataOutputStream dos = new DataOutputStream(
509: new DigestOutputStream(stream, md));
510:
511: dos.writeInt(MAGIC);
512: // always write the latest version
513: dos.writeInt(VERSION_2);
514:
515: dos.writeInt(entries.size());
516:
517: for (Enumeration e = entries.keys(); e.hasMoreElements();) {
518:
519: String alias = (String) e.nextElement();
520: Object entry = entries.get(alias);
521:
522: if (entry instanceof KeyEntry) {
523:
524: // Store this entry as a KeyEntry
525: dos.writeInt(1);
526:
527: // Write the alias
528: dos.writeUTF(alias);
529:
530: // Write the (entry creation) date
531: dos.writeLong(((KeyEntry) entry).date.getTime());
532:
533: // Write the protected private key
534: dos
535: .writeInt(((KeyEntry) entry).protectedPrivKey.length);
536: dos.write(((KeyEntry) entry).protectedPrivKey);
537:
538: // Write the certificate chain
539: int chainLen;
540: if (((KeyEntry) entry).chain == null) {
541: chainLen = 0;
542: } else {
543: chainLen = ((KeyEntry) entry).chain.length;
544: }
545: dos.writeInt(chainLen);
546: for (int i = 0; i < chainLen; i++) {
547: encoded = ((KeyEntry) entry).chain[i]
548: .getEncoded();
549: dos.writeUTF(((KeyEntry) entry).chain[i]
550: .getType());
551: dos.writeInt(encoded.length);
552: dos.write(encoded);
553: }
554: } else {
555:
556: // Store this entry as a certificate
557: dos.writeInt(2);
558:
559: // Write the alias
560: dos.writeUTF(alias);
561:
562: // Write the (entry creation) date
563: dos.writeLong(((TrustedCertEntry) entry).date
564: .getTime());
565:
566: // Write the trusted certificate
567: encoded = ((TrustedCertEntry) entry).cert
568: .getEncoded();
569: dos.writeUTF(((TrustedCertEntry) entry).cert
570: .getType());
571: dos.writeInt(encoded.length);
572: dos.write(encoded);
573: }
574: }
575:
576: /*
577: * Write the keyed hash which is used to detect tampering with
578: * the keystore (such as deleting or modifying key or
579: * certificate entries).
580: */
581: byte digest[] = md.digest();
582:
583: dos.write(digest);
584: dos.flush();
585: }
586: }
587:
588: /**
589: * Loads the keystore from the given input stream.
590: *
591: * <p>If a password is given, it is used to check the integrity of the
592: * keystore data. Otherwise, the integrity of the keystore is not checked.
593: *
594: * @param stream the input stream from which the keystore is loaded
595: * @param password the (optional) password used to check the integrity of
596: * the keystore.
597: *
598: * @exception IOException if there is an I/O or format problem with the
599: * keystore data
600: * @exception NoSuchAlgorithmException if the algorithm used to check
601: * the integrity of the keystore cannot be found
602: * @exception CertificateException if any of the certificates in the
603: * keystore could not be loaded
604: */
605: public void engineLoad(InputStream stream, char[] password)
606: throws IOException, NoSuchAlgorithmException,
607: CertificateException {
608: synchronized (entries) {
609: DataInputStream dis;
610: MessageDigest md = null;
611: CertificateFactory cf = null;
612: Hashtable cfs = null;
613: ByteArrayInputStream bais = null;
614: byte[] encoded = null;
615:
616: if (stream == null)
617: return;
618:
619: if (password != null) {
620: md = getPreKeyedHash(password);
621: dis = new DataInputStream(new DigestInputStream(stream,
622: md));
623: } else {
624: dis = new DataInputStream(stream);
625: }
626:
627: // Body format: see store method
628:
629: int xMagic = dis.readInt();
630: int xVersion = dis.readInt();
631:
632: if (xMagic != MAGIC
633: || (xVersion != VERSION_1 && xVersion != VERSION_2)) {
634: throw new IOException("Invalid keystore format");
635: }
636:
637: if (xVersion == VERSION_1) {
638: cf = CertificateFactory.getInstance("X509");
639: } else {
640: // version 2
641: cfs = new Hashtable(3);
642: }
643:
644: entries.clear();
645: int count = dis.readInt();
646:
647: for (int i = 0; i < count; i++) {
648: int tag;
649: String alias;
650:
651: tag = dis.readInt();
652:
653: if (tag == 1) { // private key entry
654:
655: KeyEntry entry = new KeyEntry();
656:
657: // Read the alias
658: alias = dis.readUTF();
659:
660: // Read the (entry creation) date
661: entry.date = new Date(dis.readLong());
662:
663: // Read the private key
664: try {
665: entry.protectedPrivKey = new byte[dis.readInt()];
666: } catch (OutOfMemoryError e) {
667: throw new IOException("Keysize too big");
668: }
669: dis.readFully(entry.protectedPrivKey);
670:
671: // Read the certificate chain
672: int numOfCerts = dis.readInt();
673: try {
674: if (numOfCerts > 0) {
675: entry.chain = new Certificate[numOfCerts];
676: }
677: } catch (OutOfMemoryError e) {
678: throw new IOException(
679: "Too many certificates in chain");
680: }
681: for (int j = 0; j < numOfCerts; j++) {
682: if (xVersion == 2) {
683: // read the certificate type, and instantiate a
684: // certificate factory of that type (reuse
685: // existing factory if possible)
686: String certType = dis.readUTF();
687: if (cfs.containsKey(certType)) {
688: // reuse certificate factory
689: cf = (CertificateFactory) cfs
690: .get(certType);
691: } else {
692: // create new certificate factory
693: cf = CertificateFactory
694: .getInstance(certType);
695: // store the certificate factory so we can
696: // reuse it later
697: cfs.put(certType, cf);
698: }
699: }
700: // instantiate the certificate
701: try {
702: encoded = new byte[dis.readInt()];
703: } catch (OutOfMemoryError e) {
704: throw new IOException("Certificate too big");
705: }
706: dis.readFully(encoded);
707: bais = new ByteArrayInputStream(encoded);
708: entry.chain[j] = cf.generateCertificate(bais);
709: bais.close();
710: }
711:
712: // Add the entry to the list
713: entries.put(alias, entry);
714:
715: } else if (tag == 2) { // trusted certificate entry
716:
717: TrustedCertEntry entry = new TrustedCertEntry();
718:
719: // Read the alias
720: alias = dis.readUTF();
721:
722: // Read the (entry creation) date
723: entry.date = new Date(dis.readLong());
724:
725: // Read the trusted certificate
726: if (xVersion == 2) {
727: // read the certificate type, and instantiate a
728: // certificate factory of that type (reuse
729: // existing factory if possible)
730: String certType = dis.readUTF();
731: if (cfs.containsKey(certType)) {
732: // reuse certificate factory
733: cf = (CertificateFactory) cfs.get(certType);
734: } else {
735: // create new certificate factory
736: cf = CertificateFactory
737: .getInstance(certType);
738: // store the certificate factory so we can
739: // reuse it later
740: cfs.put(certType, cf);
741: }
742: }
743: try {
744: encoded = new byte[dis.readInt()];
745: } catch (OutOfMemoryError e) {
746: throw new IOException("Certificate too big");
747: }
748: dis.readFully(encoded);
749: bais = new ByteArrayInputStream(encoded);
750: entry.cert = cf.generateCertificate(bais);
751: bais.close();
752:
753: // Add the entry to the list
754: entries.put(alias, entry);
755:
756: } else {
757: throw new IOException("Unrecognized keystore entry");
758: }
759: }
760:
761: /*
762: * If a password has been provided, we check the keyed digest
763: * at the end. If this check fails, the store has been tampered
764: * with
765: */
766: if (password != null) {
767: byte computed[], actual[];
768: computed = md.digest();
769: actual = new byte[computed.length];
770: dis.readFully(actual);
771: for (int i = 0; i < computed.length; i++) {
772: if (computed[i] != actual[i]) {
773: throw new IOException(
774: "Keystore was tampered with, or "
775: + "password was incorrect");
776: }
777: }
778: }
779: }
780: }
781:
782: /**
783: * To guard against tampering with the keystore, we append a keyed
784: * hash with a bit of whitener.
785: */
786: private MessageDigest getPreKeyedHash(char[] password)
787: throws NoSuchAlgorithmException,
788: UnsupportedEncodingException {
789: int i, j;
790:
791: MessageDigest md = MessageDigest.getInstance("SHA");
792: byte[] passwdBytes = new byte[password.length * 2];
793: for (i = 0, j = 0; i < password.length; i++) {
794: passwdBytes[j++] = (byte) (password[i] >> 8);
795: passwdBytes[j++] = (byte) password[i];
796: }
797: md.update(passwdBytes);
798: for (i = 0; i < passwdBytes.length; i++)
799: passwdBytes[i] = 0;
800: md.update("Mighty Aphrodite".getBytes("UTF8"));
801: return md;
802: }
803: }
|