1a198e1ecc615e26a167d0f2dca9fa7e5fc62de10Brian Carlstrompackage org.bouncycastle.jcajce.provider.keystore.bc;
2b61a96e7ef1a78acf013bbf08fe537e5b5f129caPeter Hallam
3b61a96e7ef1a78acf013bbf08fe537e5b5f129caPeter Hallamimport java.io.ByteArrayInputStream;
4b61a96e7ef1a78acf013bbf08fe537e5b5f129caPeter Hallamimport java.io.ByteArrayOutputStream;
5b61a96e7ef1a78acf013bbf08fe537e5b5f129caPeter Hallamimport java.io.DataInputStream;
6b61a96e7ef1a78acf013bbf08fe537e5b5f129caPeter Hallamimport java.io.DataOutputStream;
7b61a96e7ef1a78acf013bbf08fe537e5b5f129caPeter Hallamimport java.io.IOException;
8b61a96e7ef1a78acf013bbf08fe537e5b5f129caPeter Hallamimport java.io.InputStream;
9b61a96e7ef1a78acf013bbf08fe537e5b5f129caPeter Hallamimport java.io.OutputStream;
10c37f4a04ef89e73a39a59f3c5a179af8c8ab5974Brian Carlstromimport java.security.Key;
11c37f4a04ef89e73a39a59f3c5a179af8c8ab5974Brian Carlstromimport java.security.KeyFactory;
12c37f4a04ef89e73a39a59f3c5a179af8c8ab5974Brian Carlstromimport java.security.KeyStoreException;
13c37f4a04ef89e73a39a59f3c5a179af8c8ab5974Brian Carlstromimport java.security.KeyStoreSpi;
14c37f4a04ef89e73a39a59f3c5a179af8c8ab5974Brian Carlstromimport java.security.NoSuchAlgorithmException;
15c37f4a04ef89e73a39a59f3c5a179af8c8ab5974Brian Carlstromimport java.security.NoSuchProviderException;
16c37f4a04ef89e73a39a59f3c5a179af8c8ab5974Brian Carlstromimport java.security.PrivateKey;
17c37f4a04ef89e73a39a59f3c5a179af8c8ab5974Brian Carlstromimport java.security.PublicKey;
18c37f4a04ef89e73a39a59f3c5a179af8c8ab5974Brian Carlstromimport java.security.SecureRandom;
19c37f4a04ef89e73a39a59f3c5a179af8c8ab5974Brian Carlstromimport java.security.UnrecoverableKeyException;
20b61a96e7ef1a78acf013bbf08fe537e5b5f129caPeter Hallamimport java.security.cert.Certificate;
21b61a96e7ef1a78acf013bbf08fe537e5b5f129caPeter Hallamimport java.security.cert.CertificateEncodingException;
22b61a96e7ef1a78acf013bbf08fe537e5b5f129caPeter Hallamimport java.security.cert.CertificateException;
23b61a96e7ef1a78acf013bbf08fe537e5b5f129caPeter Hallamimport java.security.cert.CertificateFactory;
24b61a96e7ef1a78acf013bbf08fe537e5b5f129caPeter Hallamimport java.security.spec.KeySpec;
25b61a96e7ef1a78acf013bbf08fe537e5b5f129caPeter Hallamimport java.security.spec.PKCS8EncodedKeySpec;
26b61a96e7ef1a78acf013bbf08fe537e5b5f129caPeter Hallamimport java.security.spec.X509EncodedKeySpec;
27b61a96e7ef1a78acf013bbf08fe537e5b5f129caPeter Hallamimport java.util.Date;
28b61a96e7ef1a78acf013bbf08fe537e5b5f129caPeter Hallamimport java.util.Enumeration;
29b61a96e7ef1a78acf013bbf08fe537e5b5f129caPeter Hallamimport java.util.Hashtable;
30b61a96e7ef1a78acf013bbf08fe537e5b5f129caPeter Hallam
31b61a96e7ef1a78acf013bbf08fe537e5b5f129caPeter Hallamimport javax.crypto.Cipher;
32b61a96e7ef1a78acf013bbf08fe537e5b5f129caPeter Hallamimport javax.crypto.CipherInputStream;
33b61a96e7ef1a78acf013bbf08fe537e5b5f129caPeter Hallamimport javax.crypto.CipherOutputStream;
34b61a96e7ef1a78acf013bbf08fe537e5b5f129caPeter Hallamimport javax.crypto.SecretKeyFactory;
35b61a96e7ef1a78acf013bbf08fe537e5b5f129caPeter Hallamimport javax.crypto.spec.PBEKeySpec;
36b61a96e7ef1a78acf013bbf08fe537e5b5f129caPeter Hallamimport javax.crypto.spec.PBEParameterSpec;
37b61a96e7ef1a78acf013bbf08fe537e5b5f129caPeter Hallamimport javax.crypto.spec.SecretKeySpec;
38b61a96e7ef1a78acf013bbf08fe537e5b5f129caPeter Hallam
39c37f4a04ef89e73a39a59f3c5a179af8c8ab5974Brian Carlstromimport org.bouncycastle.crypto.CipherParameters;
40b61a96e7ef1a78acf013bbf08fe537e5b5f129caPeter Hallamimport org.bouncycastle.crypto.Digest;
41b61a96e7ef1a78acf013bbf08fe537e5b5f129caPeter Hallamimport org.bouncycastle.crypto.PBEParametersGenerator;
42a198e1ecc615e26a167d0f2dca9fa7e5fc62de10Brian Carlstromimport org.bouncycastle.crypto.digests.SHA1Digest;
43b61a96e7ef1a78acf013bbf08fe537e5b5f129caPeter Hallamimport org.bouncycastle.crypto.generators.PKCS12ParametersGenerator;
44b61a96e7ef1a78acf013bbf08fe537e5b5f129caPeter Hallamimport org.bouncycastle.crypto.io.DigestInputStream;
45b61a96e7ef1a78acf013bbf08fe537e5b5f129caPeter Hallamimport org.bouncycastle.crypto.io.DigestOutputStream;
46b61a96e7ef1a78acf013bbf08fe537e5b5f129caPeter Hallamimport org.bouncycastle.crypto.io.MacInputStream;
47b61a96e7ef1a78acf013bbf08fe537e5b5f129caPeter Hallamimport org.bouncycastle.crypto.io.MacOutputStream;
48b61a96e7ef1a78acf013bbf08fe537e5b5f129caPeter Hallamimport org.bouncycastle.crypto.macs.HMac;
49b61a96e7ef1a78acf013bbf08fe537e5b5f129caPeter Hallamimport org.bouncycastle.jce.interfaces.BCKeyStore;
50a198e1ecc615e26a167d0f2dca9fa7e5fc62de10Brian Carlstromimport org.bouncycastle.jce.provider.BouncyCastleProvider;
51c37f4a04ef89e73a39a59f3c5a179af8c8ab5974Brian Carlstromimport org.bouncycastle.util.Arrays;
52c37f4a04ef89e73a39a59f3c5a179af8c8ab5974Brian Carlstromimport org.bouncycastle.util.io.Streams;
534c111300c39cb2e27f07fc2ae3b00e23ed4443b2Brian Carlstromimport org.bouncycastle.util.io.TeeOutputStream;
54b61a96e7ef1a78acf013bbf08fe537e5b5f129caPeter Hallam
55a198e1ecc615e26a167d0f2dca9fa7e5fc62de10Brian Carlstrompublic class BcKeyStoreSpi
56b61a96e7ef1a78acf013bbf08fe537e5b5f129caPeter Hallam    extends KeyStoreSpi
57b61a96e7ef1a78acf013bbf08fe537e5b5f129caPeter Hallam    implements BCKeyStore
58b61a96e7ef1a78acf013bbf08fe537e5b5f129caPeter Hallam{
594c111300c39cb2e27f07fc2ae3b00e23ed4443b2Brian Carlstrom    private static final int    STORE_VERSION = 2;
60b61a96e7ef1a78acf013bbf08fe537e5b5f129caPeter Hallam
61b61a96e7ef1a78acf013bbf08fe537e5b5f129caPeter Hallam    private static final int    STORE_SALT_SIZE = 20;
62b61a96e7ef1a78acf013bbf08fe537e5b5f129caPeter Hallam    private static final String STORE_CIPHER = "PBEWithSHAAndTwofish-CBC";
63b61a96e7ef1a78acf013bbf08fe537e5b5f129caPeter Hallam
64b61a96e7ef1a78acf013bbf08fe537e5b5f129caPeter Hallam    private static final int    KEY_SALT_SIZE = 20;
65b61a96e7ef1a78acf013bbf08fe537e5b5f129caPeter Hallam    private static final int    MIN_ITERATIONS = 1024;
66b61a96e7ef1a78acf013bbf08fe537e5b5f129caPeter Hallam
67b61a96e7ef1a78acf013bbf08fe537e5b5f129caPeter Hallam    private static final String KEY_CIPHER = "PBEWithSHAAnd3-KeyTripleDES-CBC";
68b61a96e7ef1a78acf013bbf08fe537e5b5f129caPeter Hallam
69b61a96e7ef1a78acf013bbf08fe537e5b5f129caPeter Hallam    //
70b61a96e7ef1a78acf013bbf08fe537e5b5f129caPeter Hallam    // generic object types
71b61a96e7ef1a78acf013bbf08fe537e5b5f129caPeter Hallam    //
72b61a96e7ef1a78acf013bbf08fe537e5b5f129caPeter Hallam    static final int NULL           = 0;
73b61a96e7ef1a78acf013bbf08fe537e5b5f129caPeter Hallam    static final int CERTIFICATE    = 1;
74b61a96e7ef1a78acf013bbf08fe537e5b5f129caPeter Hallam    static final int KEY            = 2;
75b61a96e7ef1a78acf013bbf08fe537e5b5f129caPeter Hallam    static final int SECRET         = 3;
76b61a96e7ef1a78acf013bbf08fe537e5b5f129caPeter Hallam    static final int SEALED         = 4;
77b61a96e7ef1a78acf013bbf08fe537e5b5f129caPeter Hallam
78b61a96e7ef1a78acf013bbf08fe537e5b5f129caPeter Hallam    //
79b61a96e7ef1a78acf013bbf08fe537e5b5f129caPeter Hallam    // key types
80b61a96e7ef1a78acf013bbf08fe537e5b5f129caPeter Hallam    //
81b61a96e7ef1a78acf013bbf08fe537e5b5f129caPeter Hallam    static final int    KEY_PRIVATE = 0;
82b61a96e7ef1a78acf013bbf08fe537e5b5f129caPeter Hallam    static final int    KEY_PUBLIC  = 1;
83b61a96e7ef1a78acf013bbf08fe537e5b5f129caPeter Hallam    static final int    KEY_SECRET  = 2;
84b61a96e7ef1a78acf013bbf08fe537e5b5f129caPeter Hallam
85b61a96e7ef1a78acf013bbf08fe537e5b5f129caPeter Hallam    protected Hashtable       table = new Hashtable();
86b61a96e7ef1a78acf013bbf08fe537e5b5f129caPeter Hallam
87b61a96e7ef1a78acf013bbf08fe537e5b5f129caPeter Hallam    protected SecureRandom    random = new SecureRandom();
88b61a96e7ef1a78acf013bbf08fe537e5b5f129caPeter Hallam
89a198e1ecc615e26a167d0f2dca9fa7e5fc62de10Brian Carlstrom    protected int              version;
90a198e1ecc615e26a167d0f2dca9fa7e5fc62de10Brian Carlstrom
91a198e1ecc615e26a167d0f2dca9fa7e5fc62de10Brian Carlstrom    public BcKeyStoreSpi(int version)
92b61a96e7ef1a78acf013bbf08fe537e5b5f129caPeter Hallam    {
93a198e1ecc615e26a167d0f2dca9fa7e5fc62de10Brian Carlstrom        this.version = version;
94b61a96e7ef1a78acf013bbf08fe537e5b5f129caPeter Hallam    }
95b61a96e7ef1a78acf013bbf08fe537e5b5f129caPeter Hallam
96b61a96e7ef1a78acf013bbf08fe537e5b5f129caPeter Hallam    private class StoreEntry
97b61a96e7ef1a78acf013bbf08fe537e5b5f129caPeter Hallam    {
98b61a96e7ef1a78acf013bbf08fe537e5b5f129caPeter Hallam        int             type;
99b61a96e7ef1a78acf013bbf08fe537e5b5f129caPeter Hallam        String          alias;
100b61a96e7ef1a78acf013bbf08fe537e5b5f129caPeter Hallam        Object          obj;
101b61a96e7ef1a78acf013bbf08fe537e5b5f129caPeter Hallam        Certificate[]   certChain;
102b61a96e7ef1a78acf013bbf08fe537e5b5f129caPeter Hallam        Date            date = new Date();
103b61a96e7ef1a78acf013bbf08fe537e5b5f129caPeter Hallam
104b61a96e7ef1a78acf013bbf08fe537e5b5f129caPeter Hallam        StoreEntry(
105b61a96e7ef1a78acf013bbf08fe537e5b5f129caPeter Hallam            String       alias,
106b61a96e7ef1a78acf013bbf08fe537e5b5f129caPeter Hallam            Certificate  obj)
107b61a96e7ef1a78acf013bbf08fe537e5b5f129caPeter Hallam        {
108b61a96e7ef1a78acf013bbf08fe537e5b5f129caPeter Hallam            this.type = CERTIFICATE;
109b61a96e7ef1a78acf013bbf08fe537e5b5f129caPeter Hallam            this.alias = alias;
110b61a96e7ef1a78acf013bbf08fe537e5b5f129caPeter Hallam            this.obj = obj;
111b61a96e7ef1a78acf013bbf08fe537e5b5f129caPeter Hallam            this.certChain = null;
112b61a96e7ef1a78acf013bbf08fe537e5b5f129caPeter Hallam        }
113b61a96e7ef1a78acf013bbf08fe537e5b5f129caPeter Hallam
114b61a96e7ef1a78acf013bbf08fe537e5b5f129caPeter Hallam        StoreEntry(
115b61a96e7ef1a78acf013bbf08fe537e5b5f129caPeter Hallam            String          alias,
116b61a96e7ef1a78acf013bbf08fe537e5b5f129caPeter Hallam            byte[]          obj,
117b61a96e7ef1a78acf013bbf08fe537e5b5f129caPeter Hallam            Certificate[]   certChain)
118b61a96e7ef1a78acf013bbf08fe537e5b5f129caPeter Hallam        {
119b61a96e7ef1a78acf013bbf08fe537e5b5f129caPeter Hallam            this.type = SECRET;
120b61a96e7ef1a78acf013bbf08fe537e5b5f129caPeter Hallam            this.alias = alias;
121b61a96e7ef1a78acf013bbf08fe537e5b5f129caPeter Hallam            this.obj = obj;
122b61a96e7ef1a78acf013bbf08fe537e5b5f129caPeter Hallam            this.certChain = certChain;
123b61a96e7ef1a78acf013bbf08fe537e5b5f129caPeter Hallam        }
124b61a96e7ef1a78acf013bbf08fe537e5b5f129caPeter Hallam
125b61a96e7ef1a78acf013bbf08fe537e5b5f129caPeter Hallam        StoreEntry(
126b61a96e7ef1a78acf013bbf08fe537e5b5f129caPeter Hallam            String          alias,
127b61a96e7ef1a78acf013bbf08fe537e5b5f129caPeter Hallam            Key             key,
128b61a96e7ef1a78acf013bbf08fe537e5b5f129caPeter Hallam            char[]          password,
129b61a96e7ef1a78acf013bbf08fe537e5b5f129caPeter Hallam            Certificate[]   certChain)
130b61a96e7ef1a78acf013bbf08fe537e5b5f129caPeter Hallam            throws Exception
131b61a96e7ef1a78acf013bbf08fe537e5b5f129caPeter Hallam        {
132b61a96e7ef1a78acf013bbf08fe537e5b5f129caPeter Hallam            this.type = SEALED;
133b61a96e7ef1a78acf013bbf08fe537e5b5f129caPeter Hallam            this.alias = alias;
134b61a96e7ef1a78acf013bbf08fe537e5b5f129caPeter Hallam            this.certChain = certChain;
135b61a96e7ef1a78acf013bbf08fe537e5b5f129caPeter Hallam
136b61a96e7ef1a78acf013bbf08fe537e5b5f129caPeter Hallam            byte[] salt = new byte[KEY_SALT_SIZE];
137b61a96e7ef1a78acf013bbf08fe537e5b5f129caPeter Hallam
138b61a96e7ef1a78acf013bbf08fe537e5b5f129caPeter Hallam            random.setSeed(System.currentTimeMillis());
139b61a96e7ef1a78acf013bbf08fe537e5b5f129caPeter Hallam            random.nextBytes(salt);
140b61a96e7ef1a78acf013bbf08fe537e5b5f129caPeter Hallam
141b61a96e7ef1a78acf013bbf08fe537e5b5f129caPeter Hallam            int iterationCount = MIN_ITERATIONS + (random.nextInt() & 0x3ff);
142b61a96e7ef1a78acf013bbf08fe537e5b5f129caPeter Hallam
143b61a96e7ef1a78acf013bbf08fe537e5b5f129caPeter Hallam
144b61a96e7ef1a78acf013bbf08fe537e5b5f129caPeter Hallam            ByteArrayOutputStream   bOut = new ByteArrayOutputStream();
145b61a96e7ef1a78acf013bbf08fe537e5b5f129caPeter Hallam            DataOutputStream        dOut = new DataOutputStream(bOut);
146b61a96e7ef1a78acf013bbf08fe537e5b5f129caPeter Hallam
147b61a96e7ef1a78acf013bbf08fe537e5b5f129caPeter Hallam            dOut.writeInt(salt.length);
148b61a96e7ef1a78acf013bbf08fe537e5b5f129caPeter Hallam            dOut.write(salt);
149b61a96e7ef1a78acf013bbf08fe537e5b5f129caPeter Hallam            dOut.writeInt(iterationCount);
150b61a96e7ef1a78acf013bbf08fe537e5b5f129caPeter Hallam
151b61a96e7ef1a78acf013bbf08fe537e5b5f129caPeter Hallam            Cipher              cipher = makePBECipher(KEY_CIPHER, Cipher.ENCRYPT_MODE, password, salt, iterationCount);
152b61a96e7ef1a78acf013bbf08fe537e5b5f129caPeter Hallam            CipherOutputStream  cOut = new CipherOutputStream(dOut, cipher);
153b61a96e7ef1a78acf013bbf08fe537e5b5f129caPeter Hallam
154b61a96e7ef1a78acf013bbf08fe537e5b5f129caPeter Hallam            dOut = new DataOutputStream(cOut);
155b61a96e7ef1a78acf013bbf08fe537e5b5f129caPeter Hallam
156b61a96e7ef1a78acf013bbf08fe537e5b5f129caPeter Hallam            encodeKey(key, dOut);
157b61a96e7ef1a78acf013bbf08fe537e5b5f129caPeter Hallam
158b61a96e7ef1a78acf013bbf08fe537e5b5f129caPeter Hallam            dOut.close();
159b61a96e7ef1a78acf013bbf08fe537e5b5f129caPeter Hallam
160b61a96e7ef1a78acf013bbf08fe537e5b5f129caPeter Hallam            obj = bOut.toByteArray();
161b61a96e7ef1a78acf013bbf08fe537e5b5f129caPeter Hallam        }
162b61a96e7ef1a78acf013bbf08fe537e5b5f129caPeter Hallam
163b61a96e7ef1a78acf013bbf08fe537e5b5f129caPeter Hallam        StoreEntry(
164b61a96e7ef1a78acf013bbf08fe537e5b5f129caPeter Hallam            String          alias,
165b61a96e7ef1a78acf013bbf08fe537e5b5f129caPeter Hallam            Date            date,
166b61a96e7ef1a78acf013bbf08fe537e5b5f129caPeter Hallam            int             type,
167b61a96e7ef1a78acf013bbf08fe537e5b5f129caPeter Hallam            Object          obj)
168b61a96e7ef1a78acf013bbf08fe537e5b5f129caPeter Hallam        {
169b61a96e7ef1a78acf013bbf08fe537e5b5f129caPeter Hallam            this.alias = alias;
170b61a96e7ef1a78acf013bbf08fe537e5b5f129caPeter Hallam            this.date = date;
171b61a96e7ef1a78acf013bbf08fe537e5b5f129caPeter Hallam            this.type = type;
172b61a96e7ef1a78acf013bbf08fe537e5b5f129caPeter Hallam            this.obj = obj;
173b61a96e7ef1a78acf013bbf08fe537e5b5f129caPeter Hallam        }
174b61a96e7ef1a78acf013bbf08fe537e5b5f129caPeter Hallam
175b61a96e7ef1a78acf013bbf08fe537e5b5f129caPeter Hallam        StoreEntry(
176b61a96e7ef1a78acf013bbf08fe537e5b5f129caPeter Hallam            String          alias,
177b61a96e7ef1a78acf013bbf08fe537e5b5f129caPeter Hallam            Date            date,
178b61a96e7ef1a78acf013bbf08fe537e5b5f129caPeter Hallam            int             type,
179b61a96e7ef1a78acf013bbf08fe537e5b5f129caPeter Hallam            Object          obj,
180b61a96e7ef1a78acf013bbf08fe537e5b5f129caPeter Hallam            Certificate[]   certChain)
181b61a96e7ef1a78acf013bbf08fe537e5b5f129caPeter Hallam        {
182b61a96e7ef1a78acf013bbf08fe537e5b5f129caPeter Hallam            this.alias = alias;
183b61a96e7ef1a78acf013bbf08fe537e5b5f129caPeter Hallam            this.date = date;
184b61a96e7ef1a78acf013bbf08fe537e5b5f129caPeter Hallam            this.type = type;
185b61a96e7ef1a78acf013bbf08fe537e5b5f129caPeter Hallam            this.obj = obj;
186b61a96e7ef1a78acf013bbf08fe537e5b5f129caPeter Hallam            this.certChain = certChain;
187b61a96e7ef1a78acf013bbf08fe537e5b5f129caPeter Hallam        }
188b61a96e7ef1a78acf013bbf08fe537e5b5f129caPeter Hallam
189b61a96e7ef1a78acf013bbf08fe537e5b5f129caPeter Hallam        int getType()
190b61a96e7ef1a78acf013bbf08fe537e5b5f129caPeter Hallam        {
191b61a96e7ef1a78acf013bbf08fe537e5b5f129caPeter Hallam            return type;
192b61a96e7ef1a78acf013bbf08fe537e5b5f129caPeter Hallam        }
193b61a96e7ef1a78acf013bbf08fe537e5b5f129caPeter Hallam
194b61a96e7ef1a78acf013bbf08fe537e5b5f129caPeter Hallam        String getAlias()
195b61a96e7ef1a78acf013bbf08fe537e5b5f129caPeter Hallam        {
196b61a96e7ef1a78acf013bbf08fe537e5b5f129caPeter Hallam            return alias;
197b61a96e7ef1a78acf013bbf08fe537e5b5f129caPeter Hallam        }
198b61a96e7ef1a78acf013bbf08fe537e5b5f129caPeter Hallam
199b61a96e7ef1a78acf013bbf08fe537e5b5f129caPeter Hallam        Object getObject()
200b61a96e7ef1a78acf013bbf08fe537e5b5f129caPeter Hallam        {
201b61a96e7ef1a78acf013bbf08fe537e5b5f129caPeter Hallam            return obj;
202b61a96e7ef1a78acf013bbf08fe537e5b5f129caPeter Hallam        }
203b61a96e7ef1a78acf013bbf08fe537e5b5f129caPeter Hallam
204b61a96e7ef1a78acf013bbf08fe537e5b5f129caPeter Hallam        Object getObject(
205b61a96e7ef1a78acf013bbf08fe537e5b5f129caPeter Hallam            char[]  password)
206b61a96e7ef1a78acf013bbf08fe537e5b5f129caPeter Hallam            throws NoSuchAlgorithmException, UnrecoverableKeyException
207b61a96e7ef1a78acf013bbf08fe537e5b5f129caPeter Hallam        {
208b61a96e7ef1a78acf013bbf08fe537e5b5f129caPeter Hallam            if (password == null || password.length == 0)
209b61a96e7ef1a78acf013bbf08fe537e5b5f129caPeter Hallam            {
210b61a96e7ef1a78acf013bbf08fe537e5b5f129caPeter Hallam                if (obj instanceof Key)
211b61a96e7ef1a78acf013bbf08fe537e5b5f129caPeter Hallam                {
212b61a96e7ef1a78acf013bbf08fe537e5b5f129caPeter Hallam                    return obj;
213b61a96e7ef1a78acf013bbf08fe537e5b5f129caPeter Hallam                }
214b61a96e7ef1a78acf013bbf08fe537e5b5f129caPeter Hallam            }
215b61a96e7ef1a78acf013bbf08fe537e5b5f129caPeter Hallam
216b61a96e7ef1a78acf013bbf08fe537e5b5f129caPeter Hallam            if (type == SEALED)
217b61a96e7ef1a78acf013bbf08fe537e5b5f129caPeter Hallam            {
218b61a96e7ef1a78acf013bbf08fe537e5b5f129caPeter Hallam                ByteArrayInputStream    bIn = new ByteArrayInputStream((byte[])obj);
219b61a96e7ef1a78acf013bbf08fe537e5b5f129caPeter Hallam                DataInputStream         dIn = new DataInputStream(bIn);
220b61a96e7ef1a78acf013bbf08fe537e5b5f129caPeter Hallam
221b61a96e7ef1a78acf013bbf08fe537e5b5f129caPeter Hallam                try
222b61a96e7ef1a78acf013bbf08fe537e5b5f129caPeter Hallam                {
223b61a96e7ef1a78acf013bbf08fe537e5b5f129caPeter Hallam                    byte[]      salt = new byte[dIn.readInt()];
224b61a96e7ef1a78acf013bbf08fe537e5b5f129caPeter Hallam
225b61a96e7ef1a78acf013bbf08fe537e5b5f129caPeter Hallam                    dIn.readFully(salt);
226b61a96e7ef1a78acf013bbf08fe537e5b5f129caPeter Hallam
227b61a96e7ef1a78acf013bbf08fe537e5b5f129caPeter Hallam                    int     iterationCount = dIn.readInt();
228b61a96e7ef1a78acf013bbf08fe537e5b5f129caPeter Hallam
229b61a96e7ef1a78acf013bbf08fe537e5b5f129caPeter Hallam                    Cipher      cipher = makePBECipher(KEY_CIPHER, Cipher.DECRYPT_MODE, password, salt, iterationCount);
230b61a96e7ef1a78acf013bbf08fe537e5b5f129caPeter Hallam
231b61a96e7ef1a78acf013bbf08fe537e5b5f129caPeter Hallam                    CipherInputStream cIn = new CipherInputStream(dIn, cipher);
232b61a96e7ef1a78acf013bbf08fe537e5b5f129caPeter Hallam
233b61a96e7ef1a78acf013bbf08fe537e5b5f129caPeter Hallam                    try
234b61a96e7ef1a78acf013bbf08fe537e5b5f129caPeter Hallam                    {
235b61a96e7ef1a78acf013bbf08fe537e5b5f129caPeter Hallam                        return decodeKey(new DataInputStream(cIn));
236b61a96e7ef1a78acf013bbf08fe537e5b5f129caPeter Hallam                    }
237b61a96e7ef1a78acf013bbf08fe537e5b5f129caPeter Hallam                    catch (Exception x)
238b61a96e7ef1a78acf013bbf08fe537e5b5f129caPeter Hallam                    {
239b61a96e7ef1a78acf013bbf08fe537e5b5f129caPeter Hallam                        bIn = new ByteArrayInputStream((byte[])obj);
240b61a96e7ef1a78acf013bbf08fe537e5b5f129caPeter Hallam                        dIn = new DataInputStream(bIn);
241b61a96e7ef1a78acf013bbf08fe537e5b5f129caPeter Hallam
242b61a96e7ef1a78acf013bbf08fe537e5b5f129caPeter Hallam                        salt = new byte[dIn.readInt()];
243b61a96e7ef1a78acf013bbf08fe537e5b5f129caPeter Hallam
244b61a96e7ef1a78acf013bbf08fe537e5b5f129caPeter Hallam                        dIn.readFully(salt);
245b61a96e7ef1a78acf013bbf08fe537e5b5f129caPeter Hallam
246b61a96e7ef1a78acf013bbf08fe537e5b5f129caPeter Hallam                        iterationCount = dIn.readInt();
247b61a96e7ef1a78acf013bbf08fe537e5b5f129caPeter Hallam
248b61a96e7ef1a78acf013bbf08fe537e5b5f129caPeter Hallam                        cipher = makePBECipher("Broken" + KEY_CIPHER, Cipher.DECRYPT_MODE, password, salt, iterationCount);
249b61a96e7ef1a78acf013bbf08fe537e5b5f129caPeter Hallam
250b61a96e7ef1a78acf013bbf08fe537e5b5f129caPeter Hallam                        cIn = new CipherInputStream(dIn, cipher);
251b61a96e7ef1a78acf013bbf08fe537e5b5f129caPeter Hallam
252b61a96e7ef1a78acf013bbf08fe537e5b5f129caPeter Hallam                        Key k = null;
253b61a96e7ef1a78acf013bbf08fe537e5b5f129caPeter Hallam
254b61a96e7ef1a78acf013bbf08fe537e5b5f129caPeter Hallam                        try
255b61a96e7ef1a78acf013bbf08fe537e5b5f129caPeter Hallam                        {
256b61a96e7ef1a78acf013bbf08fe537e5b5f129caPeter Hallam                            k = decodeKey(new DataInputStream(cIn));
257b61a96e7ef1a78acf013bbf08fe537e5b5f129caPeter Hallam                        }
258b61a96e7ef1a78acf013bbf08fe537e5b5f129caPeter Hallam                        catch (Exception y)
259b61a96e7ef1a78acf013bbf08fe537e5b5f129caPeter Hallam                        {
260b61a96e7ef1a78acf013bbf08fe537e5b5f129caPeter Hallam                            bIn = new ByteArrayInputStream((byte[])obj);
261b61a96e7ef1a78acf013bbf08fe537e5b5f129caPeter Hallam                            dIn = new DataInputStream(bIn);
262b61a96e7ef1a78acf013bbf08fe537e5b5f129caPeter Hallam
263b61a96e7ef1a78acf013bbf08fe537e5b5f129caPeter Hallam                            salt = new byte[dIn.readInt()];
264b61a96e7ef1a78acf013bbf08fe537e5b5f129caPeter Hallam
265b61a96e7ef1a78acf013bbf08fe537e5b5f129caPeter Hallam                            dIn.readFully(salt);
266b61a96e7ef1a78acf013bbf08fe537e5b5f129caPeter Hallam
267b61a96e7ef1a78acf013bbf08fe537e5b5f129caPeter Hallam                            iterationCount = dIn.readInt();
268b61a96e7ef1a78acf013bbf08fe537e5b5f129caPeter Hallam
269b61a96e7ef1a78acf013bbf08fe537e5b5f129caPeter Hallam                            cipher = makePBECipher("Old" + KEY_CIPHER, Cipher.DECRYPT_MODE, password, salt, iterationCount);
270b61a96e7ef1a78acf013bbf08fe537e5b5f129caPeter Hallam
271b61a96e7ef1a78acf013bbf08fe537e5b5f129caPeter Hallam                            cIn = new CipherInputStream(dIn, cipher);
272b61a96e7ef1a78acf013bbf08fe537e5b5f129caPeter Hallam
273b61a96e7ef1a78acf013bbf08fe537e5b5f129caPeter Hallam                            k = decodeKey(new DataInputStream(cIn));
274b61a96e7ef1a78acf013bbf08fe537e5b5f129caPeter Hallam                        }
275b61a96e7ef1a78acf013bbf08fe537e5b5f129caPeter Hallam
276b61a96e7ef1a78acf013bbf08fe537e5b5f129caPeter Hallam                        //
277b61a96e7ef1a78acf013bbf08fe537e5b5f129caPeter Hallam                        // reencrypt key with correct cipher.
278b61a96e7ef1a78acf013bbf08fe537e5b5f129caPeter Hallam                        //
279b61a96e7ef1a78acf013bbf08fe537e5b5f129caPeter Hallam                        if (k != null)
280b61a96e7ef1a78acf013bbf08fe537e5b5f129caPeter Hallam                        {
281b61a96e7ef1a78acf013bbf08fe537e5b5f129caPeter Hallam                            ByteArrayOutputStream   bOut = new ByteArrayOutputStream();
282b61a96e7ef1a78acf013bbf08fe537e5b5f129caPeter Hallam                            DataOutputStream        dOut = new DataOutputStream(bOut);
283b61a96e7ef1a78acf013bbf08fe537e5b5f129caPeter Hallam
284b61a96e7ef1a78acf013bbf08fe537e5b5f129caPeter Hallam                            dOut.writeInt(salt.length);
285b61a96e7ef1a78acf013bbf08fe537e5b5f129caPeter Hallam                            dOut.write(salt);
286b61a96e7ef1a78acf013bbf08fe537e5b5f129caPeter Hallam                            dOut.writeInt(iterationCount);
287b61a96e7ef1a78acf013bbf08fe537e5b5f129caPeter Hallam
288b61a96e7ef1a78acf013bbf08fe537e5b5f129caPeter Hallam                            Cipher              out = makePBECipher(KEY_CIPHER, Cipher.ENCRYPT_MODE, password, salt, iterationCount);
289b61a96e7ef1a78acf013bbf08fe537e5b5f129caPeter Hallam                            CipherOutputStream  cOut = new CipherOutputStream(dOut, out);
290b61a96e7ef1a78acf013bbf08fe537e5b5f129caPeter Hallam
291b61a96e7ef1a78acf013bbf08fe537e5b5f129caPeter Hallam                            dOut = new DataOutputStream(cOut);
292b61a96e7ef1a78acf013bbf08fe537e5b5f129caPeter Hallam
293b61a96e7ef1a78acf013bbf08fe537e5b5f129caPeter Hallam                            encodeKey(k, dOut);
294b61a96e7ef1a78acf013bbf08fe537e5b5f129caPeter Hallam
295b61a96e7ef1a78acf013bbf08fe537e5b5f129caPeter Hallam                            dOut.close();
296b61a96e7ef1a78acf013bbf08fe537e5b5f129caPeter Hallam
297b61a96e7ef1a78acf013bbf08fe537e5b5f129caPeter Hallam                            obj = bOut.toByteArray();
298b61a96e7ef1a78acf013bbf08fe537e5b5f129caPeter Hallam
299b61a96e7ef1a78acf013bbf08fe537e5b5f129caPeter Hallam                            return k;
300b61a96e7ef1a78acf013bbf08fe537e5b5f129caPeter Hallam                        }
301b61a96e7ef1a78acf013bbf08fe537e5b5f129caPeter Hallam                        else
302b61a96e7ef1a78acf013bbf08fe537e5b5f129caPeter Hallam                        {
303b61a96e7ef1a78acf013bbf08fe537e5b5f129caPeter Hallam                            throw new UnrecoverableKeyException("no match");
304b61a96e7ef1a78acf013bbf08fe537e5b5f129caPeter Hallam                        }
305b61a96e7ef1a78acf013bbf08fe537e5b5f129caPeter Hallam                    }
306b61a96e7ef1a78acf013bbf08fe537e5b5f129caPeter Hallam                }
307b61a96e7ef1a78acf013bbf08fe537e5b5f129caPeter Hallam                catch (Exception e)
308b61a96e7ef1a78acf013bbf08fe537e5b5f129caPeter Hallam                {
309b61a96e7ef1a78acf013bbf08fe537e5b5f129caPeter Hallam                    throw new UnrecoverableKeyException("no match");
310b61a96e7ef1a78acf013bbf08fe537e5b5f129caPeter Hallam                }
311b61a96e7ef1a78acf013bbf08fe537e5b5f129caPeter Hallam            }
312b61a96e7ef1a78acf013bbf08fe537e5b5f129caPeter Hallam            else
313b61a96e7ef1a78acf013bbf08fe537e5b5f129caPeter Hallam            {
314b61a96e7ef1a78acf013bbf08fe537e5b5f129caPeter Hallam                throw new RuntimeException("forget something!");
315b61a96e7ef1a78acf013bbf08fe537e5b5f129caPeter Hallam                // TODO
316b61a96e7ef1a78acf013bbf08fe537e5b5f129caPeter Hallam                // if we get to here key was saved as byte data, which
317b61a96e7ef1a78acf013bbf08fe537e5b5f129caPeter Hallam                // according to the docs means it must be a private key
318b61a96e7ef1a78acf013bbf08fe537e5b5f129caPeter Hallam                // in EncryptedPrivateKeyInfo (PKCS8 format), later...
319b61a96e7ef1a78acf013bbf08fe537e5b5f129caPeter Hallam                //
320b61a96e7ef1a78acf013bbf08fe537e5b5f129caPeter Hallam            }
321b61a96e7ef1a78acf013bbf08fe537e5b5f129caPeter Hallam        }
322b61a96e7ef1a78acf013bbf08fe537e5b5f129caPeter Hallam
323b61a96e7ef1a78acf013bbf08fe537e5b5f129caPeter Hallam        Certificate[] getCertificateChain()
324b61a96e7ef1a78acf013bbf08fe537e5b5f129caPeter Hallam        {
325b61a96e7ef1a78acf013bbf08fe537e5b5f129caPeter Hallam            return certChain;
326b61a96e7ef1a78acf013bbf08fe537e5b5f129caPeter Hallam        }
327b61a96e7ef1a78acf013bbf08fe537e5b5f129caPeter Hallam
328b61a96e7ef1a78acf013bbf08fe537e5b5f129caPeter Hallam        Date getDate()
329b61a96e7ef1a78acf013bbf08fe537e5b5f129caPeter Hallam        {
330b61a96e7ef1a78acf013bbf08fe537e5b5f129caPeter Hallam            return date;
331b61a96e7ef1a78acf013bbf08fe537e5b5f129caPeter Hallam        }
332b61a96e7ef1a78acf013bbf08fe537e5b5f129caPeter Hallam    }
333b61a96e7ef1a78acf013bbf08fe537e5b5f129caPeter Hallam
334b61a96e7ef1a78acf013bbf08fe537e5b5f129caPeter Hallam    private void encodeCertificate(
335b61a96e7ef1a78acf013bbf08fe537e5b5f129caPeter Hallam        Certificate         cert,
336b61a96e7ef1a78acf013bbf08fe537e5b5f129caPeter Hallam        DataOutputStream    dOut)
337b61a96e7ef1a78acf013bbf08fe537e5b5f129caPeter Hallam        throws IOException
338b61a96e7ef1a78acf013bbf08fe537e5b5f129caPeter Hallam    {
339b61a96e7ef1a78acf013bbf08fe537e5b5f129caPeter Hallam        try
340b61a96e7ef1a78acf013bbf08fe537e5b5f129caPeter Hallam        {
341b61a96e7ef1a78acf013bbf08fe537e5b5f129caPeter Hallam            byte[]      cEnc = cert.getEncoded();
342b61a96e7ef1a78acf013bbf08fe537e5b5f129caPeter Hallam
343b61a96e7ef1a78acf013bbf08fe537e5b5f129caPeter Hallam            dOut.writeUTF(cert.getType());
344b61a96e7ef1a78acf013bbf08fe537e5b5f129caPeter Hallam            dOut.writeInt(cEnc.length);
345b61a96e7ef1a78acf013bbf08fe537e5b5f129caPeter Hallam            dOut.write(cEnc);
346b61a96e7ef1a78acf013bbf08fe537e5b5f129caPeter Hallam        }
347b61a96e7ef1a78acf013bbf08fe537e5b5f129caPeter Hallam        catch (CertificateEncodingException ex)
348b61a96e7ef1a78acf013bbf08fe537e5b5f129caPeter Hallam        {
349b61a96e7ef1a78acf013bbf08fe537e5b5f129caPeter Hallam            throw new IOException(ex.toString());
350b61a96e7ef1a78acf013bbf08fe537e5b5f129caPeter Hallam        }
351b61a96e7ef1a78acf013bbf08fe537e5b5f129caPeter Hallam    }
352b61a96e7ef1a78acf013bbf08fe537e5b5f129caPeter Hallam
353b61a96e7ef1a78acf013bbf08fe537e5b5f129caPeter Hallam    private Certificate decodeCertificate(
354b61a96e7ef1a78acf013bbf08fe537e5b5f129caPeter Hallam        DataInputStream   dIn)
355b61a96e7ef1a78acf013bbf08fe537e5b5f129caPeter Hallam        throws IOException
356b61a96e7ef1a78acf013bbf08fe537e5b5f129caPeter Hallam    {
357b61a96e7ef1a78acf013bbf08fe537e5b5f129caPeter Hallam        String      type = dIn.readUTF();
358b61a96e7ef1a78acf013bbf08fe537e5b5f129caPeter Hallam        byte[]      cEnc = new byte[dIn.readInt()];
359b61a96e7ef1a78acf013bbf08fe537e5b5f129caPeter Hallam
360b61a96e7ef1a78acf013bbf08fe537e5b5f129caPeter Hallam        dIn.readFully(cEnc);
361b61a96e7ef1a78acf013bbf08fe537e5b5f129caPeter Hallam
362b61a96e7ef1a78acf013bbf08fe537e5b5f129caPeter Hallam        try
363b61a96e7ef1a78acf013bbf08fe537e5b5f129caPeter Hallam        {
3646e736056d64d0e33b26cf9f7c4e351b496241fdeBrian Carlstrom            CertificateFactory cFact = CertificateFactory.getInstance(type, BouncyCastleProvider.PROVIDER_NAME);
365b61a96e7ef1a78acf013bbf08fe537e5b5f129caPeter Hallam            ByteArrayInputStream bIn = new ByteArrayInputStream(cEnc);
366b61a96e7ef1a78acf013bbf08fe537e5b5f129caPeter Hallam
367b61a96e7ef1a78acf013bbf08fe537e5b5f129caPeter Hallam            return cFact.generateCertificate(bIn);
368b61a96e7ef1a78acf013bbf08fe537e5b5f129caPeter Hallam        }
369b61a96e7ef1a78acf013bbf08fe537e5b5f129caPeter Hallam        catch (NoSuchProviderException ex)
370b61a96e7ef1a78acf013bbf08fe537e5b5f129caPeter Hallam        {
371b61a96e7ef1a78acf013bbf08fe537e5b5f129caPeter Hallam            throw new IOException(ex.toString());
372b61a96e7ef1a78acf013bbf08fe537e5b5f129caPeter Hallam        }
373b61a96e7ef1a78acf013bbf08fe537e5b5f129caPeter Hallam        catch (CertificateException ex)
374b61a96e7ef1a78acf013bbf08fe537e5b5f129caPeter Hallam        {
375b61a96e7ef1a78acf013bbf08fe537e5b5f129caPeter Hallam            throw new IOException(ex.toString());
376b61a96e7ef1a78acf013bbf08fe537e5b5f129caPeter Hallam        }
377b61a96e7ef1a78acf013bbf08fe537e5b5f129caPeter Hallam    }
378b61a96e7ef1a78acf013bbf08fe537e5b5f129caPeter Hallam
379b61a96e7ef1a78acf013bbf08fe537e5b5f129caPeter Hallam    private void encodeKey(
380b61a96e7ef1a78acf013bbf08fe537e5b5f129caPeter Hallam        Key                 key,
381b61a96e7ef1a78acf013bbf08fe537e5b5f129caPeter Hallam        DataOutputStream    dOut)
382b61a96e7ef1a78acf013bbf08fe537e5b5f129caPeter Hallam        throws IOException
383b61a96e7ef1a78acf013bbf08fe537e5b5f129caPeter Hallam    {
384b61a96e7ef1a78acf013bbf08fe537e5b5f129caPeter Hallam        byte[]      enc = key.getEncoded();
385b61a96e7ef1a78acf013bbf08fe537e5b5f129caPeter Hallam
386b61a96e7ef1a78acf013bbf08fe537e5b5f129caPeter Hallam        if (key instanceof PrivateKey)
387b61a96e7ef1a78acf013bbf08fe537e5b5f129caPeter Hallam        {
388b61a96e7ef1a78acf013bbf08fe537e5b5f129caPeter Hallam            dOut.write(KEY_PRIVATE);
389b61a96e7ef1a78acf013bbf08fe537e5b5f129caPeter Hallam        }
390b61a96e7ef1a78acf013bbf08fe537e5b5f129caPeter Hallam        else if (key instanceof PublicKey)
391b61a96e7ef1a78acf013bbf08fe537e5b5f129caPeter Hallam        {
392b61a96e7ef1a78acf013bbf08fe537e5b5f129caPeter Hallam            dOut.write(KEY_PUBLIC);
393b61a96e7ef1a78acf013bbf08fe537e5b5f129caPeter Hallam        }
394b61a96e7ef1a78acf013bbf08fe537e5b5f129caPeter Hallam        else
395b61a96e7ef1a78acf013bbf08fe537e5b5f129caPeter Hallam        {
396b61a96e7ef1a78acf013bbf08fe537e5b5f129caPeter Hallam            dOut.write(KEY_SECRET);
397b61a96e7ef1a78acf013bbf08fe537e5b5f129caPeter Hallam        }
398b61a96e7ef1a78acf013bbf08fe537e5b5f129caPeter Hallam
399b61a96e7ef1a78acf013bbf08fe537e5b5f129caPeter Hallam        dOut.writeUTF(key.getFormat());
400b61a96e7ef1a78acf013bbf08fe537e5b5f129caPeter Hallam        dOut.writeUTF(key.getAlgorithm());
401b61a96e7ef1a78acf013bbf08fe537e5b5f129caPeter Hallam        dOut.writeInt(enc.length);
402b61a96e7ef1a78acf013bbf08fe537e5b5f129caPeter Hallam        dOut.write(enc);
403b61a96e7ef1a78acf013bbf08fe537e5b5f129caPeter Hallam    }
404b61a96e7ef1a78acf013bbf08fe537e5b5f129caPeter Hallam
405b61a96e7ef1a78acf013bbf08fe537e5b5f129caPeter Hallam    private Key decodeKey(
406b61a96e7ef1a78acf013bbf08fe537e5b5f129caPeter Hallam        DataInputStream dIn)
407b61a96e7ef1a78acf013bbf08fe537e5b5f129caPeter Hallam        throws IOException
408b61a96e7ef1a78acf013bbf08fe537e5b5f129caPeter Hallam    {
409b61a96e7ef1a78acf013bbf08fe537e5b5f129caPeter Hallam        int         keyType = dIn.read();
410b61a96e7ef1a78acf013bbf08fe537e5b5f129caPeter Hallam        String      format = dIn.readUTF();
411b61a96e7ef1a78acf013bbf08fe537e5b5f129caPeter Hallam        String      algorithm = dIn.readUTF();
412b61a96e7ef1a78acf013bbf08fe537e5b5f129caPeter Hallam        byte[]      enc = new byte[dIn.readInt()];
413b61a96e7ef1a78acf013bbf08fe537e5b5f129caPeter Hallam        KeySpec     spec;
414b61a96e7ef1a78acf013bbf08fe537e5b5f129caPeter Hallam
415b61a96e7ef1a78acf013bbf08fe537e5b5f129caPeter Hallam        dIn.readFully(enc);
416b61a96e7ef1a78acf013bbf08fe537e5b5f129caPeter Hallam
417b61a96e7ef1a78acf013bbf08fe537e5b5f129caPeter Hallam        if (format.equals("PKCS#8") || format.equals("PKCS8"))
418b61a96e7ef1a78acf013bbf08fe537e5b5f129caPeter Hallam        {
419b61a96e7ef1a78acf013bbf08fe537e5b5f129caPeter Hallam            spec = new PKCS8EncodedKeySpec(enc);
420b61a96e7ef1a78acf013bbf08fe537e5b5f129caPeter Hallam        }
421b61a96e7ef1a78acf013bbf08fe537e5b5f129caPeter Hallam        else if (format.equals("X.509") || format.equals("X509"))
422b61a96e7ef1a78acf013bbf08fe537e5b5f129caPeter Hallam        {
423b61a96e7ef1a78acf013bbf08fe537e5b5f129caPeter Hallam            spec = new X509EncodedKeySpec(enc);
424b61a96e7ef1a78acf013bbf08fe537e5b5f129caPeter Hallam        }
425b61a96e7ef1a78acf013bbf08fe537e5b5f129caPeter Hallam        else if (format.equals("RAW"))
426b61a96e7ef1a78acf013bbf08fe537e5b5f129caPeter Hallam        {
427b61a96e7ef1a78acf013bbf08fe537e5b5f129caPeter Hallam            return new SecretKeySpec(enc, algorithm);
428b61a96e7ef1a78acf013bbf08fe537e5b5f129caPeter Hallam        }
429b61a96e7ef1a78acf013bbf08fe537e5b5f129caPeter Hallam        else
430b61a96e7ef1a78acf013bbf08fe537e5b5f129caPeter Hallam        {
431b61a96e7ef1a78acf013bbf08fe537e5b5f129caPeter Hallam            throw new IOException("Key format " + format + " not recognised!");
432b61a96e7ef1a78acf013bbf08fe537e5b5f129caPeter Hallam        }
433b61a96e7ef1a78acf013bbf08fe537e5b5f129caPeter Hallam
434b61a96e7ef1a78acf013bbf08fe537e5b5f129caPeter Hallam        try
435b61a96e7ef1a78acf013bbf08fe537e5b5f129caPeter Hallam        {
436b61a96e7ef1a78acf013bbf08fe537e5b5f129caPeter Hallam            switch (keyType)
437b61a96e7ef1a78acf013bbf08fe537e5b5f129caPeter Hallam            {
438b61a96e7ef1a78acf013bbf08fe537e5b5f129caPeter Hallam            case KEY_PRIVATE:
4396e736056d64d0e33b26cf9f7c4e351b496241fdeBrian Carlstrom                return KeyFactory.getInstance(algorithm, BouncyCastleProvider.PROVIDER_NAME).generatePrivate(spec);
440b61a96e7ef1a78acf013bbf08fe537e5b5f129caPeter Hallam            case KEY_PUBLIC:
4416e736056d64d0e33b26cf9f7c4e351b496241fdeBrian Carlstrom                return KeyFactory.getInstance(algorithm, BouncyCastleProvider.PROVIDER_NAME).generatePublic(spec);
442b61a96e7ef1a78acf013bbf08fe537e5b5f129caPeter Hallam            case KEY_SECRET:
4436e736056d64d0e33b26cf9f7c4e351b496241fdeBrian Carlstrom                return SecretKeyFactory.getInstance(algorithm, BouncyCastleProvider.PROVIDER_NAME).generateSecret(spec);
444b61a96e7ef1a78acf013bbf08fe537e5b5f129caPeter Hallam            default:
445b61a96e7ef1a78acf013bbf08fe537e5b5f129caPeter Hallam                throw new IOException("Key type " + keyType + " not recognised!");
446b61a96e7ef1a78acf013bbf08fe537e5b5f129caPeter Hallam            }
447b61a96e7ef1a78acf013bbf08fe537e5b5f129caPeter Hallam        }
448b61a96e7ef1a78acf013bbf08fe537e5b5f129caPeter Hallam        catch (Exception e)
449b61a96e7ef1a78acf013bbf08fe537e5b5f129caPeter Hallam        {
450b61a96e7ef1a78acf013bbf08fe537e5b5f129caPeter Hallam            throw new IOException("Exception creating key: " + e.toString());
451b61a96e7ef1a78acf013bbf08fe537e5b5f129caPeter Hallam        }
452b61a96e7ef1a78acf013bbf08fe537e5b5f129caPeter Hallam    }
453b61a96e7ef1a78acf013bbf08fe537e5b5f129caPeter Hallam
454b61a96e7ef1a78acf013bbf08fe537e5b5f129caPeter Hallam    protected Cipher makePBECipher(
455b61a96e7ef1a78acf013bbf08fe537e5b5f129caPeter Hallam        String  algorithm,
456b61a96e7ef1a78acf013bbf08fe537e5b5f129caPeter Hallam        int     mode,
457b61a96e7ef1a78acf013bbf08fe537e5b5f129caPeter Hallam        char[]  password,
458b61a96e7ef1a78acf013bbf08fe537e5b5f129caPeter Hallam        byte[]  salt,
459b61a96e7ef1a78acf013bbf08fe537e5b5f129caPeter Hallam        int     iterationCount)
460b61a96e7ef1a78acf013bbf08fe537e5b5f129caPeter Hallam        throws IOException
461b61a96e7ef1a78acf013bbf08fe537e5b5f129caPeter Hallam    {
462b61a96e7ef1a78acf013bbf08fe537e5b5f129caPeter Hallam        try
463b61a96e7ef1a78acf013bbf08fe537e5b5f129caPeter Hallam        {
464b61a96e7ef1a78acf013bbf08fe537e5b5f129caPeter Hallam            PBEKeySpec          pbeSpec = new PBEKeySpec(password);
4656e736056d64d0e33b26cf9f7c4e351b496241fdeBrian Carlstrom            SecretKeyFactory    keyFact = SecretKeyFactory.getInstance(algorithm, BouncyCastleProvider.PROVIDER_NAME);
466b61a96e7ef1a78acf013bbf08fe537e5b5f129caPeter Hallam            PBEParameterSpec    defParams = new PBEParameterSpec(salt, iterationCount);
467b61a96e7ef1a78acf013bbf08fe537e5b5f129caPeter Hallam
4686e736056d64d0e33b26cf9f7c4e351b496241fdeBrian Carlstrom            Cipher cipher = Cipher.getInstance(algorithm, BouncyCastleProvider.PROVIDER_NAME);
469b61a96e7ef1a78acf013bbf08fe537e5b5f129caPeter Hallam
470b61a96e7ef1a78acf013bbf08fe537e5b5f129caPeter Hallam            cipher.init(mode, keyFact.generateSecret(pbeSpec), defParams);
471b61a96e7ef1a78acf013bbf08fe537e5b5f129caPeter Hallam
472b61a96e7ef1a78acf013bbf08fe537e5b5f129caPeter Hallam            return cipher;
473b61a96e7ef1a78acf013bbf08fe537e5b5f129caPeter Hallam        }
474b61a96e7ef1a78acf013bbf08fe537e5b5f129caPeter Hallam        catch (Exception e)
475b61a96e7ef1a78acf013bbf08fe537e5b5f129caPeter Hallam        {
476b61a96e7ef1a78acf013bbf08fe537e5b5f129caPeter Hallam            throw new IOException("Error initialising store of key store: " + e);
477b61a96e7ef1a78acf013bbf08fe537e5b5f129caPeter Hallam        }
478b61a96e7ef1a78acf013bbf08fe537e5b5f129caPeter Hallam    }
479b61a96e7ef1a78acf013bbf08fe537e5b5f129caPeter Hallam
480b61a96e7ef1a78acf013bbf08fe537e5b5f129caPeter Hallam    public void setRandom(
481b61a96e7ef1a78acf013bbf08fe537e5b5f129caPeter Hallam            SecureRandom    rand)
482b61a96e7ef1a78acf013bbf08fe537e5b5f129caPeter Hallam    {
483b61a96e7ef1a78acf013bbf08fe537e5b5f129caPeter Hallam        this.random = rand;
484b61a96e7ef1a78acf013bbf08fe537e5b5f129caPeter Hallam    }
485b61a96e7ef1a78acf013bbf08fe537e5b5f129caPeter Hallam
486b61a96e7ef1a78acf013bbf08fe537e5b5f129caPeter Hallam    public Enumeration engineAliases()
487b61a96e7ef1a78acf013bbf08fe537e5b5f129caPeter Hallam    {
488b61a96e7ef1a78acf013bbf08fe537e5b5f129caPeter Hallam        return table.keys();
489b61a96e7ef1a78acf013bbf08fe537e5b5f129caPeter Hallam    }
490b61a96e7ef1a78acf013bbf08fe537e5b5f129caPeter Hallam
491b61a96e7ef1a78acf013bbf08fe537e5b5f129caPeter Hallam    public boolean engineContainsAlias(
492b61a96e7ef1a78acf013bbf08fe537e5b5f129caPeter Hallam        String  alias)
493b61a96e7ef1a78acf013bbf08fe537e5b5f129caPeter Hallam    {
494b61a96e7ef1a78acf013bbf08fe537e5b5f129caPeter Hallam        return (table.get(alias) != null);
495b61a96e7ef1a78acf013bbf08fe537e5b5f129caPeter Hallam    }
496b61a96e7ef1a78acf013bbf08fe537e5b5f129caPeter Hallam
497b61a96e7ef1a78acf013bbf08fe537e5b5f129caPeter Hallam    public void engineDeleteEntry(
498b61a96e7ef1a78acf013bbf08fe537e5b5f129caPeter Hallam        String  alias)
499b61a96e7ef1a78acf013bbf08fe537e5b5f129caPeter Hallam        throws KeyStoreException
500b61a96e7ef1a78acf013bbf08fe537e5b5f129caPeter Hallam    {
501b61a96e7ef1a78acf013bbf08fe537e5b5f129caPeter Hallam        Object  entry = table.get(alias);
502b61a96e7ef1a78acf013bbf08fe537e5b5f129caPeter Hallam
503b61a96e7ef1a78acf013bbf08fe537e5b5f129caPeter Hallam        if (entry == null)
504b61a96e7ef1a78acf013bbf08fe537e5b5f129caPeter Hallam        {
5058e551503a8d09fb57fd4efe9a2aa0392e7ba56e9Brian Carlstrom            return;
506b61a96e7ef1a78acf013bbf08fe537e5b5f129caPeter Hallam        }
507b61a96e7ef1a78acf013bbf08fe537e5b5f129caPeter Hallam
508b61a96e7ef1a78acf013bbf08fe537e5b5f129caPeter Hallam        table.remove(alias);
509b61a96e7ef1a78acf013bbf08fe537e5b5f129caPeter Hallam    }
510b61a96e7ef1a78acf013bbf08fe537e5b5f129caPeter Hallam
511b61a96e7ef1a78acf013bbf08fe537e5b5f129caPeter Hallam    public Certificate engineGetCertificate(
512b61a96e7ef1a78acf013bbf08fe537e5b5f129caPeter Hallam        String alias)
513b61a96e7ef1a78acf013bbf08fe537e5b5f129caPeter Hallam    {
514b61a96e7ef1a78acf013bbf08fe537e5b5f129caPeter Hallam        StoreEntry  entry = (StoreEntry)table.get(alias);
515b61a96e7ef1a78acf013bbf08fe537e5b5f129caPeter Hallam
516b61a96e7ef1a78acf013bbf08fe537e5b5f129caPeter Hallam        if (entry != null)
517b61a96e7ef1a78acf013bbf08fe537e5b5f129caPeter Hallam        {
518b61a96e7ef1a78acf013bbf08fe537e5b5f129caPeter Hallam            if (entry.getType() == CERTIFICATE)
519b61a96e7ef1a78acf013bbf08fe537e5b5f129caPeter Hallam            {
520b61a96e7ef1a78acf013bbf08fe537e5b5f129caPeter Hallam                return (Certificate)entry.getObject();
521b61a96e7ef1a78acf013bbf08fe537e5b5f129caPeter Hallam            }
522b61a96e7ef1a78acf013bbf08fe537e5b5f129caPeter Hallam            else
523b61a96e7ef1a78acf013bbf08fe537e5b5f129caPeter Hallam            {
524b61a96e7ef1a78acf013bbf08fe537e5b5f129caPeter Hallam                Certificate[]   chain = entry.getCertificateChain();
525b61a96e7ef1a78acf013bbf08fe537e5b5f129caPeter Hallam
526b61a96e7ef1a78acf013bbf08fe537e5b5f129caPeter Hallam                if (chain != null)
527b61a96e7ef1a78acf013bbf08fe537e5b5f129caPeter Hallam                {
528b61a96e7ef1a78acf013bbf08fe537e5b5f129caPeter Hallam                    return chain[0];
529b61a96e7ef1a78acf013bbf08fe537e5b5f129caPeter Hallam                }
530b61a96e7ef1a78acf013bbf08fe537e5b5f129caPeter Hallam            }
531b61a96e7ef1a78acf013bbf08fe537e5b5f129caPeter Hallam        }
532b61a96e7ef1a78acf013bbf08fe537e5b5f129caPeter Hallam
533b61a96e7ef1a78acf013bbf08fe537e5b5f129caPeter Hallam        return null;
534b61a96e7ef1a78acf013bbf08fe537e5b5f129caPeter Hallam    }
535b61a96e7ef1a78acf013bbf08fe537e5b5f129caPeter Hallam
536b61a96e7ef1a78acf013bbf08fe537e5b5f129caPeter Hallam    public String engineGetCertificateAlias(
537b61a96e7ef1a78acf013bbf08fe537e5b5f129caPeter Hallam        Certificate cert)
538b61a96e7ef1a78acf013bbf08fe537e5b5f129caPeter Hallam    {
539b61a96e7ef1a78acf013bbf08fe537e5b5f129caPeter Hallam        Enumeration e = table.elements();
540b61a96e7ef1a78acf013bbf08fe537e5b5f129caPeter Hallam        while (e.hasMoreElements())
541b61a96e7ef1a78acf013bbf08fe537e5b5f129caPeter Hallam        {
542b61a96e7ef1a78acf013bbf08fe537e5b5f129caPeter Hallam            StoreEntry  entry = (StoreEntry)e.nextElement();
543b61a96e7ef1a78acf013bbf08fe537e5b5f129caPeter Hallam
544b61a96e7ef1a78acf013bbf08fe537e5b5f129caPeter Hallam            if (entry.getObject() instanceof Certificate)
545b61a96e7ef1a78acf013bbf08fe537e5b5f129caPeter Hallam            {
546b61a96e7ef1a78acf013bbf08fe537e5b5f129caPeter Hallam                Certificate c = (Certificate)entry.getObject();
547b61a96e7ef1a78acf013bbf08fe537e5b5f129caPeter Hallam
548b61a96e7ef1a78acf013bbf08fe537e5b5f129caPeter Hallam                if (c.equals(cert))
549b61a96e7ef1a78acf013bbf08fe537e5b5f129caPeter Hallam                {
550b61a96e7ef1a78acf013bbf08fe537e5b5f129caPeter Hallam                    return entry.getAlias();
551b61a96e7ef1a78acf013bbf08fe537e5b5f129caPeter Hallam                }
552b61a96e7ef1a78acf013bbf08fe537e5b5f129caPeter Hallam            }
553b61a96e7ef1a78acf013bbf08fe537e5b5f129caPeter Hallam            else
554b61a96e7ef1a78acf013bbf08fe537e5b5f129caPeter Hallam            {
555b61a96e7ef1a78acf013bbf08fe537e5b5f129caPeter Hallam                Certificate[]   chain = entry.getCertificateChain();
556b61a96e7ef1a78acf013bbf08fe537e5b5f129caPeter Hallam
557b61a96e7ef1a78acf013bbf08fe537e5b5f129caPeter Hallam                if (chain != null && chain[0].equals(cert))
558b61a96e7ef1a78acf013bbf08fe537e5b5f129caPeter Hallam                {
559b61a96e7ef1a78acf013bbf08fe537e5b5f129caPeter Hallam                    return entry.getAlias();
560b61a96e7ef1a78acf013bbf08fe537e5b5f129caPeter Hallam                }
561b61a96e7ef1a78acf013bbf08fe537e5b5f129caPeter Hallam            }
562b61a96e7ef1a78acf013bbf08fe537e5b5f129caPeter Hallam        }
563b61a96e7ef1a78acf013bbf08fe537e5b5f129caPeter Hallam
564b61a96e7ef1a78acf013bbf08fe537e5b5f129caPeter Hallam        return null;
565b61a96e7ef1a78acf013bbf08fe537e5b5f129caPeter Hallam    }
566b61a96e7ef1a78acf013bbf08fe537e5b5f129caPeter Hallam
567b61a96e7ef1a78acf013bbf08fe537e5b5f129caPeter Hallam    public Certificate[] engineGetCertificateChain(
568b61a96e7ef1a78acf013bbf08fe537e5b5f129caPeter Hallam        String alias)
569b61a96e7ef1a78acf013bbf08fe537e5b5f129caPeter Hallam    {
570b61a96e7ef1a78acf013bbf08fe537e5b5f129caPeter Hallam        StoreEntry  entry = (StoreEntry)table.get(alias);
571b61a96e7ef1a78acf013bbf08fe537e5b5f129caPeter Hallam
572b61a96e7ef1a78acf013bbf08fe537e5b5f129caPeter Hallam        if (entry != null)
573b61a96e7ef1a78acf013bbf08fe537e5b5f129caPeter Hallam        {
574b61a96e7ef1a78acf013bbf08fe537e5b5f129caPeter Hallam            return entry.getCertificateChain();
575b61a96e7ef1a78acf013bbf08fe537e5b5f129caPeter Hallam        }
576b61a96e7ef1a78acf013bbf08fe537e5b5f129caPeter Hallam
577b61a96e7ef1a78acf013bbf08fe537e5b5f129caPeter Hallam        return null;
578b61a96e7ef1a78acf013bbf08fe537e5b5f129caPeter Hallam    }
579b61a96e7ef1a78acf013bbf08fe537e5b5f129caPeter Hallam
580b61a96e7ef1a78acf013bbf08fe537e5b5f129caPeter Hallam    public Date engineGetCreationDate(String alias)
581b61a96e7ef1a78acf013bbf08fe537e5b5f129caPeter Hallam    {
582b61a96e7ef1a78acf013bbf08fe537e5b5f129caPeter Hallam        StoreEntry  entry = (StoreEntry)table.get(alias);
583b61a96e7ef1a78acf013bbf08fe537e5b5f129caPeter Hallam
584b61a96e7ef1a78acf013bbf08fe537e5b5f129caPeter Hallam        if (entry != null)
585b61a96e7ef1a78acf013bbf08fe537e5b5f129caPeter Hallam        {
586b61a96e7ef1a78acf013bbf08fe537e5b5f129caPeter Hallam            return entry.getDate();
587b61a96e7ef1a78acf013bbf08fe537e5b5f129caPeter Hallam        }
588b61a96e7ef1a78acf013bbf08fe537e5b5f129caPeter Hallam
589b61a96e7ef1a78acf013bbf08fe537e5b5f129caPeter Hallam        return null;
590b61a96e7ef1a78acf013bbf08fe537e5b5f129caPeter Hallam    }
591b61a96e7ef1a78acf013bbf08fe537e5b5f129caPeter Hallam
592b61a96e7ef1a78acf013bbf08fe537e5b5f129caPeter Hallam    public Key engineGetKey(
593b61a96e7ef1a78acf013bbf08fe537e5b5f129caPeter Hallam        String alias,
594b61a96e7ef1a78acf013bbf08fe537e5b5f129caPeter Hallam        char[] password)
595b61a96e7ef1a78acf013bbf08fe537e5b5f129caPeter Hallam        throws NoSuchAlgorithmException, UnrecoverableKeyException
596b61a96e7ef1a78acf013bbf08fe537e5b5f129caPeter Hallam    {
597b61a96e7ef1a78acf013bbf08fe537e5b5f129caPeter Hallam        StoreEntry  entry = (StoreEntry)table.get(alias);
598b61a96e7ef1a78acf013bbf08fe537e5b5f129caPeter Hallam
599b61a96e7ef1a78acf013bbf08fe537e5b5f129caPeter Hallam        if (entry == null || entry.getType() == CERTIFICATE)
600b61a96e7ef1a78acf013bbf08fe537e5b5f129caPeter Hallam        {
601b61a96e7ef1a78acf013bbf08fe537e5b5f129caPeter Hallam            return null;
602b61a96e7ef1a78acf013bbf08fe537e5b5f129caPeter Hallam        }
603b61a96e7ef1a78acf013bbf08fe537e5b5f129caPeter Hallam
604b61a96e7ef1a78acf013bbf08fe537e5b5f129caPeter Hallam        return (Key)entry.getObject(password);
605b61a96e7ef1a78acf013bbf08fe537e5b5f129caPeter Hallam    }
606b61a96e7ef1a78acf013bbf08fe537e5b5f129caPeter Hallam
607b61a96e7ef1a78acf013bbf08fe537e5b5f129caPeter Hallam    public boolean engineIsCertificateEntry(
608b61a96e7ef1a78acf013bbf08fe537e5b5f129caPeter Hallam        String alias)
609b61a96e7ef1a78acf013bbf08fe537e5b5f129caPeter Hallam    {
610b61a96e7ef1a78acf013bbf08fe537e5b5f129caPeter Hallam        StoreEntry  entry = (StoreEntry)table.get(alias);
611b61a96e7ef1a78acf013bbf08fe537e5b5f129caPeter Hallam
612b61a96e7ef1a78acf013bbf08fe537e5b5f129caPeter Hallam        if (entry != null && entry.getType() == CERTIFICATE)
613b61a96e7ef1a78acf013bbf08fe537e5b5f129caPeter Hallam        {
614b61a96e7ef1a78acf013bbf08fe537e5b5f129caPeter Hallam            return true;
615b61a96e7ef1a78acf013bbf08fe537e5b5f129caPeter Hallam        }
616b61a96e7ef1a78acf013bbf08fe537e5b5f129caPeter Hallam
617b61a96e7ef1a78acf013bbf08fe537e5b5f129caPeter Hallam        return false;
618b61a96e7ef1a78acf013bbf08fe537e5b5f129caPeter Hallam    }
619b61a96e7ef1a78acf013bbf08fe537e5b5f129caPeter Hallam
620b61a96e7ef1a78acf013bbf08fe537e5b5f129caPeter Hallam    public boolean engineIsKeyEntry(
621b61a96e7ef1a78acf013bbf08fe537e5b5f129caPeter Hallam        String alias)
622b61a96e7ef1a78acf013bbf08fe537e5b5f129caPeter Hallam    {
623b61a96e7ef1a78acf013bbf08fe537e5b5f129caPeter Hallam        StoreEntry  entry = (StoreEntry)table.get(alias);
624b61a96e7ef1a78acf013bbf08fe537e5b5f129caPeter Hallam
625b61a96e7ef1a78acf013bbf08fe537e5b5f129caPeter Hallam        if (entry != null && entry.getType() != CERTIFICATE)
626b61a96e7ef1a78acf013bbf08fe537e5b5f129caPeter Hallam        {
627b61a96e7ef1a78acf013bbf08fe537e5b5f129caPeter Hallam            return true;
628b61a96e7ef1a78acf013bbf08fe537e5b5f129caPeter Hallam        }
629b61a96e7ef1a78acf013bbf08fe537e5b5f129caPeter Hallam
630b61a96e7ef1a78acf013bbf08fe537e5b5f129caPeter Hallam        return false;
631b61a96e7ef1a78acf013bbf08fe537e5b5f129caPeter Hallam    }
632b61a96e7ef1a78acf013bbf08fe537e5b5f129caPeter Hallam
633b61a96e7ef1a78acf013bbf08fe537e5b5f129caPeter Hallam    public void engineSetCertificateEntry(
634b61a96e7ef1a78acf013bbf08fe537e5b5f129caPeter Hallam        String      alias,
635b61a96e7ef1a78acf013bbf08fe537e5b5f129caPeter Hallam        Certificate cert)
636b61a96e7ef1a78acf013bbf08fe537e5b5f129caPeter Hallam        throws KeyStoreException
637b61a96e7ef1a78acf013bbf08fe537e5b5f129caPeter Hallam    {
638b61a96e7ef1a78acf013bbf08fe537e5b5f129caPeter Hallam        StoreEntry  entry = (StoreEntry)table.get(alias);
639b61a96e7ef1a78acf013bbf08fe537e5b5f129caPeter Hallam
640b61a96e7ef1a78acf013bbf08fe537e5b5f129caPeter Hallam        if (entry != null && entry.getType() != CERTIFICATE)
641b61a96e7ef1a78acf013bbf08fe537e5b5f129caPeter Hallam        {
642c37f4a04ef89e73a39a59f3c5a179af8c8ab5974Brian Carlstrom            throw new KeyStoreException("key store already has a key entry with alias " + alias);
643b61a96e7ef1a78acf013bbf08fe537e5b5f129caPeter Hallam        }
644b61a96e7ef1a78acf013bbf08fe537e5b5f129caPeter Hallam
645b61a96e7ef1a78acf013bbf08fe537e5b5f129caPeter Hallam        table.put(alias, new StoreEntry(alias, cert));
646b61a96e7ef1a78acf013bbf08fe537e5b5f129caPeter Hallam    }
647b61a96e7ef1a78acf013bbf08fe537e5b5f129caPeter Hallam
648b61a96e7ef1a78acf013bbf08fe537e5b5f129caPeter Hallam    public void engineSetKeyEntry(
649b61a96e7ef1a78acf013bbf08fe537e5b5f129caPeter Hallam        String alias,
650b61a96e7ef1a78acf013bbf08fe537e5b5f129caPeter Hallam        byte[] key,
651b61a96e7ef1a78acf013bbf08fe537e5b5f129caPeter Hallam        Certificate[] chain)
652b61a96e7ef1a78acf013bbf08fe537e5b5f129caPeter Hallam        throws KeyStoreException
653b61a96e7ef1a78acf013bbf08fe537e5b5f129caPeter Hallam    {
654b61a96e7ef1a78acf013bbf08fe537e5b5f129caPeter Hallam        table.put(alias, new StoreEntry(alias, key, chain));
655b61a96e7ef1a78acf013bbf08fe537e5b5f129caPeter Hallam    }
656b61a96e7ef1a78acf013bbf08fe537e5b5f129caPeter Hallam
657b61a96e7ef1a78acf013bbf08fe537e5b5f129caPeter Hallam    public void engineSetKeyEntry(
658b61a96e7ef1a78acf013bbf08fe537e5b5f129caPeter Hallam        String          alias,
659b61a96e7ef1a78acf013bbf08fe537e5b5f129caPeter Hallam        Key             key,
660b61a96e7ef1a78acf013bbf08fe537e5b5f129caPeter Hallam        char[]          password,
661b61a96e7ef1a78acf013bbf08fe537e5b5f129caPeter Hallam        Certificate[]   chain)
662b61a96e7ef1a78acf013bbf08fe537e5b5f129caPeter Hallam        throws KeyStoreException
663b61a96e7ef1a78acf013bbf08fe537e5b5f129caPeter Hallam    {
664b61a96e7ef1a78acf013bbf08fe537e5b5f129caPeter Hallam        if ((key instanceof PrivateKey) && (chain == null))
665b61a96e7ef1a78acf013bbf08fe537e5b5f129caPeter Hallam        {
666b61a96e7ef1a78acf013bbf08fe537e5b5f129caPeter Hallam            throw new KeyStoreException("no certificate chain for private key");
667b61a96e7ef1a78acf013bbf08fe537e5b5f129caPeter Hallam        }
668b61a96e7ef1a78acf013bbf08fe537e5b5f129caPeter Hallam
669b61a96e7ef1a78acf013bbf08fe537e5b5f129caPeter Hallam        try
670b61a96e7ef1a78acf013bbf08fe537e5b5f129caPeter Hallam        {
671b61a96e7ef1a78acf013bbf08fe537e5b5f129caPeter Hallam            table.put(alias, new StoreEntry(alias, key, password, chain));
672b61a96e7ef1a78acf013bbf08fe537e5b5f129caPeter Hallam        }
673b61a96e7ef1a78acf013bbf08fe537e5b5f129caPeter Hallam        catch (Exception e)
674b61a96e7ef1a78acf013bbf08fe537e5b5f129caPeter Hallam        {
675b61a96e7ef1a78acf013bbf08fe537e5b5f129caPeter Hallam            throw new KeyStoreException(e.toString());
676b61a96e7ef1a78acf013bbf08fe537e5b5f129caPeter Hallam        }
677b61a96e7ef1a78acf013bbf08fe537e5b5f129caPeter Hallam    }
678b61a96e7ef1a78acf013bbf08fe537e5b5f129caPeter Hallam
679b61a96e7ef1a78acf013bbf08fe537e5b5f129caPeter Hallam    public int engineSize()
680b61a96e7ef1a78acf013bbf08fe537e5b5f129caPeter Hallam    {
681b61a96e7ef1a78acf013bbf08fe537e5b5f129caPeter Hallam        return table.size();
682b61a96e7ef1a78acf013bbf08fe537e5b5f129caPeter Hallam    }
683b61a96e7ef1a78acf013bbf08fe537e5b5f129caPeter Hallam
684b61a96e7ef1a78acf013bbf08fe537e5b5f129caPeter Hallam    protected void loadStore(
685b61a96e7ef1a78acf013bbf08fe537e5b5f129caPeter Hallam        InputStream in)
686b61a96e7ef1a78acf013bbf08fe537e5b5f129caPeter Hallam        throws IOException
687b61a96e7ef1a78acf013bbf08fe537e5b5f129caPeter Hallam    {
688b61a96e7ef1a78acf013bbf08fe537e5b5f129caPeter Hallam        DataInputStream     dIn = new DataInputStream(in);
689b61a96e7ef1a78acf013bbf08fe537e5b5f129caPeter Hallam        int                 type = dIn.read();
690b61a96e7ef1a78acf013bbf08fe537e5b5f129caPeter Hallam
691b61a96e7ef1a78acf013bbf08fe537e5b5f129caPeter Hallam        while (type > NULL)
692b61a96e7ef1a78acf013bbf08fe537e5b5f129caPeter Hallam        {
693b61a96e7ef1a78acf013bbf08fe537e5b5f129caPeter Hallam            String          alias = dIn.readUTF();
694b61a96e7ef1a78acf013bbf08fe537e5b5f129caPeter Hallam            Date            date = new Date(dIn.readLong());
695b61a96e7ef1a78acf013bbf08fe537e5b5f129caPeter Hallam            int             chainLength = dIn.readInt();
696b61a96e7ef1a78acf013bbf08fe537e5b5f129caPeter Hallam            Certificate[]   chain = null;
697b61a96e7ef1a78acf013bbf08fe537e5b5f129caPeter Hallam
698b61a96e7ef1a78acf013bbf08fe537e5b5f129caPeter Hallam            if (chainLength != 0)
699b61a96e7ef1a78acf013bbf08fe537e5b5f129caPeter Hallam            {
700b61a96e7ef1a78acf013bbf08fe537e5b5f129caPeter Hallam                chain = new Certificate[chainLength];
701b61a96e7ef1a78acf013bbf08fe537e5b5f129caPeter Hallam
702b61a96e7ef1a78acf013bbf08fe537e5b5f129caPeter Hallam                for (int i = 0; i != chainLength; i++)
703b61a96e7ef1a78acf013bbf08fe537e5b5f129caPeter Hallam                {
704b61a96e7ef1a78acf013bbf08fe537e5b5f129caPeter Hallam                    chain[i] = decodeCertificate(dIn);
705b61a96e7ef1a78acf013bbf08fe537e5b5f129caPeter Hallam                }
706b61a96e7ef1a78acf013bbf08fe537e5b5f129caPeter Hallam            }
707b61a96e7ef1a78acf013bbf08fe537e5b5f129caPeter Hallam
708b61a96e7ef1a78acf013bbf08fe537e5b5f129caPeter Hallam            switch (type)
709b61a96e7ef1a78acf013bbf08fe537e5b5f129caPeter Hallam            {
710b61a96e7ef1a78acf013bbf08fe537e5b5f129caPeter Hallam            case CERTIFICATE:
711b61a96e7ef1a78acf013bbf08fe537e5b5f129caPeter Hallam                    Certificate     cert = decodeCertificate(dIn);
712b61a96e7ef1a78acf013bbf08fe537e5b5f129caPeter Hallam
713b61a96e7ef1a78acf013bbf08fe537e5b5f129caPeter Hallam                    table.put(alias, new StoreEntry(alias, date, CERTIFICATE, cert));
714b61a96e7ef1a78acf013bbf08fe537e5b5f129caPeter Hallam                    break;
715b61a96e7ef1a78acf013bbf08fe537e5b5f129caPeter Hallam            case KEY:
716b61a96e7ef1a78acf013bbf08fe537e5b5f129caPeter Hallam                    Key     key = decodeKey(dIn);
717b61a96e7ef1a78acf013bbf08fe537e5b5f129caPeter Hallam                    table.put(alias, new StoreEntry(alias, date, KEY, key, chain));
718b61a96e7ef1a78acf013bbf08fe537e5b5f129caPeter Hallam                    break;
719b61a96e7ef1a78acf013bbf08fe537e5b5f129caPeter Hallam            case SECRET:
720b61a96e7ef1a78acf013bbf08fe537e5b5f129caPeter Hallam            case SEALED:
721b61a96e7ef1a78acf013bbf08fe537e5b5f129caPeter Hallam                    byte[]      b = new byte[dIn.readInt()];
722b61a96e7ef1a78acf013bbf08fe537e5b5f129caPeter Hallam
723b61a96e7ef1a78acf013bbf08fe537e5b5f129caPeter Hallam                    dIn.readFully(b);
724b61a96e7ef1a78acf013bbf08fe537e5b5f129caPeter Hallam                    table.put(alias, new StoreEntry(alias, date, type, b, chain));
725b61a96e7ef1a78acf013bbf08fe537e5b5f129caPeter Hallam                    break;
726b61a96e7ef1a78acf013bbf08fe537e5b5f129caPeter Hallam            default:
727b61a96e7ef1a78acf013bbf08fe537e5b5f129caPeter Hallam                    throw new RuntimeException("Unknown object type in store.");
728b61a96e7ef1a78acf013bbf08fe537e5b5f129caPeter Hallam            }
729b61a96e7ef1a78acf013bbf08fe537e5b5f129caPeter Hallam
730b61a96e7ef1a78acf013bbf08fe537e5b5f129caPeter Hallam            type = dIn.read();
731b61a96e7ef1a78acf013bbf08fe537e5b5f129caPeter Hallam        }
732b61a96e7ef1a78acf013bbf08fe537e5b5f129caPeter Hallam    }
733b61a96e7ef1a78acf013bbf08fe537e5b5f129caPeter Hallam
734b61a96e7ef1a78acf013bbf08fe537e5b5f129caPeter Hallam    protected void saveStore(
735b61a96e7ef1a78acf013bbf08fe537e5b5f129caPeter Hallam        OutputStream    out)
736b61a96e7ef1a78acf013bbf08fe537e5b5f129caPeter Hallam        throws IOException
737b61a96e7ef1a78acf013bbf08fe537e5b5f129caPeter Hallam    {
738b61a96e7ef1a78acf013bbf08fe537e5b5f129caPeter Hallam        Enumeration         e = table.elements();
739b61a96e7ef1a78acf013bbf08fe537e5b5f129caPeter Hallam        DataOutputStream    dOut = new DataOutputStream(out);
740b61a96e7ef1a78acf013bbf08fe537e5b5f129caPeter Hallam
741b61a96e7ef1a78acf013bbf08fe537e5b5f129caPeter Hallam        while (e.hasMoreElements())
742b61a96e7ef1a78acf013bbf08fe537e5b5f129caPeter Hallam        {
743b61a96e7ef1a78acf013bbf08fe537e5b5f129caPeter Hallam            StoreEntry  entry = (StoreEntry)e.nextElement();
744b61a96e7ef1a78acf013bbf08fe537e5b5f129caPeter Hallam
745b61a96e7ef1a78acf013bbf08fe537e5b5f129caPeter Hallam            dOut.write(entry.getType());
746b61a96e7ef1a78acf013bbf08fe537e5b5f129caPeter Hallam            dOut.writeUTF(entry.getAlias());
747b61a96e7ef1a78acf013bbf08fe537e5b5f129caPeter Hallam            dOut.writeLong(entry.getDate().getTime());
748b61a96e7ef1a78acf013bbf08fe537e5b5f129caPeter Hallam
749b61a96e7ef1a78acf013bbf08fe537e5b5f129caPeter Hallam            Certificate[]   chain = entry.getCertificateChain();
750b61a96e7ef1a78acf013bbf08fe537e5b5f129caPeter Hallam            if (chain == null)
751b61a96e7ef1a78acf013bbf08fe537e5b5f129caPeter Hallam            {
752b61a96e7ef1a78acf013bbf08fe537e5b5f129caPeter Hallam                dOut.writeInt(0);
753b61a96e7ef1a78acf013bbf08fe537e5b5f129caPeter Hallam            }
754b61a96e7ef1a78acf013bbf08fe537e5b5f129caPeter Hallam            else
755b61a96e7ef1a78acf013bbf08fe537e5b5f129caPeter Hallam            {
756b61a96e7ef1a78acf013bbf08fe537e5b5f129caPeter Hallam                dOut.writeInt(chain.length);
757b61a96e7ef1a78acf013bbf08fe537e5b5f129caPeter Hallam                for (int i = 0; i != chain.length; i++)
758b61a96e7ef1a78acf013bbf08fe537e5b5f129caPeter Hallam                {
759b61a96e7ef1a78acf013bbf08fe537e5b5f129caPeter Hallam                    encodeCertificate(chain[i], dOut);
760b61a96e7ef1a78acf013bbf08fe537e5b5f129caPeter Hallam                }
761b61a96e7ef1a78acf013bbf08fe537e5b5f129caPeter Hallam            }
762b61a96e7ef1a78acf013bbf08fe537e5b5f129caPeter Hallam
763b61a96e7ef1a78acf013bbf08fe537e5b5f129caPeter Hallam            switch (entry.getType())
764b61a96e7ef1a78acf013bbf08fe537e5b5f129caPeter Hallam            {
765b61a96e7ef1a78acf013bbf08fe537e5b5f129caPeter Hallam            case CERTIFICATE:
766b61a96e7ef1a78acf013bbf08fe537e5b5f129caPeter Hallam                    encodeCertificate((Certificate)entry.getObject(), dOut);
767b61a96e7ef1a78acf013bbf08fe537e5b5f129caPeter Hallam                    break;
768b61a96e7ef1a78acf013bbf08fe537e5b5f129caPeter Hallam            case KEY:
769b61a96e7ef1a78acf013bbf08fe537e5b5f129caPeter Hallam                    encodeKey((Key)entry.getObject(), dOut);
770b61a96e7ef1a78acf013bbf08fe537e5b5f129caPeter Hallam                    break;
771b61a96e7ef1a78acf013bbf08fe537e5b5f129caPeter Hallam            case SEALED:
772b61a96e7ef1a78acf013bbf08fe537e5b5f129caPeter Hallam            case SECRET:
773b61a96e7ef1a78acf013bbf08fe537e5b5f129caPeter Hallam                    byte[]  b = (byte[])entry.getObject();
774b61a96e7ef1a78acf013bbf08fe537e5b5f129caPeter Hallam
775b61a96e7ef1a78acf013bbf08fe537e5b5f129caPeter Hallam                    dOut.writeInt(b.length);
776b61a96e7ef1a78acf013bbf08fe537e5b5f129caPeter Hallam                    dOut.write(b);
777b61a96e7ef1a78acf013bbf08fe537e5b5f129caPeter Hallam                    break;
778b61a96e7ef1a78acf013bbf08fe537e5b5f129caPeter Hallam            default:
779b61a96e7ef1a78acf013bbf08fe537e5b5f129caPeter Hallam                    throw new RuntimeException("Unknown object type in store.");
780b61a96e7ef1a78acf013bbf08fe537e5b5f129caPeter Hallam            }
781b61a96e7ef1a78acf013bbf08fe537e5b5f129caPeter Hallam        }
782b61a96e7ef1a78acf013bbf08fe537e5b5f129caPeter Hallam
783b61a96e7ef1a78acf013bbf08fe537e5b5f129caPeter Hallam        dOut.write(NULL);
784b61a96e7ef1a78acf013bbf08fe537e5b5f129caPeter Hallam    }
785b61a96e7ef1a78acf013bbf08fe537e5b5f129caPeter Hallam
786b61a96e7ef1a78acf013bbf08fe537e5b5f129caPeter Hallam    public void engineLoad(
787b61a96e7ef1a78acf013bbf08fe537e5b5f129caPeter Hallam        InputStream stream,
788b61a96e7ef1a78acf013bbf08fe537e5b5f129caPeter Hallam        char[]      password)
789b61a96e7ef1a78acf013bbf08fe537e5b5f129caPeter Hallam        throws IOException
790b61a96e7ef1a78acf013bbf08fe537e5b5f129caPeter Hallam    {
791b61a96e7ef1a78acf013bbf08fe537e5b5f129caPeter Hallam        table.clear();
792b61a96e7ef1a78acf013bbf08fe537e5b5f129caPeter Hallam
793b61a96e7ef1a78acf013bbf08fe537e5b5f129caPeter Hallam        if (stream == null)     // just initialising
794b61a96e7ef1a78acf013bbf08fe537e5b5f129caPeter Hallam        {
795b61a96e7ef1a78acf013bbf08fe537e5b5f129caPeter Hallam            return;
796b61a96e7ef1a78acf013bbf08fe537e5b5f129caPeter Hallam        }
797b61a96e7ef1a78acf013bbf08fe537e5b5f129caPeter Hallam
798b61a96e7ef1a78acf013bbf08fe537e5b5f129caPeter Hallam        DataInputStream     dIn = new DataInputStream(stream);
799b61a96e7ef1a78acf013bbf08fe537e5b5f129caPeter Hallam        int                 version = dIn.readInt();
800b61a96e7ef1a78acf013bbf08fe537e5b5f129caPeter Hallam
801b61a96e7ef1a78acf013bbf08fe537e5b5f129caPeter Hallam        if (version != STORE_VERSION)
802b61a96e7ef1a78acf013bbf08fe537e5b5f129caPeter Hallam        {
8034c111300c39cb2e27f07fc2ae3b00e23ed4443b2Brian Carlstrom            if (version != 0 && version != 1)
804b61a96e7ef1a78acf013bbf08fe537e5b5f129caPeter Hallam            {
805b61a96e7ef1a78acf013bbf08fe537e5b5f129caPeter Hallam                throw new IOException("Wrong version of key store.");
806b61a96e7ef1a78acf013bbf08fe537e5b5f129caPeter Hallam            }
807b61a96e7ef1a78acf013bbf08fe537e5b5f129caPeter Hallam        }
808b61a96e7ef1a78acf013bbf08fe537e5b5f129caPeter Hallam
8094c111300c39cb2e27f07fc2ae3b00e23ed4443b2Brian Carlstrom        int saltLength = dIn.readInt();
8104c111300c39cb2e27f07fc2ae3b00e23ed4443b2Brian Carlstrom        if (saltLength <= 0)
8114c111300c39cb2e27f07fc2ae3b00e23ed4443b2Brian Carlstrom        {
8124c111300c39cb2e27f07fc2ae3b00e23ed4443b2Brian Carlstrom            throw new IOException("Invalid salt detected");
8134c111300c39cb2e27f07fc2ae3b00e23ed4443b2Brian Carlstrom        }
8144c111300c39cb2e27f07fc2ae3b00e23ed4443b2Brian Carlstrom
8154c111300c39cb2e27f07fc2ae3b00e23ed4443b2Brian Carlstrom        byte[]      salt = new byte[saltLength];
816b61a96e7ef1a78acf013bbf08fe537e5b5f129caPeter Hallam
817b61a96e7ef1a78acf013bbf08fe537e5b5f129caPeter Hallam        dIn.readFully(salt);
818b61a96e7ef1a78acf013bbf08fe537e5b5f129caPeter Hallam
819b61a96e7ef1a78acf013bbf08fe537e5b5f129caPeter Hallam        int         iterationCount = dIn.readInt();
820b61a96e7ef1a78acf013bbf08fe537e5b5f129caPeter Hallam
821c37f4a04ef89e73a39a59f3c5a179af8c8ab5974Brian Carlstrom        //
822c37f4a04ef89e73a39a59f3c5a179af8c8ab5974Brian Carlstrom        // we only do an integrity check if the password is provided.
823c37f4a04ef89e73a39a59f3c5a179af8c8ab5974Brian Carlstrom        //
824a198e1ecc615e26a167d0f2dca9fa7e5fc62de10Brian Carlstrom        HMac hMac = new HMac(new SHA1Digest());
825c37f4a04ef89e73a39a59f3c5a179af8c8ab5974Brian Carlstrom        if (password != null && password.length != 0)
826c37f4a04ef89e73a39a59f3c5a179af8c8ab5974Brian Carlstrom        {
827c37f4a04ef89e73a39a59f3c5a179af8c8ab5974Brian Carlstrom            byte[] passKey = PBEParametersGenerator.PKCS12PasswordToBytes(password);
828b61a96e7ef1a78acf013bbf08fe537e5b5f129caPeter Hallam
829a198e1ecc615e26a167d0f2dca9fa7e5fc62de10Brian Carlstrom            PBEParametersGenerator pbeGen = new PKCS12ParametersGenerator(new SHA1Digest());
830c37f4a04ef89e73a39a59f3c5a179af8c8ab5974Brian Carlstrom            pbeGen.init(passKey, salt, iterationCount);
8314c111300c39cb2e27f07fc2ae3b00e23ed4443b2Brian Carlstrom
8324c111300c39cb2e27f07fc2ae3b00e23ed4443b2Brian Carlstrom            CipherParameters macParams;
8334c111300c39cb2e27f07fc2ae3b00e23ed4443b2Brian Carlstrom
8344c111300c39cb2e27f07fc2ae3b00e23ed4443b2Brian Carlstrom            if (version != 2)
8354c111300c39cb2e27f07fc2ae3b00e23ed4443b2Brian Carlstrom            {
8364c111300c39cb2e27f07fc2ae3b00e23ed4443b2Brian Carlstrom                macParams = pbeGen.generateDerivedMacParameters(hMac.getMacSize());
8374c111300c39cb2e27f07fc2ae3b00e23ed4443b2Brian Carlstrom            }
8384c111300c39cb2e27f07fc2ae3b00e23ed4443b2Brian Carlstrom            else
8394c111300c39cb2e27f07fc2ae3b00e23ed4443b2Brian Carlstrom            {
8404c111300c39cb2e27f07fc2ae3b00e23ed4443b2Brian Carlstrom                macParams = pbeGen.generateDerivedMacParameters(hMac.getMacSize() * 8);
8414c111300c39cb2e27f07fc2ae3b00e23ed4443b2Brian Carlstrom            }
8424c111300c39cb2e27f07fc2ae3b00e23ed4443b2Brian Carlstrom
843c37f4a04ef89e73a39a59f3c5a179af8c8ab5974Brian Carlstrom            Arrays.fill(passKey, (byte)0);
844b61a96e7ef1a78acf013bbf08fe537e5b5f129caPeter Hallam
845c37f4a04ef89e73a39a59f3c5a179af8c8ab5974Brian Carlstrom            hMac.init(macParams);
846c37f4a04ef89e73a39a59f3c5a179af8c8ab5974Brian Carlstrom            MacInputStream mIn = new MacInputStream(dIn, hMac);
847b61a96e7ef1a78acf013bbf08fe537e5b5f129caPeter Hallam
848c37f4a04ef89e73a39a59f3c5a179af8c8ab5974Brian Carlstrom            loadStore(mIn);
849b61a96e7ef1a78acf013bbf08fe537e5b5f129caPeter Hallam
850c37f4a04ef89e73a39a59f3c5a179af8c8ab5974Brian Carlstrom            // Finalise our mac calculation
851c37f4a04ef89e73a39a59f3c5a179af8c8ab5974Brian Carlstrom            byte[] mac = new byte[hMac.getMacSize()];
852c37f4a04ef89e73a39a59f3c5a179af8c8ab5974Brian Carlstrom            hMac.doFinal(mac, 0);
853b61a96e7ef1a78acf013bbf08fe537e5b5f129caPeter Hallam
854c37f4a04ef89e73a39a59f3c5a179af8c8ab5974Brian Carlstrom            // TODO Should this actually be reading the remainder of the stream?
855c37f4a04ef89e73a39a59f3c5a179af8c8ab5974Brian Carlstrom            // Read the original mac from the stream
856c37f4a04ef89e73a39a59f3c5a179af8c8ab5974Brian Carlstrom            byte[] oldMac = new byte[hMac.getMacSize()];
857c37f4a04ef89e73a39a59f3c5a179af8c8ab5974Brian Carlstrom            dIn.readFully(oldMac);
858b61a96e7ef1a78acf013bbf08fe537e5b5f129caPeter Hallam
859c37f4a04ef89e73a39a59f3c5a179af8c8ab5974Brian Carlstrom            if (!Arrays.constantTimeAreEqual(mac, oldMac))
860c37f4a04ef89e73a39a59f3c5a179af8c8ab5974Brian Carlstrom            {
861c37f4a04ef89e73a39a59f3c5a179af8c8ab5974Brian Carlstrom                table.clear();
862c37f4a04ef89e73a39a59f3c5a179af8c8ab5974Brian Carlstrom                throw new IOException("KeyStore integrity check failed.");
863c37f4a04ef89e73a39a59f3c5a179af8c8ab5974Brian Carlstrom            }
864b61a96e7ef1a78acf013bbf08fe537e5b5f129caPeter Hallam        }
865c37f4a04ef89e73a39a59f3c5a179af8c8ab5974Brian Carlstrom        else
866b61a96e7ef1a78acf013bbf08fe537e5b5f129caPeter Hallam        {
867c37f4a04ef89e73a39a59f3c5a179af8c8ab5974Brian Carlstrom            loadStore(dIn);
868c37f4a04ef89e73a39a59f3c5a179af8c8ab5974Brian Carlstrom
869c37f4a04ef89e73a39a59f3c5a179af8c8ab5974Brian Carlstrom            // TODO Should this actually be reading the remainder of the stream?
870c37f4a04ef89e73a39a59f3c5a179af8c8ab5974Brian Carlstrom            // Parse the original mac from the stream too
871c37f4a04ef89e73a39a59f3c5a179af8c8ab5974Brian Carlstrom            byte[] oldMac = new byte[hMac.getMacSize()];
872c37f4a04ef89e73a39a59f3c5a179af8c8ab5974Brian Carlstrom            dIn.readFully(oldMac);
873b61a96e7ef1a78acf013bbf08fe537e5b5f129caPeter Hallam        }
874b61a96e7ef1a78acf013bbf08fe537e5b5f129caPeter Hallam    }
875b61a96e7ef1a78acf013bbf08fe537e5b5f129caPeter Hallam
876b61a96e7ef1a78acf013bbf08fe537e5b5f129caPeter Hallam
877b61a96e7ef1a78acf013bbf08fe537e5b5f129caPeter Hallam    public void engineStore(OutputStream stream, char[] password)
878b61a96e7ef1a78acf013bbf08fe537e5b5f129caPeter Hallam        throws IOException
879b61a96e7ef1a78acf013bbf08fe537e5b5f129caPeter Hallam    {
880b61a96e7ef1a78acf013bbf08fe537e5b5f129caPeter Hallam        DataOutputStream    dOut = new DataOutputStream(stream);
881b61a96e7ef1a78acf013bbf08fe537e5b5f129caPeter Hallam        byte[]              salt = new byte[STORE_SALT_SIZE];
882b61a96e7ef1a78acf013bbf08fe537e5b5f129caPeter Hallam        int                 iterationCount = MIN_ITERATIONS + (random.nextInt() & 0x3ff);
883b61a96e7ef1a78acf013bbf08fe537e5b5f129caPeter Hallam
884b61a96e7ef1a78acf013bbf08fe537e5b5f129caPeter Hallam        random.nextBytes(salt);
885b61a96e7ef1a78acf013bbf08fe537e5b5f129caPeter Hallam
886a198e1ecc615e26a167d0f2dca9fa7e5fc62de10Brian Carlstrom        dOut.writeInt(version);
887b61a96e7ef1a78acf013bbf08fe537e5b5f129caPeter Hallam        dOut.writeInt(salt.length);
888b61a96e7ef1a78acf013bbf08fe537e5b5f129caPeter Hallam        dOut.write(salt);
889b61a96e7ef1a78acf013bbf08fe537e5b5f129caPeter Hallam        dOut.writeInt(iterationCount);
890b61a96e7ef1a78acf013bbf08fe537e5b5f129caPeter Hallam
891a198e1ecc615e26a167d0f2dca9fa7e5fc62de10Brian Carlstrom        HMac                    hMac = new HMac(new SHA1Digest());
8924c111300c39cb2e27f07fc2ae3b00e23ed4443b2Brian Carlstrom        MacOutputStream         mOut = new MacOutputStream(hMac);
893a198e1ecc615e26a167d0f2dca9fa7e5fc62de10Brian Carlstrom        PBEParametersGenerator  pbeGen = new PKCS12ParametersGenerator(new SHA1Digest());
894b61a96e7ef1a78acf013bbf08fe537e5b5f129caPeter Hallam        byte[]                  passKey = PBEParametersGenerator.PKCS12PasswordToBytes(password);
895b61a96e7ef1a78acf013bbf08fe537e5b5f129caPeter Hallam
896b61a96e7ef1a78acf013bbf08fe537e5b5f129caPeter Hallam        pbeGen.init(passKey, salt, iterationCount);
897b61a96e7ef1a78acf013bbf08fe537e5b5f129caPeter Hallam
898a198e1ecc615e26a167d0f2dca9fa7e5fc62de10Brian Carlstrom        if (version < 2)
899a198e1ecc615e26a167d0f2dca9fa7e5fc62de10Brian Carlstrom        {
900a198e1ecc615e26a167d0f2dca9fa7e5fc62de10Brian Carlstrom            hMac.init(pbeGen.generateDerivedMacParameters(hMac.getMacSize()));
901a198e1ecc615e26a167d0f2dca9fa7e5fc62de10Brian Carlstrom        }
902a198e1ecc615e26a167d0f2dca9fa7e5fc62de10Brian Carlstrom        else
903a198e1ecc615e26a167d0f2dca9fa7e5fc62de10Brian Carlstrom        {
904a198e1ecc615e26a167d0f2dca9fa7e5fc62de10Brian Carlstrom            hMac.init(pbeGen.generateDerivedMacParameters(hMac.getMacSize() * 8));
905a198e1ecc615e26a167d0f2dca9fa7e5fc62de10Brian Carlstrom        }
906b61a96e7ef1a78acf013bbf08fe537e5b5f129caPeter Hallam
907b61a96e7ef1a78acf013bbf08fe537e5b5f129caPeter Hallam        for (int i = 0; i != passKey.length; i++)
908b61a96e7ef1a78acf013bbf08fe537e5b5f129caPeter Hallam        {
909b61a96e7ef1a78acf013bbf08fe537e5b5f129caPeter Hallam            passKey[i] = 0;
910b61a96e7ef1a78acf013bbf08fe537e5b5f129caPeter Hallam        }
911b61a96e7ef1a78acf013bbf08fe537e5b5f129caPeter Hallam
9124c111300c39cb2e27f07fc2ae3b00e23ed4443b2Brian Carlstrom        saveStore(new TeeOutputStream(dOut, mOut));
913b61a96e7ef1a78acf013bbf08fe537e5b5f129caPeter Hallam
914b61a96e7ef1a78acf013bbf08fe537e5b5f129caPeter Hallam        byte[]  mac = new byte[hMac.getMacSize()];
915b61a96e7ef1a78acf013bbf08fe537e5b5f129caPeter Hallam
916b61a96e7ef1a78acf013bbf08fe537e5b5f129caPeter Hallam        hMac.doFinal(mac, 0);
917b61a96e7ef1a78acf013bbf08fe537e5b5f129caPeter Hallam
918b61a96e7ef1a78acf013bbf08fe537e5b5f129caPeter Hallam        dOut.write(mac);
919b61a96e7ef1a78acf013bbf08fe537e5b5f129caPeter Hallam
920b61a96e7ef1a78acf013bbf08fe537e5b5f129caPeter Hallam        dOut.close();
921b61a96e7ef1a78acf013bbf08fe537e5b5f129caPeter Hallam    }
922b61a96e7ef1a78acf013bbf08fe537e5b5f129caPeter Hallam
923b61a96e7ef1a78acf013bbf08fe537e5b5f129caPeter Hallam    /**
924b61a96e7ef1a78acf013bbf08fe537e5b5f129caPeter Hallam     * the BouncyCastle store. This wont work with the key tool as the
9254c111300c39cb2e27f07fc2ae3b00e23ed4443b2Brian Carlstrom     * store is stored encrypted on disk, so the password is mandatory,
926b61a96e7ef1a78acf013bbf08fe537e5b5f129caPeter Hallam     * however if you hard drive is in a bad part of town and you absolutely,
927b61a96e7ef1a78acf013bbf08fe537e5b5f129caPeter Hallam     * positively, don't want nobody peeking at your things, this is the
928b61a96e7ef1a78acf013bbf08fe537e5b5f129caPeter Hallam     * one to use, no problem! After all in a Bouncy Castle nothing can
929b61a96e7ef1a78acf013bbf08fe537e5b5f129caPeter Hallam     * touch you.
930b61a96e7ef1a78acf013bbf08fe537e5b5f129caPeter Hallam     *
931b61a96e7ef1a78acf013bbf08fe537e5b5f129caPeter Hallam     * Also referred to by the alias UBER.
932b61a96e7ef1a78acf013bbf08fe537e5b5f129caPeter Hallam     */
933b61a96e7ef1a78acf013bbf08fe537e5b5f129caPeter Hallam    public static class BouncyCastleStore
934a198e1ecc615e26a167d0f2dca9fa7e5fc62de10Brian Carlstrom        extends BcKeyStoreSpi
935b61a96e7ef1a78acf013bbf08fe537e5b5f129caPeter Hallam    {
936a198e1ecc615e26a167d0f2dca9fa7e5fc62de10Brian Carlstrom        public BouncyCastleStore()
937a198e1ecc615e26a167d0f2dca9fa7e5fc62de10Brian Carlstrom        {
938a198e1ecc615e26a167d0f2dca9fa7e5fc62de10Brian Carlstrom            super(1);
939a198e1ecc615e26a167d0f2dca9fa7e5fc62de10Brian Carlstrom        }
940a198e1ecc615e26a167d0f2dca9fa7e5fc62de10Brian Carlstrom
941b61a96e7ef1a78acf013bbf08fe537e5b5f129caPeter Hallam        public void engineLoad(
942b61a96e7ef1a78acf013bbf08fe537e5b5f129caPeter Hallam            InputStream stream,
943b61a96e7ef1a78acf013bbf08fe537e5b5f129caPeter Hallam            char[]      password)
944b61a96e7ef1a78acf013bbf08fe537e5b5f129caPeter Hallam            throws IOException
945b61a96e7ef1a78acf013bbf08fe537e5b5f129caPeter Hallam        {
946b61a96e7ef1a78acf013bbf08fe537e5b5f129caPeter Hallam            table.clear();
947b61a96e7ef1a78acf013bbf08fe537e5b5f129caPeter Hallam
948b61a96e7ef1a78acf013bbf08fe537e5b5f129caPeter Hallam            if (stream == null)     // just initialising
949b61a96e7ef1a78acf013bbf08fe537e5b5f129caPeter Hallam            {
950b61a96e7ef1a78acf013bbf08fe537e5b5f129caPeter Hallam                return;
951b61a96e7ef1a78acf013bbf08fe537e5b5f129caPeter Hallam            }
952b61a96e7ef1a78acf013bbf08fe537e5b5f129caPeter Hallam
953b61a96e7ef1a78acf013bbf08fe537e5b5f129caPeter Hallam            DataInputStream     dIn = new DataInputStream(stream);
954b61a96e7ef1a78acf013bbf08fe537e5b5f129caPeter Hallam            int                 version = dIn.readInt();
955b61a96e7ef1a78acf013bbf08fe537e5b5f129caPeter Hallam
956b61a96e7ef1a78acf013bbf08fe537e5b5f129caPeter Hallam            if (version != STORE_VERSION)
957b61a96e7ef1a78acf013bbf08fe537e5b5f129caPeter Hallam            {
9584c111300c39cb2e27f07fc2ae3b00e23ed4443b2Brian Carlstrom                if (version != 0 && version != 1)
959b61a96e7ef1a78acf013bbf08fe537e5b5f129caPeter Hallam                {
960b61a96e7ef1a78acf013bbf08fe537e5b5f129caPeter Hallam                    throw new IOException("Wrong version of key store.");
961b61a96e7ef1a78acf013bbf08fe537e5b5f129caPeter Hallam                }
962b61a96e7ef1a78acf013bbf08fe537e5b5f129caPeter Hallam            }
963b61a96e7ef1a78acf013bbf08fe537e5b5f129caPeter Hallam
964b61a96e7ef1a78acf013bbf08fe537e5b5f129caPeter Hallam            byte[]      salt = new byte[dIn.readInt()];
965b61a96e7ef1a78acf013bbf08fe537e5b5f129caPeter Hallam
966b61a96e7ef1a78acf013bbf08fe537e5b5f129caPeter Hallam            if (salt.length != STORE_SALT_SIZE)
967b61a96e7ef1a78acf013bbf08fe537e5b5f129caPeter Hallam            {
968b61a96e7ef1a78acf013bbf08fe537e5b5f129caPeter Hallam                throw new IOException("Key store corrupted.");
969b61a96e7ef1a78acf013bbf08fe537e5b5f129caPeter Hallam            }
970b61a96e7ef1a78acf013bbf08fe537e5b5f129caPeter Hallam
971b61a96e7ef1a78acf013bbf08fe537e5b5f129caPeter Hallam            dIn.readFully(salt);
972b61a96e7ef1a78acf013bbf08fe537e5b5f129caPeter Hallam
973b61a96e7ef1a78acf013bbf08fe537e5b5f129caPeter Hallam            int         iterationCount = dIn.readInt();
974b61a96e7ef1a78acf013bbf08fe537e5b5f129caPeter Hallam
975b61a96e7ef1a78acf013bbf08fe537e5b5f129caPeter Hallam            if ((iterationCount < 0) || (iterationCount > 4 *  MIN_ITERATIONS))
976b61a96e7ef1a78acf013bbf08fe537e5b5f129caPeter Hallam            {
977b61a96e7ef1a78acf013bbf08fe537e5b5f129caPeter Hallam                throw new IOException("Key store corrupted.");
978b61a96e7ef1a78acf013bbf08fe537e5b5f129caPeter Hallam            }
979b61a96e7ef1a78acf013bbf08fe537e5b5f129caPeter Hallam
980c37f4a04ef89e73a39a59f3c5a179af8c8ab5974Brian Carlstrom            String cipherAlg;
981b61a96e7ef1a78acf013bbf08fe537e5b5f129caPeter Hallam            if (version == 0)
982b61a96e7ef1a78acf013bbf08fe537e5b5f129caPeter Hallam            {
983c37f4a04ef89e73a39a59f3c5a179af8c8ab5974Brian Carlstrom                cipherAlg = "Old" + STORE_CIPHER;
984b61a96e7ef1a78acf013bbf08fe537e5b5f129caPeter Hallam            }
985b61a96e7ef1a78acf013bbf08fe537e5b5f129caPeter Hallam            else
986b61a96e7ef1a78acf013bbf08fe537e5b5f129caPeter Hallam            {
987c37f4a04ef89e73a39a59f3c5a179af8c8ab5974Brian Carlstrom                cipherAlg = STORE_CIPHER;
988b61a96e7ef1a78acf013bbf08fe537e5b5f129caPeter Hallam            }
989b61a96e7ef1a78acf013bbf08fe537e5b5f129caPeter Hallam
990c37f4a04ef89e73a39a59f3c5a179af8c8ab5974Brian Carlstrom            Cipher cipher = this.makePBECipher(cipherAlg, Cipher.DECRYPT_MODE, password, salt, iterationCount);
991c37f4a04ef89e73a39a59f3c5a179af8c8ab5974Brian Carlstrom            CipherInputStream cIn = new CipherInputStream(dIn, cipher);
992c37f4a04ef89e73a39a59f3c5a179af8c8ab5974Brian Carlstrom
993a198e1ecc615e26a167d0f2dca9fa7e5fc62de10Brian Carlstrom            Digest dig = new SHA1Digest();
994c37f4a04ef89e73a39a59f3c5a179af8c8ab5974Brian Carlstrom            DigestInputStream  dgIn = new DigestInputStream(cIn, dig);
995b61a96e7ef1a78acf013bbf08fe537e5b5f129caPeter Hallam
996c37f4a04ef89e73a39a59f3c5a179af8c8ab5974Brian Carlstrom            this.loadStore(dgIn);
997c37f4a04ef89e73a39a59f3c5a179af8c8ab5974Brian Carlstrom
998c37f4a04ef89e73a39a59f3c5a179af8c8ab5974Brian Carlstrom            // Finalise our digest calculation
999c37f4a04ef89e73a39a59f3c5a179af8c8ab5974Brian Carlstrom            byte[] hash = new byte[dig.getDigestSize()];
1000b61a96e7ef1a78acf013bbf08fe537e5b5f129caPeter Hallam            dig.doFinal(hash, 0);
1001c37f4a04ef89e73a39a59f3c5a179af8c8ab5974Brian Carlstrom
1002c37f4a04ef89e73a39a59f3c5a179af8c8ab5974Brian Carlstrom            // TODO Should this actually be reading the remainder of the stream?
1003c37f4a04ef89e73a39a59f3c5a179af8c8ab5974Brian Carlstrom            // Read the original digest from the stream
1004c37f4a04ef89e73a39a59f3c5a179af8c8ab5974Brian Carlstrom            byte[] oldHash = new byte[dig.getDigestSize()];
1005c37f4a04ef89e73a39a59f3c5a179af8c8ab5974Brian Carlstrom            Streams.readFully(cIn, oldHash);
1006c37f4a04ef89e73a39a59f3c5a179af8c8ab5974Brian Carlstrom
1007c37f4a04ef89e73a39a59f3c5a179af8c8ab5974Brian Carlstrom            if (!Arrays.constantTimeAreEqual(hash, oldHash))
1008b61a96e7ef1a78acf013bbf08fe537e5b5f129caPeter Hallam            {
1009b61a96e7ef1a78acf013bbf08fe537e5b5f129caPeter Hallam                table.clear();
1010b61a96e7ef1a78acf013bbf08fe537e5b5f129caPeter Hallam                throw new IOException("KeyStore integrity check failed.");
1011b61a96e7ef1a78acf013bbf08fe537e5b5f129caPeter Hallam            }
1012b61a96e7ef1a78acf013bbf08fe537e5b5f129caPeter Hallam        }
10134c111300c39cb2e27f07fc2ae3b00e23ed4443b2Brian Carlstrom
1014b61a96e7ef1a78acf013bbf08fe537e5b5f129caPeter Hallam        public void engineStore(OutputStream stream, char[] password)
1015b61a96e7ef1a78acf013bbf08fe537e5b5f129caPeter Hallam            throws IOException
1016b61a96e7ef1a78acf013bbf08fe537e5b5f129caPeter Hallam        {
1017b61a96e7ef1a78acf013bbf08fe537e5b5f129caPeter Hallam            Cipher              cipher;
1018b61a96e7ef1a78acf013bbf08fe537e5b5f129caPeter Hallam            DataOutputStream    dOut = new DataOutputStream(stream);
1019b61a96e7ef1a78acf013bbf08fe537e5b5f129caPeter Hallam            byte[]              salt = new byte[STORE_SALT_SIZE];
1020b61a96e7ef1a78acf013bbf08fe537e5b5f129caPeter Hallam            int                 iterationCount = MIN_ITERATIONS + (random.nextInt() & 0x3ff);
1021b61a96e7ef1a78acf013bbf08fe537e5b5f129caPeter Hallam
1022b61a96e7ef1a78acf013bbf08fe537e5b5f129caPeter Hallam            random.nextBytes(salt);
1023b61a96e7ef1a78acf013bbf08fe537e5b5f129caPeter Hallam
1024a198e1ecc615e26a167d0f2dca9fa7e5fc62de10Brian Carlstrom            dOut.writeInt(version);
1025b61a96e7ef1a78acf013bbf08fe537e5b5f129caPeter Hallam            dOut.writeInt(salt.length);
1026b61a96e7ef1a78acf013bbf08fe537e5b5f129caPeter Hallam            dOut.write(salt);
1027b61a96e7ef1a78acf013bbf08fe537e5b5f129caPeter Hallam            dOut.writeInt(iterationCount);
1028b61a96e7ef1a78acf013bbf08fe537e5b5f129caPeter Hallam
1029b61a96e7ef1a78acf013bbf08fe537e5b5f129caPeter Hallam            cipher = this.makePBECipher(STORE_CIPHER, Cipher.ENCRYPT_MODE, password, salt, iterationCount);
1030b61a96e7ef1a78acf013bbf08fe537e5b5f129caPeter Hallam
1031b61a96e7ef1a78acf013bbf08fe537e5b5f129caPeter Hallam            CipherOutputStream  cOut = new CipherOutputStream(dOut, cipher);
1032a198e1ecc615e26a167d0f2dca9fa7e5fc62de10Brian Carlstrom            DigestOutputStream  dgOut = new DigestOutputStream(new SHA1Digest());
1033b61a96e7ef1a78acf013bbf08fe537e5b5f129caPeter Hallam
10344c111300c39cb2e27f07fc2ae3b00e23ed4443b2Brian Carlstrom            this.saveStore(new TeeOutputStream(cOut, dgOut));
1035b61a96e7ef1a78acf013bbf08fe537e5b5f129caPeter Hallam
10364c111300c39cb2e27f07fc2ae3b00e23ed4443b2Brian Carlstrom            byte[]  dig = dgOut.getDigest();
10374c111300c39cb2e27f07fc2ae3b00e23ed4443b2Brian Carlstrom
10384c111300c39cb2e27f07fc2ae3b00e23ed4443b2Brian Carlstrom            cOut.write(dig);
1039b61a96e7ef1a78acf013bbf08fe537e5b5f129caPeter Hallam
1040b61a96e7ef1a78acf013bbf08fe537e5b5f129caPeter Hallam            cOut.close();
1041b61a96e7ef1a78acf013bbf08fe537e5b5f129caPeter Hallam        }
10424c111300c39cb2e27f07fc2ae3b00e23ed4443b2Brian Carlstrom    }
1043a198e1ecc615e26a167d0f2dca9fa7e5fc62de10Brian Carlstrom
1044a198e1ecc615e26a167d0f2dca9fa7e5fc62de10Brian Carlstrom    public static class Std
1045a198e1ecc615e26a167d0f2dca9fa7e5fc62de10Brian Carlstrom       extends BcKeyStoreSpi
1046a198e1ecc615e26a167d0f2dca9fa7e5fc62de10Brian Carlstrom    {
1047a198e1ecc615e26a167d0f2dca9fa7e5fc62de10Brian Carlstrom        public Std()
1048a198e1ecc615e26a167d0f2dca9fa7e5fc62de10Brian Carlstrom        {
1049a198e1ecc615e26a167d0f2dca9fa7e5fc62de10Brian Carlstrom            super(STORE_VERSION);
1050a198e1ecc615e26a167d0f2dca9fa7e5fc62de10Brian Carlstrom        }
1051a198e1ecc615e26a167d0f2dca9fa7e5fc62de10Brian Carlstrom    }
1052a198e1ecc615e26a167d0f2dca9fa7e5fc62de10Brian Carlstrom
1053a198e1ecc615e26a167d0f2dca9fa7e5fc62de10Brian Carlstrom    public static class Version1
1054a198e1ecc615e26a167d0f2dca9fa7e5fc62de10Brian Carlstrom        extends BcKeyStoreSpi
1055a198e1ecc615e26a167d0f2dca9fa7e5fc62de10Brian Carlstrom    {
1056a198e1ecc615e26a167d0f2dca9fa7e5fc62de10Brian Carlstrom        public Version1()
1057a198e1ecc615e26a167d0f2dca9fa7e5fc62de10Brian Carlstrom        {
1058a198e1ecc615e26a167d0f2dca9fa7e5fc62de10Brian Carlstrom            super(1);
1059a198e1ecc615e26a167d0f2dca9fa7e5fc62de10Brian Carlstrom        }
1060a198e1ecc615e26a167d0f2dca9fa7e5fc62de10Brian Carlstrom    }
1061b61a96e7ef1a78acf013bbf08fe537e5b5f129caPeter Hallam}
1062