1 package org.bouncycastle.jcajce.provider.keystore.bcfks; 2 3 import java.io.ByteArrayInputStream; 4 import java.io.IOException; 5 import java.io.InputStream; 6 import java.io.OutputStream; 7 import java.math.BigInteger; 8 import java.security.AlgorithmParameters; 9 import java.security.InvalidKeyException; 10 import java.security.Key; 11 import java.security.KeyFactory; 12 import java.security.KeyStore; 13 import java.security.KeyStoreException; 14 import java.security.KeyStoreSpi; 15 import java.security.NoSuchAlgorithmException; 16 import java.security.PrivateKey; 17 import java.security.SecureRandom; 18 import java.security.UnrecoverableKeyException; 19 import java.security.cert.Certificate; 20 import java.security.cert.CertificateEncodingException; 21 import java.security.cert.CertificateException; 22 import java.security.cert.CertificateFactory; 23 import java.security.cert.X509Certificate; 24 import java.security.spec.PKCS8EncodedKeySpec; 25 import java.text.ParseException; 26 import java.util.Date; 27 import java.util.Enumeration; 28 import java.util.HashMap; 29 import java.util.HashSet; 30 import java.util.Iterator; 31 import java.util.Map; 32 33 import javax.crypto.BadPaddingException; 34 import javax.crypto.Cipher; 35 import javax.crypto.IllegalBlockSizeException; 36 import javax.crypto.Mac; 37 import javax.crypto.NoSuchPaddingException; 38 import javax.crypto.SecretKey; 39 import javax.crypto.SecretKeyFactory; 40 import javax.crypto.spec.SecretKeySpec; 41 import javax.security.auth.callback.Callback; 42 import javax.security.auth.callback.CallbackHandler; 43 import javax.security.auth.callback.PasswordCallback; 44 import javax.security.auth.callback.UnsupportedCallbackException; 45 46 import org.bouncycastle.asn1.ASN1Encodable; 47 import org.bouncycastle.asn1.ASN1InputStream; 48 import org.bouncycastle.asn1.ASN1ObjectIdentifier; 49 import org.bouncycastle.asn1.DERNull; 50 import org.bouncycastle.asn1.bc.EncryptedObjectStoreData; 51 import org.bouncycastle.asn1.bc.EncryptedPrivateKeyData; 52 import org.bouncycastle.asn1.bc.EncryptedSecretKeyData; 53 import org.bouncycastle.asn1.bc.ObjectData; 54 import org.bouncycastle.asn1.bc.ObjectDataSequence; 55 import org.bouncycastle.asn1.bc.ObjectStore; 56 import org.bouncycastle.asn1.bc.ObjectStoreData; 57 import org.bouncycastle.asn1.bc.ObjectStoreIntegrityCheck; 58 import org.bouncycastle.asn1.bc.PbkdMacIntegrityCheck; 59 import org.bouncycastle.asn1.bc.SecretKeyData; 60 import org.bouncycastle.internal.asn1.cms.CCMParameters; 61 import org.bouncycastle.asn1.misc.MiscObjectIdentifiers; 62 import org.bouncycastle.asn1.misc.ScryptParams; 63 import org.bouncycastle.asn1.nist.NISTObjectIdentifiers; 64 import org.bouncycastle.asn1.oiw.OIWObjectIdentifiers; 65 import org.bouncycastle.asn1.pkcs.EncryptedPrivateKeyInfo; 66 import org.bouncycastle.asn1.pkcs.EncryptionScheme; 67 import org.bouncycastle.asn1.pkcs.KeyDerivationFunc; 68 import org.bouncycastle.asn1.pkcs.PBES2Parameters; 69 import org.bouncycastle.asn1.pkcs.PBKDF2Params; 70 import org.bouncycastle.asn1.pkcs.PKCSObjectIdentifiers; 71 import org.bouncycastle.asn1.pkcs.PrivateKeyInfo; 72 import org.bouncycastle.asn1.x509.AlgorithmIdentifier; 73 import org.bouncycastle.asn1.x9.X9ObjectIdentifiers; 74 import org.bouncycastle.crypto.CryptoServicesRegistrar; 75 import org.bouncycastle.crypto.PBEParametersGenerator; 76 import org.bouncycastle.crypto.digests.SHA3Digest; 77 import org.bouncycastle.crypto.digests.SHA512Digest; 78 import org.bouncycastle.crypto.generators.PKCS5S2ParametersGenerator; 79 import org.bouncycastle.crypto.generators.SCrypt; 80 import org.bouncycastle.crypto.params.KeyParameter; 81 import org.bouncycastle.crypto.util.PBKDF2Config; 82 import org.bouncycastle.crypto.util.PBKDFConfig; 83 import org.bouncycastle.crypto.util.ScryptConfig; 84 import org.bouncycastle.jce.provider.BouncyCastleProvider; 85 import org.bouncycastle.util.Arrays; 86 import org.bouncycastle.util.Strings; 87 88 class BcFKSKeyStoreSpi 89 extends KeyStoreSpi 90 { 91 private static final Map<String, ASN1ObjectIdentifier> oidMap = new HashMap<String, ASN1ObjectIdentifier>(); 92 private static final Map<ASN1ObjectIdentifier, String> publicAlgMap = new HashMap<ASN1ObjectIdentifier, String>(); 93 94 static 95 { 96 // Note: AES handled inline 97 oidMap.put("DESEDE", OIWObjectIdentifiers.desEDE); 98 oidMap.put("TRIPLEDES", OIWObjectIdentifiers.desEDE); 99 oidMap.put("TDEA", OIWObjectIdentifiers.desEDE); 100 oidMap.put("HMACSHA1", PKCSObjectIdentifiers.id_hmacWithSHA1); 101 oidMap.put("HMACSHA224", PKCSObjectIdentifiers.id_hmacWithSHA224); 102 oidMap.put("HMACSHA256", PKCSObjectIdentifiers.id_hmacWithSHA256); 103 oidMap.put("HMACSHA384", PKCSObjectIdentifiers.id_hmacWithSHA384); 104 oidMap.put("HMACSHA512", PKCSObjectIdentifiers.id_hmacWithSHA512); 105 publicAlgMap.put(PKCSObjectIdentifiers.rsaEncryption, R)106 publicAlgMap.put(PKCSObjectIdentifiers.rsaEncryption, "RSA"); publicAlgMap.put(X9ObjectIdentifiers.id_ecPublicKey, R)107 publicAlgMap.put(X9ObjectIdentifiers.id_ecPublicKey, "EC"); publicAlgMap.put(OIWObjectIdentifiers.elGamalAlgorithm, R)108 publicAlgMap.put(OIWObjectIdentifiers.elGamalAlgorithm, "DH"); publicAlgMap.put(PKCSObjectIdentifiers.dhKeyAgreement, R)109 publicAlgMap.put(PKCSObjectIdentifiers.dhKeyAgreement, "DH"); publicAlgMap.put(X9ObjectIdentifiers.id_dsa, R)110 publicAlgMap.put(X9ObjectIdentifiers.id_dsa, "DSA"); 111 } 112 getPublicKeyAlg(ASN1ObjectIdentifier oid)113 private static String getPublicKeyAlg(ASN1ObjectIdentifier oid) 114 { 115 String algName = (String)publicAlgMap.get(oid); 116 117 if (algName != null) 118 { 119 return algName; 120 } 121 122 return oid.getId(); 123 } 124 125 private final static BigInteger CERTIFICATE = BigInteger.valueOf(0); 126 private final static BigInteger PRIVATE_KEY = BigInteger.valueOf(1); 127 private final static BigInteger SECRET_KEY = BigInteger.valueOf(2); 128 private final static BigInteger PROTECTED_PRIVATE_KEY = BigInteger.valueOf(3); 129 private final static BigInteger PROTECTED_SECRET_KEY = BigInteger.valueOf(4); 130 131 private final BouncyCastleProvider provider; 132 private final Map<String, ObjectData> entries = new HashMap<String, ObjectData>(); 133 private final Map<String, PrivateKey> privateKeyCache = new HashMap<String, PrivateKey>(); 134 135 private AlgorithmIdentifier hmacAlgorithm; 136 private KeyDerivationFunc hmacPkbdAlgorithm; 137 private Date creationDate; 138 private Date lastModifiedDate; 139 BcFKSKeyStoreSpi(BouncyCastleProvider provider)140 BcFKSKeyStoreSpi(BouncyCastleProvider provider) 141 { 142 this.provider = provider; 143 } 144 engineGetKey(String alias, char[] password)145 public Key engineGetKey(String alias, char[] password) 146 throws NoSuchAlgorithmException, UnrecoverableKeyException 147 { 148 ObjectData ent = (ObjectData)entries.get(alias); 149 150 if (ent != null) 151 { 152 if (ent.getType().equals(PRIVATE_KEY) || ent.getType().equals(PROTECTED_PRIVATE_KEY)) 153 { 154 PrivateKey cachedKey = (PrivateKey)privateKeyCache.get(alias); 155 if (cachedKey != null) 156 { 157 return cachedKey; 158 } 159 160 EncryptedPrivateKeyData encPrivData = EncryptedPrivateKeyData.getInstance(ent.getData()); 161 EncryptedPrivateKeyInfo encInfo = EncryptedPrivateKeyInfo.getInstance(encPrivData.getEncryptedPrivateKeyInfo()); 162 163 try 164 { 165 PrivateKeyInfo pInfo = PrivateKeyInfo.getInstance(decryptData("PRIVATE_KEY_ENCRYPTION", encInfo.getEncryptionAlgorithm(), password, encInfo.getEncryptedData())); 166 167 KeyFactory kFact; 168 if (provider != null) 169 { 170 kFact = KeyFactory.getInstance(pInfo.getPrivateKeyAlgorithm().getAlgorithm().getId(), provider); 171 } 172 else 173 { 174 kFact = KeyFactory.getInstance(getPublicKeyAlg(pInfo.getPrivateKeyAlgorithm().getAlgorithm())); 175 } 176 177 PrivateKey privateKey = kFact.generatePrivate(new PKCS8EncodedKeySpec(pInfo.getEncoded())); 178 179 // check that the key pair and the certificate public key are consistent 180 // TODO: new ConsistentKeyPair(engineGetCertificate(alias).getPublicKey(), privateKey); 181 182 privateKeyCache.put(alias, privateKey); 183 184 return privateKey; 185 } 186 catch (Exception e) 187 { 188 throw new UnrecoverableKeyException("BCFKS KeyStore unable to recover private key (" + alias + "): " + e.getMessage()); 189 } 190 } 191 else if (ent.getType().equals(SECRET_KEY) || ent.getType().equals(PROTECTED_SECRET_KEY)) 192 { 193 EncryptedSecretKeyData encKeyData = EncryptedSecretKeyData.getInstance(ent.getData()); 194 195 try 196 { 197 SecretKeyData keyData = SecretKeyData.getInstance(decryptData("SECRET_KEY_ENCRYPTION", encKeyData.getKeyEncryptionAlgorithm(), password, encKeyData.getEncryptedKeyData())); 198 SecretKeyFactory kFact; 199 if (provider != null) 200 { 201 kFact = SecretKeyFactory.getInstance(keyData.getKeyAlgorithm().getId(), provider); 202 } 203 else 204 { 205 kFact = SecretKeyFactory.getInstance(keyData.getKeyAlgorithm().getId()); 206 } 207 208 return kFact.generateSecret(new SecretKeySpec(keyData.getKeyBytes(), keyData.getKeyAlgorithm().getId())); 209 } 210 catch (Exception e) 211 { 212 throw new UnrecoverableKeyException("BCFKS KeyStore unable to recover secret key (" + alias + "): " + e.getMessage()); 213 } 214 } 215 else 216 { 217 throw new UnrecoverableKeyException("BCFKS KeyStore unable to recover secret key (" + alias + "): type not recognized"); 218 } 219 } 220 221 return null; 222 } 223 engineGetCertificateChain(String alias)224 public Certificate[] engineGetCertificateChain(String alias) 225 { 226 ObjectData ent = (ObjectData)entries.get(alias); 227 228 if (ent != null) 229 { 230 if (ent.getType().equals(PRIVATE_KEY) || ent.getType().equals(PROTECTED_PRIVATE_KEY)) 231 { 232 EncryptedPrivateKeyData encPrivData = EncryptedPrivateKeyData.getInstance(ent.getData()); 233 org.bouncycastle.asn1.x509.Certificate[] certificates = encPrivData.getCertificateChain(); 234 Certificate[] chain = new X509Certificate[certificates.length]; 235 236 for (int i = 0; i != chain.length; i++) 237 { 238 chain[i] = decodeCertificate(certificates[i]); 239 } 240 241 return chain; 242 } 243 } 244 245 return null; 246 } 247 engineGetCertificate(String s)248 public Certificate engineGetCertificate(String s) 249 { 250 ObjectData ent = (ObjectData)entries.get(s); 251 252 if (ent != null) 253 { 254 if (ent.getType().equals(PRIVATE_KEY) || ent.getType().equals(PROTECTED_PRIVATE_KEY)) 255 { 256 EncryptedPrivateKeyData encPrivData = EncryptedPrivateKeyData.getInstance(ent.getData()); 257 org.bouncycastle.asn1.x509.Certificate[] certificates = encPrivData.getCertificateChain(); 258 259 return decodeCertificate(certificates[0]); 260 } 261 else if (ent.getType().equals(CERTIFICATE)) 262 { 263 return decodeCertificate(ent.getData()); 264 } 265 } 266 267 return null; 268 } 269 decodeCertificate(Object cert)270 private Certificate decodeCertificate(Object cert) 271 { 272 if (provider != null) 273 { 274 try 275 { 276 CertificateFactory certFact = CertificateFactory.getInstance("X.509", provider); 277 278 return certFact.generateCertificate(new ByteArrayInputStream(org.bouncycastle.asn1.x509.Certificate.getInstance(cert).getEncoded())); 279 } 280 catch (Exception e) 281 { 282 return null; 283 } 284 } 285 else 286 { 287 try 288 { 289 CertificateFactory certFact = CertificateFactory.getInstance("X.509"); 290 291 return certFact.generateCertificate(new ByteArrayInputStream(org.bouncycastle.asn1.x509.Certificate.getInstance(cert).getEncoded())); 292 } 293 catch (Exception e) 294 { 295 return null; 296 } 297 } 298 } 299 engineGetCreationDate(String s)300 public Date engineGetCreationDate(String s) 301 { 302 ObjectData ent = (ObjectData)entries.get(s); 303 304 if (ent != null) 305 { 306 try 307 { 308 // we return last modified as it represents date current state of entry was created 309 return ent.getLastModifiedDate().getDate(); 310 } 311 catch (ParseException e) 312 { 313 return new Date(); // it's here, but... 314 } 315 } 316 317 return null; 318 } 319 engineSetKeyEntry(String alias, Key key, char[] password, Certificate[] chain)320 public void engineSetKeyEntry(String alias, Key key, char[] password, Certificate[] chain) 321 throws KeyStoreException 322 { 323 Date creationDate = new Date(); 324 Date lastEditDate = creationDate; 325 326 ObjectData entry = (ObjectData)entries.get(alias); 327 if (entry != null) 328 { 329 creationDate = extractCreationDate(entry, creationDate); 330 } 331 332 privateKeyCache.remove(alias); 333 334 if (key instanceof PrivateKey) 335 { 336 if (chain == null) 337 { 338 throw new KeyStoreException("BCFKS KeyStore requires a certificate chain for private key storage."); 339 } 340 341 try 342 { 343 // check that the key pair and the certificate public are consistent 344 // TODO: new ConsistentKeyPair(chain[0].getPublicKey(), (PrivateKey)key); 345 346 byte[] encodedKey = key.getEncoded(); 347 348 KeyDerivationFunc pbkdAlgId = generatePkbdAlgorithmIdentifier(PKCSObjectIdentifiers.id_PBKDF2, 256 / 8); 349 byte[] keyBytes = generateKey(pbkdAlgId, "PRIVATE_KEY_ENCRYPTION", ((password != null) ? password : new char[0])); 350 351 Cipher c; 352 if (provider == null) 353 { 354 c = Cipher.getInstance("AES/CCM/NoPadding"); 355 } 356 else 357 { 358 c = Cipher.getInstance("AES/CCM/NoPadding", provider); 359 } 360 361 c.init(Cipher.ENCRYPT_MODE, new SecretKeySpec(keyBytes, "AES")); 362 363 byte[] encryptedKey = c.doFinal(encodedKey); 364 365 AlgorithmParameters algParams = c.getParameters(); 366 367 PBES2Parameters pbeParams = new PBES2Parameters(pbkdAlgId, new EncryptionScheme(NISTObjectIdentifiers.id_aes256_CCM, CCMParameters.getInstance(algParams.getEncoded()))); 368 369 EncryptedPrivateKeyInfo keyInfo = new EncryptedPrivateKeyInfo(new AlgorithmIdentifier(PKCSObjectIdentifiers.id_PBES2, pbeParams), encryptedKey); 370 371 EncryptedPrivateKeyData keySeq = createPrivateKeySequence(keyInfo, chain); 372 373 entries.put(alias, new ObjectData(PRIVATE_KEY, alias, creationDate, lastEditDate, keySeq.getEncoded(), null)); 374 } 375 catch (Exception e) 376 { 377 throw new ExtKeyStoreException("BCFKS KeyStore exception storing private key: " + e.toString(), e); 378 } 379 } 380 else if (key instanceof SecretKey) 381 { 382 if (chain != null) 383 { 384 throw new KeyStoreException("BCFKS KeyStore cannot store certificate chain with secret key."); 385 } 386 387 try 388 { 389 byte[] encodedKey = key.getEncoded(); 390 391 KeyDerivationFunc pbkdAlgId = generatePkbdAlgorithmIdentifier(PKCSObjectIdentifiers.id_PBKDF2, 256 / 8); 392 byte[] keyBytes = generateKey(pbkdAlgId, "SECRET_KEY_ENCRYPTION", ((password != null) ? password : new char[0])); 393 394 Cipher c; 395 if (provider == null) 396 { 397 c = Cipher.getInstance("AES/CCM/NoPadding"); 398 } 399 else 400 { 401 c = Cipher.getInstance("AES/CCM/NoPadding", provider); 402 } 403 404 c.init(Cipher.ENCRYPT_MODE, new SecretKeySpec(keyBytes, "AES")); 405 406 407 String keyAlg = Strings.toUpperCase(key.getAlgorithm()); 408 byte[] encryptedKey; 409 410 if (keyAlg.indexOf("AES") > -1) 411 { 412 encryptedKey = c.doFinal(new SecretKeyData(NISTObjectIdentifiers.aes, encodedKey).getEncoded()); 413 } 414 else 415 { 416 ASN1ObjectIdentifier algOid = (ASN1ObjectIdentifier)oidMap.get(keyAlg); 417 if (algOid != null) 418 { 419 encryptedKey = c.doFinal(new SecretKeyData(algOid, encodedKey).getEncoded()); 420 } 421 else 422 { 423 throw new KeyStoreException("BCFKS KeyStore cannot recognize secret key (" + keyAlg + ") for storage."); 424 } 425 } 426 427 428 AlgorithmParameters algParams = c.getParameters(); 429 430 PBES2Parameters pbeParams = new PBES2Parameters(pbkdAlgId, new EncryptionScheme(NISTObjectIdentifiers.id_aes256_CCM, CCMParameters.getInstance(algParams.getEncoded()))); 431 432 EncryptedSecretKeyData keyData = new EncryptedSecretKeyData(new AlgorithmIdentifier(PKCSObjectIdentifiers.id_PBES2, pbeParams), encryptedKey); 433 434 entries.put(alias, new ObjectData(SECRET_KEY, alias, creationDate, lastEditDate, keyData.getEncoded(), null)); 435 } 436 catch (Exception e) 437 { 438 throw new ExtKeyStoreException("BCFKS KeyStore exception storing private key: " + e.toString(), e); 439 } 440 } 441 else 442 { 443 throw new KeyStoreException("BCFKS KeyStore unable to recognize key."); 444 } 445 446 lastModifiedDate = lastEditDate; 447 } 448 getDefaultSecureRandom()449 private SecureRandom getDefaultSecureRandom() 450 { 451 return CryptoServicesRegistrar.getSecureRandom(); 452 } 453 createPrivateKeySequence(EncryptedPrivateKeyInfo encryptedPrivateKeyInfo, Certificate[] chain)454 private EncryptedPrivateKeyData createPrivateKeySequence(EncryptedPrivateKeyInfo encryptedPrivateKeyInfo, Certificate[] chain) 455 throws CertificateEncodingException 456 { 457 org.bouncycastle.asn1.x509.Certificate[] certChain = new org.bouncycastle.asn1.x509.Certificate[chain.length]; 458 for (int i = 0; i != chain.length; i++) 459 { 460 certChain[i] = org.bouncycastle.asn1.x509.Certificate.getInstance(chain[i].getEncoded()); 461 } 462 463 return new EncryptedPrivateKeyData(encryptedPrivateKeyInfo, certChain); 464 } 465 engineSetKeyEntry(String alias, byte[] keyBytes, Certificate[] chain)466 public void engineSetKeyEntry(String alias, byte[] keyBytes, Certificate[] chain) 467 throws KeyStoreException 468 { 469 Date creationDate = new Date(); 470 Date lastEditDate = creationDate; 471 472 ObjectData entry = (ObjectData)entries.get(alias); 473 if (entry != null) 474 { 475 creationDate = extractCreationDate(entry, creationDate); 476 } 477 478 if (chain != null) 479 { 480 EncryptedPrivateKeyInfo encInfo; 481 482 try 483 { 484 encInfo = EncryptedPrivateKeyInfo.getInstance(keyBytes); 485 } 486 catch (Exception e) 487 { 488 throw new ExtKeyStoreException("BCFKS KeyStore private key encoding must be an EncryptedPrivateKeyInfo.", e); 489 } 490 491 try 492 { 493 privateKeyCache.remove(alias); 494 entries.put(alias, new ObjectData(PROTECTED_PRIVATE_KEY, alias, creationDate, lastEditDate, createPrivateKeySequence(encInfo, chain).getEncoded(), null)); 495 } 496 catch (Exception e) 497 { 498 throw new ExtKeyStoreException("BCFKS KeyStore exception storing protected private key: " + e.toString(), e); 499 } 500 } 501 else 502 { 503 try 504 { 505 entries.put(alias, new ObjectData(PROTECTED_SECRET_KEY, alias, creationDate, lastEditDate, keyBytes, null)); 506 } 507 catch (Exception e) 508 { 509 throw new ExtKeyStoreException("BCFKS KeyStore exception storing protected private key: " + e.toString(), e); 510 } 511 } 512 513 lastModifiedDate = lastEditDate; 514 } 515 engineSetCertificateEntry(String alias, Certificate certificate)516 public void engineSetCertificateEntry(String alias, Certificate certificate) 517 throws KeyStoreException 518 { 519 ObjectData entry = (ObjectData)entries.get(alias); 520 Date creationDate = new Date(); 521 Date lastEditDate = creationDate; 522 523 if (entry != null) 524 { 525 if (!entry.getType().equals(CERTIFICATE)) 526 { 527 throw new KeyStoreException("BCFKS KeyStore already has a key entry with alias " + alias); 528 } 529 530 creationDate = extractCreationDate(entry, creationDate); 531 } 532 533 try 534 { 535 entries.put(alias, new ObjectData(CERTIFICATE, alias, creationDate, lastEditDate, certificate.getEncoded(), null)); 536 } 537 catch (CertificateEncodingException e) 538 { 539 throw new ExtKeyStoreException("BCFKS KeyStore unable to handle certificate: " + e.getMessage(), e); 540 } 541 542 lastModifiedDate = lastEditDate; 543 } 544 extractCreationDate(ObjectData entry, Date creationDate)545 private Date extractCreationDate(ObjectData entry, Date creationDate) 546 { 547 try 548 { 549 creationDate = entry.getCreationDate().getDate(); 550 } 551 catch (ParseException e) 552 { 553 // this should never happen, if it does we'll leave creation date unmodified and hope for the best. 554 } 555 return creationDate; 556 } 557 engineDeleteEntry(String alias)558 public void engineDeleteEntry(String alias) 559 throws KeyStoreException 560 { 561 ObjectData entry = (ObjectData)entries.get(alias); 562 563 if (entry == null) 564 { 565 return; 566 } 567 568 privateKeyCache.remove(alias); 569 entries.remove(alias); 570 571 lastModifiedDate = new Date(); 572 } 573 engineAliases()574 public Enumeration<String> engineAliases() 575 { 576 final Iterator<String> it = new HashSet(entries.keySet()).iterator(); 577 578 return new Enumeration() 579 { 580 public boolean hasMoreElements() 581 { 582 return it.hasNext(); 583 } 584 585 public Object nextElement() 586 { 587 return it.next(); 588 } 589 }; 590 } 591 592 public boolean engineContainsAlias(String alias) 593 { 594 if (alias == null) 595 { 596 throw new NullPointerException("alias value is null"); 597 } 598 599 return entries.containsKey(alias); 600 } 601 602 public int engineSize() 603 { 604 return entries.size(); 605 } 606 607 public boolean engineIsKeyEntry(String alias) 608 { 609 ObjectData ent = (ObjectData)entries.get(alias); 610 611 if (ent != null) 612 { 613 BigInteger entryType = ent.getType(); 614 return entryType.equals(PRIVATE_KEY) || entryType.equals(SECRET_KEY) 615 || entryType.equals(PROTECTED_PRIVATE_KEY) || entryType.equals(PROTECTED_SECRET_KEY); 616 } 617 618 return false; 619 } 620 621 public boolean engineIsCertificateEntry(String alias) 622 { 623 ObjectData ent = (ObjectData)entries.get(alias); 624 625 if (ent != null) 626 { 627 return ent.getType().equals(CERTIFICATE); 628 } 629 630 return false; 631 } 632 633 public String engineGetCertificateAlias(Certificate certificate) 634 { 635 if (certificate == null) 636 { 637 return null; 638 } 639 640 byte[] encodedCert; 641 try 642 { 643 encodedCert = certificate.getEncoded(); 644 } 645 catch (CertificateEncodingException e) 646 { 647 return null; 648 } 649 650 for (Iterator<String> it = entries.keySet().iterator(); it.hasNext(); ) 651 { 652 String alias = (String)it.next(); 653 ObjectData ent = (ObjectData)entries.get(alias); 654 655 if (ent.getType().equals(CERTIFICATE)) 656 { 657 if (Arrays.areEqual(ent.getData(), encodedCert)) 658 { 659 return alias; 660 } 661 } 662 else if (ent.getType().equals(PRIVATE_KEY) || ent.getType().equals(PROTECTED_PRIVATE_KEY)) 663 { 664 try 665 { 666 EncryptedPrivateKeyData encPrivData = EncryptedPrivateKeyData.getInstance(ent.getData()); 667 if (Arrays.areEqual(encPrivData.getCertificateChain()[0].toASN1Primitive().getEncoded(), encodedCert)) 668 { 669 return alias; 670 } 671 } 672 catch (IOException e) 673 { 674 // ignore - this should never happen 675 } 676 } 677 } 678 679 return null; 680 } 681 682 private byte[] generateKey(KeyDerivationFunc pbkdAlgorithm, String purpose, char[] password) 683 throws IOException 684 { 685 byte[] encPassword = PBEParametersGenerator.PKCS12PasswordToBytes(password); 686 byte[] differentiator = PBEParametersGenerator.PKCS12PasswordToBytes(purpose.toCharArray()); 687 688 int keySizeInBytes; 689 690 if (MiscObjectIdentifiers.id_scrypt.equals(pbkdAlgorithm.getAlgorithm())) 691 { 692 ScryptParams params = ScryptParams.getInstance(pbkdAlgorithm.getParameters()); 693 694 return SCrypt.generate(Arrays.concatenate(encPassword, differentiator), params.getSalt(), 695 params.getCostParameter().intValue(), params.getBlockSize().intValue(), 696 params.getBlockSize().intValue(), params.getKeyLength().intValue()); 697 } 698 else if (pbkdAlgorithm.getAlgorithm().equals(PKCSObjectIdentifiers.id_PBKDF2)) 699 { 700 PBKDF2Params pbkdf2Params = PBKDF2Params.getInstance(pbkdAlgorithm.getParameters()); 701 702 if (pbkdf2Params.getPrf().getAlgorithm().equals(PKCSObjectIdentifiers.id_hmacWithSHA512)) 703 { 704 PKCS5S2ParametersGenerator pGen = new PKCS5S2ParametersGenerator(new SHA512Digest()); 705 706 pGen.init(Arrays.concatenate(encPassword, differentiator), pbkdf2Params.getSalt(), pbkdf2Params.getIterationCount().intValue()); 707 708 keySizeInBytes = pbkdf2Params.getKeyLength().intValue(); 709 710 return ((KeyParameter)pGen.generateDerivedParameters(keySizeInBytes * 8)).getKey(); 711 } 712 else if (pbkdf2Params.getPrf().getAlgorithm().equals(NISTObjectIdentifiers.id_hmacWithSHA3_512)) 713 { 714 PKCS5S2ParametersGenerator pGen = new PKCS5S2ParametersGenerator(new SHA3Digest(512)); 715 716 pGen.init(Arrays.concatenate(encPassword, differentiator), pbkdf2Params.getSalt(), pbkdf2Params.getIterationCount().intValue()); 717 718 keySizeInBytes = pbkdf2Params.getKeyLength().intValue(); 719 720 return ((KeyParameter)pGen.generateDerivedParameters(keySizeInBytes * 8)).getKey(); 721 } 722 else 723 { 724 throw new IOException("BCFKS KeyStore: unrecognized MAC PBKD PRF: " + pbkdf2Params.getPrf().getAlgorithm()); 725 } 726 } 727 else 728 { 729 throw new IOException("BCFKS KeyStore: unrecognized MAC PBKD."); 730 } 731 } 732 733 private void verifyMac(byte[] content, PbkdMacIntegrityCheck integrityCheck, char[] password) 734 throws NoSuchAlgorithmException, IOException 735 { 736 byte[] check = calculateMac(content, integrityCheck.getMacAlgorithm(), integrityCheck.getPbkdAlgorithm(), password); 737 738 if (!Arrays.constantTimeAreEqual(check, integrityCheck.getMac())) 739 { 740 throw new IOException("BCFKS KeyStore corrupted: MAC calculation failed."); 741 } 742 } 743 744 private byte[] calculateMac(byte[] content, AlgorithmIdentifier algorithm, KeyDerivationFunc pbkdAlgorithm, char[] password) 745 throws NoSuchAlgorithmException, IOException 746 { 747 String algorithmId = algorithm.getAlgorithm().getId(); 748 749 Mac mac; 750 if (provider != null) 751 { 752 mac = Mac.getInstance(algorithmId, provider); 753 } 754 else 755 { 756 mac = Mac.getInstance(algorithmId); 757 } 758 759 try 760 { 761 mac.init(new SecretKeySpec(generateKey(pbkdAlgorithm, "INTEGRITY_CHECK", ((password != null) ? password : new char[0])), algorithmId)); 762 } 763 catch (InvalidKeyException e) 764 { 765 throw new IOException("Cannot set up MAC calculation: " + e.getMessage()); 766 } 767 768 return mac.doFinal(content); 769 } 770 771 public void engineStore(OutputStream outputStream, char[] password) 772 throws IOException, NoSuchAlgorithmException, CertificateException 773 { 774 ObjectData[] dataArray = (ObjectData[])entries.values().toArray(new ObjectData[entries.size()]); 775 776 KeyDerivationFunc pbkdAlgId = generatePkbdAlgorithmIdentifier(hmacPkbdAlgorithm, 256 / 8); 777 byte[] keyBytes = generateKey(pbkdAlgId, "STORE_ENCRYPTION", ((password != null) ? password : new char[0])); 778 779 ObjectStoreData storeData = new ObjectStoreData(hmacAlgorithm, creationDate, lastModifiedDate, new ObjectDataSequence(dataArray), null); 780 EncryptedObjectStoreData encStoreData; 781 782 try 783 { 784 Cipher c; 785 if (provider == null) 786 { 787 c = Cipher.getInstance("AES/CCM/NoPadding"); 788 } 789 else 790 { 791 c = Cipher.getInstance("AES/CCM/NoPadding", provider); 792 } 793 794 c.init(Cipher.ENCRYPT_MODE, new SecretKeySpec(keyBytes, "AES")); 795 796 byte[] encOut = c.doFinal(storeData.getEncoded()); 797 798 AlgorithmParameters algorithmParameters = c.getParameters(); 799 800 PBES2Parameters pbeParams = new PBES2Parameters(pbkdAlgId, new EncryptionScheme(NISTObjectIdentifiers.id_aes256_CCM, CCMParameters.getInstance(algorithmParameters.getEncoded()))); 801 802 encStoreData = new EncryptedObjectStoreData(new AlgorithmIdentifier(PKCSObjectIdentifiers.id_PBES2, pbeParams), encOut); 803 } 804 catch (NoSuchPaddingException e) 805 { 806 throw new NoSuchAlgorithmException(e.toString()); 807 } 808 catch (BadPaddingException e) 809 { 810 throw new IOException(e.toString()); 811 } 812 catch (IllegalBlockSizeException e) 813 { 814 throw new IOException(e.toString()); 815 } 816 catch (InvalidKeyException e) 817 { 818 throw new IOException(e.toString()); 819 } 820 821 // update the salt 822 if (MiscObjectIdentifiers.id_scrypt.equals(hmacPkbdAlgorithm.getAlgorithm())) 823 { 824 ScryptParams sParams = ScryptParams.getInstance(hmacPkbdAlgorithm.getParameters()); 825 826 hmacPkbdAlgorithm = generatePkbdAlgorithmIdentifier(hmacPkbdAlgorithm, sParams.getKeyLength().intValue()); 827 } 828 else 829 { 830 PBKDF2Params pbkdf2Params = PBKDF2Params.getInstance(hmacPkbdAlgorithm.getParameters()); 831 832 hmacPkbdAlgorithm = generatePkbdAlgorithmIdentifier(hmacPkbdAlgorithm, pbkdf2Params.getKeyLength().intValue()); 833 } 834 byte[] mac = calculateMac(encStoreData.getEncoded(), hmacAlgorithm, hmacPkbdAlgorithm, password); 835 836 ObjectStore store = new ObjectStore(encStoreData, new ObjectStoreIntegrityCheck(new PbkdMacIntegrityCheck(hmacAlgorithm, hmacPkbdAlgorithm, mac))); 837 838 outputStream.write(store.getEncoded()); 839 840 outputStream.flush(); 841 } 842 843 public void engineLoad(InputStream inputStream, char[] password) 844 throws IOException, NoSuchAlgorithmException, CertificateException 845 { 846 // reset any current values 847 entries.clear(); 848 privateKeyCache.clear(); 849 850 lastModifiedDate = creationDate = null; 851 hmacAlgorithm = null; 852 853 if (inputStream == null) 854 { 855 // initialise defaults 856 lastModifiedDate = creationDate = new Date(); 857 858 // basic initialisation 859 hmacAlgorithm = new AlgorithmIdentifier(PKCSObjectIdentifiers.id_hmacWithSHA512, DERNull.INSTANCE); 860 hmacPkbdAlgorithm = generatePkbdAlgorithmIdentifier(PKCSObjectIdentifiers.id_PBKDF2, 512 / 8); 861 862 return; 863 } 864 865 ASN1InputStream aIn = new ASN1InputStream(inputStream); 866 867 ObjectStore store; 868 869 try 870 { 871 store = ObjectStore.getInstance(aIn.readObject()); 872 } 873 catch (Exception e) 874 { 875 throw new IOException(e.getMessage()); 876 } 877 878 ObjectStoreIntegrityCheck integrityCheck = store.getIntegrityCheck(); 879 if (integrityCheck.getType() == ObjectStoreIntegrityCheck.PBKD_MAC_CHECK) 880 { 881 PbkdMacIntegrityCheck pbkdMacIntegrityCheck = PbkdMacIntegrityCheck.getInstance(integrityCheck.getIntegrityCheck()); 882 883 hmacAlgorithm = pbkdMacIntegrityCheck.getMacAlgorithm(); 884 hmacPkbdAlgorithm = pbkdMacIntegrityCheck.getPbkdAlgorithm(); 885 886 verifyMac(store.getStoreData().toASN1Primitive().getEncoded(), pbkdMacIntegrityCheck, password); 887 } 888 else 889 { 890 throw new IOException("BCFKS KeyStore unable to recognize integrity check."); 891 } 892 893 ASN1Encodable sData = store.getStoreData(); 894 895 ObjectStoreData storeData; 896 if (sData instanceof EncryptedObjectStoreData) 897 { 898 EncryptedObjectStoreData encryptedStoreData = (EncryptedObjectStoreData)sData; 899 AlgorithmIdentifier protectAlgId = encryptedStoreData.getEncryptionAlgorithm(); 900 901 storeData = ObjectStoreData.getInstance(decryptData("STORE_ENCRYPTION", protectAlgId, password, encryptedStoreData.getEncryptedContent().getOctets())); 902 } 903 else 904 { 905 storeData = ObjectStoreData.getInstance(sData); 906 } 907 908 try 909 { 910 creationDate = storeData.getCreationDate().getDate(); 911 lastModifiedDate = storeData.getLastModifiedDate().getDate(); 912 } 913 catch (ParseException e) 914 { 915 throw new IOException("BCFKS KeyStore unable to parse store data information."); 916 } 917 918 if (!storeData.getIntegrityAlgorithm().equals(hmacAlgorithm)) 919 { 920 throw new IOException("BCFKS KeyStore storeData integrity algorithm does not match store integrity algorithm."); 921 } 922 923 for (Iterator it = storeData.getObjectDataSequence().iterator(); it.hasNext(); ) 924 { 925 ObjectData objData = ObjectData.getInstance(it.next()); 926 927 entries.put(objData.getIdentifier(), objData); 928 } 929 } 930 931 private byte[] decryptData(String purpose, AlgorithmIdentifier protectAlgId, char[] password, byte[] encryptedData) 932 throws IOException 933 { 934 if (!protectAlgId.getAlgorithm().equals(PKCSObjectIdentifiers.id_PBES2)) 935 { 936 throw new IOException("BCFKS KeyStore cannot recognize protection algorithm."); 937 } 938 939 PBES2Parameters pbes2Parameters = PBES2Parameters.getInstance(protectAlgId.getParameters()); 940 EncryptionScheme algId = pbes2Parameters.getEncryptionScheme(); 941 942 if (!algId.getAlgorithm().equals(NISTObjectIdentifiers.id_aes256_CCM)) 943 { 944 throw new IOException("BCFKS KeyStore cannot recognize protection encryption algorithm."); 945 } 946 947 try 948 { 949 CCMParameters ccmParameters = CCMParameters.getInstance(algId.getParameters()); 950 Cipher c; 951 AlgorithmParameters algParams; 952 if (provider == null) 953 { 954 c = Cipher.getInstance("AES/CCM/NoPadding"); 955 algParams = AlgorithmParameters.getInstance("CCM"); 956 } 957 else 958 { 959 c = Cipher.getInstance("AES/CCM/NoPadding", provider); 960 algParams = AlgorithmParameters.getInstance("CCM", provider); 961 } 962 963 algParams.init(ccmParameters.getEncoded()); 964 965 byte[] keyBytes = generateKey(pbes2Parameters.getKeyDerivationFunc(), purpose, ((password != null) ? password : new char[0])); 966 967 c.init(Cipher.DECRYPT_MODE, new SecretKeySpec(keyBytes, "AES"), algParams); 968 969 byte[] rv = c.doFinal(encryptedData); 970 return rv; 971 } 972 catch (Exception e) 973 { 974 throw new IOException(e.toString()); 975 } 976 } 977 978 private KeyDerivationFunc generatePkbdAlgorithmIdentifier(PBKDFConfig pbkdfConfig, int keySizeInBytes) 979 { 980 if (MiscObjectIdentifiers.id_scrypt.equals(pbkdfConfig.getAlgorithm())) 981 { 982 ScryptConfig scryptConfig = (ScryptConfig)pbkdfConfig; 983 984 byte[] pbkdSalt = new byte[scryptConfig.getSaltLength()]; 985 getDefaultSecureRandom().nextBytes(pbkdSalt); 986 987 ScryptParams params = new ScryptParams( 988 pbkdSalt, 989 scryptConfig.getCostParameter(), scryptConfig.getBlockSize(), scryptConfig.getParallelizationParameter(), keySizeInBytes); 990 991 return new KeyDerivationFunc(MiscObjectIdentifiers.id_scrypt, params); 992 } 993 else 994 { 995 PBKDF2Config pbkdf2Config = (PBKDF2Config)pbkdfConfig; 996 997 byte[] pbkdSalt = new byte[pbkdf2Config.getSaltLength()]; 998 getDefaultSecureRandom().nextBytes(pbkdSalt); 999 1000 return new KeyDerivationFunc(PKCSObjectIdentifiers.id_PBKDF2, new PBKDF2Params(pbkdSalt, pbkdf2Config.getIterationCount(), keySizeInBytes, pbkdf2Config.getPRF())); 1001 } 1002 } 1003 1004 private KeyDerivationFunc generatePkbdAlgorithmIdentifier(KeyDerivationFunc baseAlg, int keySizeInBytes) 1005 { 1006 if (MiscObjectIdentifiers.id_scrypt.equals(baseAlg.getAlgorithm())) 1007 { 1008 ScryptParams oldParams = ScryptParams.getInstance(baseAlg.getParameters()); 1009 1010 byte[] pbkdSalt = new byte[oldParams.getSalt().length]; 1011 getDefaultSecureRandom().nextBytes(pbkdSalt); 1012 1013 ScryptParams params = new ScryptParams( 1014 pbkdSalt, 1015 oldParams.getCostParameter(), oldParams.getBlockSize(), oldParams.getParallelizationParameter(), BigInteger.valueOf(keySizeInBytes)); 1016 1017 return new KeyDerivationFunc(MiscObjectIdentifiers.id_scrypt, params); 1018 } 1019 else 1020 { 1021 PBKDF2Params oldParams = PBKDF2Params.getInstance(baseAlg.getParameters()); 1022 1023 byte[] pbkdSalt = new byte[oldParams.getSalt().length]; 1024 getDefaultSecureRandom().nextBytes(pbkdSalt); 1025 1026 PBKDF2Params params = new PBKDF2Params(pbkdSalt, 1027 oldParams.getIterationCount().intValue(), keySizeInBytes, oldParams.getPrf()); 1028 return new KeyDerivationFunc(PKCSObjectIdentifiers.id_PBKDF2, params); 1029 } 1030 } 1031 1032 private KeyDerivationFunc generatePkbdAlgorithmIdentifier(ASN1ObjectIdentifier derivationAlgorithm, int keySizeInBytes) 1033 { 1034 byte[] pbkdSalt = new byte[512 / 8]; 1035 getDefaultSecureRandom().nextBytes(pbkdSalt); 1036 1037 if (PKCSObjectIdentifiers.id_PBKDF2.equals(derivationAlgorithm)) 1038 { 1039 return new KeyDerivationFunc(PKCSObjectIdentifiers.id_PBKDF2, new PBKDF2Params(pbkdSalt, 50 * 1024, keySizeInBytes, new AlgorithmIdentifier(PKCSObjectIdentifiers.id_hmacWithSHA512, DERNull.INSTANCE))); 1040 } 1041 else 1042 { 1043 throw new IllegalStateException("unknown derivation algorithm: " + derivationAlgorithm); 1044 } 1045 } 1046 1047 public static class Std 1048 extends BcFKSKeyStoreSpi 1049 { 1050 public Std() 1051 { 1052 super(new BouncyCastleProvider()); 1053 } 1054 } 1055 1056 public static class Def 1057 extends BcFKSKeyStoreSpi 1058 { 1059 public Def() 1060 { 1061 super(null); 1062 } 1063 } 1064 1065 private static class ExtKeyStoreException 1066 extends KeyStoreException 1067 { 1068 private final Throwable cause; 1069 1070 ExtKeyStoreException(String msg, Throwable cause) 1071 { 1072 super(msg); 1073 this.cause = cause; 1074 } 1075 1076 public Throwable getCause() 1077 { 1078 return cause; 1079 } 1080 } 1081 } 1082