|
43 | 43 | import javax.crypto.NoSuchPaddingException; |
44 | 44 | import javax.crypto.SecretKey; |
45 | 45 | import javax.crypto.SecretKeyFactory; |
| 46 | +import javax.crypto.interfaces.PBEKey; |
46 | 47 | import javax.crypto.spec.SecretKeySpec; |
47 | 48 |
|
48 | 49 | import org.bouncycastle.asn1.ASN1Encodable; |
|
58 | 59 | import org.bouncycastle.asn1.bc.ObjectStore; |
59 | 60 | import org.bouncycastle.asn1.bc.ObjectStoreData; |
60 | 61 | import org.bouncycastle.asn1.bc.ObjectStoreIntegrityCheck; |
| 62 | +import org.bouncycastle.asn1.bc.PbkdKeyData; |
61 | 63 | import org.bouncycastle.asn1.bc.PbkdMacIntegrityCheck; |
62 | 64 | import org.bouncycastle.asn1.bc.SecretKeyData; |
63 | 65 | import org.bouncycastle.asn1.bc.SignatureCheck; |
@@ -165,6 +167,7 @@ private static String getPublicKeyAlg(ASN1ObjectIdentifier oid) |
165 | 167 | private final static BigInteger SECRET_KEY = BigInteger.valueOf(2); |
166 | 168 | private final static BigInteger PROTECTED_PRIVATE_KEY = BigInteger.valueOf(3); |
167 | 169 | private final static BigInteger PROTECTED_SECRET_KEY = BigInteger.valueOf(4); |
| 170 | + private final static BigInteger PBKDF_KEY = BigInteger.valueOf(5); |
168 | 171 |
|
169 | 172 | private final JcaJceHelper helper; |
170 | 173 | private final Map<String, ObjectData> entries = new HashMap<String, ObjectData>(); |
@@ -236,6 +239,26 @@ else if (ent.getType().equals(SECRET_KEY) || ent.getType().equals(PROTECTED_SECR |
236 | 239 | throw new UnrecoverableKeyException("BCFKS KeyStore unable to recover secret key (" + alias + "): " + e.getMessage()); |
237 | 240 | } |
238 | 241 | } |
| 242 | + else if (ent.getType().equals(PBKDF_KEY)) |
| 243 | + { |
| 244 | + EncryptedSecretKeyData encKeyData = EncryptedSecretKeyData.getInstance(ent.getData()); |
| 245 | + |
| 246 | + try |
| 247 | + { |
| 248 | + PbkdKeyData keyData = PbkdKeyData.getInstance(decryptData("SECRET_KEY_ENCRYPTION", encKeyData.getKeyEncryptionAlgorithm(), password, encKeyData.getEncryptedKeyData())); |
| 249 | + |
| 250 | + return new RecoveredPBEKey( |
| 251 | + keyData.getKeyAlgorithm(), |
| 252 | + bytesToChars(keyData.getPassword()), |
| 253 | + keyData.getSalt(), |
| 254 | + keyData.getIterationCount(), |
| 255 | + keyData.getKeyEncoding()); |
| 256 | + } |
| 257 | + catch (Exception e) |
| 258 | + { |
| 259 | + throw new UnrecoverableKeyException("BCFKS KeyStore unable to recover PBE key (" + alias + "): " + e.getMessage()); |
| 260 | + } |
| 261 | + } |
239 | 262 | else |
240 | 263 | { |
241 | 264 | throw new UnrecoverableKeyException("BCFKS KeyStore unable to recover secret key (" + alias + "): type not recognized"); |
@@ -405,6 +428,56 @@ public void engineSetKeyEntry(String alias, Key key, char[] password, Certificat |
405 | 428 | throw new ExtKeyStoreException("BCFKS KeyStore exception storing private key: " + e.toString(), e); |
406 | 429 | } |
407 | 430 | } |
| 431 | + else if (key instanceof PBEKey) |
| 432 | + { |
| 433 | + if (chain != null) |
| 434 | + { |
| 435 | + throw new KeyStoreException("BCFKS KeyStore cannot store certificate chain with PBE key."); |
| 436 | + } |
| 437 | + |
| 438 | + try |
| 439 | + { |
| 440 | + PBEKey pbeKey = (PBEKey)key; |
| 441 | + PbkdKeyData pbeData = new PbkdKeyData( |
| 442 | + pbeKey.getAlgorithm(), |
| 443 | + charsToBytes(pbeKey.getPassword()), |
| 444 | + pbeKey.getSalt(), |
| 445 | + pbeKey.getIterationCount(), |
| 446 | + pbeKey.getEncoded()); |
| 447 | + |
| 448 | + KeyDerivationFunc pbkdAlgId = generatePkbdAlgorithmIdentifier(PKCSObjectIdentifiers.id_PBKDF2, 256 / 8); |
| 449 | + byte[] keyBytes = generateKey(pbkdAlgId, "SECRET_KEY_ENCRYPTION", ((password != null) ? password : new char[0]), 32); |
| 450 | + |
| 451 | + EncryptedSecretKeyData keyData; |
| 452 | + if (storeEncryptionAlgorithm.equals(NISTObjectIdentifiers.id_aes256_CCM)) |
| 453 | + { |
| 454 | + Cipher c = createCipher("AES/CCM/NoPadding", keyBytes); |
| 455 | + |
| 456 | + byte[] encryptedKey = c.doFinal(pbeData.getEncoded()); |
| 457 | + |
| 458 | + AlgorithmParameters algParams = c.getParameters(); |
| 459 | + |
| 460 | + PBES2Parameters pbeParams = new PBES2Parameters(pbkdAlgId, new EncryptionScheme(NISTObjectIdentifiers.id_aes256_CCM, CCMParameters.getInstance(algParams.getEncoded()))); |
| 461 | + |
| 462 | + keyData = new EncryptedSecretKeyData(new AlgorithmIdentifier(PKCSObjectIdentifiers.id_PBES2, pbeParams), encryptedKey); |
| 463 | + } |
| 464 | + else |
| 465 | + { |
| 466 | + Cipher c = createCipher("AESKWP", keyBytes); |
| 467 | + |
| 468 | + byte[] encryptedKey = c.doFinal(pbeData.getEncoded()); |
| 469 | + |
| 470 | + PBES2Parameters pbeParams = new PBES2Parameters(pbkdAlgId, new EncryptionScheme(NISTObjectIdentifiers.id_aes256_wrap_pad)); |
| 471 | + |
| 472 | + keyData = new EncryptedSecretKeyData(new AlgorithmIdentifier(PKCSObjectIdentifiers.id_PBES2, pbeParams), encryptedKey); |
| 473 | + } |
| 474 | + entries.put(alias, new ObjectData(PBKDF_KEY, alias, creationDate, lastEditDate, keyData.getEncoded(), null)); |
| 475 | + } |
| 476 | + catch (Exception e) |
| 477 | + { |
| 478 | + throw new ExtKeyStoreException("BCFKS KeyStore exception storing PBE key: " + e.toString(), e); |
| 479 | + } |
| 480 | + } |
408 | 481 | else if (key instanceof SecretKey) |
409 | 482 | { |
410 | 483 | if (chain != null) |
@@ -495,6 +568,35 @@ private Cipher createCipher(String algorithm, byte[] keyBytes) |
495 | 568 | return c; |
496 | 569 | } |
497 | 570 |
|
| 571 | + private static byte[] charsToBytes(char[] chars) |
| 572 | + { |
| 573 | + if (chars == null) |
| 574 | + { |
| 575 | + return new byte[0]; |
| 576 | + } |
| 577 | + byte[] bytes = new byte[chars.length * 2]; |
| 578 | + for (int i = 0; i != chars.length; i++) |
| 579 | + { |
| 580 | + bytes[2 * i] = (byte)(chars[i] >>> 8); |
| 581 | + bytes[2 * i + 1] = (byte)chars[i]; |
| 582 | + } |
| 583 | + return bytes; |
| 584 | + } |
| 585 | + |
| 586 | + private static char[] bytesToChars(byte[] bytes) |
| 587 | + { |
| 588 | + if (bytes == null || bytes.length == 0) |
| 589 | + { |
| 590 | + return new char[0]; |
| 591 | + } |
| 592 | + char[] chars = new char[bytes.length / 2]; |
| 593 | + for (int i = 0; i != chars.length; i++) |
| 594 | + { |
| 595 | + chars[i] = (char)(((bytes[2 * i] & 0xff) << 8) | (bytes[2 * i + 1] & 0xff)); |
| 596 | + } |
| 597 | + return chars; |
| 598 | + } |
| 599 | + |
498 | 600 | private SecureRandom getDefaultSecureRandom() |
499 | 601 | { |
500 | 602 | return CryptoServicesRegistrar.getSecureRandom(); |
@@ -1672,4 +1774,53 @@ public Throwable getCause() |
1672 | 1774 | return cause; |
1673 | 1775 | } |
1674 | 1776 | } |
| 1777 | + |
| 1778 | + private static class RecoveredPBEKey |
| 1779 | + implements PBEKey |
| 1780 | + { |
| 1781 | + private final String algorithm; |
| 1782 | + private final char[] password; |
| 1783 | + private final byte[] salt; |
| 1784 | + private final int iterationCount; |
| 1785 | + private final byte[] encoded; |
| 1786 | + |
| 1787 | + RecoveredPBEKey(String algorithm, char[] password, byte[] salt, int iterationCount, byte[] encoded) |
| 1788 | + { |
| 1789 | + this.algorithm = algorithm; |
| 1790 | + this.password = password; |
| 1791 | + this.salt = (salt != null) ? (byte[])salt.clone() : null; |
| 1792 | + this.iterationCount = iterationCount; |
| 1793 | + this.encoded = (encoded != null) ? (byte[])encoded.clone() : null; |
| 1794 | + } |
| 1795 | + |
| 1796 | + public String getAlgorithm() |
| 1797 | + { |
| 1798 | + return algorithm; |
| 1799 | + } |
| 1800 | + |
| 1801 | + public String getFormat() |
| 1802 | + { |
| 1803 | + return (encoded != null) ? "RAW" : null; |
| 1804 | + } |
| 1805 | + |
| 1806 | + public byte[] getEncoded() |
| 1807 | + { |
| 1808 | + return (encoded != null) ? (byte[])encoded.clone() : null; |
| 1809 | + } |
| 1810 | + |
| 1811 | + public char[] getPassword() |
| 1812 | + { |
| 1813 | + return (char[])password.clone(); |
| 1814 | + } |
| 1815 | + |
| 1816 | + public byte[] getSalt() |
| 1817 | + { |
| 1818 | + return (salt != null) ? (byte[])salt.clone() : null; |
| 1819 | + } |
| 1820 | + |
| 1821 | + public int getIterationCount() |
| 1822 | + { |
| 1823 | + return iterationCount; |
| 1824 | + } |
| 1825 | + } |
1675 | 1826 | } |
0 commit comments