1package org.bouncycastle.jce.provider; 2 3import java.io.ByteArrayInputStream; 4import java.io.ByteArrayOutputStream; 5import java.io.DataInputStream; 6import java.io.DataOutputStream; 7import java.io.IOException; 8import java.io.InputStream; 9import java.io.OutputStream; 10import java.security.Key; 11import java.security.KeyFactory; 12import java.security.KeyStoreException; 13import java.security.KeyStoreSpi; 14import java.security.NoSuchAlgorithmException; 15import java.security.NoSuchProviderException; 16import java.security.PrivateKey; 17import java.security.PublicKey; 18import java.security.SecureRandom; 19import java.security.UnrecoverableKeyException; 20import java.security.cert.Certificate; 21import java.security.cert.CertificateEncodingException; 22import java.security.cert.CertificateException; 23import java.security.cert.CertificateFactory; 24import java.security.spec.KeySpec; 25import java.security.spec.PKCS8EncodedKeySpec; 26import java.security.spec.X509EncodedKeySpec; 27import java.util.Date; 28import java.util.Enumeration; 29import java.util.Hashtable; 30 31import javax.crypto.Cipher; 32import javax.crypto.CipherInputStream; 33import javax.crypto.CipherOutputStream; 34import javax.crypto.SecretKeyFactory; 35import javax.crypto.spec.PBEKeySpec; 36import javax.crypto.spec.PBEParameterSpec; 37import javax.crypto.spec.SecretKeySpec; 38 39import org.bouncycastle.crypto.CipherParameters; 40import org.bouncycastle.crypto.Digest; 41import org.bouncycastle.crypto.PBEParametersGenerator; 42// BEGIN android-added 43import org.bouncycastle.crypto.digests.AndroidDigestFactory; 44// END android-added 45// BEGIN android-removed 46// import org.bouncycastle.crypto.digests.SHA1Digest; 47// END android-removed 48import org.bouncycastle.crypto.generators.PKCS12ParametersGenerator; 49import org.bouncycastle.crypto.io.DigestInputStream; 50import org.bouncycastle.crypto.io.DigestOutputStream; 51import org.bouncycastle.crypto.io.MacInputStream; 52import org.bouncycastle.crypto.io.MacOutputStream; 53import org.bouncycastle.crypto.macs.HMac; 54import org.bouncycastle.jce.interfaces.BCKeyStore; 55import org.bouncycastle.util.Arrays; 56import org.bouncycastle.util.io.Streams; 57import org.bouncycastle.util.io.TeeOutputStream; 58 59public class JDKKeyStore 60 extends KeyStoreSpi 61 implements BCKeyStore 62{ 63 private static final int STORE_VERSION = 2; 64 65 private static final int STORE_SALT_SIZE = 20; 66 private static final String STORE_CIPHER = "PBEWithSHAAndTwofish-CBC"; 67 68 private static final int KEY_SALT_SIZE = 20; 69 private static final int MIN_ITERATIONS = 1024; 70 71 private static final String KEY_CIPHER = "PBEWithSHAAnd3-KeyTripleDES-CBC"; 72 73 // 74 // generic object types 75 // 76 static final int NULL = 0; 77 static final int CERTIFICATE = 1; 78 static final int KEY = 2; 79 static final int SECRET = 3; 80 static final int SEALED = 4; 81 82 // 83 // key types 84 // 85 static final int KEY_PRIVATE = 0; 86 static final int KEY_PUBLIC = 1; 87 static final int KEY_SECRET = 2; 88 89 protected Hashtable table = new Hashtable(); 90 91 protected SecureRandom random = new SecureRandom(); 92 93 public JDKKeyStore() 94 { 95 } 96 97 private class StoreEntry 98 { 99 int type; 100 String alias; 101 Object obj; 102 Certificate[] certChain; 103 Date date = new Date(); 104 105 StoreEntry( 106 String alias, 107 Certificate obj) 108 { 109 this.type = CERTIFICATE; 110 this.alias = alias; 111 this.obj = obj; 112 this.certChain = null; 113 } 114 115 StoreEntry( 116 String alias, 117 byte[] obj, 118 Certificate[] certChain) 119 { 120 this.type = SECRET; 121 this.alias = alias; 122 this.obj = obj; 123 this.certChain = certChain; 124 } 125 126 StoreEntry( 127 String alias, 128 Key key, 129 char[] password, 130 Certificate[] certChain) 131 throws Exception 132 { 133 this.type = SEALED; 134 this.alias = alias; 135 this.certChain = certChain; 136 137 byte[] salt = new byte[KEY_SALT_SIZE]; 138 139 random.setSeed(System.currentTimeMillis()); 140 random.nextBytes(salt); 141 142 int iterationCount = MIN_ITERATIONS + (random.nextInt() & 0x3ff); 143 144 145 ByteArrayOutputStream bOut = new ByteArrayOutputStream(); 146 DataOutputStream dOut = new DataOutputStream(bOut); 147 148 dOut.writeInt(salt.length); 149 dOut.write(salt); 150 dOut.writeInt(iterationCount); 151 152 Cipher cipher = makePBECipher(KEY_CIPHER, Cipher.ENCRYPT_MODE, password, salt, iterationCount); 153 CipherOutputStream cOut = new CipherOutputStream(dOut, cipher); 154 155 dOut = new DataOutputStream(cOut); 156 157 encodeKey(key, dOut); 158 159 dOut.close(); 160 161 obj = bOut.toByteArray(); 162 } 163 164 StoreEntry( 165 String alias, 166 Date date, 167 int type, 168 Object obj) 169 { 170 this.alias = alias; 171 this.date = date; 172 this.type = type; 173 this.obj = obj; 174 } 175 176 StoreEntry( 177 String alias, 178 Date date, 179 int type, 180 Object obj, 181 Certificate[] certChain) 182 { 183 this.alias = alias; 184 this.date = date; 185 this.type = type; 186 this.obj = obj; 187 this.certChain = certChain; 188 } 189 190 int getType() 191 { 192 return type; 193 } 194 195 String getAlias() 196 { 197 return alias; 198 } 199 200 Object getObject() 201 { 202 return obj; 203 } 204 205 Object getObject( 206 char[] password) 207 throws NoSuchAlgorithmException, UnrecoverableKeyException 208 { 209 if (password == null || password.length == 0) 210 { 211 if (obj instanceof Key) 212 { 213 return obj; 214 } 215 } 216 217 if (type == SEALED) 218 { 219 ByteArrayInputStream bIn = new ByteArrayInputStream((byte[])obj); 220 DataInputStream dIn = new DataInputStream(bIn); 221 222 try 223 { 224 byte[] salt = new byte[dIn.readInt()]; 225 226 dIn.readFully(salt); 227 228 int iterationCount = dIn.readInt(); 229 230 Cipher cipher = makePBECipher(KEY_CIPHER, Cipher.DECRYPT_MODE, password, salt, iterationCount); 231 232 CipherInputStream cIn = new CipherInputStream(dIn, cipher); 233 234 try 235 { 236 return decodeKey(new DataInputStream(cIn)); 237 } 238 catch (Exception x) 239 { 240 bIn = new ByteArrayInputStream((byte[])obj); 241 dIn = new DataInputStream(bIn); 242 243 salt = new byte[dIn.readInt()]; 244 245 dIn.readFully(salt); 246 247 iterationCount = dIn.readInt(); 248 249 cipher = makePBECipher("Broken" + KEY_CIPHER, Cipher.DECRYPT_MODE, password, salt, iterationCount); 250 251 cIn = new CipherInputStream(dIn, cipher); 252 253 Key k = null; 254 255 try 256 { 257 k = decodeKey(new DataInputStream(cIn)); 258 } 259 catch (Exception y) 260 { 261 bIn = new ByteArrayInputStream((byte[])obj); 262 dIn = new DataInputStream(bIn); 263 264 salt = new byte[dIn.readInt()]; 265 266 dIn.readFully(salt); 267 268 iterationCount = dIn.readInt(); 269 270 cipher = makePBECipher("Old" + KEY_CIPHER, Cipher.DECRYPT_MODE, password, salt, iterationCount); 271 272 cIn = new CipherInputStream(dIn, cipher); 273 274 k = decodeKey(new DataInputStream(cIn)); 275 } 276 277 // 278 // reencrypt key with correct cipher. 279 // 280 if (k != null) 281 { 282 ByteArrayOutputStream bOut = new ByteArrayOutputStream(); 283 DataOutputStream dOut = new DataOutputStream(bOut); 284 285 dOut.writeInt(salt.length); 286 dOut.write(salt); 287 dOut.writeInt(iterationCount); 288 289 Cipher out = makePBECipher(KEY_CIPHER, Cipher.ENCRYPT_MODE, password, salt, iterationCount); 290 CipherOutputStream cOut = new CipherOutputStream(dOut, out); 291 292 dOut = new DataOutputStream(cOut); 293 294 encodeKey(k, dOut); 295 296 dOut.close(); 297 298 obj = bOut.toByteArray(); 299 300 return k; 301 } 302 else 303 { 304 throw new UnrecoverableKeyException("no match"); 305 } 306 } 307 } 308 catch (Exception e) 309 { 310 throw new UnrecoverableKeyException("no match"); 311 } 312 } 313 else 314 { 315 throw new RuntimeException("forget something!"); 316 // TODO 317 // if we get to here key was saved as byte data, which 318 // according to the docs means it must be a private key 319 // in EncryptedPrivateKeyInfo (PKCS8 format), later... 320 // 321 } 322 } 323 324 Certificate[] getCertificateChain() 325 { 326 return certChain; 327 } 328 329 Date getDate() 330 { 331 return date; 332 } 333 } 334 335 private void encodeCertificate( 336 Certificate cert, 337 DataOutputStream dOut) 338 throws IOException 339 { 340 try 341 { 342 byte[] cEnc = cert.getEncoded(); 343 344 dOut.writeUTF(cert.getType()); 345 dOut.writeInt(cEnc.length); 346 dOut.write(cEnc); 347 } 348 catch (CertificateEncodingException ex) 349 { 350 throw new IOException(ex.toString()); 351 } 352 } 353 354 private Certificate decodeCertificate( 355 DataInputStream dIn) 356 throws IOException 357 { 358 String type = dIn.readUTF(); 359 byte[] cEnc = new byte[dIn.readInt()]; 360 361 dIn.readFully(cEnc); 362 363 try 364 { 365 CertificateFactory cFact = CertificateFactory.getInstance(type, BouncyCastleProvider.PROVIDER_NAME); 366 ByteArrayInputStream bIn = new ByteArrayInputStream(cEnc); 367 368 return cFact.generateCertificate(bIn); 369 } 370 catch (NoSuchProviderException ex) 371 { 372 throw new IOException(ex.toString()); 373 } 374 catch (CertificateException ex) 375 { 376 throw new IOException(ex.toString()); 377 } 378 } 379 380 private void encodeKey( 381 Key key, 382 DataOutputStream dOut) 383 throws IOException 384 { 385 byte[] enc = key.getEncoded(); 386 387 if (key instanceof PrivateKey) 388 { 389 dOut.write(KEY_PRIVATE); 390 } 391 else if (key instanceof PublicKey) 392 { 393 dOut.write(KEY_PUBLIC); 394 } 395 else 396 { 397 dOut.write(KEY_SECRET); 398 } 399 400 dOut.writeUTF(key.getFormat()); 401 dOut.writeUTF(key.getAlgorithm()); 402 dOut.writeInt(enc.length); 403 dOut.write(enc); 404 } 405 406 private Key decodeKey( 407 DataInputStream dIn) 408 throws IOException 409 { 410 int keyType = dIn.read(); 411 String format = dIn.readUTF(); 412 String algorithm = dIn.readUTF(); 413 byte[] enc = new byte[dIn.readInt()]; 414 KeySpec spec; 415 416 dIn.readFully(enc); 417 418 if (format.equals("PKCS#8") || format.equals("PKCS8")) 419 { 420 spec = new PKCS8EncodedKeySpec(enc); 421 } 422 else if (format.equals("X.509") || format.equals("X509")) 423 { 424 spec = new X509EncodedKeySpec(enc); 425 } 426 else if (format.equals("RAW")) 427 { 428 return new SecretKeySpec(enc, algorithm); 429 } 430 else 431 { 432 throw new IOException("Key format " + format + " not recognised!"); 433 } 434 435 try 436 { 437 switch (keyType) 438 { 439 case KEY_PRIVATE: 440 return KeyFactory.getInstance(algorithm, BouncyCastleProvider.PROVIDER_NAME).generatePrivate(spec); 441 case KEY_PUBLIC: 442 return KeyFactory.getInstance(algorithm, BouncyCastleProvider.PROVIDER_NAME).generatePublic(spec); 443 case KEY_SECRET: 444 return SecretKeyFactory.getInstance(algorithm, BouncyCastleProvider.PROVIDER_NAME).generateSecret(spec); 445 default: 446 throw new IOException("Key type " + keyType + " not recognised!"); 447 } 448 } 449 catch (Exception e) 450 { 451 throw new IOException("Exception creating key: " + e.toString()); 452 } 453 } 454 455 protected Cipher makePBECipher( 456 String algorithm, 457 int mode, 458 char[] password, 459 byte[] salt, 460 int iterationCount) 461 throws IOException 462 { 463 try 464 { 465 PBEKeySpec pbeSpec = new PBEKeySpec(password); 466 SecretKeyFactory keyFact = SecretKeyFactory.getInstance(algorithm, BouncyCastleProvider.PROVIDER_NAME); 467 PBEParameterSpec defParams = new PBEParameterSpec(salt, iterationCount); 468 469 Cipher cipher = Cipher.getInstance(algorithm, BouncyCastleProvider.PROVIDER_NAME); 470 471 cipher.init(mode, keyFact.generateSecret(pbeSpec), defParams); 472 473 return cipher; 474 } 475 catch (Exception e) 476 { 477 throw new IOException("Error initialising store of key store: " + e); 478 } 479 } 480 481 public void setRandom( 482 SecureRandom rand) 483 { 484 this.random = rand; 485 } 486 487 public Enumeration engineAliases() 488 { 489 return table.keys(); 490 } 491 492 public boolean engineContainsAlias( 493 String alias) 494 { 495 return (table.get(alias) != null); 496 } 497 498 public void engineDeleteEntry( 499 String alias) 500 throws KeyStoreException 501 { 502 Object entry = table.get(alias); 503 504 if (entry == null) 505 { 506 // BEGIN android-removed 507 // Only throw if there is a problem removing, not if missing 508 // throw new KeyStoreException("no such entry as " + alias); 509 // END android-removed 510 // BEGIN android-added 511 return; 512 // END android-added 513 } 514 515 table.remove(alias); 516 } 517 518 public Certificate engineGetCertificate( 519 String alias) 520 { 521 StoreEntry entry = (StoreEntry)table.get(alias); 522 523 if (entry != null) 524 { 525 if (entry.getType() == CERTIFICATE) 526 { 527 return (Certificate)entry.getObject(); 528 } 529 else 530 { 531 Certificate[] chain = entry.getCertificateChain(); 532 533 if (chain != null) 534 { 535 return chain[0]; 536 } 537 } 538 } 539 540 return null; 541 } 542 543 public String engineGetCertificateAlias( 544 Certificate cert) 545 { 546 Enumeration e = table.elements(); 547 while (e.hasMoreElements()) 548 { 549 StoreEntry entry = (StoreEntry)e.nextElement(); 550 551 if (entry.getObject() instanceof Certificate) 552 { 553 Certificate c = (Certificate)entry.getObject(); 554 555 if (c.equals(cert)) 556 { 557 return entry.getAlias(); 558 } 559 } 560 else 561 { 562 Certificate[] chain = entry.getCertificateChain(); 563 564 if (chain != null && chain[0].equals(cert)) 565 { 566 return entry.getAlias(); 567 } 568 } 569 } 570 571 return null; 572 } 573 574 public Certificate[] engineGetCertificateChain( 575 String alias) 576 { 577 StoreEntry entry = (StoreEntry)table.get(alias); 578 579 if (entry != null) 580 { 581 return entry.getCertificateChain(); 582 } 583 584 return null; 585 } 586 587 public Date engineGetCreationDate(String alias) 588 { 589 StoreEntry entry = (StoreEntry)table.get(alias); 590 591 if (entry != null) 592 { 593 return entry.getDate(); 594 } 595 596 return null; 597 } 598 599 public Key engineGetKey( 600 String alias, 601 char[] password) 602 throws NoSuchAlgorithmException, UnrecoverableKeyException 603 { 604 StoreEntry entry = (StoreEntry)table.get(alias); 605 606 if (entry == null || entry.getType() == CERTIFICATE) 607 { 608 return null; 609 } 610 611 return (Key)entry.getObject(password); 612 } 613 614 public boolean engineIsCertificateEntry( 615 String alias) 616 { 617 StoreEntry entry = (StoreEntry)table.get(alias); 618 619 if (entry != null && entry.getType() == CERTIFICATE) 620 { 621 return true; 622 } 623 624 return false; 625 } 626 627 public boolean engineIsKeyEntry( 628 String alias) 629 { 630 StoreEntry entry = (StoreEntry)table.get(alias); 631 632 if (entry != null && entry.getType() != CERTIFICATE) 633 { 634 return true; 635 } 636 637 return false; 638 } 639 640 public void engineSetCertificateEntry( 641 String alias, 642 Certificate cert) 643 throws KeyStoreException 644 { 645 StoreEntry entry = (StoreEntry)table.get(alias); 646 647 if (entry != null && entry.getType() != CERTIFICATE) 648 { 649 throw new KeyStoreException("key store already has a key entry with alias " + alias); 650 } 651 652 table.put(alias, new StoreEntry(alias, cert)); 653 } 654 655 public void engineSetKeyEntry( 656 String alias, 657 byte[] key, 658 Certificate[] chain) 659 throws KeyStoreException 660 { 661 table.put(alias, new StoreEntry(alias, key, chain)); 662 } 663 664 public void engineSetKeyEntry( 665 String alias, 666 Key key, 667 char[] password, 668 Certificate[] chain) 669 throws KeyStoreException 670 { 671 if ((key instanceof PrivateKey) && (chain == null)) 672 { 673 throw new KeyStoreException("no certificate chain for private key"); 674 } 675 676 try 677 { 678 table.put(alias, new StoreEntry(alias, key, password, chain)); 679 } 680 catch (Exception e) 681 { 682 throw new KeyStoreException(e.toString()); 683 } 684 } 685 686 public int engineSize() 687 { 688 return table.size(); 689 } 690 691 protected void loadStore( 692 InputStream in) 693 throws IOException 694 { 695 DataInputStream dIn = new DataInputStream(in); 696 int type = dIn.read(); 697 698 while (type > NULL) 699 { 700 String alias = dIn.readUTF(); 701 Date date = new Date(dIn.readLong()); 702 int chainLength = dIn.readInt(); 703 Certificate[] chain = null; 704 705 if (chainLength != 0) 706 { 707 chain = new Certificate[chainLength]; 708 709 for (int i = 0; i != chainLength; i++) 710 { 711 chain[i] = decodeCertificate(dIn); 712 } 713 } 714 715 switch (type) 716 { 717 case CERTIFICATE: 718 Certificate cert = decodeCertificate(dIn); 719 720 table.put(alias, new StoreEntry(alias, date, CERTIFICATE, cert)); 721 break; 722 case KEY: 723 Key key = decodeKey(dIn); 724 table.put(alias, new StoreEntry(alias, date, KEY, key, chain)); 725 break; 726 case SECRET: 727 case SEALED: 728 byte[] b = new byte[dIn.readInt()]; 729 730 dIn.readFully(b); 731 table.put(alias, new StoreEntry(alias, date, type, b, chain)); 732 break; 733 default: 734 throw new RuntimeException("Unknown object type in store."); 735 } 736 737 type = dIn.read(); 738 } 739 } 740 741 protected void saveStore( 742 OutputStream out) 743 throws IOException 744 { 745 Enumeration e = table.elements(); 746 DataOutputStream dOut = new DataOutputStream(out); 747 748 while (e.hasMoreElements()) 749 { 750 StoreEntry entry = (StoreEntry)e.nextElement(); 751 752 dOut.write(entry.getType()); 753 dOut.writeUTF(entry.getAlias()); 754 dOut.writeLong(entry.getDate().getTime()); 755 756 Certificate[] chain = entry.getCertificateChain(); 757 if (chain == null) 758 { 759 dOut.writeInt(0); 760 } 761 else 762 { 763 dOut.writeInt(chain.length); 764 for (int i = 0; i != chain.length; i++) 765 { 766 encodeCertificate(chain[i], dOut); 767 } 768 } 769 770 switch (entry.getType()) 771 { 772 case CERTIFICATE: 773 encodeCertificate((Certificate)entry.getObject(), dOut); 774 break; 775 case KEY: 776 encodeKey((Key)entry.getObject(), dOut); 777 break; 778 case SEALED: 779 case SECRET: 780 byte[] b = (byte[])entry.getObject(); 781 782 dOut.writeInt(b.length); 783 dOut.write(b); 784 break; 785 default: 786 throw new RuntimeException("Unknown object type in store."); 787 } 788 } 789 790 dOut.write(NULL); 791 } 792 793 public void engineLoad( 794 InputStream stream, 795 char[] password) 796 throws IOException 797 { 798 table.clear(); 799 800 if (stream == null) // just initialising 801 { 802 return; 803 } 804 805 DataInputStream dIn = new DataInputStream(stream); 806 int version = dIn.readInt(); 807 808 if (version != STORE_VERSION) 809 { 810 if (version != 0 && version != 1) 811 { 812 throw new IOException("Wrong version of key store."); 813 } 814 } 815 816 int saltLength = dIn.readInt(); 817 if (saltLength <= 0) 818 { 819 throw new IOException("Invalid salt detected"); 820 } 821 822 byte[] salt = new byte[saltLength]; 823 824 dIn.readFully(salt); 825 826 int iterationCount = dIn.readInt(); 827 828 // 829 // we only do an integrity check if the password is provided. 830 // 831 // BEGIN android-changed 832 HMac hMac = new HMac(AndroidDigestFactory.getSHA1()); 833 // END android-changed 834 if (password != null && password.length != 0) 835 { 836 byte[] passKey = PBEParametersGenerator.PKCS12PasswordToBytes(password); 837 838 // BEGIN android-changed 839 PBEParametersGenerator pbeGen = new PKCS12ParametersGenerator(AndroidDigestFactory.getSHA1()); 840 // END android-changed 841 pbeGen.init(passKey, salt, iterationCount); 842 843 CipherParameters macParams; 844 845 if (version != 2) 846 { 847 macParams = pbeGen.generateDerivedMacParameters(hMac.getMacSize()); 848 } 849 else 850 { 851 macParams = pbeGen.generateDerivedMacParameters(hMac.getMacSize() * 8); 852 } 853 854 Arrays.fill(passKey, (byte)0); 855 856 hMac.init(macParams); 857 MacInputStream mIn = new MacInputStream(dIn, hMac); 858 859 loadStore(mIn); 860 861 // Finalise our mac calculation 862 byte[] mac = new byte[hMac.getMacSize()]; 863 hMac.doFinal(mac, 0); 864 865 // TODO Should this actually be reading the remainder of the stream? 866 // Read the original mac from the stream 867 byte[] oldMac = new byte[hMac.getMacSize()]; 868 dIn.readFully(oldMac); 869 870 if (!Arrays.constantTimeAreEqual(mac, oldMac)) 871 { 872 table.clear(); 873 throw new IOException("KeyStore integrity check failed."); 874 } 875 } 876 else 877 { 878 loadStore(dIn); 879 880 // TODO Should this actually be reading the remainder of the stream? 881 // Parse the original mac from the stream too 882 byte[] oldMac = new byte[hMac.getMacSize()]; 883 dIn.readFully(oldMac); 884 } 885 } 886 887 888 public void engineStore(OutputStream stream, char[] password) 889 throws IOException 890 { 891 DataOutputStream dOut = new DataOutputStream(stream); 892 byte[] salt = new byte[STORE_SALT_SIZE]; 893 int iterationCount = MIN_ITERATIONS + (random.nextInt() & 0x3ff); 894 895 random.nextBytes(salt); 896 897 dOut.writeInt(STORE_VERSION); 898 dOut.writeInt(salt.length); 899 dOut.write(salt); 900 dOut.writeInt(iterationCount); 901 902 // BEGIN android-changed 903 HMac hMac = new HMac(AndroidDigestFactory.getSHA1()); 904 MacOutputStream mOut = new MacOutputStream(hMac); 905 PBEParametersGenerator pbeGen = new PKCS12ParametersGenerator(AndroidDigestFactory.getSHA1()); 906 // END android-changed 907 byte[] passKey = PBEParametersGenerator.PKCS12PasswordToBytes(password); 908 909 pbeGen.init(passKey, salt, iterationCount); 910 911 hMac.init(pbeGen.generateDerivedMacParameters(hMac.getMacSize() * 8)); 912 913 for (int i = 0; i != passKey.length; i++) 914 { 915 passKey[i] = 0; 916 } 917 918 saveStore(new TeeOutputStream(dOut, mOut)); 919 920 byte[] mac = new byte[hMac.getMacSize()]; 921 922 hMac.doFinal(mac, 0); 923 924 dOut.write(mac); 925 926 dOut.close(); 927 } 928 929 /** 930 * the BouncyCastle store. This wont work with the key tool as the 931 * store is stored encrypted on disk, so the password is mandatory, 932 * however if you hard drive is in a bad part of town and you absolutely, 933 * positively, don't want nobody peeking at your things, this is the 934 * one to use, no problem! After all in a Bouncy Castle nothing can 935 * touch you. 936 * 937 * Also referred to by the alias UBER. 938 */ 939 public static class BouncyCastleStore 940 extends JDKKeyStore 941 { 942 public void engineLoad( 943 InputStream stream, 944 char[] password) 945 throws IOException 946 { 947 table.clear(); 948 949 if (stream == null) // just initialising 950 { 951 return; 952 } 953 954 DataInputStream dIn = new DataInputStream(stream); 955 int version = dIn.readInt(); 956 957 if (version != STORE_VERSION) 958 { 959 if (version != 0 && version != 1) 960 { 961 throw new IOException("Wrong version of key store."); 962 } 963 } 964 965 byte[] salt = new byte[dIn.readInt()]; 966 967 if (salt.length != STORE_SALT_SIZE) 968 { 969 throw new IOException("Key store corrupted."); 970 } 971 972 dIn.readFully(salt); 973 974 int iterationCount = dIn.readInt(); 975 976 if ((iterationCount < 0) || (iterationCount > 4 * MIN_ITERATIONS)) 977 { 978 throw new IOException("Key store corrupted."); 979 } 980 981 String cipherAlg; 982 if (version == 0) 983 { 984 cipherAlg = "Old" + STORE_CIPHER; 985 } 986 else 987 { 988 cipherAlg = STORE_CIPHER; 989 } 990 991 Cipher cipher = this.makePBECipher(cipherAlg, Cipher.DECRYPT_MODE, password, salt, iterationCount); 992 CipherInputStream cIn = new CipherInputStream(dIn, cipher); 993 994 // BEGIN android-changed 995 Digest dig = AndroidDigestFactory.getSHA1(); 996 // END android-changed 997 DigestInputStream dgIn = new DigestInputStream(cIn, dig); 998 999 this.loadStore(dgIn); 1000 1001 // Finalise our digest calculation 1002 byte[] hash = new byte[dig.getDigestSize()]; 1003 dig.doFinal(hash, 0); 1004 1005 // TODO Should this actually be reading the remainder of the stream? 1006 // Read the original digest from the stream 1007 byte[] oldHash = new byte[dig.getDigestSize()]; 1008 Streams.readFully(cIn, oldHash); 1009 1010 if (!Arrays.constantTimeAreEqual(hash, oldHash)) 1011 { 1012 table.clear(); 1013 throw new IOException("KeyStore integrity check failed."); 1014 } 1015 } 1016 1017 public void engineStore(OutputStream stream, char[] password) 1018 throws IOException 1019 { 1020 Cipher cipher; 1021 DataOutputStream dOut = new DataOutputStream(stream); 1022 byte[] salt = new byte[STORE_SALT_SIZE]; 1023 int iterationCount = MIN_ITERATIONS + (random.nextInt() & 0x3ff); 1024 1025 random.nextBytes(salt); 1026 1027 dOut.writeInt(STORE_VERSION); 1028 dOut.writeInt(salt.length); 1029 dOut.write(salt); 1030 dOut.writeInt(iterationCount); 1031 1032 cipher = this.makePBECipher(STORE_CIPHER, Cipher.ENCRYPT_MODE, password, salt, iterationCount); 1033 1034 CipherOutputStream cOut = new CipherOutputStream(dOut, cipher); 1035 // BEGIN android-changed 1036 DigestOutputStream dgOut = new DigestOutputStream(AndroidDigestFactory.getSHA1()); 1037 // END android-changed 1038 1039 this.saveStore(new TeeOutputStream(cOut, dgOut)); 1040 1041 byte[] dig = dgOut.getDigest(); 1042 1043 cOut.write(dig); 1044 1045 cOut.close(); 1046 } 1047 } 1048} 1049