1 /*
2 * Copyright (c) 1997, 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.provider;
27
28 import java.io.*;
29 import java.security.*;
30 import java.security.cert.Certificate;
31 import java.security.cert.CertificateFactory;
32 import java.security.cert.CertificateException;
33 import java.util.*;
34
35 import sun.misc.IOUtils;
36 import sun.security.pkcs.EncryptedPrivateKeyInfo;
37 import sun.security.pkcs12.PKCS12KeyStore;
38
39 /**
40 * This class provides the keystore implementation referred to as "JKS".
41 *
42 * @author Jan Luehe
43 * @author David Brownell
44 *
45 *
46 * @see KeyProtector
47 * @see java.security.KeyStoreSpi
48 * @see KeyTool
49 *
50 * @since 1.2
51 */
52
53 abstract class JavaKeyStore extends KeyStoreSpi {
54
55 // regular JKS
56 public static final class JKS extends JavaKeyStore {
57 String convertAlias(String alias) {
58 return alias.toLowerCase(Locale.ENGLISH);
59 }
60 }
61
62 // special JKS that uses case sensitive aliases
63 public static final class CaseExactJKS extends JavaKeyStore {
64 String convertAlias(String alias) {
65 return alias;
66 }
67 }
68
69 // special JKS that supports JKS and PKCS12 file formats
70 public static final class DualFormatJKS extends KeyStoreDelegator {
71 public DualFormatJKS() {
72 super("JKS", JKS.class, "PKCS12", PKCS12KeyStore.class);
73 }
74 }
75
76 private static final int MAGIC = 0xfeedfeed;
77 private static final int VERSION_1 = 0x01;
78 private static final int VERSION_2 = 0x02;
79
80 // Private keys and their supporting certificate chains
81 private static class KeyEntry {
82 Date date; // the creation date of this entry
83 byte[] protectedPrivKey;
84 Certificate chain[];
85 };
86
87 // Trusted certificates
88 private static class TrustedCertEntry {
89 Date date; // the creation date of this entry
90 Certificate cert;
91 };
92
93 /**
94 * Private keys and certificates are stored in a hashtable.
95 * Hash entries are keyed by alias names.
625 * @param password the (optional) password used to check the integrity of
626 * the keystore.
627 *
628 * @exception IOException if there is an I/O or format problem with the
629 * keystore data
630 * @exception NoSuchAlgorithmException if the algorithm used to check
631 * the integrity of the keystore cannot be found
632 * @exception CertificateException if any of the certificates in the
633 * keystore could not be loaded
634 */
635 public void engineLoad(InputStream stream, char[] password)
636 throws IOException, NoSuchAlgorithmException, CertificateException
637 {
638 synchronized(entries) {
639 DataInputStream dis;
640 MessageDigest md = null;
641 CertificateFactory cf = null;
642 Hashtable<String, CertificateFactory> cfs = null;
643 ByteArrayInputStream bais = null;
644 byte[] encoded = null;
645
646 if (stream == null)
647 return;
648
649 if (password != null) {
650 md = getPreKeyedHash(password);
651 dis = new DataInputStream(new DigestInputStream(stream, md));
652 } else {
653 dis = new DataInputStream(stream);
654 }
655
656 // Body format: see store method
657
658 int xMagic = dis.readInt();
659 int xVersion = dis.readInt();
660
661 if (xMagic!=MAGIC ||
662 (xVersion!=VERSION_1 && xVersion!=VERSION_2)) {
663 throw new IOException("Invalid keystore format");
664 }
665
666 if (xVersion == VERSION_1) {
667 cf = CertificateFactory.getInstance("X509");
668 } else {
669 // version 2
670 cfs = new Hashtable<String, CertificateFactory>(3);
671 }
672
673 entries.clear();
674 int count = dis.readInt();
675
676 for (int i = 0; i < count; i++) {
677 int tag;
678 String alias;
679
680 tag = dis.readInt();
681
682 if (tag == 1) { // private key entry
683
684 KeyEntry entry = new KeyEntry();
685
686 // Read the alias
687 alias = dis.readUTF();
688
689 // Read the (entry creation) date
690 entry.date = new Date(dis.readLong());
691
692 // Read the private key
693 entry.protectedPrivKey =
694 IOUtils.readFully(dis, dis.readInt(), true);
695
696 // Read the certificate chain
697 int numOfCerts = dis.readInt();
698 if (numOfCerts > 0) {
699 List<Certificate> certs = new ArrayList<>(
700 numOfCerts > 10 ? 10 : numOfCerts);
701 for (int j = 0; j < numOfCerts; j++) {
702 if (xVersion == 2) {
703 // read the certificate type, and instantiate a
712 cf = CertificateFactory.getInstance(certType);
713 // store the certificate factory so we can
714 // reuse it later
715 cfs.put(certType, cf);
716 }
717 }
718 // instantiate the certificate
719 encoded = IOUtils.readFully(dis, dis.readInt(), true);
720 bais = new ByteArrayInputStream(encoded);
721 certs.add(cf.generateCertificate(bais));
722 bais.close();
723 }
724 // We can be sure now that numOfCerts of certs are read
725 entry.chain = certs.toArray(new Certificate[numOfCerts]);
726 }
727
728 // Add the entry to the list
729 entries.put(alias, entry);
730
731 } else if (tag == 2) { // trusted certificate entry
732
733 TrustedCertEntry entry = new TrustedCertEntry();
734
735 // Read the alias
736 alias = dis.readUTF();
737
738 // Read the (entry creation) date
739 entry.date = new Date(dis.readLong());
740
741 // Read the trusted certificate
742 if (xVersion == 2) {
743 // read the certificate type, and instantiate a
744 // certificate factory of that type (reuse
745 // existing factory if possible)
746 String certType = dis.readUTF();
747 if (cfs.containsKey(certType)) {
748 // reuse certificate factory
749 cf = cfs.get(certType);
750 } else {
751 // create new certificate factory
752 cf = CertificateFactory.getInstance(certType);
753 // store the certificate factory so we can
754 // reuse it later
755 cfs.put(certType, cf);
756 }
757 }
758 encoded = IOUtils.readFully(dis, dis.readInt(), true);
759 bais = new ByteArrayInputStream(encoded);
760 entry.cert = cf.generateCertificate(bais);
761 bais.close();
762
763 // Add the entry to the list
764 entries.put(alias, entry);
765
766 } else {
767 throw new IOException("Unrecognized keystore entry");
768 }
769 }
770
771 /*
772 * If a password has been provided, we check the keyed digest
773 * at the end. If this check fails, the store has been tampered
774 * with
775 */
776 if (password != null) {
777 byte computed[], actual[];
778 computed = md.digest();
779 actual = new byte[computed.length];
780 dis.readFully(actual);
781 for (int i = 0; i < computed.length; i++) {
782 if (computed[i] != actual[i]) {
783 Throwable t = new UnrecoverableKeyException
784 ("Password verification failed");
785 throw (IOException)new IOException
786 ("Keystore was tampered with, or "
787 + "password was incorrect").initCause(t);
788 }
|
1 /*
2 * Copyright (c) 1997, 2019, 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.provider;
27
28 import java.io.*;
29 import java.security.*;
30 import java.security.cert.Certificate;
31 import java.security.cert.CertificateFactory;
32 import java.security.cert.CertificateException;
33 import java.util.*;
34
35 import sun.misc.IOUtils;
36 import sun.security.pkcs.EncryptedPrivateKeyInfo;
37 import sun.security.pkcs12.PKCS12KeyStore;
38 import sun.security.util.Debug;
39
40 /**
41 * This class provides the keystore implementation referred to as "JKS".
42 *
43 * @author Jan Luehe
44 * @author David Brownell
45 *
46 *
47 * @see KeyProtector
48 * @see java.security.KeyStoreSpi
49 * @see KeyTool
50 *
51 * @since 1.2
52 */
53
54 abstract class JavaKeyStore extends KeyStoreSpi {
55
56 // regular JKS
57 public static final class JKS extends JavaKeyStore {
58 String convertAlias(String alias) {
59 return alias.toLowerCase(Locale.ENGLISH);
60 }
61 }
62
63 // special JKS that uses case sensitive aliases
64 public static final class CaseExactJKS extends JavaKeyStore {
65 String convertAlias(String alias) {
66 return alias;
67 }
68 }
69
70 // special JKS that supports JKS and PKCS12 file formats
71 public static final class DualFormatJKS extends KeyStoreDelegator {
72 public DualFormatJKS() {
73 super("JKS", JKS.class, "PKCS12", PKCS12KeyStore.class);
74 }
75 }
76
77 private static final Debug debug = Debug.getInstance("keystore");
78 private static final int MAGIC = 0xfeedfeed;
79 private static final int VERSION_1 = 0x01;
80 private static final int VERSION_2 = 0x02;
81
82 // Private keys and their supporting certificate chains
83 private static class KeyEntry {
84 Date date; // the creation date of this entry
85 byte[] protectedPrivKey;
86 Certificate chain[];
87 };
88
89 // Trusted certificates
90 private static class TrustedCertEntry {
91 Date date; // the creation date of this entry
92 Certificate cert;
93 };
94
95 /**
96 * Private keys and certificates are stored in a hashtable.
97 * Hash entries are keyed by alias names.
627 * @param password the (optional) password used to check the integrity of
628 * the keystore.
629 *
630 * @exception IOException if there is an I/O or format problem with the
631 * keystore data
632 * @exception NoSuchAlgorithmException if the algorithm used to check
633 * the integrity of the keystore cannot be found
634 * @exception CertificateException if any of the certificates in the
635 * keystore could not be loaded
636 */
637 public void engineLoad(InputStream stream, char[] password)
638 throws IOException, NoSuchAlgorithmException, CertificateException
639 {
640 synchronized(entries) {
641 DataInputStream dis;
642 MessageDigest md = null;
643 CertificateFactory cf = null;
644 Hashtable<String, CertificateFactory> cfs = null;
645 ByteArrayInputStream bais = null;
646 byte[] encoded = null;
647 int trustedKeyCount = 0, privateKeyCount = 0;
648
649 if (stream == null)
650 return;
651
652 if (password != null) {
653 md = getPreKeyedHash(password);
654 dis = new DataInputStream(new DigestInputStream(stream, md));
655 } else {
656 dis = new DataInputStream(stream);
657 }
658
659 // Body format: see store method
660
661 int xMagic = dis.readInt();
662 int xVersion = dis.readInt();
663
664 if (xMagic!=MAGIC ||
665 (xVersion!=VERSION_1 && xVersion!=VERSION_2)) {
666 throw new IOException("Invalid keystore format");
667 }
668
669 if (xVersion == VERSION_1) {
670 cf = CertificateFactory.getInstance("X509");
671 } else {
672 // version 2
673 cfs = new Hashtable<String, CertificateFactory>(3);
674 }
675
676 entries.clear();
677 int count = dis.readInt();
678
679 for (int i = 0; i < count; i++) {
680 int tag;
681 String alias;
682
683 tag = dis.readInt();
684
685 if (tag == 1) { // private key entry
686 privateKeyCount++;
687 KeyEntry entry = new KeyEntry();
688
689 // Read the alias
690 alias = dis.readUTF();
691
692 // Read the (entry creation) date
693 entry.date = new Date(dis.readLong());
694
695 // Read the private key
696 entry.protectedPrivKey =
697 IOUtils.readFully(dis, dis.readInt(), true);
698
699 // Read the certificate chain
700 int numOfCerts = dis.readInt();
701 if (numOfCerts > 0) {
702 List<Certificate> certs = new ArrayList<>(
703 numOfCerts > 10 ? 10 : numOfCerts);
704 for (int j = 0; j < numOfCerts; j++) {
705 if (xVersion == 2) {
706 // read the certificate type, and instantiate a
715 cf = CertificateFactory.getInstance(certType);
716 // store the certificate factory so we can
717 // reuse it later
718 cfs.put(certType, cf);
719 }
720 }
721 // instantiate the certificate
722 encoded = IOUtils.readFully(dis, dis.readInt(), true);
723 bais = new ByteArrayInputStream(encoded);
724 certs.add(cf.generateCertificate(bais));
725 bais.close();
726 }
727 // We can be sure now that numOfCerts of certs are read
728 entry.chain = certs.toArray(new Certificate[numOfCerts]);
729 }
730
731 // Add the entry to the list
732 entries.put(alias, entry);
733
734 } else if (tag == 2) { // trusted certificate entry
735 trustedKeyCount++;
736 TrustedCertEntry entry = new TrustedCertEntry();
737
738 // Read the alias
739 alias = dis.readUTF();
740
741 // Read the (entry creation) date
742 entry.date = new Date(dis.readLong());
743
744 // Read the trusted certificate
745 if (xVersion == 2) {
746 // read the certificate type, and instantiate a
747 // certificate factory of that type (reuse
748 // existing factory if possible)
749 String certType = dis.readUTF();
750 if (cfs.containsKey(certType)) {
751 // reuse certificate factory
752 cf = cfs.get(certType);
753 } else {
754 // create new certificate factory
755 cf = CertificateFactory.getInstance(certType);
756 // store the certificate factory so we can
757 // reuse it later
758 cfs.put(certType, cf);
759 }
760 }
761 encoded = IOUtils.readFully(dis, dis.readInt(), true);
762 bais = new ByteArrayInputStream(encoded);
763 entry.cert = cf.generateCertificate(bais);
764 bais.close();
765
766 // Add the entry to the list
767 entries.put(alias, entry);
768
769 } else {
770 throw new IOException("Unrecognized keystore entry: " +
771 tag);
772 }
773 }
774
775 if (debug != null) {
776 debug.println("JavaKeyStore load: private key count: " +
777 privateKeyCount + ". trusted key count: " + trustedKeyCount);
778 }
779
780 /*
781 * If a password has been provided, we check the keyed digest
782 * at the end. If this check fails, the store has been tampered
783 * with
784 */
785 if (password != null) {
786 byte computed[], actual[];
787 computed = md.digest();
788 actual = new byte[computed.length];
789 dis.readFully(actual);
790 for (int i = 0; i < computed.length; i++) {
791 if (computed[i] != actual[i]) {
792 Throwable t = new UnrecoverableKeyException
793 ("Password verification failed");
794 throw (IOException)new IOException
795 ("Keystore was tampered with, or "
796 + "password was incorrect").initCause(t);
797 }
|