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.GeneralSecurityException; 10 import java.security.InvalidKeyException; 11 import java.security.Key; 12 import java.security.KeyFactory; 13 import java.security.KeyStore; 14 import java.security.KeyStoreException; 15 import java.security.KeyStoreSpi; 16 import java.security.NoSuchAlgorithmException; 17 import java.security.NoSuchProviderException; 18 import java.security.PrivateKey; 19 import java.security.PublicKey; 20 import java.security.SecureRandom; 21 import java.security.Signature; 22 import java.security.UnrecoverableKeyException; 23 import java.security.cert.Certificate; 24 import java.security.cert.CertificateEncodingException; 25 import java.security.cert.CertificateException; 26 import java.security.cert.CertificateFactory; 27 import java.security.cert.X509Certificate; 28 import java.security.interfaces.DSAKey; 29 import java.security.interfaces.RSAKey; 30 import java.security.spec.PKCS8EncodedKeySpec; 31 import java.text.ParseException; 32 import java.util.Date; 33 import java.util.Enumeration; 34 import java.util.HashMap; 35 import java.util.HashSet; 36 import java.util.Iterator; 37 import java.util.Map; 38 39 import javax.crypto.BadPaddingException; 40 import javax.crypto.Cipher; 41 import javax.crypto.IllegalBlockSizeException; 42 import javax.crypto.Mac; 43 import javax.crypto.NoSuchPaddingException; 44 import javax.crypto.SecretKey; 45 import javax.crypto.SecretKeyFactory; 46 import javax.crypto.spec.SecretKeySpec; 47 48 import org.bouncycastle.asn1.ASN1Encodable; 49 import org.bouncycastle.asn1.ASN1Encoding; 50 import org.bouncycastle.asn1.ASN1InputStream; 51 import org.bouncycastle.asn1.ASN1ObjectIdentifier; 52 import org.bouncycastle.asn1.DERNull; 53 import org.bouncycastle.asn1.bc.EncryptedObjectStoreData; 54 import org.bouncycastle.asn1.bc.EncryptedPrivateKeyData; 55 import org.bouncycastle.asn1.bc.EncryptedSecretKeyData; 56 import org.bouncycastle.asn1.bc.ObjectData; 57 import org.bouncycastle.asn1.bc.ObjectDataSequence; 58 import org.bouncycastle.asn1.bc.ObjectStore; 59 import org.bouncycastle.asn1.bc.ObjectStoreData; 60 import org.bouncycastle.asn1.bc.ObjectStoreIntegrityCheck; 61 import org.bouncycastle.asn1.bc.PbkdMacIntegrityCheck; 62 import org.bouncycastle.asn1.bc.SecretKeyData; 63 import org.bouncycastle.asn1.bc.SignatureCheck; 64 import org.bouncycastle.internal.asn1.cms.CCMParameters; 65 import org.bouncycastle.asn1.kisa.KISAObjectIdentifiers; 66 import org.bouncycastle.asn1.misc.MiscObjectIdentifiers; 67 import org.bouncycastle.asn1.misc.ScryptParams; 68 import org.bouncycastle.asn1.nist.NISTObjectIdentifiers; 69 import org.bouncycastle.asn1.nsri.NSRIObjectIdentifiers; 70 import org.bouncycastle.asn1.ntt.NTTObjectIdentifiers; 71 import org.bouncycastle.asn1.oiw.OIWObjectIdentifiers; 72 import org.bouncycastle.asn1.pkcs.EncryptedPrivateKeyInfo; 73 import org.bouncycastle.asn1.pkcs.EncryptionScheme; 74 import org.bouncycastle.asn1.pkcs.KeyDerivationFunc; 75 import org.bouncycastle.asn1.pkcs.PBES2Parameters; 76 import org.bouncycastle.asn1.pkcs.PBKDF2Params; 77 import org.bouncycastle.asn1.pkcs.PKCSObjectIdentifiers; 78 import org.bouncycastle.asn1.pkcs.PrivateKeyInfo; 79 import org.bouncycastle.asn1.x509.AlgorithmIdentifier; 80 import org.bouncycastle.asn1.x509.X509ObjectIdentifiers; 81 import org.bouncycastle.asn1.x9.X9ObjectIdentifiers; 82 import org.bouncycastle.crypto.CryptoServicesRegistrar; 83 import org.bouncycastle.crypto.PBEParametersGenerator; 84 import org.bouncycastle.crypto.digests.SHA3Digest; 85 import org.bouncycastle.crypto.digests.SHA512Digest; 86 import org.bouncycastle.crypto.generators.PKCS5S2ParametersGenerator; 87 import org.bouncycastle.crypto.generators.SCrypt; 88 import org.bouncycastle.crypto.params.KeyParameter; 89 import org.bouncycastle.crypto.util.PBKDF2Config; 90 import org.bouncycastle.crypto.util.PBKDFConfig; 91 import org.bouncycastle.crypto.util.ScryptConfig; 92 import org.bouncycastle.jcajce.BCFKSLoadStoreParameter; 93 import org.bouncycastle.jcajce.BCFKSStoreParameter; 94 import org.bouncycastle.jcajce.BCLoadStoreParameter; 95 import org.bouncycastle.jcajce.provider.keystore.util.AdaptingKeyStoreSpi; 96 import org.bouncycastle.jcajce.provider.keystore.util.ParameterUtil; 97 import org.bouncycastle.jcajce.util.BCJcaJceHelper; 98 import org.bouncycastle.jcajce.util.DefaultJcaJceHelper; 99 import org.bouncycastle.jcajce.util.JcaJceHelper; 100 import org.bouncycastle.jce.interfaces.ECKey; 101 import org.bouncycastle.util.Arrays; 102 import org.bouncycastle.util.Strings; 103 104 class BcFKSKeyStoreSpi 105 extends KeyStoreSpi 106 { 107 private static final Map<String, ASN1ObjectIdentifier> oidMap = new HashMap<String, ASN1ObjectIdentifier>(); 108 private static final Map<ASN1ObjectIdentifier, String> publicAlgMap = new HashMap<ASN1ObjectIdentifier, String>(); 109 110 static 111 { 112 // Note: AES handled inline 113 oidMap.put("DESEDE", OIWObjectIdentifiers.desEDE); 114 oidMap.put("TRIPLEDES", OIWObjectIdentifiers.desEDE); 115 oidMap.put("TDEA", OIWObjectIdentifiers.desEDE); 116 oidMap.put("HMACSHA1", PKCSObjectIdentifiers.id_hmacWithSHA1); 117 oidMap.put("HMACSHA224", PKCSObjectIdentifiers.id_hmacWithSHA224); 118 oidMap.put("HMACSHA256", PKCSObjectIdentifiers.id_hmacWithSHA256); 119 oidMap.put("HMACSHA384", PKCSObjectIdentifiers.id_hmacWithSHA384); 120 oidMap.put("HMACSHA512", PKCSObjectIdentifiers.id_hmacWithSHA512); 121 oidMap.put("SEED", KISAObjectIdentifiers.id_seedCBC); 122 123 oidMap.put("CAMELLIA.128", NTTObjectIdentifiers.id_camellia128_cbc); 124 oidMap.put("CAMELLIA.192", NTTObjectIdentifiers.id_camellia192_cbc); 125 oidMap.put("CAMELLIA.256", NTTObjectIdentifiers.id_camellia256_cbc); 126 127 oidMap.put("ARIA.128", NSRIObjectIdentifiers.id_aria128_cbc); 128 oidMap.put("ARIA.192", NSRIObjectIdentifiers.id_aria192_cbc); 129 oidMap.put("ARIA.256", NSRIObjectIdentifiers.id_aria256_cbc); 130 publicAlgMap.put(PKCSObjectIdentifiers.rsaEncryption, R)131 publicAlgMap.put(PKCSObjectIdentifiers.rsaEncryption, "RSA"); publicAlgMap.put(X9ObjectIdentifiers.id_ecPublicKey, R)132 publicAlgMap.put(X9ObjectIdentifiers.id_ecPublicKey, "EC"); publicAlgMap.put(OIWObjectIdentifiers.elGamalAlgorithm, R)133 publicAlgMap.put(OIWObjectIdentifiers.elGamalAlgorithm, "DH"); publicAlgMap.put(PKCSObjectIdentifiers.dhKeyAgreement, R)134 publicAlgMap.put(PKCSObjectIdentifiers.dhKeyAgreement, "DH"); publicAlgMap.put(X9ObjectIdentifiers.id_dsa, R)135 publicAlgMap.put(X9ObjectIdentifiers.id_dsa, "DSA"); 136 } 137 138 private PublicKey verificationKey; 139 private BCFKSLoadStoreParameter.CertChainValidator validator; 140 getPublicKeyAlg(ASN1ObjectIdentifier oid)141 private static String getPublicKeyAlg(ASN1ObjectIdentifier oid) 142 { 143 String algName = (String)publicAlgMap.get(oid); 144 145 if (algName != null) 146 { 147 return algName; 148 } 149 150 return oid.getId(); 151 } 152 153 private final static BigInteger CERTIFICATE = BigInteger.valueOf(0); 154 private final static BigInteger PRIVATE_KEY = BigInteger.valueOf(1); 155 private final static BigInteger SECRET_KEY = BigInteger.valueOf(2); 156 private final static BigInteger PROTECTED_PRIVATE_KEY = BigInteger.valueOf(3); 157 private final static BigInteger PROTECTED_SECRET_KEY = BigInteger.valueOf(4); 158 159 private final JcaJceHelper helper; 160 private final Map<String, ObjectData> entries = new HashMap<String, ObjectData>(); 161 private final Map<String, PrivateKey> privateKeyCache = new HashMap<String, PrivateKey>(); 162 163 private AlgorithmIdentifier hmacAlgorithm; 164 private KeyDerivationFunc hmacPkbdAlgorithm; 165 private AlgorithmIdentifier signatureAlgorithm; 166 private Date creationDate; 167 private Date lastModifiedDate; 168 private ASN1ObjectIdentifier storeEncryptionAlgorithm = NISTObjectIdentifiers.id_aes256_CCM; 169 BcFKSKeyStoreSpi(JcaJceHelper helper)170 BcFKSKeyStoreSpi(JcaJceHelper helper) 171 { 172 this.helper = helper; 173 } 174 engineGetKey(String alias, char[] password)175 public Key engineGetKey(String alias, char[] password) 176 throws NoSuchAlgorithmException, UnrecoverableKeyException 177 { 178 ObjectData ent = (ObjectData)entries.get(alias); 179 180 if (ent != null) 181 { 182 if (ent.getType().equals(PRIVATE_KEY) || ent.getType().equals(PROTECTED_PRIVATE_KEY)) 183 { 184 PrivateKey cachedKey = (PrivateKey)privateKeyCache.get(alias); 185 if (cachedKey != null) 186 { 187 return cachedKey; 188 } 189 190 EncryptedPrivateKeyData encPrivData = EncryptedPrivateKeyData.getInstance(ent.getData()); 191 EncryptedPrivateKeyInfo encInfo = EncryptedPrivateKeyInfo.getInstance(encPrivData.getEncryptedPrivateKeyInfo()); 192 193 try 194 { 195 PrivateKeyInfo pInfo = PrivateKeyInfo.getInstance(decryptData("PRIVATE_KEY_ENCRYPTION", encInfo.getEncryptionAlgorithm(), password, encInfo.getEncryptedData())); 196 197 KeyFactory kFact = helper.createKeyFactory(getPublicKeyAlg(pInfo.getPrivateKeyAlgorithm().getAlgorithm())); 198 199 PrivateKey privateKey = kFact.generatePrivate(new PKCS8EncodedKeySpec(pInfo.getEncoded())); 200 201 // check that the key pair and the certificate public key are consistent 202 // TODO: new ConsistentKeyPair(engineGetCertificate(alias).getPublicKey(), privateKey); 203 204 privateKeyCache.put(alias, privateKey); 205 206 return privateKey; 207 } 208 catch (Exception e) 209 { 210 throw new UnrecoverableKeyException("BCFKS KeyStore unable to recover private key (" + alias + "): " + e.getMessage()); 211 } 212 } 213 else if (ent.getType().equals(SECRET_KEY) || ent.getType().equals(PROTECTED_SECRET_KEY)) 214 { 215 EncryptedSecretKeyData encKeyData = EncryptedSecretKeyData.getInstance(ent.getData()); 216 217 try 218 { 219 SecretKeyData keyData = SecretKeyData.getInstance(decryptData("SECRET_KEY_ENCRYPTION", encKeyData.getKeyEncryptionAlgorithm(), password, encKeyData.getEncryptedKeyData())); 220 SecretKeyFactory kFact = helper.createSecretKeyFactory(keyData.getKeyAlgorithm().getId()); 221 222 return kFact.generateSecret(new SecretKeySpec(keyData.getKeyBytes(), keyData.getKeyAlgorithm().getId())); 223 } 224 catch (Exception e) 225 { 226 throw new UnrecoverableKeyException("BCFKS KeyStore unable to recover secret key (" + alias + "): " + e.getMessage()); 227 } 228 } 229 else 230 { 231 throw new UnrecoverableKeyException("BCFKS KeyStore unable to recover secret key (" + alias + "): type not recognized"); 232 } 233 } 234 235 return null; 236 } 237 engineGetCertificateChain(String alias)238 public Certificate[] engineGetCertificateChain(String alias) 239 { 240 ObjectData ent = (ObjectData)entries.get(alias); 241 242 if (ent != null) 243 { 244 if (ent.getType().equals(PRIVATE_KEY) || ent.getType().equals(PROTECTED_PRIVATE_KEY)) 245 { 246 EncryptedPrivateKeyData encPrivData = EncryptedPrivateKeyData.getInstance(ent.getData()); 247 org.bouncycastle.asn1.x509.Certificate[] certificates = encPrivData.getCertificateChain(); 248 Certificate[] chain = new X509Certificate[certificates.length]; 249 250 for (int i = 0; i != chain.length; i++) 251 { 252 chain[i] = decodeCertificate(certificates[i]); 253 } 254 255 return chain; 256 } 257 } 258 259 return null; 260 } 261 engineGetCertificate(String s)262 public Certificate engineGetCertificate(String s) 263 { 264 ObjectData ent = (ObjectData)entries.get(s); 265 266 if (ent != null) 267 { 268 if (ent.getType().equals(PRIVATE_KEY) || ent.getType().equals(PROTECTED_PRIVATE_KEY)) 269 { 270 EncryptedPrivateKeyData encPrivData = EncryptedPrivateKeyData.getInstance(ent.getData()); 271 org.bouncycastle.asn1.x509.Certificate[] certificates = encPrivData.getCertificateChain(); 272 273 return decodeCertificate(certificates[0]); 274 } 275 else if (ent.getType().equals(CERTIFICATE)) 276 { 277 return decodeCertificate(ent.getData()); 278 } 279 } 280 281 return null; 282 } 283 decodeCertificate(Object cert)284 private Certificate decodeCertificate(Object cert) 285 { 286 if (helper != null) 287 { 288 try 289 { 290 CertificateFactory certFact = helper.createCertificateFactory("X.509"); 291 292 return certFact.generateCertificate(new ByteArrayInputStream(org.bouncycastle.asn1.x509.Certificate.getInstance(cert).getEncoded())); 293 } 294 catch (Exception e) 295 { 296 return null; 297 } 298 } 299 else 300 { 301 try 302 { 303 CertificateFactory certFact = CertificateFactory.getInstance("X.509"); 304 305 return certFact.generateCertificate(new ByteArrayInputStream(org.bouncycastle.asn1.x509.Certificate.getInstance(cert).getEncoded())); 306 } 307 catch (Exception e) 308 { 309 return null; 310 } 311 } 312 } 313 engineGetCreationDate(String s)314 public Date engineGetCreationDate(String s) 315 { 316 ObjectData ent = (ObjectData)entries.get(s); 317 318 if (ent != null) 319 { 320 try 321 { 322 // we return last modified as it represents date current state of entry was created 323 return ent.getLastModifiedDate().getDate(); 324 } 325 catch (ParseException e) 326 { 327 return new Date(); // it's here, but... 328 } 329 } 330 331 return null; 332 } 333 engineSetKeyEntry(String alias, Key key, char[] password, Certificate[] chain)334 public void engineSetKeyEntry(String alias, Key key, char[] password, Certificate[] chain) 335 throws KeyStoreException 336 { 337 Date creationDate = new Date(); 338 Date lastEditDate = creationDate; 339 340 ObjectData entry = (ObjectData)entries.get(alias); 341 if (entry != null) 342 { 343 creationDate = extractCreationDate(entry, creationDate); 344 } 345 346 privateKeyCache.remove(alias); 347 348 if (key instanceof PrivateKey) 349 { 350 if (chain == null) 351 { 352 throw new KeyStoreException("BCFKS KeyStore requires a certificate chain for private key storage."); 353 } 354 355 try 356 { 357 // check that the key pair and the certificate public are consistent 358 // TODO: new ConsistentKeyPair(chain[0].getPublicKey(), (PrivateKey)key); 359 360 byte[] encodedKey = key.getEncoded(); 361 362 KeyDerivationFunc pbkdAlgId = generatePkbdAlgorithmIdentifier(PKCSObjectIdentifiers.id_PBKDF2, 256 / 8); 363 byte[] keyBytes = generateKey(pbkdAlgId, "PRIVATE_KEY_ENCRYPTION", ((password != null) ? password : new char[0]), 32); 364 365 EncryptedPrivateKeyInfo keyInfo; 366 if (storeEncryptionAlgorithm.equals(NISTObjectIdentifiers.id_aes256_CCM)) 367 { 368 Cipher c = createCipher("AES/CCM/NoPadding", keyBytes); 369 370 byte[] encryptedKey = c.doFinal(encodedKey); 371 372 AlgorithmParameters algParams = c.getParameters(); 373 374 PBES2Parameters pbeParams = new PBES2Parameters(pbkdAlgId, new EncryptionScheme(NISTObjectIdentifiers.id_aes256_CCM, CCMParameters.getInstance(algParams.getEncoded()))); 375 376 keyInfo = new EncryptedPrivateKeyInfo(new AlgorithmIdentifier(PKCSObjectIdentifiers.id_PBES2, pbeParams), encryptedKey); 377 } 378 else 379 { 380 Cipher c = createCipher("AESKWP", keyBytes); 381 382 byte[] encryptedKey = c.doFinal(encodedKey); 383 384 PBES2Parameters pbeParams = new PBES2Parameters(pbkdAlgId, new EncryptionScheme(NISTObjectIdentifiers.id_aes256_wrap_pad)); 385 386 keyInfo = new EncryptedPrivateKeyInfo(new AlgorithmIdentifier(PKCSObjectIdentifiers.id_PBES2, pbeParams), encryptedKey); 387 } 388 389 EncryptedPrivateKeyData keySeq = createPrivateKeySequence(keyInfo, chain); 390 391 entries.put(alias, new ObjectData(PRIVATE_KEY, alias, creationDate, lastEditDate, keySeq.getEncoded(), null)); 392 } 393 catch (Exception e) 394 { 395 throw new ExtKeyStoreException("BCFKS KeyStore exception storing private key: " + e.toString(), e); 396 } 397 } 398 else if (key instanceof SecretKey) 399 { 400 if (chain != null) 401 { 402 throw new KeyStoreException("BCFKS KeyStore cannot store certificate chain with secret key."); 403 } 404 405 try 406 { 407 byte[] encodedKey = key.getEncoded(); 408 409 KeyDerivationFunc pbkdAlgId = generatePkbdAlgorithmIdentifier(PKCSObjectIdentifiers.id_PBKDF2, 256 / 8); 410 byte[] keyBytes = generateKey(pbkdAlgId, "SECRET_KEY_ENCRYPTION", ((password != null) ? password : new char[0]), 32); 411 412 String keyAlg = Strings.toUpperCase(key.getAlgorithm()); 413 SecretKeyData secKeyData; 414 415 if (keyAlg.indexOf("AES") > -1) 416 { 417 secKeyData = new SecretKeyData(NISTObjectIdentifiers.aes, encodedKey); 418 } 419 else 420 { 421 ASN1ObjectIdentifier algOid = (ASN1ObjectIdentifier)oidMap.get(keyAlg); 422 if (algOid != null) 423 { 424 secKeyData = new SecretKeyData(algOid, encodedKey); 425 } 426 else 427 { 428 algOid = (ASN1ObjectIdentifier)oidMap.get(keyAlg + "." + (encodedKey.length * 8)); 429 if (algOid != null) 430 { 431 secKeyData = new SecretKeyData(algOid, encodedKey); 432 } 433 else 434 { 435 throw new KeyStoreException("BCFKS KeyStore cannot recognize secret key (" + keyAlg + ") for storage."); 436 } 437 } 438 } 439 440 EncryptedSecretKeyData keyData; 441 if (storeEncryptionAlgorithm.equals(NISTObjectIdentifiers.id_aes256_CCM)) 442 { 443 Cipher c = createCipher("AES/CCM/NoPadding", keyBytes); 444 445 byte[] encryptedKey = c.doFinal(secKeyData.getEncoded()); 446 447 AlgorithmParameters algParams = c.getParameters(); 448 449 PBES2Parameters pbeParams = new PBES2Parameters(pbkdAlgId, new EncryptionScheme(NISTObjectIdentifiers.id_aes256_CCM, CCMParameters.getInstance(algParams.getEncoded()))); 450 451 keyData = new EncryptedSecretKeyData(new AlgorithmIdentifier(PKCSObjectIdentifiers.id_PBES2, pbeParams), encryptedKey); 452 } 453 else 454 { 455 Cipher c = createCipher("AESKWP", keyBytes); 456 457 byte[] encryptedKey = c.doFinal(secKeyData.getEncoded()); 458 459 PBES2Parameters pbeParams = new PBES2Parameters(pbkdAlgId, new EncryptionScheme(NISTObjectIdentifiers.id_aes256_wrap_pad)); 460 461 keyData = new EncryptedSecretKeyData(new AlgorithmIdentifier(PKCSObjectIdentifiers.id_PBES2, pbeParams), encryptedKey); 462 } 463 entries.put(alias, new ObjectData(SECRET_KEY, alias, creationDate, lastEditDate, keyData.getEncoded(), null)); 464 } 465 catch (Exception e) 466 { 467 throw new ExtKeyStoreException("BCFKS KeyStore exception storing private key: " + e.toString(), e); 468 } 469 } 470 else 471 { 472 throw new KeyStoreException("BCFKS KeyStore unable to recognize key."); 473 } 474 475 lastModifiedDate = lastEditDate; 476 } 477 createCipher(String algorithm, byte[] keyBytes)478 private Cipher createCipher(String algorithm, byte[] keyBytes) 479 throws NoSuchAlgorithmException, NoSuchPaddingException, InvalidKeyException, NoSuchProviderException 480 { 481 Cipher c = helper.createCipher(algorithm); 482 483 c.init(Cipher.ENCRYPT_MODE, new SecretKeySpec(keyBytes, "AES")); 484 485 return c; 486 } 487 getDefaultSecureRandom()488 private SecureRandom getDefaultSecureRandom() 489 { 490 return CryptoServicesRegistrar.getSecureRandom(); 491 } 492 createPrivateKeySequence(EncryptedPrivateKeyInfo encryptedPrivateKeyInfo, Certificate[] chain)493 private EncryptedPrivateKeyData createPrivateKeySequence(EncryptedPrivateKeyInfo encryptedPrivateKeyInfo, Certificate[] chain) 494 throws CertificateEncodingException 495 { 496 org.bouncycastle.asn1.x509.Certificate[] certChain = new org.bouncycastle.asn1.x509.Certificate[chain.length]; 497 for (int i = 0; i != chain.length; i++) 498 { 499 certChain[i] = org.bouncycastle.asn1.x509.Certificate.getInstance(chain[i].getEncoded()); 500 } 501 502 return new EncryptedPrivateKeyData(encryptedPrivateKeyInfo, certChain); 503 } 504 engineSetKeyEntry(String alias, byte[] keyBytes, Certificate[] chain)505 public void engineSetKeyEntry(String alias, byte[] keyBytes, Certificate[] chain) 506 throws KeyStoreException 507 { 508 Date creationDate = new Date(); 509 Date lastEditDate = creationDate; 510 511 ObjectData entry = (ObjectData)entries.get(alias); 512 if (entry != null) 513 { 514 creationDate = extractCreationDate(entry, creationDate); 515 } 516 517 if (chain != null) 518 { 519 EncryptedPrivateKeyInfo encInfo; 520 521 try 522 { 523 encInfo = EncryptedPrivateKeyInfo.getInstance(keyBytes); 524 } 525 catch (Exception e) 526 { 527 throw new ExtKeyStoreException("BCFKS KeyStore private key encoding must be an EncryptedPrivateKeyInfo.", e); 528 } 529 530 try 531 { 532 privateKeyCache.remove(alias); 533 entries.put(alias, new ObjectData(PROTECTED_PRIVATE_KEY, alias, creationDate, lastEditDate, createPrivateKeySequence(encInfo, chain).getEncoded(), null)); 534 } 535 catch (Exception e) 536 { 537 throw new ExtKeyStoreException("BCFKS KeyStore exception storing protected private key: " + e.toString(), e); 538 } 539 } 540 else 541 { 542 try 543 { 544 entries.put(alias, new ObjectData(PROTECTED_SECRET_KEY, alias, creationDate, lastEditDate, keyBytes, null)); 545 } 546 catch (Exception e) 547 { 548 throw new ExtKeyStoreException("BCFKS KeyStore exception storing protected private key: " + e.toString(), e); 549 } 550 } 551 552 lastModifiedDate = lastEditDate; 553 } 554 engineSetCertificateEntry(String alias, Certificate certificate)555 public void engineSetCertificateEntry(String alias, Certificate certificate) 556 throws KeyStoreException 557 { 558 ObjectData entry = (ObjectData)entries.get(alias); 559 Date creationDate = new Date(); 560 Date lastEditDate = creationDate; 561 562 if (entry != null) 563 { 564 if (!entry.getType().equals(CERTIFICATE)) 565 { 566 throw new KeyStoreException("BCFKS KeyStore already has a key entry with alias " + alias); 567 } 568 569 creationDate = extractCreationDate(entry, creationDate); 570 } 571 572 try 573 { 574 entries.put(alias, new ObjectData(CERTIFICATE, alias, creationDate, lastEditDate, certificate.getEncoded(), null)); 575 } 576 catch (CertificateEncodingException e) 577 { 578 throw new ExtKeyStoreException("BCFKS KeyStore unable to handle certificate: " + e.getMessage(), e); 579 } 580 581 lastModifiedDate = lastEditDate; 582 } 583 extractCreationDate(ObjectData entry, Date creationDate)584 private Date extractCreationDate(ObjectData entry, Date creationDate) 585 { 586 try 587 { 588 creationDate = entry.getCreationDate().getDate(); 589 } 590 catch (ParseException e) 591 { 592 // this should never happen, if it does we'll leave creation date unmodified and hope for the best. 593 } 594 return creationDate; 595 } 596 engineDeleteEntry(String alias)597 public void engineDeleteEntry(String alias) 598 throws KeyStoreException 599 { 600 ObjectData entry = (ObjectData)entries.get(alias); 601 602 if (entry == null) 603 { 604 return; 605 } 606 607 privateKeyCache.remove(alias); 608 entries.remove(alias); 609 610 lastModifiedDate = new Date(); 611 } 612 engineAliases()613 public Enumeration<String> engineAliases() 614 { 615 final Iterator<String> it = new HashSet(entries.keySet()).iterator(); 616 617 return new Enumeration() 618 { 619 public boolean hasMoreElements() 620 { 621 return it.hasNext(); 622 } 623 624 public Object nextElement() 625 { 626 return it.next(); 627 } 628 }; 629 } 630 631 public boolean engineContainsAlias(String alias) 632 { 633 if (alias == null) 634 { 635 throw new NullPointerException("alias value is null"); 636 } 637 638 return entries.containsKey(alias); 639 } 640 641 public int engineSize() 642 { 643 return entries.size(); 644 } 645 646 public boolean engineIsKeyEntry(String alias) 647 { 648 ObjectData ent = (ObjectData)entries.get(alias); 649 650 if (ent != null) 651 { 652 BigInteger entryType = ent.getType(); 653 return entryType.equals(PRIVATE_KEY) || entryType.equals(SECRET_KEY) 654 || entryType.equals(PROTECTED_PRIVATE_KEY) || entryType.equals(PROTECTED_SECRET_KEY); 655 } 656 657 return false; 658 } 659 660 public boolean engineIsCertificateEntry(String alias) 661 { 662 ObjectData ent = (ObjectData)entries.get(alias); 663 664 if (ent != null) 665 { 666 return ent.getType().equals(CERTIFICATE); 667 } 668 669 return false; 670 } 671 672 public String engineGetCertificateAlias(Certificate certificate) 673 { 674 if (certificate == null) 675 { 676 return null; 677 } 678 679 byte[] encodedCert; 680 try 681 { 682 encodedCert = certificate.getEncoded(); 683 } 684 catch (CertificateEncodingException e) 685 { 686 return null; 687 } 688 689 for (Iterator<String> it = entries.keySet().iterator(); it.hasNext(); ) 690 { 691 String alias = (String)it.next(); 692 ObjectData ent = (ObjectData)entries.get(alias); 693 694 if (ent.getType().equals(CERTIFICATE)) 695 { 696 if (Arrays.areEqual(ent.getData(), encodedCert)) 697 { 698 return alias; 699 } 700 } 701 else if (ent.getType().equals(PRIVATE_KEY) || ent.getType().equals(PROTECTED_PRIVATE_KEY)) 702 { 703 try 704 { 705 EncryptedPrivateKeyData encPrivData = EncryptedPrivateKeyData.getInstance(ent.getData()); 706 if (Arrays.areEqual(encPrivData.getCertificateChain()[0].toASN1Primitive().getEncoded(), encodedCert)) 707 { 708 return alias; 709 } 710 } 711 catch (IOException e) 712 { 713 // ignore - this should never happen 714 } 715 } 716 } 717 718 return null; 719 } 720 721 private byte[] generateKey(KeyDerivationFunc pbkdAlgorithm, String purpose, char[] password, int defKeySize) 722 throws IOException 723 { 724 byte[] encPassword = PBEParametersGenerator.PKCS12PasswordToBytes(password); 725 byte[] differentiator = PBEParametersGenerator.PKCS12PasswordToBytes(purpose.toCharArray()); 726 727 int keySizeInBytes = defKeySize; 728 729 if (MiscObjectIdentifiers.id_scrypt.equals(pbkdAlgorithm.getAlgorithm())) 730 { 731 ScryptParams params = ScryptParams.getInstance(pbkdAlgorithm.getParameters()); 732 733 if (params.getKeyLength() != null) 734 { 735 keySizeInBytes = params.getKeyLength().intValue(); 736 } 737 else if (keySizeInBytes == -1) 738 { 739 throw new IOException("no keyLength found in ScryptParams"); 740 } 741 return SCrypt.generate(Arrays.concatenate(encPassword, differentiator), params.getSalt(), 742 params.getCostParameter().intValue(), params.getBlockSize().intValue(), 743 params.getBlockSize().intValue(), keySizeInBytes); 744 } 745 else if (pbkdAlgorithm.getAlgorithm().equals(PKCSObjectIdentifiers.id_PBKDF2)) 746 { 747 PBKDF2Params pbkdf2Params = PBKDF2Params.getInstance(pbkdAlgorithm.getParameters()); 748 749 if (pbkdf2Params.getKeyLength() != null) 750 { 751 keySizeInBytes = pbkdf2Params.getKeyLength().intValue(); 752 } 753 else if (keySizeInBytes == -1) 754 { 755 throw new IOException("no keyLength found in PBKDF2Params"); 756 } 757 758 if (pbkdf2Params.getPrf().getAlgorithm().equals(PKCSObjectIdentifiers.id_hmacWithSHA512)) 759 { 760 PKCS5S2ParametersGenerator pGen = new PKCS5S2ParametersGenerator(new SHA512Digest()); 761 762 pGen.init(Arrays.concatenate(encPassword, differentiator), pbkdf2Params.getSalt(), pbkdf2Params.getIterationCount().intValue()); 763 764 return ((KeyParameter)pGen.generateDerivedParameters(keySizeInBytes * 8)).getKey(); 765 } 766 else if (pbkdf2Params.getPrf().getAlgorithm().equals(NISTObjectIdentifiers.id_hmacWithSHA3_512)) 767 { 768 PKCS5S2ParametersGenerator pGen = new PKCS5S2ParametersGenerator(new SHA3Digest(512)); 769 770 pGen.init(Arrays.concatenate(encPassword, differentiator), pbkdf2Params.getSalt(), pbkdf2Params.getIterationCount().intValue()); 771 772 return ((KeyParameter)pGen.generateDerivedParameters(keySizeInBytes * 8)).getKey(); 773 } 774 else 775 { 776 throw new IOException("BCFKS KeyStore: unrecognized MAC PBKD PRF: " + pbkdf2Params.getPrf().getAlgorithm()); 777 } 778 } 779 else 780 { 781 throw new IOException("BCFKS KeyStore: unrecognized MAC PBKD."); 782 } 783 } 784 785 private void verifySig(ASN1Encodable store, SignatureCheck integrityCheck, PublicKey key) 786 throws GeneralSecurityException, IOException 787 { 788 Signature sig = helper.createSignature(integrityCheck.getSignatureAlgorithm().getAlgorithm().getId()); 789 790 sig.initVerify(key); 791 792 sig.update(store.toASN1Primitive().getEncoded(ASN1Encoding.DER)); 793 794 if (!sig.verify(integrityCheck.getSignature().getOctets())) 795 { 796 throw new IOException("BCFKS KeyStore corrupted: signature calculation failed"); 797 } 798 } 799 800 private void verifyMac(byte[] content, PbkdMacIntegrityCheck integrityCheck, char[] password) 801 throws NoSuchAlgorithmException, IOException, NoSuchProviderException 802 { 803 byte[] check = calculateMac(content, integrityCheck.getMacAlgorithm(), integrityCheck.getPbkdAlgorithm(), password); 804 805 if (!Arrays.constantTimeAreEqual(check, integrityCheck.getMac())) 806 { 807 throw new IOException("BCFKS KeyStore corrupted: MAC calculation failed"); 808 } 809 } 810 811 private byte[] calculateMac(byte[] content, AlgorithmIdentifier algorithm, KeyDerivationFunc pbkdAlgorithm, char[] password) 812 throws NoSuchAlgorithmException, IOException, NoSuchProviderException 813 { 814 String algorithmId = algorithm.getAlgorithm().getId(); 815 816 Mac mac = helper.createMac(algorithmId); 817 818 try 819 { 820 // no default key size for MAC. 821 mac.init(new SecretKeySpec(generateKey(pbkdAlgorithm, "INTEGRITY_CHECK", ((password != null) ? password : new char[0]), -1), algorithmId)); 822 } 823 catch (InvalidKeyException e) 824 { 825 throw new IOException("Cannot set up MAC calculation: " + e.getMessage()); 826 } 827 828 return mac.doFinal(content); 829 } 830 831 public void engineStore(KeyStore.LoadStoreParameter parameter) 832 throws CertificateException, NoSuchAlgorithmException, IOException 833 { 834 if (parameter == null) 835 { 836 throw new IllegalArgumentException("'parameter' arg cannot be null"); 837 } 838 839 if (parameter instanceof BCFKSStoreParameter) 840 { 841 BCFKSStoreParameter bcParam = (BCFKSStoreParameter)parameter; 842 843 char[] password = ParameterUtil.extractPassword(parameter); 844 845 hmacPkbdAlgorithm = generatePkbdAlgorithmIdentifier(bcParam.getStorePBKDFConfig(), 512 / 8); 846 847 engineStore(bcParam.getOutputStream(), password); 848 } 849 else if (parameter instanceof BCFKSLoadStoreParameter) 850 { 851 BCFKSLoadStoreParameter bcParam = (BCFKSLoadStoreParameter)parameter; 852 853 if (bcParam.getStoreSignatureKey() != null) 854 { 855 signatureAlgorithm = generateSignatureAlgId(bcParam.getStoreSignatureKey(), bcParam.getStoreSignatureAlgorithm()); 856 857 hmacPkbdAlgorithm = generatePkbdAlgorithmIdentifier(bcParam.getStorePBKDFConfig(), 512 / 8); 858 859 if (bcParam.getStoreEncryptionAlgorithm() == BCFKSLoadStoreParameter.EncryptionAlgorithm.AES256_CCM) 860 { 861 storeEncryptionAlgorithm = NISTObjectIdentifiers.id_aes256_CCM; 862 } 863 else 864 { 865 storeEncryptionAlgorithm = NISTObjectIdentifiers.id_aes256_wrap_pad; 866 } 867 868 if (bcParam.getStoreMacAlgorithm() == BCFKSLoadStoreParameter.MacAlgorithm.HmacSHA512) 869 { 870 hmacAlgorithm = new AlgorithmIdentifier(PKCSObjectIdentifiers.id_hmacWithSHA512, DERNull.INSTANCE); 871 } 872 else 873 { 874 hmacAlgorithm = new AlgorithmIdentifier(NISTObjectIdentifiers.id_hmacWithSHA3_512, DERNull.INSTANCE); 875 } 876 877 char[] password = ParameterUtil.extractPassword(bcParam); 878 879 EncryptedObjectStoreData encStoreData = getEncryptedObjectStoreData(signatureAlgorithm, password); 880 881 try 882 { 883 Signature sig = helper.createSignature(signatureAlgorithm.getAlgorithm().getId()); 884 885 sig.initSign((PrivateKey)bcParam.getStoreSignatureKey()); 886 887 sig.update(encStoreData.getEncoded()); 888 889 SignatureCheck signatureCheck; 890 X509Certificate[] certs = bcParam.getStoreCertificates(); 891 892 if (certs != null) 893 { 894 org.bouncycastle.asn1.x509.Certificate[] certificates = new org.bouncycastle.asn1.x509.Certificate[certs.length]; 895 for (int i = 0; i != certificates.length; i++) 896 { 897 certificates[i] = org.bouncycastle.asn1.x509.Certificate.getInstance(certs[i].getEncoded()); 898 } 899 signatureCheck = new SignatureCheck(signatureAlgorithm, certificates, sig.sign()); 900 } 901 else 902 { 903 signatureCheck = new SignatureCheck(signatureAlgorithm, sig.sign()); 904 } 905 ObjectStore store = new ObjectStore(encStoreData, new ObjectStoreIntegrityCheck(signatureCheck)); 906 907 bcParam.getOutputStream().write(store.getEncoded()); 908 909 bcParam.getOutputStream().flush(); 910 } 911 catch (GeneralSecurityException e) 912 { 913 throw new IOException("error creating signature: " + e.getMessage(), e); 914 } 915 } 916 else 917 { 918 char[] password = ParameterUtil.extractPassword(bcParam); 919 920 hmacPkbdAlgorithm = generatePkbdAlgorithmIdentifier(bcParam.getStorePBKDFConfig(), 512 / 8); 921 922 if (bcParam.getStoreEncryptionAlgorithm() == BCFKSLoadStoreParameter.EncryptionAlgorithm.AES256_CCM) 923 { 924 storeEncryptionAlgorithm = NISTObjectIdentifiers.id_aes256_CCM; 925 } 926 else 927 { 928 storeEncryptionAlgorithm = NISTObjectIdentifiers.id_aes256_wrap_pad; 929 } 930 931 if (bcParam.getStoreMacAlgorithm() == BCFKSLoadStoreParameter.MacAlgorithm.HmacSHA512) 932 { 933 hmacAlgorithm = new AlgorithmIdentifier(PKCSObjectIdentifiers.id_hmacWithSHA512, DERNull.INSTANCE); 934 } 935 else 936 { 937 hmacAlgorithm = new AlgorithmIdentifier(NISTObjectIdentifiers.id_hmacWithSHA3_512, DERNull.INSTANCE); 938 } 939 940 engineStore(bcParam.getOutputStream(), password); 941 } 942 } 943 else if (parameter instanceof BCLoadStoreParameter) 944 { 945 BCLoadStoreParameter bcParam = (BCLoadStoreParameter)parameter; 946 947 engineStore(bcParam.getOutputStream(), ParameterUtil.extractPassword(parameter)); 948 } 949 else 950 { 951 throw new IllegalArgumentException( 952 "no support for 'parameter' of type " + parameter.getClass().getName()); 953 } 954 955 } 956 957 public void engineStore(OutputStream outputStream, char[] password) 958 throws IOException, NoSuchAlgorithmException, CertificateException 959 { 960 if (creationDate == null) 961 { 962 throw new IOException("KeyStore not initialized"); 963 } 964 965 EncryptedObjectStoreData encStoreData = getEncryptedObjectStoreData(hmacAlgorithm, password); 966 967 // update the salt 968 if (MiscObjectIdentifiers.id_scrypt.equals(hmacPkbdAlgorithm.getAlgorithm())) 969 { 970 ScryptParams sParams = ScryptParams.getInstance(hmacPkbdAlgorithm.getParameters()); 971 972 hmacPkbdAlgorithm = generatePkbdAlgorithmIdentifier(hmacPkbdAlgorithm, sParams.getKeyLength().intValue()); 973 } 974 else 975 { 976 PBKDF2Params pbkdf2Params = PBKDF2Params.getInstance(hmacPkbdAlgorithm.getParameters()); 977 978 hmacPkbdAlgorithm = generatePkbdAlgorithmIdentifier(hmacPkbdAlgorithm, pbkdf2Params.getKeyLength().intValue()); 979 } 980 byte[] mac; 981 try 982 { 983 mac = calculateMac(encStoreData.getEncoded(), hmacAlgorithm, hmacPkbdAlgorithm, password); 984 } 985 catch (NoSuchProviderException e) 986 { 987 throw new IOException("cannot calculate mac: " + e.getMessage()); 988 } 989 990 ObjectStore store = new ObjectStore(encStoreData, new ObjectStoreIntegrityCheck(new PbkdMacIntegrityCheck(hmacAlgorithm, hmacPkbdAlgorithm, mac))); 991 992 outputStream.write(store.getEncoded()); 993 994 outputStream.flush(); 995 } 996 997 private EncryptedObjectStoreData getEncryptedObjectStoreData(AlgorithmIdentifier integrityAlgorithm, char[] password) 998 throws IOException, NoSuchAlgorithmException 999 { 1000 ObjectData[] dataArray = (ObjectData[])entries.values().toArray(new ObjectData[entries.size()]); 1001 1002 KeyDerivationFunc pbkdAlgId = generatePkbdAlgorithmIdentifier(hmacPkbdAlgorithm, 256 / 8); 1003 byte[] keyBytes = generateKey(pbkdAlgId, "STORE_ENCRYPTION", ((password != null) ? password : new char[0]), 256 / 8); 1004 1005 ObjectStoreData storeData = new ObjectStoreData(integrityAlgorithm, creationDate, lastModifiedDate, new ObjectDataSequence(dataArray), null); 1006 EncryptedObjectStoreData encStoreData; 1007 1008 try 1009 { 1010 if (storeEncryptionAlgorithm.equals(NISTObjectIdentifiers.id_aes256_CCM)) 1011 { 1012 Cipher c = createCipher("AES/CCM/NoPadding", keyBytes); 1013 1014 byte[] encOut = c.doFinal(storeData.getEncoded()); 1015 1016 AlgorithmParameters algorithmParameters = c.getParameters(); 1017 1018 PBES2Parameters pbeParams = new PBES2Parameters(pbkdAlgId, new EncryptionScheme(NISTObjectIdentifiers.id_aes256_CCM, CCMParameters.getInstance(algorithmParameters.getEncoded()))); 1019 1020 encStoreData = new EncryptedObjectStoreData(new AlgorithmIdentifier(PKCSObjectIdentifiers.id_PBES2, pbeParams), encOut); 1021 } 1022 else 1023 { 1024 Cipher c = createCipher("AESKWP", keyBytes); 1025 1026 byte[] encOut = c.doFinal(storeData.getEncoded()); 1027 PBES2Parameters pbeParams = new PBES2Parameters(pbkdAlgId, new EncryptionScheme(NISTObjectIdentifiers.id_aes256_wrap_pad)); 1028 1029 encStoreData = new EncryptedObjectStoreData(new AlgorithmIdentifier(PKCSObjectIdentifiers.id_PBES2, pbeParams), encOut); 1030 } 1031 } 1032 catch (NoSuchPaddingException e) 1033 { 1034 throw new NoSuchAlgorithmException(e.toString()); 1035 } 1036 catch (BadPaddingException e) 1037 { 1038 throw new IOException(e.toString()); 1039 } 1040 catch (IllegalBlockSizeException e) 1041 { 1042 throw new IOException(e.toString()); 1043 } 1044 catch (InvalidKeyException e) 1045 { 1046 throw new IOException(e.toString()); 1047 } 1048 catch (NoSuchProviderException e) 1049 { 1050 throw new IOException(e.toString()); 1051 } 1052 return encStoreData; 1053 } 1054 1055 public void engineLoad(KeyStore.LoadStoreParameter parameter) 1056 throws CertificateException, NoSuchAlgorithmException, IOException 1057 { 1058 if (parameter == null) 1059 { 1060 engineLoad(null, null); 1061 } 1062 else if (parameter instanceof BCFKSLoadStoreParameter) 1063 { 1064 BCFKSLoadStoreParameter bcParam = (BCFKSLoadStoreParameter)parameter; 1065 1066 char[] password = ParameterUtil.extractPassword(bcParam); 1067 1068 hmacPkbdAlgorithm = generatePkbdAlgorithmIdentifier(bcParam.getStorePBKDFConfig(), 512 / 8); 1069 1070 if (bcParam.getStoreEncryptionAlgorithm() == BCFKSLoadStoreParameter.EncryptionAlgorithm.AES256_CCM) 1071 { 1072 storeEncryptionAlgorithm = NISTObjectIdentifiers.id_aes256_CCM; 1073 } 1074 else 1075 { 1076 storeEncryptionAlgorithm = NISTObjectIdentifiers.id_aes256_wrap_pad; 1077 } 1078 1079 if (bcParam.getStoreMacAlgorithm() == BCFKSLoadStoreParameter.MacAlgorithm.HmacSHA512) 1080 { 1081 hmacAlgorithm = new AlgorithmIdentifier(PKCSObjectIdentifiers.id_hmacWithSHA512, DERNull.INSTANCE); 1082 } 1083 else 1084 { 1085 hmacAlgorithm = new AlgorithmIdentifier(NISTObjectIdentifiers.id_hmacWithSHA3_512, DERNull.INSTANCE); 1086 } 1087 1088 this.verificationKey = (PublicKey)bcParam.getStoreSignatureKey(); 1089 this.validator = bcParam.getCertChainValidator(); 1090 this.signatureAlgorithm = generateSignatureAlgId(verificationKey, bcParam.getStoreSignatureAlgorithm()); 1091 1092 AlgorithmIdentifier presetHmacAlgorithm = hmacAlgorithm; 1093 ASN1ObjectIdentifier presetStoreEncryptionAlgorithm = storeEncryptionAlgorithm; 1094 1095 InputStream inputStream = bcParam.getInputStream(); 1096 1097 engineLoad(inputStream, password); 1098 1099 if (inputStream != null) 1100 { 1101 if (//!presetHmacAlgorithm.equals(hmacAlgorithm) 1102 !isSimilarHmacPbkd(bcParam.getStorePBKDFConfig(), hmacPkbdAlgorithm) 1103 || !presetStoreEncryptionAlgorithm.equals(storeEncryptionAlgorithm)) 1104 { 1105 throw new IOException("configuration parameters do not match existing store"); 1106 } 1107 } 1108 } 1109 else if (parameter instanceof BCLoadStoreParameter) 1110 { 1111 BCLoadStoreParameter bcParam = (BCLoadStoreParameter)parameter; 1112 1113 engineLoad(bcParam.getInputStream(), ParameterUtil.extractPassword(parameter)); 1114 } 1115 else 1116 { 1117 throw new IllegalArgumentException( 1118 "no support for 'parameter' of type " + parameter.getClass().getName()); 1119 } 1120 } 1121 1122 private boolean isSimilarHmacPbkd(PBKDFConfig storePBKDFConfig, KeyDerivationFunc hmacPkbdAlgorithm) 1123 { 1124 if (!storePBKDFConfig.getAlgorithm().equals(hmacPkbdAlgorithm.getAlgorithm())) 1125 { 1126 return false; 1127 } 1128 1129 if (MiscObjectIdentifiers.id_scrypt.equals(hmacPkbdAlgorithm.getAlgorithm())) 1130 { 1131 if (!(storePBKDFConfig instanceof ScryptConfig)) 1132 { 1133 return false; 1134 } 1135 1136 ScryptConfig scryptConfig = (ScryptConfig)storePBKDFConfig; 1137 ScryptParams sParams = ScryptParams.getInstance(hmacPkbdAlgorithm.getParameters()); 1138 1139 if (scryptConfig.getSaltLength() != sParams.getSalt().length 1140 || scryptConfig.getBlockSize() != sParams.getBlockSize().intValue() 1141 || scryptConfig.getCostParameter() != sParams.getCostParameter().intValue() 1142 || scryptConfig.getParallelizationParameter() != sParams.getParallelizationParameter().intValue()) 1143 { 1144 return false; 1145 } 1146 } 1147 else 1148 { 1149 if (!(storePBKDFConfig instanceof PBKDF2Config)) 1150 { 1151 return false; 1152 } 1153 1154 PBKDF2Config pbkdf2Config = (PBKDF2Config)storePBKDFConfig; 1155 PBKDF2Params pbkdf2Params = PBKDF2Params.getInstance(hmacPkbdAlgorithm.getParameters()); 1156 1157 if (pbkdf2Config.getSaltLength() != pbkdf2Params.getSalt().length 1158 || pbkdf2Config.getIterationCount() != pbkdf2Params.getIterationCount().intValue()) 1159 { 1160 return false; 1161 } 1162 } 1163 1164 return true; 1165 } 1166 1167 public void engineLoad(InputStream inputStream, char[] password) 1168 throws IOException, NoSuchAlgorithmException, CertificateException 1169 { 1170 // reset any current values 1171 entries.clear(); 1172 privateKeyCache.clear(); 1173 1174 lastModifiedDate = creationDate = null; 1175 hmacAlgorithm = null; 1176 1177 if (inputStream == null) 1178 { 1179 // initialise defaults 1180 lastModifiedDate = creationDate = new Date(); 1181 verificationKey = null; 1182 validator = null; 1183 1184 // basic initialisation 1185 hmacAlgorithm = new AlgorithmIdentifier(PKCSObjectIdentifiers.id_hmacWithSHA512, DERNull.INSTANCE); 1186 hmacPkbdAlgorithm = generatePkbdAlgorithmIdentifier(PKCSObjectIdentifiers.id_PBKDF2, 512 / 8); 1187 1188 return; 1189 } 1190 1191 ASN1InputStream aIn = new ASN1InputStream(inputStream); 1192 1193 ObjectStore store; 1194 1195 try 1196 { 1197 store = ObjectStore.getInstance(aIn.readObject()); 1198 } 1199 catch (Exception e) 1200 { 1201 throw new IOException(e.getMessage()); 1202 } 1203 1204 ObjectStoreIntegrityCheck integrityCheck = store.getIntegrityCheck(); 1205 AlgorithmIdentifier integrityAlg; 1206 1207 if (integrityCheck.getType() == ObjectStoreIntegrityCheck.PBKD_MAC_CHECK) 1208 { 1209 PbkdMacIntegrityCheck pbkdMacIntegrityCheck = PbkdMacIntegrityCheck.getInstance(integrityCheck.getIntegrityCheck()); 1210 1211 hmacAlgorithm = pbkdMacIntegrityCheck.getMacAlgorithm(); 1212 hmacPkbdAlgorithm = pbkdMacIntegrityCheck.getPbkdAlgorithm(); 1213 1214 integrityAlg = hmacAlgorithm; 1215 1216 try 1217 { 1218 verifyMac(store.getStoreData().toASN1Primitive().getEncoded(), pbkdMacIntegrityCheck, password); 1219 } 1220 catch (NoSuchProviderException e) 1221 { 1222 throw new IOException(e.getMessage()); 1223 } 1224 } 1225 else if (integrityCheck.getType() == ObjectStoreIntegrityCheck.SIG_CHECK) 1226 { 1227 SignatureCheck sigCheck = SignatureCheck.getInstance(integrityCheck.getIntegrityCheck()); 1228 1229 integrityAlg = sigCheck.getSignatureAlgorithm(); 1230 1231 try 1232 { 1233 org.bouncycastle.asn1.x509.Certificate[] certificates = sigCheck.getCertificates(); 1234 if (validator != null) 1235 { 1236 if (certificates == null) 1237 { 1238 throw new IOException("validator specified but no certifcates in store"); 1239 } 1240 CertificateFactory certFact = helper.createCertificateFactory("X.509"); 1241 X509Certificate[] certs = new X509Certificate[certificates.length]; 1242 1243 for (int i = 0; i != certs.length; i++) 1244 { 1245 certs[i] = (X509Certificate)certFact.generateCertificate( 1246 new ByteArrayInputStream(certificates[i].getEncoded())); 1247 } 1248 1249 if (validator.isValid(certs)) 1250 { 1251 verifySig(store.getStoreData(), sigCheck, certs[0].getPublicKey()); 1252 } 1253 else 1254 { 1255 throw new IOException("certificate chain in key store signature not valid"); 1256 } 1257 } 1258 else 1259 { 1260 verifySig(store.getStoreData(), sigCheck, verificationKey); 1261 } 1262 } 1263 catch (GeneralSecurityException e) 1264 { 1265 throw new IOException("error verifying signature: " + e.getMessage(), e); 1266 } 1267 } 1268 else 1269 { 1270 throw new IOException("BCFKS KeyStore unable to recognize integrity check."); 1271 } 1272 1273 ASN1Encodable sData = store.getStoreData(); 1274 1275 ObjectStoreData storeData; 1276 if (sData instanceof EncryptedObjectStoreData) 1277 { 1278 EncryptedObjectStoreData encryptedStoreData = (EncryptedObjectStoreData)sData; 1279 AlgorithmIdentifier protectAlgId = encryptedStoreData.getEncryptionAlgorithm(); 1280 1281 storeData = ObjectStoreData.getInstance(decryptData("STORE_ENCRYPTION", protectAlgId, password, encryptedStoreData.getEncryptedContent().getOctets())); 1282 } 1283 else 1284 { 1285 storeData = ObjectStoreData.getInstance(sData); 1286 } 1287 1288 try 1289 { 1290 creationDate = storeData.getCreationDate().getDate(); 1291 lastModifiedDate = storeData.getLastModifiedDate().getDate(); 1292 } 1293 catch (ParseException e) 1294 { 1295 throw new IOException("BCFKS KeyStore unable to parse store data information."); 1296 } 1297 1298 if (!storeData.getIntegrityAlgorithm().equals(integrityAlg)) 1299 { 1300 throw new IOException("BCFKS KeyStore storeData integrity algorithm does not match store integrity algorithm."); 1301 } 1302 1303 for (Iterator it = storeData.getObjectDataSequence().iterator(); it.hasNext(); ) 1304 { 1305 ObjectData objData = ObjectData.getInstance(it.next()); 1306 1307 entries.put(objData.getIdentifier(), objData); 1308 } 1309 } 1310 1311 private byte[] decryptData(String purpose, AlgorithmIdentifier protectAlgId, char[] password, byte[] encryptedData) 1312 throws IOException 1313 { 1314 if (!protectAlgId.getAlgorithm().equals(PKCSObjectIdentifiers.id_PBES2)) 1315 { 1316 throw new IOException("BCFKS KeyStore cannot recognize protection algorithm."); 1317 } 1318 1319 PBES2Parameters pbes2Parameters = PBES2Parameters.getInstance(protectAlgId.getParameters()); 1320 EncryptionScheme algId = pbes2Parameters.getEncryptionScheme(); 1321 1322 try 1323 { 1324 Cipher c; 1325 AlgorithmParameters algParams; 1326 if (algId.getAlgorithm().equals(NISTObjectIdentifiers.id_aes256_CCM)) 1327 { 1328 c = helper.createCipher("AES/CCM/NoPadding"); 1329 algParams = helper.createAlgorithmParameters("CCM"); 1330 1331 CCMParameters ccmParameters = CCMParameters.getInstance(algId.getParameters()); 1332 1333 algParams.init(ccmParameters.getEncoded()); 1334 } 1335 else if (algId.getAlgorithm().equals(NISTObjectIdentifiers.id_aes256_wrap_pad)) 1336 { 1337 c = helper.createCipher("AESKWP"); 1338 algParams = null; 1339 } 1340 else 1341 { 1342 throw new IOException("BCFKS KeyStore cannot recognize protection encryption algorithm."); 1343 } 1344 1345 byte[] keyBytes = generateKey(pbes2Parameters.getKeyDerivationFunc(), purpose, ((password != null) ? password : new char[0]), 32); 1346 1347 c.init(Cipher.DECRYPT_MODE, new SecretKeySpec(keyBytes, "AES"), algParams); 1348 1349 byte[] rv = c.doFinal(encryptedData); 1350 return rv; 1351 } 1352 catch (IOException e) 1353 { 1354 throw e; 1355 } 1356 catch (Exception e) 1357 { 1358 throw new IOException(e.toString()); 1359 } 1360 } 1361 1362 private AlgorithmIdentifier generateSignatureAlgId(Key key, BCFKSLoadStoreParameter.SignatureAlgorithm sigAlg) 1363 throws IOException 1364 { 1365 if (key== null) 1366 { 1367 return null; 1368 } 1369 1370 if (key instanceof ECKey) 1371 { 1372 if (sigAlg == BCFKSLoadStoreParameter.SignatureAlgorithm.SHA512withECDSA) 1373 { 1374 return new AlgorithmIdentifier(X9ObjectIdentifiers.ecdsa_with_SHA512); 1375 } 1376 else if (sigAlg == BCFKSLoadStoreParameter.SignatureAlgorithm.SHA3_512withECDSA) 1377 { 1378 return new AlgorithmIdentifier(NISTObjectIdentifiers.id_ecdsa_with_sha3_512); 1379 } 1380 } 1381 if (key instanceof DSAKey) 1382 { 1383 if (sigAlg == BCFKSLoadStoreParameter.SignatureAlgorithm.SHA512withDSA) 1384 { 1385 return new AlgorithmIdentifier(NISTObjectIdentifiers.dsa_with_sha512); 1386 } 1387 else if (sigAlg == BCFKSLoadStoreParameter.SignatureAlgorithm.SHA3_512withDSA) 1388 { 1389 return new AlgorithmIdentifier(NISTObjectIdentifiers.id_dsa_with_sha3_512); 1390 } 1391 } 1392 if (key instanceof RSAKey) 1393 { 1394 if (sigAlg == BCFKSLoadStoreParameter.SignatureAlgorithm.SHA512withRSA) 1395 { 1396 return new AlgorithmIdentifier(PKCSObjectIdentifiers.sha512WithRSAEncryption, DERNull.INSTANCE); 1397 } 1398 else if (sigAlg == BCFKSLoadStoreParameter.SignatureAlgorithm.SHA3_512withRSA) 1399 { 1400 return new AlgorithmIdentifier(NISTObjectIdentifiers.id_rsassa_pkcs1_v1_5_with_sha3_512, DERNull.INSTANCE); 1401 } 1402 } 1403 throw new IOException("unknown signature algorithm"); 1404 } 1405 1406 private KeyDerivationFunc generatePkbdAlgorithmIdentifier(PBKDFConfig pbkdfConfig, int keySizeInBytes) 1407 { 1408 if (MiscObjectIdentifiers.id_scrypt.equals(pbkdfConfig.getAlgorithm())) 1409 { 1410 ScryptConfig scryptConfig = (ScryptConfig)pbkdfConfig; 1411 1412 byte[] pbkdSalt = new byte[scryptConfig.getSaltLength()]; 1413 getDefaultSecureRandom().nextBytes(pbkdSalt); 1414 1415 ScryptParams params = new ScryptParams( 1416 pbkdSalt, 1417 scryptConfig.getCostParameter(), scryptConfig.getBlockSize(), scryptConfig.getParallelizationParameter(), keySizeInBytes); 1418 1419 return new KeyDerivationFunc(MiscObjectIdentifiers.id_scrypt, params); 1420 } 1421 else 1422 { 1423 PBKDF2Config pbkdf2Config = (PBKDF2Config)pbkdfConfig; 1424 1425 byte[] pbkdSalt = new byte[pbkdf2Config.getSaltLength()]; 1426 getDefaultSecureRandom().nextBytes(pbkdSalt); 1427 1428 return new KeyDerivationFunc(PKCSObjectIdentifiers.id_PBKDF2, new PBKDF2Params(pbkdSalt, pbkdf2Config.getIterationCount(), keySizeInBytes, pbkdf2Config.getPRF())); 1429 } 1430 } 1431 1432 private KeyDerivationFunc generatePkbdAlgorithmIdentifier(KeyDerivationFunc baseAlg, int keySizeInBytes) 1433 { 1434 if (MiscObjectIdentifiers.id_scrypt.equals(baseAlg.getAlgorithm())) 1435 { 1436 ScryptParams oldParams = ScryptParams.getInstance(baseAlg.getParameters()); 1437 1438 byte[] pbkdSalt = new byte[oldParams.getSalt().length]; 1439 getDefaultSecureRandom().nextBytes(pbkdSalt); 1440 1441 ScryptParams params = new ScryptParams( 1442 pbkdSalt, 1443 oldParams.getCostParameter(), oldParams.getBlockSize(), oldParams.getParallelizationParameter(), BigInteger.valueOf(keySizeInBytes)); 1444 1445 return new KeyDerivationFunc(MiscObjectIdentifiers.id_scrypt, params); 1446 } 1447 else 1448 { 1449 PBKDF2Params oldParams = PBKDF2Params.getInstance(baseAlg.getParameters()); 1450 1451 byte[] pbkdSalt = new byte[oldParams.getSalt().length]; 1452 getDefaultSecureRandom().nextBytes(pbkdSalt); 1453 1454 PBKDF2Params params = new PBKDF2Params(pbkdSalt, 1455 oldParams.getIterationCount().intValue(), keySizeInBytes, oldParams.getPrf()); 1456 return new KeyDerivationFunc(PKCSObjectIdentifiers.id_PBKDF2, params); 1457 } 1458 } 1459 1460 private KeyDerivationFunc generatePkbdAlgorithmIdentifier(ASN1ObjectIdentifier derivationAlgorithm, int keySizeInBytes) 1461 { 1462 byte[] pbkdSalt = new byte[512 / 8]; 1463 getDefaultSecureRandom().nextBytes(pbkdSalt); 1464 1465 if (PKCSObjectIdentifiers.id_PBKDF2.equals(derivationAlgorithm)) 1466 { 1467 return new KeyDerivationFunc(PKCSObjectIdentifiers.id_PBKDF2, new PBKDF2Params(pbkdSalt, 50 * 1024, keySizeInBytes, new AlgorithmIdentifier(PKCSObjectIdentifiers.id_hmacWithSHA512, DERNull.INSTANCE))); 1468 } 1469 else 1470 { 1471 throw new IllegalStateException("unknown derivation algorithm: " + derivationAlgorithm); 1472 } 1473 } 1474 1475 public static class Std 1476 extends BcFKSKeyStoreSpi 1477 { 1478 public Std() 1479 { 1480 super(new BCJcaJceHelper()); 1481 } 1482 } 1483 1484 public static class Def 1485 extends BcFKSKeyStoreSpi 1486 { 1487 public Def() 1488 { 1489 super(new DefaultJcaJceHelper()); 1490 } 1491 } 1492 1493 public static class StdCompat 1494 extends AdaptingKeyStoreSpi 1495 { 1496 public StdCompat() 1497 { 1498 super(new DefaultJcaJceHelper(), new BcFKSKeyStoreSpi(new BCJcaJceHelper())); 1499 } 1500 } 1501 1502 public static class DefCompat 1503 extends AdaptingKeyStoreSpi 1504 { 1505 public DefCompat() 1506 { 1507 super(new DefaultJcaJceHelper(), new BcFKSKeyStoreSpi(new DefaultJcaJceHelper())); 1508 } 1509 } 1510 1511 private static class SharedKeyStoreSpi 1512 extends BcFKSKeyStoreSpi 1513 implements PKCSObjectIdentifiers, X509ObjectIdentifiers 1514 { 1515 private final Map<String, byte[]> cache; 1516 private final byte[] seedKey; 1517 1518 public SharedKeyStoreSpi(JcaJceHelper provider) 1519 { 1520 super(provider); 1521 1522 try 1523 { 1524 this.seedKey = new byte[32]; 1525 provider.createSecureRandom("DEFAULT").nextBytes(seedKey); 1526 } 1527 catch (GeneralSecurityException e) 1528 { 1529 throw new IllegalArgumentException("can't create random - " + e.toString()); 1530 } 1531 1532 this.cache = new HashMap<String, byte[]>(); 1533 } 1534 1535 public void engineDeleteEntry( 1536 String alias) 1537 throws KeyStoreException 1538 { 1539 throw new KeyStoreException("delete operation not supported in shared mode"); 1540 } 1541 1542 public void engineSetKeyEntry(String alias, Key key, char[] password, Certificate[] chain) 1543 throws KeyStoreException 1544 { 1545 throw new KeyStoreException("set operation not supported in shared mode"); 1546 } 1547 1548 public void engineSetKeyEntry(String alias, byte[] keyEncoding, Certificate[] chain) 1549 throws KeyStoreException 1550 { 1551 throw new KeyStoreException("set operation not supported in shared mode"); 1552 } 1553 1554 public void engineSetCertificateEntry(String alias, Certificate cert) 1555 throws KeyStoreException 1556 { 1557 throw new KeyStoreException("set operation not supported in shared mode"); 1558 } 1559 1560 public Key engineGetKey( 1561 String alias, 1562 char[] password) 1563 throws NoSuchAlgorithmException, UnrecoverableKeyException 1564 { 1565 byte[] mac; 1566 1567 try 1568 { 1569 mac = calculateMac(alias, password); 1570 } 1571 catch (InvalidKeyException e) 1572 { // this should never happen... 1573 throw new UnrecoverableKeyException("unable to recover key (" + alias + "): " + e.getMessage()); 1574 } 1575 1576 if (cache.containsKey(alias)) 1577 { 1578 byte[] hash = cache.get(alias); 1579 1580 if (!Arrays.constantTimeAreEqual(hash, mac)) 1581 { 1582 throw new UnrecoverableKeyException("unable to recover key (" + alias + ")"); 1583 } 1584 } 1585 1586 Key key = super.engineGetKey(alias, password); 1587 1588 if (key != null && !cache.containsKey(alias)) 1589 { 1590 cache.put(alias, mac); 1591 } 1592 1593 return key; 1594 } 1595 1596 private byte[] calculateMac(String alias, char[] password) 1597 throws NoSuchAlgorithmException, InvalidKeyException 1598 { 1599 byte[] encoding; 1600 if (password != null) 1601 { 1602 encoding = Arrays.concatenate(Strings.toUTF8ByteArray(password), Strings.toUTF8ByteArray(alias)); 1603 } 1604 else 1605 { 1606 encoding = Arrays.concatenate(seedKey, Strings.toUTF8ByteArray(alias)); 1607 } 1608 1609 return SCrypt.generate(encoding, seedKey, 16384, 8, 1, 32); 1610 } 1611 } 1612 1613 public static class StdShared 1614 extends SharedKeyStoreSpi 1615 { 1616 public StdShared() 1617 { 1618 super(new BCJcaJceHelper()); 1619 } 1620 } 1621 1622 public static class DefShared 1623 extends SharedKeyStoreSpi 1624 { 1625 public DefShared() 1626 { 1627 super(new DefaultJcaJceHelper()); 1628 } 1629 } 1630 1631 public static class StdSharedCompat 1632 extends AdaptingKeyStoreSpi 1633 { 1634 public StdSharedCompat() 1635 { 1636 super(new BCJcaJceHelper(), new BcFKSKeyStoreSpi(new BCJcaJceHelper())); 1637 } 1638 } 1639 1640 public static class DefSharedCompat 1641 extends AdaptingKeyStoreSpi 1642 { 1643 public DefSharedCompat() 1644 { 1645 super(new DefaultJcaJceHelper(), new BcFKSKeyStoreSpi(new DefaultJcaJceHelper())); 1646 } 1647 } 1648 1649 private static class ExtKeyStoreException 1650 extends KeyStoreException 1651 { 1652 private final Throwable cause; 1653 1654 ExtKeyStoreException(String msg, Throwable cause) 1655 { 1656 super(msg); 1657 this.cause = cause; 1658 } 1659 1660 public Throwable getCause() 1661 { 1662 return cause; 1663 } 1664 } 1665 } 1666