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