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