001: package org.bouncycastle.jce.provider;
002:
003: import java.io.ByteArrayInputStream;
004: import java.io.ByteArrayOutputStream;
005: import java.io.DataInputStream;
006: import java.io.DataOutputStream;
007: import java.io.IOException;
008: import java.io.InputStream;
009: import java.io.OutputStream;
010: import java.security.*;
011: import java.security.cert.Certificate;
012: import java.security.cert.CertificateEncodingException;
013: import java.security.cert.CertificateException;
014: import java.security.cert.CertificateFactory;
015: import java.security.spec.KeySpec;
016: import java.security.spec.PKCS8EncodedKeySpec;
017: import java.security.spec.X509EncodedKeySpec;
018: import java.util.Date;
019: import java.util.Enumeration;
020: import java.util.Hashtable;
021:
022: import javax.crypto.Cipher;
023: import javax.crypto.CipherInputStream;
024: import javax.crypto.CipherOutputStream;
025: import javax.crypto.SecretKeyFactory;
026: import javax.crypto.spec.PBEKeySpec;
027: import javax.crypto.spec.PBEParameterSpec;
028: import javax.crypto.spec.SecretKeySpec;
029:
030: import org.bouncycastle.crypto.Digest;
031: import org.bouncycastle.crypto.PBEParametersGenerator;
032: import org.bouncycastle.crypto.digests.SHA1Digest;
033: import org.bouncycastle.crypto.generators.PKCS12ParametersGenerator;
034: import org.bouncycastle.crypto.io.DigestInputStream;
035: import org.bouncycastle.crypto.io.DigestOutputStream;
036: import org.bouncycastle.crypto.io.MacInputStream;
037: import org.bouncycastle.crypto.io.MacOutputStream;
038: import org.bouncycastle.crypto.macs.HMac;
039: import org.bouncycastle.jce.interfaces.BCKeyStore;
040:
041: public class JDKKeyStore extends KeyStoreSpi implements BCKeyStore {
042: private static final int STORE_VERSION = 1;
043:
044: private static final int STORE_SALT_SIZE = 20;
045: private static final String STORE_CIPHER = "PBEWithSHAAndTwofish-CBC";
046:
047: private static final int KEY_SALT_SIZE = 20;
048: private static final int MIN_ITERATIONS = 1024;
049:
050: private static final String KEY_CIPHER = "PBEWithSHAAnd3-KeyTripleDES-CBC";
051:
052: //
053: // generic object types
054: //
055: static final int NULL = 0;
056: static final int CERTIFICATE = 1;
057: static final int KEY = 2;
058: static final int SECRET = 3;
059: static final int SEALED = 4;
060:
061: //
062: // key types
063: //
064: static final int KEY_PRIVATE = 0;
065: static final int KEY_PUBLIC = 1;
066: static final int KEY_SECRET = 2;
067:
068: protected Hashtable table = new Hashtable();
069:
070: protected SecureRandom random = new SecureRandom();
071:
072: public JDKKeyStore() {
073: }
074:
075: private class StoreEntry {
076: int type;
077: String alias;
078: Object obj;
079: Certificate[] certChain;
080: Date date = new Date();
081:
082: StoreEntry(String alias, Certificate obj) {
083: this .type = CERTIFICATE;
084: this .alias = alias;
085: this .obj = obj;
086: this .certChain = null;
087: }
088:
089: StoreEntry(String alias, Key obj, Certificate[] certChain) {
090: this .type = KEY;
091: this .alias = alias;
092: this .obj = obj;
093: this .certChain = certChain;
094: }
095:
096: StoreEntry(String alias, byte[] obj, Certificate[] certChain) {
097: this .type = SECRET;
098: this .alias = alias;
099: this .obj = obj;
100: this .certChain = certChain;
101: }
102:
103: StoreEntry(String alias, Key key, char[] password,
104: Certificate[] certChain) throws Exception {
105: this .type = SEALED;
106: this .alias = alias;
107: this .certChain = certChain;
108:
109: byte[] salt = new byte[KEY_SALT_SIZE];
110:
111: random.setSeed(System.currentTimeMillis());
112: random.nextBytes(salt);
113:
114: int iterationCount = MIN_ITERATIONS
115: + (random.nextInt() & 0x3ff);
116:
117: ByteArrayOutputStream bOut = new ByteArrayOutputStream();
118: DataOutputStream dOut = new DataOutputStream(bOut);
119:
120: dOut.writeInt(salt.length);
121: dOut.write(salt);
122: dOut.writeInt(iterationCount);
123:
124: Cipher cipher = makePBECipher(KEY_CIPHER,
125: Cipher.ENCRYPT_MODE, password, salt, iterationCount);
126: CipherOutputStream cOut = new CipherOutputStream(dOut,
127: cipher);
128:
129: dOut = new DataOutputStream(cOut);
130:
131: encodeKey(key, dOut);
132:
133: dOut.close();
134:
135: obj = bOut.toByteArray();
136: }
137:
138: StoreEntry(String alias, Date date, int type, Object obj) {
139: this .alias = alias;
140: this .date = date;
141: this .type = type;
142: this .obj = obj;
143: }
144:
145: StoreEntry(String alias, Date date, int type, Object obj,
146: Certificate[] certChain) {
147: this .alias = alias;
148: this .date = date;
149: this .type = type;
150: this .obj = obj;
151: this .certChain = certChain;
152: }
153:
154: int getType() {
155: return type;
156: }
157:
158: String getAlias() {
159: return alias;
160: }
161:
162: Object getObject() {
163: return obj;
164: }
165:
166: Object getObject(char[] password)
167: throws NoSuchAlgorithmException,
168: UnrecoverableKeyException {
169: if (password == null || password.length == 0) {
170: if (obj instanceof Key) {
171: return obj;
172: }
173: }
174:
175: if (type == SEALED) {
176: ByteArrayInputStream bIn = new ByteArrayInputStream(
177: (byte[]) obj);
178: DataInputStream dIn = new DataInputStream(bIn);
179:
180: try {
181: byte[] salt = new byte[dIn.readInt()];
182:
183: dIn.readFully(salt);
184:
185: int iterationCount = dIn.readInt();
186:
187: Cipher cipher = makePBECipher(KEY_CIPHER,
188: Cipher.DECRYPT_MODE, password, salt,
189: iterationCount);
190:
191: CipherInputStream cIn = new CipherInputStream(dIn,
192: cipher);
193:
194: try {
195: return decodeKey(new DataInputStream(cIn));
196: } catch (Exception x) {
197: bIn = new ByteArrayInputStream((byte[]) obj);
198: dIn = new DataInputStream(bIn);
199:
200: salt = new byte[dIn.readInt()];
201:
202: dIn.readFully(salt);
203:
204: iterationCount = dIn.readInt();
205:
206: cipher = makePBECipher("Broken" + KEY_CIPHER,
207: Cipher.DECRYPT_MODE, password, salt,
208: iterationCount);
209:
210: cIn = new CipherInputStream(dIn, cipher);
211:
212: Key k = null;
213:
214: try {
215: k = decodeKey(new DataInputStream(cIn));
216: } catch (Exception y) {
217: bIn = new ByteArrayInputStream((byte[]) obj);
218: dIn = new DataInputStream(bIn);
219:
220: salt = new byte[dIn.readInt()];
221:
222: dIn.readFully(salt);
223:
224: iterationCount = dIn.readInt();
225:
226: cipher = makePBECipher("Old" + KEY_CIPHER,
227: Cipher.DECRYPT_MODE, password,
228: salt, iterationCount);
229:
230: cIn = new CipherInputStream(dIn, cipher);
231:
232: k = decodeKey(new DataInputStream(cIn));
233: }
234:
235: //
236: // reencrypt key with correct cipher.
237: //
238: if (k != null) {
239: ByteArrayOutputStream bOut = new ByteArrayOutputStream();
240: DataOutputStream dOut = new DataOutputStream(
241: bOut);
242:
243: dOut.writeInt(salt.length);
244: dOut.write(salt);
245: dOut.writeInt(iterationCount);
246:
247: Cipher out = makePBECipher(KEY_CIPHER,
248: Cipher.ENCRYPT_MODE, password,
249: salt, iterationCount);
250: CipherOutputStream cOut = new CipherOutputStream(
251: dOut, out);
252:
253: dOut = new DataOutputStream(cOut);
254:
255: encodeKey(k, dOut);
256:
257: dOut.close();
258:
259: obj = bOut.toByteArray();
260:
261: return k;
262: } else {
263: throw new UnrecoverableKeyException(
264: "no match");
265: }
266: }
267: } catch (Exception e) {
268: throw new UnrecoverableKeyException("no match");
269: }
270: } else {
271: throw new RuntimeException("forget something!");
272: // TODO
273: // if we get to here key was saved as byte data, which
274: // according to the docs means it must be a private key
275: // in EncryptedPrivateKeyInfo (PKCS8 format), later...
276: //
277: }
278: }
279:
280: Certificate[] getCertificateChain() {
281: return certChain;
282: }
283:
284: Date getDate() {
285: return date;
286: }
287: }
288:
289: private void encodeCertificate(Certificate cert,
290: DataOutputStream dOut) throws IOException {
291: try {
292: byte[] cEnc = cert.getEncoded();
293:
294: dOut.writeUTF(cert.getType());
295: dOut.writeInt(cEnc.length);
296: dOut.write(cEnc);
297: } catch (CertificateEncodingException ex) {
298: throw new IOException(ex.toString());
299: }
300: }
301:
302: private Certificate decodeCertificate(DataInputStream dIn)
303: throws IOException {
304: String type = dIn.readUTF();
305: byte[] cEnc = new byte[dIn.readInt()];
306:
307: dIn.readFully(cEnc);
308:
309: try {
310: CertificateFactory cFact = CertificateFactory.getInstance(
311: type, "BC");
312: ByteArrayInputStream bIn = new ByteArrayInputStream(cEnc);
313:
314: return cFact.generateCertificate(bIn);
315: } catch (NoSuchProviderException ex) {
316: throw new IOException(ex.toString());
317: } catch (CertificateException ex) {
318: throw new IOException(ex.toString());
319: }
320: }
321:
322: private void encodeKey(Key key, DataOutputStream dOut)
323: throws IOException {
324: byte[] enc = key.getEncoded();
325:
326: if (key instanceof PrivateKey) {
327: dOut.write(KEY_PRIVATE);
328: } else if (key instanceof PublicKey) {
329: dOut.write(KEY_PUBLIC);
330: } else {
331: dOut.write(KEY_SECRET);
332: }
333:
334: dOut.writeUTF(key.getFormat());
335: dOut.writeUTF(key.getAlgorithm());
336: dOut.writeInt(enc.length);
337: dOut.write(enc);
338: }
339:
340: private Key decodeKey(DataInputStream dIn) throws IOException {
341: int keyType = dIn.read();
342: String format = dIn.readUTF();
343: String algorithm = dIn.readUTF();
344: byte[] enc = new byte[dIn.readInt()];
345: KeySpec spec;
346:
347: dIn.readFully(enc);
348:
349: if (format.equals("PKCS#8") || format.equals("PKCS8")) {
350: spec = new PKCS8EncodedKeySpec(enc);
351: } else if (format.equals("X.509") || format.equals("X509")) {
352: spec = new X509EncodedKeySpec(enc);
353: } else if (format.equals("RAW")) {
354: return new SecretKeySpec(enc, algorithm);
355: } else {
356: throw new IOException("Key format " + format
357: + " not recognised!");
358: }
359:
360: try {
361: switch (keyType) {
362: case KEY_PRIVATE:
363: return KeyFactory.getInstance(algorithm, "BC")
364: .generatePrivate(spec);
365: case KEY_PUBLIC:
366: return KeyFactory.getInstance(algorithm, "BC")
367: .generatePublic(spec);
368: case KEY_SECRET:
369: return SecretKeyFactory.getInstance(algorithm, "BC")
370: .generateSecret(spec);
371: default:
372: throw new IOException("Key type " + keyType
373: + " not recognised!");
374: }
375: } catch (Exception e) {
376: throw new IOException("Exception creating key: "
377: + e.toString());
378: }
379: }
380:
381: protected Cipher makePBECipher(String algorithm, int mode,
382: char[] password, byte[] salt, int iterationCount)
383: throws IOException {
384: try {
385: PBEKeySpec pbeSpec = new PBEKeySpec(password);
386: SecretKeyFactory keyFact = SecretKeyFactory.getInstance(
387: algorithm, "BC");
388: PBEParameterSpec defParams = new PBEParameterSpec(salt,
389: iterationCount);
390:
391: Cipher cipher = Cipher.getInstance(algorithm, "BC");
392:
393: cipher.init(mode, keyFact.generateSecret(pbeSpec),
394: defParams);
395:
396: return cipher;
397: } catch (Exception e) {
398: throw new IOException(
399: "Error initialising store of key store: " + e);
400: }
401: }
402:
403: public void setRandom(SecureRandom rand) {
404: this .random = rand;
405: }
406:
407: public Enumeration engineAliases() {
408: return table.keys();
409: }
410:
411: public boolean engineContainsAlias(String alias) {
412: return (table.get(alias) != null);
413: }
414:
415: public void engineDeleteEntry(String alias)
416: throws KeyStoreException {
417: Object entry = table.get(alias);
418:
419: if (entry == null) {
420: throw new KeyStoreException("no such entry as " + alias);
421: }
422:
423: table.remove(alias);
424: }
425:
426: public Certificate engineGetCertificate(String alias) {
427: StoreEntry entry = (StoreEntry) table.get(alias);
428:
429: if (entry != null) {
430: if (entry.getType() == CERTIFICATE) {
431: return (Certificate) entry.getObject();
432: } else {
433: Certificate[] chain = entry.getCertificateChain();
434:
435: if (chain != null) {
436: return chain[0];
437: }
438: }
439: }
440:
441: return null;
442: }
443:
444: public String engineGetCertificateAlias(Certificate cert) {
445: Enumeration e = table.elements();
446: while (e.hasMoreElements()) {
447: StoreEntry entry = (StoreEntry) e.nextElement();
448:
449: if (entry.getObject() instanceof Certificate) {
450: Certificate c = (Certificate) entry.getObject();
451:
452: if (c.equals(cert)) {
453: return entry.getAlias();
454: }
455: } else {
456: Certificate[] chain = entry.getCertificateChain();
457:
458: if (chain != null && chain[0].equals(cert)) {
459: return entry.getAlias();
460: }
461: }
462: }
463:
464: return null;
465: }
466:
467: public Certificate[] engineGetCertificateChain(String alias) {
468: StoreEntry entry = (StoreEntry) table.get(alias);
469:
470: if (entry != null) {
471: return entry.getCertificateChain();
472: }
473:
474: return null;
475: }
476:
477: public Date engineGetCreationDate(String alias) {
478: StoreEntry entry = (StoreEntry) table.get(alias);
479:
480: if (entry != null) {
481: return entry.getDate();
482: }
483:
484: return null;
485: }
486:
487: public Key engineGetKey(String alias, char[] password)
488: throws NoSuchAlgorithmException, UnrecoverableKeyException {
489: StoreEntry entry = (StoreEntry) table.get(alias);
490:
491: if (entry == null || entry.getType() == CERTIFICATE) {
492: return null;
493: }
494:
495: return (Key) entry.getObject(password);
496: }
497:
498: public boolean engineIsCertificateEntry(String alias) {
499: StoreEntry entry = (StoreEntry) table.get(alias);
500:
501: if (entry != null && entry.getType() == CERTIFICATE) {
502: return true;
503: }
504:
505: return false;
506: }
507:
508: public boolean engineIsKeyEntry(String alias) {
509: StoreEntry entry = (StoreEntry) table.get(alias);
510:
511: if (entry != null && entry.getType() != CERTIFICATE) {
512: return true;
513: }
514:
515: return false;
516: }
517:
518: public void engineSetCertificateEntry(String alias, Certificate cert)
519: throws KeyStoreException {
520: StoreEntry entry = (StoreEntry) table.get(alias);
521:
522: if (entry != null && entry.getType() != CERTIFICATE) {
523: throw new KeyStoreException(
524: "key store already has a key entry with alias "
525: + alias);
526: }
527:
528: table.put(alias, new StoreEntry(alias, cert));
529: }
530:
531: public void engineSetKeyEntry(String alias, byte[] key,
532: Certificate[] chain) throws KeyStoreException {
533: table.put(alias, new StoreEntry(alias, key, chain));
534: }
535:
536: public void engineSetKeyEntry(String alias, Key key,
537: char[] password, Certificate[] chain)
538: throws KeyStoreException {
539: if ((key instanceof PrivateKey) && (chain == null)) {
540: throw new KeyStoreException(
541: "no certificate chain for private key");
542: }
543:
544: try {
545: table.put(alias,
546: new StoreEntry(alias, key, password, chain));
547: } catch (Exception e) {
548: throw new KeyStoreException(e.toString());
549: }
550: }
551:
552: public int engineSize() {
553: return table.size();
554: }
555:
556: protected boolean isSameAs(byte[] one, byte[] two) {
557: if (one.length != two.length) {
558: return false;
559: }
560:
561: for (int i = 0; i != one.length; i++) {
562: if (one[i] != two[i]) {
563: return false;
564: }
565: }
566:
567: return true;
568: }
569:
570: protected void loadStore(InputStream in) throws IOException {
571: DataInputStream dIn = new DataInputStream(in);
572: int type = dIn.read();
573:
574: while (type > NULL) {
575: String alias = dIn.readUTF();
576: Date date = new Date(dIn.readLong());
577: int chainLength = dIn.readInt();
578: Certificate[] chain = null;
579:
580: if (chainLength != 0) {
581: chain = new Certificate[chainLength];
582:
583: for (int i = 0; i != chainLength; i++) {
584: chain[i] = decodeCertificate(dIn);
585: }
586: }
587:
588: switch (type) {
589: case CERTIFICATE:
590: Certificate cert = decodeCertificate(dIn);
591:
592: table.put(alias, new StoreEntry(alias, date,
593: CERTIFICATE, cert));
594: break;
595: case KEY:
596: Key key = decodeKey(dIn);
597: table.put(alias, new StoreEntry(alias, date, KEY, key,
598: chain));
599: break;
600: case SECRET:
601: case SEALED:
602: byte[] b = new byte[dIn.readInt()];
603:
604: dIn.readFully(b);
605: table.put(alias, new StoreEntry(alias, date, type, b,
606: chain));
607: break;
608: default:
609: throw new RuntimeException(
610: "Unknown object type in store.");
611: }
612:
613: type = dIn.read();
614: }
615: }
616:
617: protected void saveStore(OutputStream out) throws IOException {
618: Enumeration e = table.elements();
619: DataOutputStream dOut = new DataOutputStream(out);
620:
621: while (e.hasMoreElements()) {
622: StoreEntry entry = (StoreEntry) e.nextElement();
623:
624: dOut.write(entry.getType());
625: dOut.writeUTF(entry.getAlias());
626: dOut.writeLong(entry.getDate().getTime());
627:
628: Certificate[] chain = entry.getCertificateChain();
629: if (chain == null) {
630: dOut.writeInt(0);
631: } else {
632: dOut.writeInt(chain.length);
633: for (int i = 0; i != chain.length; i++) {
634: encodeCertificate(chain[i], dOut);
635: }
636: }
637:
638: switch (entry.getType()) {
639: case CERTIFICATE:
640: encodeCertificate((Certificate) entry.getObject(), dOut);
641: break;
642: case KEY:
643: encodeKey((Key) entry.getObject(), dOut);
644: break;
645: case SEALED:
646: case SECRET:
647: byte[] b = (byte[]) entry.getObject();
648:
649: dOut.writeInt(b.length);
650: dOut.write(b);
651: break;
652: default:
653: throw new RuntimeException(
654: "Unknown object type in store.");
655: }
656: }
657:
658: dOut.write(NULL);
659: }
660:
661: public void engineLoad(InputStream stream, char[] password)
662: throws IOException {
663: table.clear();
664:
665: if (stream == null) // just initialising
666: {
667: return;
668: }
669:
670: DataInputStream dIn = new DataInputStream(stream);
671: int version = dIn.readInt();
672:
673: if (version != STORE_VERSION) {
674: if (version != 0) {
675: throw new IOException("Wrong version of key store.");
676: }
677: }
678:
679: byte[] salt = new byte[dIn.readInt()];
680:
681: dIn.readFully(salt);
682:
683: int iterationCount = dIn.readInt();
684:
685: HMac hMac = new HMac(new SHA1Digest());
686: MacInputStream mIn = new MacInputStream(dIn, hMac);
687: PBEParametersGenerator pbeGen = new PKCS12ParametersGenerator(
688: new SHA1Digest());
689: byte[] passKey = PBEParametersGenerator
690: .PKCS12PasswordToBytes(password);
691:
692: pbeGen.init(passKey, salt, iterationCount);
693:
694: hMac.init(pbeGen
695: .generateDerivedMacParameters(hMac.getMacSize()));
696:
697: for (int i = 0; i != passKey.length; i++) {
698: passKey[i] = 0;
699: }
700:
701: loadStore(mIn);
702:
703: byte[] mac = new byte[hMac.getMacSize()];
704: byte[] oldMac = new byte[hMac.getMacSize()];
705:
706: hMac.doFinal(mac, 0);
707:
708: for (int i = 0; i != oldMac.length; i++) {
709: oldMac[i] = (byte) dIn.read();
710: }
711:
712: //
713: // we only do an integrity check if the password is provided.
714: //
715: if ((password != null && password.length != 0)
716: && !isSameAs(mac, oldMac)) {
717: table.clear();
718: throw new IOException("KeyStore integrity check failed.");
719: }
720: }
721:
722: public void engineStore(OutputStream stream, char[] password)
723: throws IOException {
724: DataOutputStream dOut = new DataOutputStream(stream);
725: byte[] salt = new byte[STORE_SALT_SIZE];
726: int iterationCount = MIN_ITERATIONS
727: + (random.nextInt() & 0x3ff);
728:
729: random.nextBytes(salt);
730:
731: dOut.writeInt(STORE_VERSION);
732: dOut.writeInt(salt.length);
733: dOut.write(salt);
734: dOut.writeInt(iterationCount);
735:
736: HMac hMac = new HMac(new SHA1Digest());
737: MacOutputStream mOut = new MacOutputStream(dOut, hMac);
738: PBEParametersGenerator pbeGen = new PKCS12ParametersGenerator(
739: new SHA1Digest());
740: byte[] passKey = PBEParametersGenerator
741: .PKCS12PasswordToBytes(password);
742:
743: pbeGen.init(passKey, salt, iterationCount);
744:
745: hMac.init(pbeGen
746: .generateDerivedMacParameters(hMac.getMacSize()));
747:
748: for (int i = 0; i != passKey.length; i++) {
749: passKey[i] = 0;
750: }
751:
752: saveStore(mOut);
753:
754: byte[] mac = new byte[hMac.getMacSize()];
755:
756: hMac.doFinal(mac, 0);
757:
758: dOut.write(mac);
759:
760: dOut.close();
761: }
762:
763: /**
764: * the BouncyCastle store. This wont work with the key tool as the
765: * store is stored encrypteed on disk, so the password is mandatory,
766: * however if you hard drive is in a bad part of town and you absolutely,
767: * positively, don't want nobody peeking at your things, this is the
768: * one to use, no problem! After all in a Bouncy Castle nothing can
769: * touch you.
770: *
771: * Also referred to by the alias UBER.
772: */
773: public static class BouncyCastleStore extends JDKKeyStore {
774: public void engineLoad(InputStream stream, char[] password)
775: throws IOException {
776: table.clear();
777:
778: if (stream == null) // just initialising
779: {
780: return;
781: }
782:
783: Cipher cipher;
784: DataInputStream dIn = new DataInputStream(stream);
785: int version = dIn.readInt();
786:
787: if (version != STORE_VERSION) {
788: if (version != 0) {
789: throw new IOException("Wrong version of key store.");
790: }
791: }
792:
793: byte[] salt = new byte[dIn.readInt()];
794:
795: if (salt.length != STORE_SALT_SIZE) {
796: throw new IOException("Key store corrupted.");
797: }
798:
799: dIn.readFully(salt);
800:
801: int iterationCount = dIn.readInt();
802:
803: if ((iterationCount < 0)
804: || (iterationCount > 4 * MIN_ITERATIONS)) {
805: throw new IOException("Key store corrupted.");
806: }
807:
808: if (version == 0) {
809: cipher = this .makePBECipher("Old" + STORE_CIPHER,
810: Cipher.DECRYPT_MODE, password, salt,
811: iterationCount);
812: } else {
813: cipher = this .makePBECipher(STORE_CIPHER,
814: Cipher.DECRYPT_MODE, password, salt,
815: iterationCount);
816: }
817:
818: CipherInputStream cIn = new CipherInputStream(dIn, cipher);
819:
820: DigestInputStream dgIn = new DigestInputStream(cIn,
821: new SHA1Digest());
822:
823: this .loadStore(dgIn);
824:
825: Digest dig = dgIn.getDigest();
826: int digSize = dig.getDigestSize();
827: byte[] hash = new byte[digSize];
828: byte[] oldHash = new byte[digSize];
829:
830: dig.doFinal(hash, 0);
831:
832: for (int i = 0; i != digSize; i++) {
833: oldHash[i] = (byte) cIn.read();
834: }
835:
836: if (!this .isSameAs(hash, oldHash)) {
837: table.clear();
838: throw new IOException(
839: "KeyStore integrity check failed.");
840: }
841: }
842:
843: public void engineStore(OutputStream stream, char[] password)
844: throws IOException {
845: Cipher cipher;
846: DataOutputStream dOut = new DataOutputStream(stream);
847: byte[] salt = new byte[STORE_SALT_SIZE];
848: int iterationCount = MIN_ITERATIONS
849: + (random.nextInt() & 0x3ff);
850:
851: random.nextBytes(salt);
852:
853: dOut.writeInt(STORE_VERSION);
854: dOut.writeInt(salt.length);
855: dOut.write(salt);
856: dOut.writeInt(iterationCount);
857:
858: cipher = this
859: .makePBECipher(STORE_CIPHER, Cipher.ENCRYPT_MODE,
860: password, salt, iterationCount);
861:
862: CipherOutputStream cOut = new CipherOutputStream(dOut,
863: cipher);
864: DigestOutputStream dgOut = new DigestOutputStream(cOut,
865: new SHA1Digest());
866:
867: this .saveStore(dgOut);
868:
869: Digest dig = dgOut.getDigest();
870: byte[] hash = new byte[dig.getDigestSize()];
871:
872: dig.doFinal(hash, 0);
873:
874: cOut.write(hash);
875:
876: cOut.close();
877: }
878: }
879: }
|