1 /*
2 * Copyright (c) 2005, 2015, 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
171 };
172
173 /*
174 * An X.509 certificate factory.
175 * Used to create an X.509 certificate from its DER-encoding.
176 */
177 private CertificateFactory certificateFactory = null;
178
179 /*
180 * Compatibility mode: for applications that assume keystores are
181 * stream-based this mode tolerates (but ignores) a non-null stream
182 * or password parameter when passed to the load or store methods.
183 * The mode is enabled by default.
184 */
185 private static final String KEYSTORE_COMPATIBILITY_MODE_PROP =
186 "sun.security.mscapi.keyStoreCompatibilityMode";
187 private final boolean keyStoreCompatibilityMode;
188
189 /*
190 * The keystore entries.
191 */
192 private Collection<KeyEntry> entries = new ArrayList<KeyEntry>();
193
194 /*
195 * The keystore name.
196 * Case is not significant.
197 */
198 private final String storeName;
199
200 KeyStore(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 }
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 */
237 public java.security.Key engineGetKey(String alias, char[] password)
238 throws NoSuchAlgorithmException, UnrecoverableKeyException
239 {
240 if (alias == null) {
241 return null;
242 }
243
244 if (password != null && !keyStoreCompatibilityMode) {
245 throw new UnrecoverableKeyException("Password must be null");
246 }
247
248 if (engineIsKeyEntry(alias) == false)
249 return null;
250
251 for (KeyEntry entry : entries) {
252 if (alias.equals(entry.getAlias())) {
253 return entry.getPrivateKey();
254 }
255 }
256
257 return null;
258 }
259
260 /**
261 * Returns the certificate chain associated with the given alias.
262 *
263 * @param alias the alias name
264 *
265 * @return the certificate chain (ordered with the user's certificate first
266 * and the root certificate authority last), or null if the given alias
267 * does not exist or does not contain a certificate chain (i.e., the given
268 * alias identifies either a <i>trusted certificate entry</i> or a
269 * <i>key entry</i> without a certificate chain).
270 */
271 public Certificate[] engineGetCertificateChain(String alias)
272 {
273 if (alias == null) {
274 return null;
275 }
276
277 for (KeyEntry entry : entries) {
278 if (alias.equals(entry.getAlias())) {
279 X509Certificate[] certChain = entry.getCertificateChain();
280
281 return certChain.clone();
282 }
283 }
284
285 return null;
286 }
287
288 /**
289 * Returns the certificate associated with the given alias.
290 *
291 * <p>If the given alias name identifies a
292 * <i>trusted certificate entry</i>, the certificate associated with that
293 * entry is returned. If the given alias name identifies a
294 * <i>key entry</i>, the first element of the certificate chain of that
295 * entry is returned, or null if that entry does not have a certificate
296 * chain.
297 *
298 * @param alias the alias name
299 *
300 * @return the certificate, or null if the given alias does not exist or
301 * does not contain a certificate.
302 */
303 public Certificate engineGetCertificate(String alias)
304 {
305 if (alias == null) {
306 return null;
307 }
308
309 for (KeyEntry entry : entries) {
310 if (alias.equals(entry.getAlias()))
311 {
312 X509Certificate[] certChain = entry.getCertificateChain();
313 return certChain.length == 0 ? null : certChain[0];
314 }
315 }
316
317 return null;
318 }
319
320 /**
321 * Returns the creation date of the entry identified by the given alias.
322 *
323 * @param alias the alias name
324 *
325 * @return the creation date of this entry, or null if the given alias does
326 * not exist
327 */
328 public Date engineGetCreationDate(String alias) {
329 if (alias == null) {
330 return null;
331 }
332 return new Date();
333 }
334
335 /**
336 * Stores the given private key and associated certificate chain in the
337 * keystore.
361 * <code>java.security.PrivateKey</code>).
362 *
363 * @exception KeyStoreException if the given key is not a private key,
364 * cannot be protected, or if compatibility mode is disabled and
365 * <code>password</code> is non-null, or if this operation fails for
366 * some other reason.
367 */
368 public void engineSetKeyEntry(String alias, java.security.Key key,
369 char[] password, Certificate[] chain) throws KeyStoreException
370 {
371 if (alias == null) {
372 throw new KeyStoreException("alias must not be null");
373 }
374
375 if (password != null && !keyStoreCompatibilityMode) {
376 throw new KeyStoreException("Password must be null");
377 }
378
379 if (key instanceof RSAPrivateCrtKey) {
380
381 KeyEntry entry = null;
382 boolean found = false;
383
384 for (KeyEntry e : entries) {
385 if (alias.equals(e.getAlias())) {
386 found = true;
387 entry = e;
388 break;
389 }
390 }
391
392 X509Certificate[] xchain;
393 if (chain != null) {
394 if (chain instanceof X509Certificate[]) {
395 xchain = (X509Certificate[]) chain;
396 } else {
397 xchain = new X509Certificate[chain.length];
398 System.arraycopy(chain, 0, xchain, 0, chain.length);
399 }
400 } else {
401 xchain = null;
402 }
403
404 if (! found) {
405 entry =
406 //TODO new KeyEntry(alias, key, (X509Certificate[]) chain);
407 new KeyEntry(alias, null, xchain);
408 entries.add(entry);
409 }
410
411 entry.setAlias(alias);
412
413 try {
414 entry.setPrivateKey((RSAPrivateCrtKey) key);
415 entry.setCertificateChain(xchain);
416
417 } catch (CertificateException ce) {
418 throw new KeyStoreException(ce);
419
420 } catch (InvalidKeyException ike) {
421 throw new KeyStoreException(ike);
422 }
423
424 } else {
425 throw new UnsupportedOperationException(
426 "Cannot assign the key to the given alias.");
427 }
428 }
467 *
468 * @param alias the alias name
469 * @param cert the certificate
470 *
471 * @exception KeyStoreException if the given alias already exists and does
472 * not identify a <i>trusted certificate entry</i>, or this operation
473 * fails for some other reason.
474 */
475 public void engineSetCertificateEntry(String alias, Certificate cert)
476 throws KeyStoreException
477 {
478 if (alias == null) {
479 throw new KeyStoreException("alias must not be null");
480 }
481
482 if (cert instanceof X509Certificate) {
483
484 // TODO - build CryptoAPI chain?
485 X509Certificate[] chain =
486 new X509Certificate[]{ (X509Certificate) cert };
487 KeyEntry entry = null;
488 boolean found = false;
489
490 for (KeyEntry e : entries) {
491 if (alias.equals(e.getAlias())) {
492 found = true;
493 entry = e;
494 break;
495 }
496 }
497
498 if (! found) {
499 entry =
500 new KeyEntry(alias, null, chain);
501 entries.add(entry);
502
503 }
504 if (entry.getPrivateKey() == null) { // trusted-cert entry
505 entry.setAlias(alias);
506
507 try {
508 entry.setCertificateChain(chain);
509
510 } catch (CertificateException ce) {
511 throw new KeyStoreException(ce);
512 }
513 }
514
515 } else {
516 throw new UnsupportedOperationException(
517 "Cannot assign the certificate to the given alias.");
518 }
519 }
520
521 /**
522 * Deletes the entry identified by the given alias from this keystore.
523 *
524 * @param alias the alias name
525 *
526 * @exception KeyStoreException if the entry cannot be removed.
527 */
528 public void engineDeleteEntry(String alias)
529 throws KeyStoreException
530 {
531 if (alias == null) {
532 throw new KeyStoreException("alias must not be null");
533 }
534
535 for (KeyEntry entry : entries) {
536 if (alias.equals(entry.getAlias())) {
537
538 // Get end-entity certificate and remove from system cert store
539 X509Certificate[] certChain = entry.getCertificateChain();
540 if (certChain != null) {
541
542 try {
543
544 byte[] encoding = certChain[0].getEncoded();
545 removeCertificate(getName(), alias, encoding,
546 encoding.length);
547
548 } catch (CertificateException e) {
549 throw new KeyStoreException("Cannot remove entry: " +
550 e);
551 }
552 }
553 Key privateKey = entry.getPrivateKey();
554 if (privateKey != null) {
555 destroyKeyContainer(
556 Key.getContainerName(privateKey.getHCryptProvider()));
557 }
558
559 entries.remove(entry);
560 break;
561 }
562 }
563 }
564
565 /**
566 * Lists all the alias names of this keystore.
567 *
568 * @return enumeration of the alias names
569 */
570 public Enumeration<String> engineAliases() {
571
572 final Iterator<KeyEntry> iter = entries.iterator();
573
574 return new Enumeration<String>()
575 {
576 public boolean hasMoreElements()
577 {
578 return iter.hasNext();
579 }
580
581 public String nextElement()
582 {
583 KeyEntry entry = iter.next();
584 return entry.getAlias();
585 }
586 };
587 }
588
589 /**
590 * Checks if the given alias exists in this keystore.
591 *
592 * @param alias the alias name
593 *
594 * @return true if the alias exists, false otherwise
595 */
596 public boolean engineContainsAlias(String alias) {
597 for (Enumeration<String> enumerator = engineAliases();
598 enumerator.hasMoreElements();)
599 {
600 String a = enumerator.nextElement();
601
602 if (a.equals(alias))
603 return true;
604 }
605 return false;
606 }
607
608 /**
609 * Retrieves the number of entries in this keystore.
610 *
611 * @return the number of entries in this keystore
612 */
613 public int engineSize() {
614 return entries.size();
615 }
616
617 /**
618 * Returns true if the entry identified by the given alias is a
619 * <i>key entry</i>, and false otherwise.
620 *
621 * @return true if the entry identified by the given alias is a
622 * <i>key entry</i>, false otherwise.
623 */
624 public boolean engineIsKeyEntry(String alias) {
625
626 if (alias == null) {
627 return false;
628 }
629
630 for (KeyEntry entry : entries) {
631 if (alias.equals(entry.getAlias())) {
632 return entry.getPrivateKey() != null;
633 }
634 }
635
636 return false;
637 }
638
639 /**
640 * Returns true if the entry identified by the given alias is a
641 * <i>trusted certificate entry</i>, and false otherwise.
642 *
643 * @return true if the entry identified by the given alias is a
644 * <i>trusted certificate entry</i>, false otherwise.
645 */
646 public boolean engineIsCertificateEntry(String alias)
647 {
648 for (KeyEntry entry : entries) {
649 if (alias.equals(entry.getAlias())) {
650 return entry.getPrivateKey() == null;
651 }
652 }
653
654 return false;
655 }
656
657 /**
658 * Returns the (alias) name of the first keystore entry whose certificate
659 * matches the given certificate.
660 *
661 * <p>This method attempts to match the given certificate with each
662 * keystore entry. If the entry being considered
663 * is a <i>trusted certificate entry</i>, the given certificate is
664 * compared to that entry's certificate. If the entry being considered is
665 * a <i>key entry</i>, the given certificate is compared to the first
666 * element of that entry's certificate chain (if a chain exists).
667 *
668 * @param cert the certificate to match with.
669 *
670 * @return the (alias) name of the first entry with matching certificate,
671 * or null if no such entry exists in this keystore.
672 */
673 public String engineGetCertificateAlias(Certificate cert)
674 {
675 for (KeyEntry entry : entries) {
676 if (entry.certChain != null && entry.certChain[0].equals(cert)) {
677 return entry.getAlias();
678 }
679 }
680
681 return null;
682 }
683
684 /**
685 * engineStore is currently a no-op.
686 * Entries are stored during engineSetEntry.
687 *
688 * A compatibility mode is supported for applications that assume
689 * keystores are stream-based. It permits (but ignores) a non-null
690 * <code>stream</code> or <code>password</code>.
691 * The mode is enabled by default.
692 * Set the
693 * <code>sun.security.mscapi.keyStoreCompatibilityMode</code>
694 * system property to <code>false</code> to disable compatibility mode
695 * and reject a non-null <code>stream</code> or <code>password</code>.
748
749 if (password != null && !keyStoreCompatibilityMode) {
750 throw new IOException("Keystore password must be null");
751 }
752
753 /*
754 * Use the same security check as AuthProvider.login
755 */
756 SecurityManager sm = System.getSecurityManager();
757 if (sm != null) {
758 sm.checkPermission(new SecurityPermission(
759 "authProvider.SunMSCAPI"));
760 }
761
762 // Clear all key entries
763 entries.clear();
764
765 try {
766
767 // Load keys and/or certificate chains
768 loadKeysOrCertificateChains(getName(), entries);
769
770 } catch (KeyStoreException e) {
771 throw new IOException(e);
772 }
773 }
774
775 /**
776 * Generates a certificate chain from the collection of
777 * certificates and stores the result into a key entry.
778 */
779 private void generateCertificateChain(String alias,
780 Collection<? extends Certificate> certCollection,
781 Collection<KeyEntry> entries)
782 {
783 try
784 {
785 X509Certificate[] certChain =
786 new X509Certificate[certCollection.size()];
787
788 int i = 0;
789 for (Iterator<? extends Certificate> iter =
790 certCollection.iterator(); iter.hasNext(); i++)
791 {
792 certChain[i] = (X509Certificate) iter.next();
793 }
794
795 KeyEntry entry = new KeyEntry(alias, null, certChain);
796
797 // Add cert chain
798 entries.add(entry);
799 }
800 catch (Throwable e)
801 {
802 // Ignore the exception and skip this entry
803 // TODO - throw CertificateException?
804 }
805 }
806
807 /**
808 * Generates RSA key and certificate chain from the private key handle,
809 * collection of certificates and stores the result into key entries.
810 */
811 private void generateRSAKeyAndCertificateChain(String alias,
812 long hCryptProv, long hCryptKey, int keyLength,
813 Collection<? extends Certificate> certCollection,
814 Collection<KeyEntry> entries)
815 {
816 try
817 {
818 X509Certificate[] certChain =
819 new X509Certificate[certCollection.size()];
820
821 int i = 0;
822 for (Iterator<? extends Certificate> iter =
823 certCollection.iterator(); iter.hasNext(); i++)
824 {
825 certChain[i] = (X509Certificate) iter.next();
826 }
827
828 KeyEntry entry = new KeyEntry(alias, new RSAPrivateKey(hCryptProv,
829 hCryptKey, keyLength), certChain);
830
831 // Add cert chain
832 entries.add(entry);
833 }
834 catch (Throwable e)
835 {
836 // Ignore the exception and skip this entry
837 // TODO - throw CertificateException?
838 }
839 }
840
841 /**
842 * Generates certificates from byte data and stores into cert collection.
843 *
844 * @param data Byte data.
845 * @param certCollection Collection of certificates.
846 */
847 private void generateCertificate(byte[] data,
848 Collection<Certificate> certCollection) {
849 try
850 {
851 ByteArrayInputStream bis = new ByteArrayInputStream(data);
852
869 {
870 // Ignore the exception and skip this certificate
871 // TODO - throw CertificateException?
872 }
873 }
874
875 /**
876 * Returns the name of the keystore.
877 */
878 private String getName()
879 {
880 return storeName;
881 }
882
883 /**
884 * Load keys and/or certificates from keystore into Collection.
885 *
886 * @param name Name of keystore.
887 * @param entries Collection of key/certificate.
888 */
889 private native void loadKeysOrCertificateChains(String name,
890 Collection<KeyEntry> entries) throws KeyStoreException;
891
892 /**
893 * Stores a DER-encoded certificate into the certificate store
894 *
895 * @param name Name of the keystore.
896 * @param alias Name of the certificate.
897 * @param encoding DER-encoded certificate.
898 */
899 private native void storeCertificate(String name, String alias,
900 byte[] encoding, int encodingLength, long hCryptProvider,
901 long hCryptKey) throws CertificateException, KeyStoreException;
902
903 /**
904 * Removes the certificate from the certificate store
905 *
906 * @param name Name of the keystore.
907 * @param alias Name of the certificate.
908 * @param encoding DER-encoded certificate.
909 */
910 private native void removeCertificate(String name, String alias,
|
1 /*
2 * Copyright (c) 2005, 2016, 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
171 };
172
173 /*
174 * An X.509 certificate factory.
175 * Used to create an X.509 certificate from its DER-encoding.
176 */
177 private CertificateFactory certificateFactory = null;
178
179 /*
180 * Compatibility mode: for applications that assume keystores are
181 * stream-based this mode tolerates (but ignores) a non-null stream
182 * or password parameter when passed to the load or store methods.
183 * The mode is enabled by default.
184 */
185 private static final String KEYSTORE_COMPATIBILITY_MODE_PROP =
186 "sun.security.mscapi.keyStoreCompatibilityMode";
187 private final boolean keyStoreCompatibilityMode;
188
189 /*
190 * The keystore entries.
191 * Keys in the map are unique aliases (thus can differ from
192 * KeyEntry.getAlias())
193 */
194 private Map<String,KeyEntry> entries = new HashMap<>();
195
196 /*
197 * The keystore name.
198 * Case is not significant.
199 */
200 private final String storeName;
201
202 KeyStore(String storeName) {
203 // Get the compatibility mode
204 String prop = AccessController.doPrivileged(
205 (PrivilegedAction<String>) () -> System.getProperty(KEYSTORE_COMPATIBILITY_MODE_PROP));
206
207 if ("false".equalsIgnoreCase(prop)) {
208 keyStoreCompatibilityMode = false;
209 } else {
210 keyStoreCompatibilityMode = true;
211 }
212
213 this.storeName = storeName;
214 }
233 * @exception NoSuchAlgorithmException if the algorithm for recovering the
234 * key cannot be found,
235 * or if compatibility mode is disabled and <code>password</code> is
236 * non-null.
237 * @exception UnrecoverableKeyException if the key cannot be recovered.
238 */
239 public java.security.Key engineGetKey(String alias, char[] password)
240 throws NoSuchAlgorithmException, UnrecoverableKeyException
241 {
242 if (alias == null) {
243 return null;
244 }
245
246 if (password != null && !keyStoreCompatibilityMode) {
247 throw new UnrecoverableKeyException("Password must be null");
248 }
249
250 if (engineIsKeyEntry(alias) == false)
251 return null;
252
253 KeyEntry entry = entries.get(alias);
254 return (entry == null)
255 ? null
256 : entry.getPrivateKey();
257 }
258
259 /**
260 * Returns the certificate chain associated with the given alias.
261 *
262 * @param alias the alias name
263 *
264 * @return the certificate chain (ordered with the user's certificate first
265 * and the root certificate authority last), or null if the given alias
266 * does not exist or does not contain a certificate chain (i.e., the given
267 * alias identifies either a <i>trusted certificate entry</i> or a
268 * <i>key entry</i> without a certificate chain).
269 */
270 public Certificate[] engineGetCertificateChain(String alias)
271 {
272 if (alias == null) {
273 return null;
274 }
275
276 KeyEntry entry = entries.get(alias);
277 X509Certificate[] certChain = (entry == null)
278 ? null
279 : entry.getCertificateChain();
280 return (certChain == null)
281 ? null
282 : certChain.clone();
283 }
284
285 /**
286 * Returns the certificate associated with the given alias.
287 *
288 * <p>If the given alias name identifies a
289 * <i>trusted certificate entry</i>, the certificate associated with that
290 * entry is returned. If the given alias name identifies a
291 * <i>key entry</i>, the first element of the certificate chain of that
292 * entry is returned, or null if that entry does not have a certificate
293 * chain.
294 *
295 * @param alias the alias name
296 *
297 * @return the certificate, or null if the given alias does not exist or
298 * does not contain a certificate.
299 */
300 public Certificate engineGetCertificate(String alias)
301 {
302 if (alias == null) {
303 return null;
304 }
305
306 KeyEntry entry = entries.get(alias);
307 X509Certificate[] certChain = (entry == null)
308 ? null
309 : entry.getCertificateChain();
310 return (certChain == null || certChain.length == 0)
311 ? null
312 : certChain[0];
313 }
314
315 /**
316 * Returns the creation date of the entry identified by the given alias.
317 *
318 * @param alias the alias name
319 *
320 * @return the creation date of this entry, or null if the given alias does
321 * not exist
322 */
323 public Date engineGetCreationDate(String alias) {
324 if (alias == null) {
325 return null;
326 }
327 return new Date();
328 }
329
330 /**
331 * Stores the given private key and associated certificate chain in the
332 * keystore.
356 * <code>java.security.PrivateKey</code>).
357 *
358 * @exception KeyStoreException if the given key is not a private key,
359 * cannot be protected, or if compatibility mode is disabled and
360 * <code>password</code> is non-null, or if this operation fails for
361 * some other reason.
362 */
363 public void engineSetKeyEntry(String alias, java.security.Key key,
364 char[] password, Certificate[] chain) throws KeyStoreException
365 {
366 if (alias == null) {
367 throw new KeyStoreException("alias must not be null");
368 }
369
370 if (password != null && !keyStoreCompatibilityMode) {
371 throw new KeyStoreException("Password must be null");
372 }
373
374 if (key instanceof RSAPrivateCrtKey) {
375
376 KeyEntry entry = entries.get(alias);
377
378 X509Certificate[] xchain;
379 if (chain != null) {
380 if (chain instanceof X509Certificate[]) {
381 xchain = (X509Certificate[]) chain;
382 } else {
383 xchain = new X509Certificate[chain.length];
384 System.arraycopy(chain, 0, xchain, 0, chain.length);
385 }
386 } else {
387 xchain = null;
388 }
389
390 if (entry == null) {
391 entry =
392 //TODO new KeyEntry(alias, key, (X509Certificate[]) chain);
393 new KeyEntry(alias, null, xchain);
394 storeWithUniqueAlias(alias, entry);
395 }
396
397 entry.setAlias(alias);
398
399 try {
400 entry.setPrivateKey((RSAPrivateCrtKey) key);
401 entry.setCertificateChain(xchain);
402
403 } catch (CertificateException ce) {
404 throw new KeyStoreException(ce);
405
406 } catch (InvalidKeyException ike) {
407 throw new KeyStoreException(ike);
408 }
409
410 } else {
411 throw new UnsupportedOperationException(
412 "Cannot assign the key to the given alias.");
413 }
414 }
453 *
454 * @param alias the alias name
455 * @param cert the certificate
456 *
457 * @exception KeyStoreException if the given alias already exists and does
458 * not identify a <i>trusted certificate entry</i>, or this operation
459 * fails for some other reason.
460 */
461 public void engineSetCertificateEntry(String alias, Certificate cert)
462 throws KeyStoreException
463 {
464 if (alias == null) {
465 throw new KeyStoreException("alias must not be null");
466 }
467
468 if (cert instanceof X509Certificate) {
469
470 // TODO - build CryptoAPI chain?
471 X509Certificate[] chain =
472 new X509Certificate[]{ (X509Certificate) cert };
473 KeyEntry entry = entries.get(alias);
474
475 if (entry == null) {
476 entry =
477 new KeyEntry(alias, null, chain);
478 storeWithUniqueAlias(alias, entry);
479 }
480
481 if (entry.getPrivateKey() == null) { // trusted-cert entry
482 entry.setAlias(alias);
483
484 try {
485 entry.setCertificateChain(chain);
486
487 } catch (CertificateException ce) {
488 throw new KeyStoreException(ce);
489 }
490 }
491
492 } else {
493 throw new UnsupportedOperationException(
494 "Cannot assign the certificate to the given alias.");
495 }
496 }
497
498 /**
499 * Deletes the entry identified by the given alias from this keystore.
500 *
501 * @param alias the alias name
502 *
503 * @exception KeyStoreException if the entry cannot be removed.
504 */
505 public void engineDeleteEntry(String alias)
506 throws KeyStoreException
507 {
508 if (alias == null) {
509 throw new KeyStoreException("alias must not be null");
510 }
511
512 KeyEntry entry = entries.remove(alias);
513 if (entry != null) {
514 // Get end-entity certificate and remove from system cert store
515 X509Certificate[] certChain = entry.getCertificateChain();
516 if (certChain != null) {
517
518 try {
519
520 byte[] encoding = certChain[0].getEncoded();
521 removeCertificate(getName(), entry.getAlias(), encoding,
522 encoding.length);
523
524 } catch (CertificateException e) {
525 throw new KeyStoreException("Cannot remove entry: ", e);
526 }
527 }
528 Key privateKey = entry.getPrivateKey();
529 if (privateKey != null) {
530 destroyKeyContainer(
531 Key.getContainerName(privateKey.getHCryptProvider()));
532 }
533 }
534 }
535
536 /**
537 * Lists all the alias names of this keystore.
538 *
539 * @return enumeration of the alias names
540 */
541 public Enumeration<String> engineAliases() {
542 final Iterator<String> iter = entries.keySet().iterator();
543
544 return new Enumeration<String>()
545 {
546 public boolean hasMoreElements()
547 {
548 return iter.hasNext();
549 }
550
551 public String nextElement()
552 {
553 return iter.next();
554 }
555 };
556 }
557
558 /**
559 * Checks if the given alias exists in this keystore.
560 *
561 * @param alias the alias name
562 *
563 * @return true if the alias exists, false otherwise
564 */
565 public boolean engineContainsAlias(String alias) {
566 return entries.containsKey(alias);
567 }
568
569 /**
570 * Retrieves the number of entries in this keystore.
571 *
572 * @return the number of entries in this keystore
573 */
574 public int engineSize() {
575 return entries.size();
576 }
577
578 /**
579 * Returns true if the entry identified by the given alias is a
580 * <i>key entry</i>, and false otherwise.
581 *
582 * @return true if the entry identified by the given alias is a
583 * <i>key entry</i>, false otherwise.
584 */
585 public boolean engineIsKeyEntry(String alias) {
586
587 if (alias == null) {
588 return false;
589 }
590
591 KeyEntry entry = entries.get(alias);
592 return entry != null && entry.getPrivateKey() != null;
593 }
594
595 /**
596 * Returns true if the entry identified by the given alias is a
597 * <i>trusted certificate entry</i>, and false otherwise.
598 *
599 * @return true if the entry identified by the given alias is a
600 * <i>trusted certificate entry</i>, false otherwise.
601 */
602 public boolean engineIsCertificateEntry(String alias) {
603
604 if (alias == null) {
605 return false;
606 }
607
608 KeyEntry entry = entries.get(alias);
609 return entry != null && entry.getPrivateKey() == null;
610 }
611
612 /**
613 * Returns the (alias) name of the first keystore entry whose certificate
614 * matches the given certificate.
615 *
616 * <p>This method attempts to match the given certificate with each
617 * keystore entry. If the entry being considered
618 * is a <i>trusted certificate entry</i>, the given certificate is
619 * compared to that entry's certificate. If the entry being considered is
620 * a <i>key entry</i>, the given certificate is compared to the first
621 * element of that entry's certificate chain (if a chain exists).
622 *
623 * @param cert the certificate to match with.
624 *
625 * @return the (alias) name of the first entry with matching certificate,
626 * or null if no such entry exists in this keystore.
627 */
628 public String engineGetCertificateAlias(Certificate cert) {
629
630 for (Map.Entry<String,KeyEntry> mapEntry : entries.entrySet()) {
631 KeyEntry entry = mapEntry.getValue();
632 if (entry.certChain != null && entry.certChain[0].equals(cert)) {
633 return entry.getAlias();
634 }
635 }
636
637 return null;
638 }
639
640 /**
641 * engineStore is currently a no-op.
642 * Entries are stored during engineSetEntry.
643 *
644 * A compatibility mode is supported for applications that assume
645 * keystores are stream-based. It permits (but ignores) a non-null
646 * <code>stream</code> or <code>password</code>.
647 * The mode is enabled by default.
648 * Set the
649 * <code>sun.security.mscapi.keyStoreCompatibilityMode</code>
650 * system property to <code>false</code> to disable compatibility mode
651 * and reject a non-null <code>stream</code> or <code>password</code>.
704
705 if (password != null && !keyStoreCompatibilityMode) {
706 throw new IOException("Keystore password must be null");
707 }
708
709 /*
710 * Use the same security check as AuthProvider.login
711 */
712 SecurityManager sm = System.getSecurityManager();
713 if (sm != null) {
714 sm.checkPermission(new SecurityPermission(
715 "authProvider.SunMSCAPI"));
716 }
717
718 // Clear all key entries
719 entries.clear();
720
721 try {
722
723 // Load keys and/or certificate chains
724 loadKeysOrCertificateChains(getName());
725
726 } catch (KeyStoreException e) {
727 throw new IOException(e);
728 }
729 }
730
731 /**
732 * Stores the given entry into the map, making sure
733 * the alias, used as the key is unique.
734 * If the same alias already exists, it tries to append
735 * a suffix (1), (2), etc to it until it finds a unique
736 * value.
737 */
738 private void storeWithUniqueAlias(String alias, KeyEntry entry) {
739 String uniqAlias = alias;
740 int uniqNum = 1;
741
742 while (true) {
743 if (entries.putIfAbsent(uniqAlias, entry) == null) {
744 break;
745 }
746 uniqAlias = alias + " (" + (uniqNum++) + ")";
747 }
748 }
749
750
751 /**
752 * Generates a certificate chain from the collection of
753 * certificates and stores the result into a key entry.
754 */
755 private void generateCertificateChain(String alias,
756 Collection<? extends Certificate> certCollection)
757 {
758 try
759 {
760 X509Certificate[] certChain =
761 new X509Certificate[certCollection.size()];
762
763 int i = 0;
764 for (Iterator<? extends Certificate> iter =
765 certCollection.iterator(); iter.hasNext(); i++)
766 {
767 certChain[i] = (X509Certificate) iter.next();
768 }
769
770 storeWithUniqueAlias(alias,
771 new KeyEntry(alias, null, certChain));
772 }
773 catch (Throwable e)
774 {
775 // Ignore the exception and skip this entry
776 // TODO - throw CertificateException?
777 }
778 }
779
780 /**
781 * Generates RSA key and certificate chain from the private key handle,
782 * collection of certificates and stores the result into key entries.
783 */
784 private void generateRSAKeyAndCertificateChain(String alias,
785 long hCryptProv, long hCryptKey, int keyLength,
786 Collection<? extends Certificate> certCollection)
787 {
788 try
789 {
790 X509Certificate[] certChain =
791 new X509Certificate[certCollection.size()];
792
793 int i = 0;
794 for (Iterator<? extends Certificate> iter =
795 certCollection.iterator(); iter.hasNext(); i++)
796 {
797 certChain[i] = (X509Certificate) iter.next();
798 }
799
800 storeWithUniqueAlias(alias, new KeyEntry(alias,
801 new RSAPrivateKey(hCryptProv, hCryptKey, keyLength),
802 certChain));
803 }
804 catch (Throwable e)
805 {
806 // Ignore the exception and skip this entry
807 // TODO - throw CertificateException?
808 }
809 }
810
811 /**
812 * Generates certificates from byte data and stores into cert collection.
813 *
814 * @param data Byte data.
815 * @param certCollection Collection of certificates.
816 */
817 private void generateCertificate(byte[] data,
818 Collection<Certificate> certCollection) {
819 try
820 {
821 ByteArrayInputStream bis = new ByteArrayInputStream(data);
822
839 {
840 // Ignore the exception and skip this certificate
841 // TODO - throw CertificateException?
842 }
843 }
844
845 /**
846 * Returns the name of the keystore.
847 */
848 private String getName()
849 {
850 return storeName;
851 }
852
853 /**
854 * Load keys and/or certificates from keystore into Collection.
855 *
856 * @param name Name of keystore.
857 * @param entries Collection of key/certificate.
858 */
859 private native void loadKeysOrCertificateChains(String name)
860 throws KeyStoreException;
861
862 /**
863 * Stores a DER-encoded certificate into the certificate store
864 *
865 * @param name Name of the keystore.
866 * @param alias Name of the certificate.
867 * @param encoding DER-encoded certificate.
868 */
869 private native void storeCertificate(String name, String alias,
870 byte[] encoding, int encodingLength, long hCryptProvider,
871 long hCryptKey) throws CertificateException, KeyStoreException;
872
873 /**
874 * Removes the certificate from the certificate store
875 *
876 * @param name Name of the keystore.
877 * @param alias Name of the certificate.
878 * @param encoding DER-encoded certificate.
879 */
880 private native void removeCertificate(String name, String alias,
|