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