1 /*
2  * Copyright (c) 2005, 2018, Oracle and/or its affiliates. All rights reserved.
3  * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
4  *
5  * This code is free software; you can redistribute it and/or modify it
6  * under the terms of the GNU General Public License version 2 only, as
7  * published by the Free Software Foundation.  Oracle designates this
8  * particular file as subject to the "Classpath" exception as provided
9  * by Oracle in the LICENSE file that accompanied this code.
10  *
11  * This code is distributed in the hope that it will be useful, but WITHOUT
12  * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
13  * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
14  * version 2 for more details (a copy is included in the LICENSE file that
15  * accompanied this code).
16  *
17  * You should have received a copy of the GNU General Public License version
18  * 2 along with this work; if not, write to the Free Software Foundation,
19  * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
20  *
21  * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
22  * or visit www.oracle.com if you need additional information or have any
23  * questions.
24  */
25 
26 package sun.security.mscapi;
27 
28 import java.io.ByteArrayInputStream;
29 import java.io.IOException;
30 import java.io.InputStream;
31 import java.io.OutputStream;
32 import java.security.AccessController;
33 import java.security.InvalidKeyException;
34 import java.security.Key;
35 import java.security.KeyStoreSpi;
36 import java.security.KeyStoreException;
37 import java.security.PrivilegedAction;
38 import java.security.UnrecoverableKeyException;
39 import java.security.NoSuchAlgorithmException;
40 import java.security.SecurityPermission;
41 import java.security.cert.X509Certificate;
42 import java.security.cert.Certificate;
43 import java.security.cert.CertificateException;
44 import java.security.cert.CertificateFactory;
45 import java.security.interfaces.RSAPrivateCrtKey;
46 import java.util.*;
47 
48 import sun.security.util.Debug;
49 
50 /**
51  * Implementation of key store for Windows using the Microsoft Crypto API.
52  *
53  * @since 1.6
54  */
55 abstract class CKeyStore extends KeyStoreSpi {
56 
57     public static final class MY extends CKeyStore {
MY()58         public MY() {
59             super("MY");
60         }
61     }
62 
63     public static final class ROOT extends CKeyStore {
ROOT()64         public ROOT() {
65             super("ROOT");
66         }
67     }
68 
69     class KeyEntry {
70         private CKey privateKey;
71         private X509Certificate[] certChain;
72         private String alias;
73 
KeyEntry(CKey key, X509Certificate[] chain)74         KeyEntry(CKey key, X509Certificate[] chain) {
75             this(null, key, chain);
76         }
77 
KeyEntry(String alias, CKey key, X509Certificate[] chain)78         KeyEntry(String alias, CKey key, X509Certificate[] chain) {
79             this.privateKey = key;
80             this.certChain = chain;
81             /*
82              * The default alias for both entry types is derived from a
83              * hash value intrinsic to the first certificate in the chain.
84              */
85              if (alias == null) {
86                  this.alias = Integer.toString(chain[0].hashCode());
87              } else {
88                  this.alias = alias;
89              }
90         }
91 
92         /**
93          * Gets the alias for the keystore entry.
94          */
getAlias()95         String getAlias() {
96             return alias;
97         }
98 
99         /**
100          * Sets the alias for the keystore entry.
101          */
setAlias(String alias)102         void setAlias(String alias) {
103             // TODO - set friendly name prop in cert store
104             this.alias = alias;
105         }
106 
107         /**
108          * Gets the private key for the keystore entry.
109          */
getPrivateKey()110         CKey getPrivateKey() {
111             return privateKey;
112         }
113 
114         /**
115          * Sets the private key for the keystore entry.
116          */
setRSAPrivateKey(Key k)117         void setRSAPrivateKey(Key k)
118             throws InvalidKeyException, KeyStoreException {
119             RSAPrivateCrtKey key = (RSAPrivateCrtKey) k;
120             byte[] modulusBytes = key.getModulus().toByteArray();
121 
122             // Adjust key length due to sign bit
123             int keyBitLength = (modulusBytes[0] == 0)
124                     ? (modulusBytes.length - 1) * 8
125                     : modulusBytes.length * 8;
126 
127             byte[] keyBlob = generateRSAPrivateKeyBlob(
128                     keyBitLength,
129                     modulusBytes,
130                     key.getPublicExponent().toByteArray(),
131                     key.getPrivateExponent().toByteArray(),
132                     key.getPrimeP().toByteArray(),
133                     key.getPrimeQ().toByteArray(),
134                     key.getPrimeExponentP().toByteArray(),
135                     key.getPrimeExponentQ().toByteArray(),
136                     key.getCrtCoefficient().toByteArray());
137 
138             privateKey = storePrivateKey("RSA", Objects.requireNonNull(keyBlob),
139                     "{" + UUID.randomUUID().toString() + "}", keyBitLength);
140         }
141 
142         /**
143          * Gets the certificate chain for the keystore entry.
144          */
getCertificateChain()145         X509Certificate[] getCertificateChain() {
146             return certChain;
147         }
148 
149         /**
150          * Sets the certificate chain for the keystore entry.
151          */
setCertificateChain(X509Certificate[] chain)152         void setCertificateChain(X509Certificate[] chain)
153                 throws CertificateException, KeyStoreException {
154             for (int i = 0; i < chain.length; i++) {
155                 byte[] encoding = chain[i].getEncoded();
156                 if (i == 0 && privateKey != null) {
157                     storeCertificate(getName(), alias, encoding,
158                         encoding.length, privateKey.getHCryptProvider(),
159                         privateKey.getHCryptKey());
160 
161                 } else {
162                     storeCertificate(getName(), alias, encoding,
163                         encoding.length, 0L, 0L); // no private key to attach
164                 }
165             }
166             certChain = chain;
167         }
168     }
169 
170     /*
171      * An X.509 certificate factory.
172      * Used to create an X.509 certificate from its DER-encoding.
173      */
174     private CertificateFactory certificateFactory = null;
175 
176     /*
177      * Compatibility mode: for applications that assume keystores are
178      * stream-based this mode tolerates (but ignores) a non-null stream
179      * or password parameter when passed to the load or store methods.
180      * The mode is enabled by default.
181      */
182     private static final String KEYSTORE_COMPATIBILITY_MODE_PROP =
183         "sun.security.mscapi.keyStoreCompatibilityMode";
184     private final boolean keyStoreCompatibilityMode;
185     private static final Debug debug = Debug.getInstance("keystore");
186 
187     /*
188      * The keystore entries.
189      * Keys in the map are unique aliases (thus can differ from
190      * KeyEntry.getAlias())
191      */
192     private Map<String,KeyEntry> entries = new HashMap<>();
193 
194     /*
195      * The keystore name.
196      * Case is not significant.
197      */
198     private final String storeName;
199 
CKeyStore(String storeName)200     CKeyStore(String storeName) {
201         // Get the compatibility mode
202         String prop = AccessController.doPrivileged(
203             (PrivilegedAction<String>) () -> System.getProperty(KEYSTORE_COMPATIBILITY_MODE_PROP));
204 
205         if ("false".equalsIgnoreCase(prop)) {
206             keyStoreCompatibilityMode = false;
207         } else {
208             keyStoreCompatibilityMode = true;
209         }
210 
211         this.storeName = storeName;
212     }
213 
214     /**
215      * Returns the key associated with the given alias.
216      * <p>
217      * A compatibility mode is supported for applications that assume
218      * a password must be supplied. It permits (but ignores) a non-null
219      * <code>password</code>.  The mode is enabled by default.
220      * Set the
221      * <code>sun.security.mscapi.keyStoreCompatibilityMode</code>
222      * system property to <code>false</code> to disable compatibility mode
223      * and reject a non-null <code>password</code>.
224      *
225      * @param alias the alias name
226      * @param password the password, which should be <code>null</code>
227      *
228      * @return the requested key, or null if the given alias does not exist
229      * or does not identify a <i>key entry</i>.
230      *
231      * @exception NoSuchAlgorithmException if the algorithm for recovering the
232      * key cannot be found,
233      * or if compatibility mode is disabled and <code>password</code> is
234      * non-null.
235      * @exception UnrecoverableKeyException if the key cannot be recovered.
236      */
engineGetKey(String alias, char[] password)237     public java.security.Key engineGetKey(String alias, char[] password)
238         throws NoSuchAlgorithmException, UnrecoverableKeyException {
239         if (alias == null) {
240             return null;
241         }
242 
243         if (password != null && !keyStoreCompatibilityMode) {
244             throw new UnrecoverableKeyException("Password must be null");
245         }
246 
247         if (engineIsKeyEntry(alias) == false)
248             return null;
249 
250         KeyEntry entry = entries.get(alias);
251         return (entry == null)
252                 ? null
253                 : entry.getPrivateKey();
254     }
255 
256     /**
257      * Returns the certificate chain associated with the given alias.
258      *
259      * @param alias the alias name
260      *
261      * @return the certificate chain (ordered with the user's certificate first
262      * and the root certificate authority last), or null if the given alias
263      * does not exist or does not contain a certificate chain (i.e., the given
264      * alias identifies either a <i>trusted certificate entry</i> or a
265      * <i>key entry</i> without a certificate chain).
266      */
engineGetCertificateChain(String alias)267     public Certificate[] engineGetCertificateChain(String alias) {
268         if (alias == null) {
269             return null;
270         }
271 
272         KeyEntry entry = entries.get(alias);
273         X509Certificate[] certChain = (entry == null)
274                 ? null
275                 : entry.getCertificateChain();
276         return (certChain == null)
277                 ? null
278                 : certChain.clone();
279     }
280 
281     /**
282      * Returns the certificate associated with the given alias.
283      *
284      * <p>If the given alias name identifies a
285      * <i>trusted certificate entry</i>, the certificate associated with that
286      * entry is returned. If the given alias name identifies a
287      * <i>key entry</i>, the first element of the certificate chain of that
288      * entry is returned, or null if that entry does not have a certificate
289      * chain.
290      *
291      * @param alias the alias name
292      *
293      * @return the certificate, or null if the given alias does not exist or
294      * does not contain a certificate.
295      */
engineGetCertificate(String alias)296     public Certificate engineGetCertificate(String alias) {
297         if (alias == null) {
298             return null;
299         }
300 
301         KeyEntry entry = entries.get(alias);
302         X509Certificate[] certChain = (entry == null)
303                 ? null
304                 : entry.getCertificateChain();
305         return (certChain == null || certChain.length == 0)
306                 ? null
307                 : certChain[0];
308     }
309 
310     /**
311      * Returns the creation date of the entry identified by the given alias.
312      *
313      * @param alias the alias name
314      *
315      * @return the creation date of this entry, or null if the given alias does
316      * not exist
317      */
engineGetCreationDate(String alias)318     public Date engineGetCreationDate(String alias) {
319         if (alias == null) {
320             return null;
321         }
322         return new Date();
323     }
324 
325     /**
326      * Stores the given private key and associated certificate chain in the
327      * keystore.
328      *
329      * <p>The given java.security.PrivateKey <code>key</code> must
330      * be accompanied by a certificate chain certifying the
331      * corresponding public key.
332      *
333      * <p>If the given alias already exists, the keystore information
334      * associated with it is overridden by the given key and certificate
335      * chain. Otherwise, a new entry is created.
336      *
337      * <p>
338      * A compatibility mode is supported for applications that assume
339      * a password must be supplied. It permits (but ignores) a non-null
340      * <code>password</code>.  The mode is enabled by default.
341      * Set the
342      * <code>sun.security.mscapi.keyStoreCompatibilityMode</code>
343      * system property to <code>false</code> to disable compatibility mode
344      * and reject a non-null <code>password</code>.
345      *
346      * @param alias the alias name
347      * @param key the private key to be associated with the alias
348      * @param password the password, which should be <code>null</code>
349      * @param chain the certificate chain for the corresponding public
350      *        key (only required if the given key is of type
351      *        <code>java.security.PrivateKey</code>).
352      *
353      * @exception KeyStoreException if the given key is not a private key,
354      * cannot be protected, or if compatibility mode is disabled and
355      * <code>password</code> is non-null, or if this operation fails for
356      * some other reason.
357      */
engineSetKeyEntry(String alias, java.security.Key key, char[] password, Certificate[] chain)358     public void engineSetKeyEntry(String alias, java.security.Key key,
359             char[] password, Certificate[] chain) throws KeyStoreException {
360         if (alias == null) {
361             throw new KeyStoreException("alias must not be null");
362         }
363 
364         if (password != null && !keyStoreCompatibilityMode) {
365             throw new KeyStoreException("Password must be null");
366         }
367 
368         if (key instanceof RSAPrivateCrtKey) {
369 
370             KeyEntry entry = entries.get(alias);
371 
372             X509Certificate[] xchain;
373             if (chain != null) {
374                 if (chain instanceof X509Certificate[]) {
375                     xchain = (X509Certificate[]) chain;
376                 } else {
377                     xchain = new X509Certificate[chain.length];
378                     System.arraycopy(chain, 0, xchain, 0, chain.length);
379                 }
380             } else {
381                 xchain = null;
382             }
383 
384             if (entry == null) {
385                 entry =
386                     //TODO new KeyEntry(alias, key, (X509Certificate[]) chain);
387                     new KeyEntry(alias, null, xchain);
388                 storeWithUniqueAlias(alias, entry);
389             }
390 
391             entry.setAlias(alias);
392 
393             try {
394                 entry.setRSAPrivateKey(key);
395                 entry.setCertificateChain(xchain);
396 
397             } catch (CertificateException ce) {
398                 throw new KeyStoreException(ce);
399 
400             } catch (InvalidKeyException ike) {
401                 throw new KeyStoreException(ike);
402             }
403 
404         } else {
405             throw new UnsupportedOperationException(
406                 "Cannot assign the key to the given alias.");
407         }
408     }
409 
410     /**
411      * Assigns the given key (that has already been protected) to the given
412      * alias.
413      *
414      * <p>If the protected key is of type
415      * <code>java.security.PrivateKey</code>, it must be accompanied by a
416      * certificate chain certifying the corresponding public key. If the
417      * underlying keystore implementation is of type <code>jks</code>,
418      * <code>key</code> must be encoded as an
419      * <code>EncryptedPrivateKeyInfo</code> as defined in the PKCS #8 standard.
420      *
421      * <p>If the given alias already exists, the keystore information
422      * associated with it is overridden by the given key (and possibly
423      * certificate chain).
424      *
425      * @param alias the alias name
426      * @param key the key (in protected format) to be associated with the alias
427      * @param chain the certificate chain for the corresponding public
428      * key (only useful if the protected key is of type
429      * <code>java.security.PrivateKey</code>).
430      *
431      * @exception KeyStoreException if this operation fails.
432      */
engineSetKeyEntry(String alias, byte[] key, Certificate[] chain)433     public void engineSetKeyEntry(String alias, byte[] key,
434                                   Certificate[] chain)
435             throws KeyStoreException {
436         throw new UnsupportedOperationException(
437             "Cannot assign the encoded key to the given alias.");
438     }
439 
440     /**
441      * Assigns the given certificate to the given alias.
442      *
443      * <p>If the given alias already exists in this keystore and identifies a
444      * <i>trusted certificate entry</i>, the certificate associated with it is
445      * overridden by the given certificate.
446      *
447      * @param alias the alias name
448      * @param cert the certificate
449      *
450      * @exception KeyStoreException if the given alias already exists and does
451      * not identify a <i>trusted certificate entry</i>, or this operation
452      * fails for some other reason.
453      */
engineSetCertificateEntry(String alias, Certificate cert)454     public void engineSetCertificateEntry(String alias, Certificate cert)
455             throws KeyStoreException {
456         if (alias == null) {
457             throw new KeyStoreException("alias must not be null");
458         }
459 
460         if (cert instanceof X509Certificate) {
461 
462             // TODO - build CryptoAPI chain?
463             X509Certificate[] chain =
464                 new X509Certificate[]{ (X509Certificate) cert };
465             KeyEntry entry = entries.get(alias);
466 
467             if (entry == null) {
468                 entry =
469                     new KeyEntry(alias, null, chain);
470                 storeWithUniqueAlias(alias, entry);
471             }
472 
473             if (entry.getPrivateKey() == null) { // trusted-cert entry
474                 entry.setAlias(alias);
475 
476                 try {
477                     entry.setCertificateChain(chain);
478 
479                 } catch (CertificateException ce) {
480                     throw new KeyStoreException(ce);
481                 }
482             }
483 
484         } else {
485             throw new UnsupportedOperationException(
486                 "Cannot assign the certificate to the given alias.");
487         }
488     }
489 
490     /**
491      * Deletes the entry identified by the given alias from this keystore.
492      *
493      * @param alias the alias name
494      *
495      * @exception KeyStoreException if the entry cannot be removed.
496      */
engineDeleteEntry(String alias)497     public void engineDeleteEntry(String alias) throws KeyStoreException {
498         if (alias == null) {
499             throw new KeyStoreException("alias must not be null");
500         }
501 
502         KeyEntry entry = entries.remove(alias);
503         if (entry != null) {
504             // Get end-entity certificate and remove from system cert store
505             X509Certificate[] certChain = entry.getCertificateChain();
506             if (certChain != null && certChain.length > 0) {
507 
508                 try {
509 
510                     byte[] encoding = certChain[0].getEncoded();
511                     removeCertificate(getName(), entry.getAlias(), encoding,
512                             encoding.length);
513 
514                 } catch (CertificateException e) {
515                     throw new KeyStoreException("Cannot remove entry: ", e);
516                 }
517             }
518             CKey privateKey = entry.getPrivateKey();
519             if (privateKey != null) {
520                 destroyKeyContainer(
521                     CKey.getContainerName(privateKey.getHCryptProvider()));
522             }
523         }
524     }
525 
526     /**
527      * Lists all the alias names of this keystore.
528      *
529      * @return enumeration of the alias names
530      */
engineAliases()531     public Enumeration<String> engineAliases() {
532         final Iterator<String> iter = entries.keySet().iterator();
533 
534         return new Enumeration<String>() {
535             public boolean hasMoreElements() {
536                 return iter.hasNext();
537             }
538 
539             public String nextElement() {
540                 return iter.next();
541             }
542         };
543     }
544 
545     /**
546      * Checks if the given alias exists in this keystore.
547      *
548      * @param alias the alias name
549      *
550      * @return true if the alias exists, false otherwise
551      */
552     public boolean engineContainsAlias(String alias) {
553         return entries.containsKey(alias);
554     }
555 
556     /**
557      * Retrieves the number of entries in this keystore.
558      *
559      * @return the number of entries in this keystore
560      */
561     public int engineSize() {
562         return entries.size();
563     }
564 
565     /**
566      * Returns true if the entry identified by the given alias is a
567      * <i>key entry</i>, and false otherwise.
568      *
569      * @return true if the entry identified by the given alias is a
570      * <i>key entry</i>, false otherwise.
571      */
572     public boolean engineIsKeyEntry(String alias) {
573 
574         if (alias == null) {
575             return false;
576         }
577 
578         KeyEntry entry = entries.get(alias);
579         return entry != null && entry.getPrivateKey() != null;
580     }
581 
582     /**
583      * Returns true if the entry identified by the given alias is a
584      * <i>trusted certificate entry</i>, and false otherwise.
585      *
586      * @return true if the entry identified by the given alias is a
587      * <i>trusted certificate entry</i>, false otherwise.
588      */
589     public boolean engineIsCertificateEntry(String alias) {
590 
591         if (alias == null) {
592             return false;
593         }
594 
595         KeyEntry entry = entries.get(alias);
596         return entry != null && entry.getPrivateKey() == null;
597     }
598 
599     /**
600      * Returns the (alias) name of the first keystore entry whose certificate
601      * matches the given certificate.
602      *
603      * <p>This method attempts to match the given certificate with each
604      * keystore entry. If the entry being considered
605      * is a <i>trusted certificate entry</i>, the given certificate is
606      * compared to that entry's certificate. If the entry being considered is
607      * a <i>key entry</i>, the given certificate is compared to the first
608      * element of that entry's certificate chain (if a chain exists).
609      *
610      * @param cert the certificate to match with.
611      *
612      * @return the (alias) name of the first entry with matching certificate,
613      * or null if no such entry exists in this keystore.
614      */
615     public String engineGetCertificateAlias(Certificate cert) {
616 
617         for (Map.Entry<String,KeyEntry> mapEntry : entries.entrySet()) {
618             KeyEntry entry = mapEntry.getValue();
619             if (entry.certChain != null &&
620                 entry.certChain.length > 0 &&
621                 entry.certChain[0].equals(cert)) {
622                 return entry.getAlias();
623             }
624         }
625 
626         return null;
627     }
628 
629     /**
630      * engineStore is currently a no-op.
631      * Entries are stored during engineSetEntry.
632      *
633      * A compatibility mode is supported for applications that assume
634      * keystores are stream-based. It permits (but ignores) a non-null
635      * <code>stream</code> or <code>password</code>.
636      * The mode is enabled by default.
637      * Set the
638      * <code>sun.security.mscapi.keyStoreCompatibilityMode</code>
639      * system property to <code>false</code> to disable compatibility mode
640      * and reject a non-null <code>stream</code> or <code>password</code>.
641      *
642      * @param stream the output stream, which should be <code>null</code>
643      * @param password the password, which should be <code>null</code>
644      *
645      * @exception IOException if compatibility mode is disabled and either
646      * parameter is non-null.
647      */
648     public void engineStore(OutputStream stream, char[] password)
649             throws IOException, NoSuchAlgorithmException, CertificateException {
650         if (stream != null && !keyStoreCompatibilityMode) {
651             throw new IOException("Keystore output stream must be null");
652         }
653 
654         if (password != null && !keyStoreCompatibilityMode) {
655             throw new IOException("Keystore password must be null");
656         }
657     }
658 
659     /**
660      * Loads the keystore.
661      *
662      * A compatibility mode is supported for applications that assume
663      * keystores are stream-based. It permits (but ignores) a non-null
664      * <code>stream</code> or <code>password</code>.
665      * The mode is enabled by default.
666      * Set the
667      * <code>sun.security.mscapi.keyStoreCompatibilityMode</code>
668      * system property to <code>false</code> to disable compatibility mode
669      * and reject a non-null <code>stream</code> or <code>password</code>.
670      *
671      * @param stream the input stream, which should be <code>null</code>.
672      * @param password the password, which should be <code>null</code>.
673      *
674      * @exception IOException if there is an I/O or format problem with the
675      * keystore data. Or if compatibility mode is disabled and either
676      * parameter is non-null.
677      * @exception NoSuchAlgorithmException if the algorithm used to check
678      * the integrity of the keystore cannot be found
679      * @exception CertificateException if any of the certificates in the
680      * keystore could not be loaded
681      * @exception SecurityException if the security check for
682      *  <code>SecurityPermission("authProvider.<i>name</i>")</code> does not
683      *  pass, where <i>name</i> is the value returned by
684      *  this provider's <code>getName</code> method.
685      */
686     public void engineLoad(InputStream stream, char[] password)
687             throws IOException, NoSuchAlgorithmException, CertificateException {
688         if (stream != null && !keyStoreCompatibilityMode) {
689             throw new IOException("Keystore input stream must be null");
690         }
691 
692         if (password != null && !keyStoreCompatibilityMode) {
693             throw new IOException("Keystore password must be null");
694         }
695 
696         /*
697          * Use the same security check as AuthProvider.login
698          */
699         SecurityManager sm = System.getSecurityManager();
700         if (sm != null) {
701             sm.checkPermission(new SecurityPermission(
702                 "authProvider.SunMSCAPI"));
703         }
704 
705         // Clear all key entries
706         entries.clear();
707 
708         try {
709 
710             // Load keys and/or certificate chains
711             loadKeysOrCertificateChains(getName());
712 
713         } catch (KeyStoreException e) {
714             throw new IOException(e);
715         }
716 
717         if (debug != null) {
718             debug.println("MSCAPI keystore load: entry count: " +
719                     entries.size());
720         }
721     }
722 
723     /**
724      * Stores the given entry into the map, making sure
725      * the alias, used as the key is unique.
726      * If the same alias already exists, it tries to append
727      * a suffix  (1), (2), etc to it until it finds a unique
728      * value.
729      */
730     private void storeWithUniqueAlias(String alias, KeyEntry entry) {
731         String uniqAlias = alias;
732         int uniqNum = 1;
733 
734         while (true) {
735             if (entries.putIfAbsent(uniqAlias, entry) == null) {
736                 break;
737             }
738             uniqAlias = alias + " (" + (uniqNum++) + ")";
739         }
740     }
741 
742 
743     /**
744      * Generates a certificate chain from the collection of
745      * certificates and stores the result into a key entry.
746      * <p>
747      * This method is called by native codes in security.cpp.
748      */
749     private void generateCertificateChain(String alias,
750             Collection<? extends Certificate> certCollection) {
751         try {
752             X509Certificate[] certChain =
753                 new X509Certificate[certCollection.size()];
754 
755             int i = 0;
756             for (Iterator<? extends Certificate> iter =
757                     certCollection.iterator(); iter.hasNext(); i++) {
758                 certChain[i] = (X509Certificate) iter.next();
759             }
760 
761             storeWithUniqueAlias(alias,
762                     new KeyEntry(alias, null, certChain));
763         } catch (Throwable e) {
764             // Ignore the exception and skip this entry
765             // TODO - throw CertificateException?
766         }
767     }
768 
769     /**
770      * Generates key and certificate chain from the private key handle,
771      * collection of certificates and stores the result into key entries.
772      * <p>
773      * This method is called by native codes in security.cpp.
774      */
775     private void generateKeyAndCertificateChain(boolean isRSA, String alias,
776             long hCryptProv, long hCryptKey, int keyLength,
777             Collection<? extends Certificate> certCollection) {
778         try {
779             X509Certificate[] certChain =
780                 new X509Certificate[certCollection.size()];
781 
782             int i = 0;
783             for (Iterator<? extends Certificate> iter =
784                     certCollection.iterator(); iter.hasNext(); i++) {
785                 certChain[i] = (X509Certificate) iter.next();
786             }
787             storeWithUniqueAlias(alias, new KeyEntry(alias,
788                     CPrivateKey.of(isRSA ? "RSA" : "EC", hCryptProv, hCryptKey, keyLength),
789                     certChain));
790         } catch (Throwable e) {
791             // Ignore the exception and skip this entry
792             // TODO - throw CertificateException?
793         }
794     }
795 
796     /**
797      * Generates certificates from byte data and stores into cert collection.
798      * <p>
799      * This method is called by native codes in security.cpp.
800      *
801      * @param data Byte data.
802      * @param certCollection Collection of certificates.
803      */
804     private void generateCertificate(byte[] data,
805         Collection<Certificate> certCollection) {
806         try {
807             ByteArrayInputStream bis = new ByteArrayInputStream(data);
808 
809             // Obtain certificate factory
810             if (certificateFactory == null) {
811                 certificateFactory = CertificateFactory.getInstance("X.509", "SUN");
812             }
813 
814             // Generate certificate
815             Collection<? extends Certificate> c =
816                     certificateFactory.generateCertificates(bis);
817             certCollection.addAll(c);
818         } catch (CertificateException e) {
819             // Ignore the exception and skip this certificate
820             // TODO - throw CertificateException?
821         } catch (Throwable te) {
822             // Ignore the exception and skip this certificate
823             // TODO - throw CertificateException?
824         }
825     }
826 
827     /**
828      * Returns the name of the keystore.
829      */
830     private String getName() {
831         return storeName;
832     }
833 
834     /**
835      * Load keys and/or certificates from keystore into Collection.
836      *
837      * @param name Name of keystore.
838      */
839     private native void loadKeysOrCertificateChains(String name)
840             throws KeyStoreException;
841 
842     /**
843      * Stores a DER-encoded certificate into the certificate store
844      *
845      * @param name Name of the keystore.
846      * @param alias Name of the certificate.
847      * @param encoding DER-encoded certificate.
848      */
849     private native void storeCertificate(String name, String alias,
850         byte[] encoding, int encodingLength, long hCryptProvider,
851         long hCryptKey) throws CertificateException, KeyStoreException;
852 
853     /**
854      * Removes the certificate from the certificate store
855      *
856      * @param name Name of the keystore.
857      * @param alias Name of the certificate.
858      * @param encoding DER-encoded certificate.
859      */
860     private native void removeCertificate(String name, String alias,
861         byte[] encoding, int encodingLength)
862             throws CertificateException, KeyStoreException;
863 
864     /**
865      * Destroys the key container.
866      *
867      * @param keyContainerName The name of the key container.
868      */
869     private native void destroyKeyContainer(String keyContainerName)
870         throws KeyStoreException;
871 
872     /**
873      * Generates a private-key BLOB from a key's components.
874      */
875     private native byte[] generateRSAPrivateKeyBlob(
876         int keyBitLength,
877         byte[] modulus,
878         byte[] publicExponent,
879         byte[] privateExponent,
880         byte[] primeP,
881         byte[] primeQ,
882         byte[] exponentP,
883         byte[] exponentQ,
884         byte[] crtCoefficient) throws InvalidKeyException;
885 
886     private native CPrivateKey storePrivateKey(String alg, byte[] keyBlob,
887         String keyContainerName, int keySize) throws KeyStoreException;
888 }
889