1/*
2 *  Licensed to the Apache Software Foundation (ASF) under one or more
3 *  contributor license agreements.  See the NOTICE file distributed with
4 *  this work for additional information regarding copyright ownership.
5 *  The ASF licenses this file to You under the Apache License, Version 2.0
6 *  (the "License"); you may not use this file except in compliance with
7 *  the License.  You may obtain a copy of the License at
8 *
9 *     http://www.apache.org/licenses/LICENSE-2.0
10 *
11 *  Unless required by applicable law or agreed to in writing, software
12 *  distributed under the License is distributed on an "AS IS" BASIS,
13 *  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
14 *  See the License for the specific language governing permissions and
15 *  limitations under the License.
16 */
17
18package java.security;
19
20import java.io.IOException;
21import java.io.InputStream;
22import java.io.OutputStream;
23import java.security.cert.Certificate;
24import java.security.cert.CertificateException;
25import java.util.Date;
26import java.util.Enumeration;
27import javax.crypto.SecretKey;
28import javax.security.auth.callback.CallbackHandler;
29import javax.security.auth.callback.PasswordCallback;
30
31/**
32 * {@code KeyStoreSpi} is the Service Provider Interface (SPI) definition for
33 * {@link KeyStore}.
34 *
35 * @see KeyStore
36 */
37public abstract class KeyStoreSpi {
38
39    /**
40     * Returns the key with the given alias, using the password to recover the
41     * key from the store.
42     *
43     * @param alias
44     *            the alias for the entry.
45     * @param password
46     *            the password used to recover the key.
47     * @return the key with the specified alias, or {@code null} if the
48     *         specified alias is not bound to an entry.
49     * @throws NoSuchAlgorithmException
50     *             if the algorithm for recovering the key is not available.
51     * @throws UnrecoverableKeyException
52     *             if the key can not be recovered.
53     */
54    public abstract Key engineGetKey(String alias, char[] password)
55            throws NoSuchAlgorithmException, UnrecoverableKeyException;
56
57    /**
58     * Returns the certificate chain for the entry with the given alias.
59     *
60     * @param alias
61     *            the alias for the entry
62     * @return the certificate chain for the entry with the given alias, or
63     *         {@code null} if the specified alias is not bound to an entry.
64     */
65    public abstract Certificate[] engineGetCertificateChain(String alias);
66
67    /**
68     * Returns the trusted certificate for the entry with the given alias.
69     *
70     * @param alias
71     *            the alias for the entry.
72     * @return the trusted certificate for the entry with the given alias, or
73     *         {@code null} if the specified alias is not bound to an entry.
74     */
75    public abstract Certificate engineGetCertificate(String alias);
76
77    /**
78     * Returns the creation date of the entry with the given alias.
79     *
80     * @param alias
81     *            the alias for the entry.
82     * @return the creation date, or {@code null} if the specified alias is not
83     *         bound to an entry.
84     */
85    public abstract Date engineGetCreationDate(String alias);
86
87    /**
88     * Associates the given alias with the key, password and certificate chain.
89     * <p>
90     * If the specified alias already exists, it will be reassigned.
91     *
92     * @param alias
93     *            the alias for the key.
94     * @param key
95     *            the key.
96     * @param password
97     *            the password.
98     * @param chain
99     *            the certificate chain.
100     * @throws KeyStoreException
101     *             if the specified key can not be protected, or if this
102     *             operation fails for another reason.
103     * @throws IllegalArgumentException
104     *             if {@code key} is a {@code PrivateKey} and {@code chain} does
105     *             not contain any certificates.
106     */
107    public abstract void engineSetKeyEntry(String alias, Key key,
108            char[] password, Certificate[] chain) throws KeyStoreException;
109
110    /**
111     * Associates the given alias with a key and a certificate chain.
112     * <p>
113     * If the specified alias already exists, it will be reassigned.
114     *
115     * @param alias
116     *            the alias for the key.
117     * @param key
118     *            the key in an encoded format.
119     * @param chain
120     *            the certificate chain.
121     * @throws KeyStoreException
122     *             if this operation fails.
123     * @throws IllegalArgumentException
124     *             if {@code key} is a {@code PrivateKey} and {@code chain}
125     *             does.
126     */
127    public abstract void engineSetKeyEntry(String alias, byte[] key,
128            Certificate[] chain) throws KeyStoreException;
129
130    /**
131     * Associates the given alias with a certificate.
132     * <p>
133     * If the specified alias already exists, it will be reassigned.
134     *
135     * @param alias
136     *            the alias for the certificate.
137     * @param cert
138     *            the certificate.
139     * @throws KeyStoreException
140     *             if an existing alias is not associated to an entry containing
141     *             a trusted certificate, or this method fails for any other
142     *             reason.
143     */
144    public abstract void engineSetCertificateEntry(String alias,
145            Certificate cert) throws KeyStoreException;
146
147    /**
148     * Deletes the entry identified with the given alias from this {@code
149     * KeyStoreSpi}.
150     *
151     * @param alias
152     *            the alias for the entry.
153     * @throws KeyStoreException
154     *             if the entry can not be deleted.
155     */
156    public abstract void engineDeleteEntry(String alias)
157            throws KeyStoreException;
158
159    /**
160     * Returns an {@code Enumeration} over all alias names stored in this
161     * {@code KeyStoreSpi}.
162     *
163     * @return an {@code Enumeration} over all alias names stored in this
164     *         {@code KeyStoreSpi}.
165     */
166    public abstract Enumeration<String> engineAliases();
167
168    /**
169     * Indicates whether the given alias is present in this {@code KeyStoreSpi}.
170     *
171     * @param alias
172     *            the alias of an entry.
173     * @return {@code true} if the alias exists, {@code false} otherwise.
174     */
175    public abstract boolean engineContainsAlias(String alias);
176
177    /**
178     * Returns the number of entries stored in this {@code KeyStoreSpi}.
179     *
180     * @return the number of entries stored in this {@code KeyStoreSpi}.
181     */
182    public abstract int engineSize();
183
184    /**
185     * Indicates whether the specified alias is associated with either a
186     * {@link KeyStore.PrivateKeyEntry} or a {@link KeyStore.SecretKeyEntry}.
187     *
188     * @param alias
189     *            the alias of an entry.
190     * @return {@code true} if the given alias is associated with a key entry.
191     */
192    public abstract boolean engineIsKeyEntry(String alias);
193
194    /**
195     * Indicates whether the specified alias is associated with a
196     * {@link KeyStore.TrustedCertificateEntry}.
197     *
198     * @param alias
199     *            the alias of an entry.
200     * @return {@code true} if the given alias is associated with a certificate
201     *         entry.
202     */
203    public abstract boolean engineIsCertificateEntry(String alias);
204
205    /**
206     * Returns the alias associated with the first entry whose certificate
207     * matches the specified certificate.
208     *
209     * @param cert
210     *            the certificate to find the associated entry's alias for.
211     * @return the alias or {@code null} if no entry with the specified
212     *         certificate can be found.
213     */
214    public abstract String engineGetCertificateAlias(Certificate cert);
215
216    /**
217     * Writes this {@code KeyStoreSpi} to the specified {@code OutputStream}.
218     * The data written to the {@code OutputStream} is protected by the
219     * specified password.
220     *
221     * @param stream
222     *            the {@code OutputStream} to write the store's data to.
223     * @param password
224     *            the password to protect the data.
225     * @throws IOException
226     *             if a problem occurred while writing to the stream.
227     * @throws NoSuchAlgorithmException
228     *             if the required algorithm is not available.
229     * @throws CertificateException
230     *             if the an exception occurred while storing the certificates
231     *             of this code {@code KeyStoreSpi}.
232     */
233    public abstract void engineStore(OutputStream stream, char[] password)
234            throws IOException, NoSuchAlgorithmException, CertificateException;
235
236    /**
237     * Stores this {@code KeyStoreSpi} using the specified {@code
238     * LoadStoreParameter}.
239     *
240     * @param param
241     *            the {@code LoadStoreParameter} that specifies how to store
242     *            this {@code KeyStoreSpi}, maybe {@code null}.
243     * @throws IOException
244     *             if a problem occurred while writing to the stream.
245     * @throws NoSuchAlgorithmException
246     *             if the required algorithm is not available.
247     * @throws CertificateException
248     *             if the an exception occurred while storing the certificates
249     *             of this code {@code KeyStoreSpi}.
250     * @throws IllegalArgumentException
251     *             if the given {@link KeyStore.LoadStoreParameter} is not
252     *             recognized.
253     */
254    public void engineStore(KeyStore.LoadStoreParameter param)
255            throws IOException, NoSuchAlgorithmException, CertificateException {
256        throw new UnsupportedOperationException();
257    }
258
259    /**
260     * Loads this {@code KeyStoreSpi} from the given {@code InputStream}.
261     * Utilizes the given password to verify the stored data.
262     *
263     * @param stream
264     *            the {@code InputStream} to load this {@code KeyStoreSpi}'s
265     *            data from.
266     * @param password
267     *            the password to verify the stored data, maybe {@code null}.
268     * @throws IOException
269     *             if a problem occurred while reading from the stream.
270     * @throws NoSuchAlgorithmException
271     *             if the required algorithm is not available.
272     * @throws CertificateException
273     *             if the an exception occurred while loading the certificates
274     *             of this code {@code KeyStoreSpi}.
275     */
276    public abstract void engineLoad(InputStream stream, char[] password)
277            throws IOException, NoSuchAlgorithmException, CertificateException;
278
279    /**
280     * Loads this {@code KeyStoreSpi} using the specified {@code
281     * LoadStoreParameter}.
282     *
283     * @param param
284     *            the {@code LoadStoreParameter} that specifies how to load this
285     *            {@code KeyStoreSpi}, maybe {@code null}.
286     * @throws IOException
287     *             if a problem occurred while reading from the stream.
288     * @throws NoSuchAlgorithmException
289     *             if the required algorithm is not available.
290     * @throws CertificateException
291     *             if the an exception occurred while loading the certificates
292     *             of this code {@code KeyStoreSpi}.
293     * @throws IllegalArgumentException
294     *             if the given {@link KeyStore.LoadStoreParameter} is not
295     *             recognized.
296     */
297    public void engineLoad(KeyStore.LoadStoreParameter param)
298            throws IOException, NoSuchAlgorithmException, CertificateException {
299        if (param == null) {
300            engineLoad(null, null);
301            return;
302        }
303        char[] pwd;
304        KeyStore.ProtectionParameter pp = param.getProtectionParameter();
305        if (pp instanceof KeyStore.PasswordProtection) {
306            try {
307                pwd = ((KeyStore.PasswordProtection) pp).getPassword();
308                engineLoad(null, pwd);
309                return;
310            } catch (IllegalStateException e) {
311                throw new IllegalArgumentException(e);
312            }
313        }
314        if (pp instanceof KeyStore.CallbackHandlerProtection) {
315            try {
316                pwd = getPasswordFromCallBack(pp);
317                engineLoad(null, pwd);
318                return;
319            } catch (UnrecoverableEntryException e) {
320                throw new IllegalArgumentException(e);
321            }
322        }
323        throw new UnsupportedOperationException("protectionParameter is neither PasswordProtection "
324                                                + "nor CallbackHandlerProtection instance");
325    }
326
327    /**
328     * Returns the {@code Entry} with the given alias, using the specified
329     * {@code ProtectionParameter}.
330     *
331     * @param alias
332     *            the alias of the requested entry.
333     * @param protParam
334     *            the {@code ProtectionParameter}, used to protect the requested
335     *            entry, maybe {@code null}.
336     * @return he {@code Entry} with the given alias, using the specified
337     *         {@code ProtectionParameter}.
338     * @throws NoSuchAlgorithmException
339     *             if the required algorithm is not available.
340     * @throws UnrecoverableEntryException
341     *             if the entry can not be recovered.
342     * @throws KeyStoreException
343     *             if this operation fails
344     */
345    public KeyStore.Entry engineGetEntry(String alias,
346            KeyStore.ProtectionParameter protParam) throws KeyStoreException,
347            NoSuchAlgorithmException, UnrecoverableEntryException {
348        if (!engineContainsAlias(alias)) {
349            return null;
350        }
351        if (engineIsCertificateEntry(alias)) {
352            return new KeyStore.TrustedCertificateEntry(
353                    engineGetCertificate(alias));
354        }
355        char[] passW = null;
356        if (protParam != null) {
357            if (protParam instanceof KeyStore.PasswordProtection) {
358                try {
359                    passW = ((KeyStore.PasswordProtection) protParam)
360                            .getPassword();
361                } catch (IllegalStateException ee) {
362                    throw new KeyStoreException("Password was destroyed", ee);
363                }
364            } else if (protParam instanceof KeyStore.CallbackHandlerProtection) {
365                passW = getPasswordFromCallBack(protParam);
366            } else {
367                throw new UnrecoverableEntryException("ProtectionParameter object is not "
368                                                      + "PasswordProtection: " + protParam);
369            }
370        }
371        if (engineIsKeyEntry(alias)) {
372            Key key = engineGetKey(alias, passW);
373            if (key instanceof PrivateKey) {
374                return new KeyStore.PrivateKeyEntry((PrivateKey) key,
375                                                    engineGetCertificateChain(alias));
376            }
377            if (key instanceof SecretKey) {
378                return new KeyStore.SecretKeyEntry((SecretKey) key);
379            }
380        }
381        throw new NoSuchAlgorithmException("Unknown KeyStore.Entry object");
382    }
383
384    /**
385     * Stores the given {@code Entry} in this {@code KeyStoreSpi} and associates
386     * the entry with the given {@code alias}. The entry is protected by the
387     * specified {@code ProtectionParameter}.
388     * <p>
389     * If the specified alias already exists, it will be reassigned.
390     *
391     * @param alias
392     *            the alias for the entry.
393     * @param entry
394     *            the entry to store.
395     * @param protParam
396     *            the {@code ProtectionParameter} to protect the entry.
397     * @throws KeyStoreException
398     *             if this operation fails.
399     */
400    public void engineSetEntry(String alias, KeyStore.Entry entry,
401            KeyStore.ProtectionParameter protParam) throws KeyStoreException {
402        if (entry == null) {
403            throw new KeyStoreException("entry == null");
404        }
405
406        if (engineContainsAlias(alias)) {
407            engineDeleteEntry(alias);
408        }
409
410        if (entry instanceof KeyStore.TrustedCertificateEntry) {
411            KeyStore.TrustedCertificateEntry trE = (KeyStore.TrustedCertificateEntry) entry;
412            engineSetCertificateEntry(alias, trE.getTrustedCertificate());
413            return;
414        }
415
416        char[] passW = null;
417        if (protParam != null) {
418            if (protParam instanceof KeyStore.PasswordProtection) {
419                try {
420                    passW = ((KeyStore.PasswordProtection) protParam).getPassword();
421                } catch (IllegalStateException ee) {
422                    throw new KeyStoreException("Password was destroyed", ee);
423                }
424            } else if (protParam instanceof KeyStore.CallbackHandlerProtection) {
425                try {
426                    passW = getPasswordFromCallBack(protParam);
427                } catch (Exception e) {
428                    throw new KeyStoreException(e);
429                }
430            } else {
431                throw new KeyStoreException("protParam should be PasswordProtection or "
432                                            + "CallbackHandlerProtection");
433            }
434        }
435
436        if (entry instanceof KeyStore.PrivateKeyEntry) {
437            KeyStore.PrivateKeyEntry prE = (KeyStore.PrivateKeyEntry) entry;
438            engineSetKeyEntry(alias, prE.getPrivateKey(), passW, prE
439                    .getCertificateChain());
440            return;
441        }
442
443        if (entry instanceof KeyStore.SecretKeyEntry) {
444            KeyStore.SecretKeyEntry skE = (KeyStore.SecretKeyEntry) entry;
445            engineSetKeyEntry(alias, skE.getSecretKey(), passW, null);
446            //            engineSetKeyEntry(alias, skE.getSecretKey().getEncoded(), null);
447            return;
448        }
449
450        throw new KeyStoreException("Entry object is neither PrivateKeyObject nor SecretKeyEntry "
451                                    + "nor TrustedCertificateEntry: " + entry);
452    }
453
454    /**
455     * Indicates whether the entry for the given alias is assignable to the
456     * provided {@code Class}.
457     *
458     * @param alias
459     *            the alias for the entry.
460     * @param entryClass
461     *            the type of the entry.
462     * @return {@code true} if the {@code Entry} for the alias is assignable to
463     *         the specified {@code entryClass}.
464     */
465    public boolean engineEntryInstanceOf(String alias,
466            Class<? extends KeyStore.Entry> entryClass) {
467        if (!engineContainsAlias(alias)) {
468            return false;
469        }
470
471        try {
472            if (engineIsCertificateEntry(alias)) {
473                return entryClass
474                        .isAssignableFrom(Class
475                                .forName("java.security.KeyStore$TrustedCertificateEntry"));
476            }
477
478            if (engineIsKeyEntry(alias)) {
479                if (entryClass.isAssignableFrom(Class
480                        .forName("java.security.KeyStore$PrivateKeyEntry"))) {
481                    return engineGetCertificate(alias) != null;
482                }
483
484                if (entryClass.isAssignableFrom(Class
485                        .forName("java.security.KeyStore$SecretKeyEntry"))) {
486                    return engineGetCertificate(alias) == null;
487                }
488            }
489        } catch (ClassNotFoundException ignore) {}
490
491        return false;
492    }
493
494    /*
495     * This method returns password which is encapsulated in
496     * CallbackHandlerProtection object If there is no implementation of
497     * CallbackHandler then this method returns null
498     */
499    static char[] getPasswordFromCallBack(KeyStore.ProtectionParameter protParam)
500            throws UnrecoverableEntryException {
501
502        if (protParam == null) {
503            return null;
504        }
505
506        if (!(protParam instanceof KeyStore.CallbackHandlerProtection)) {
507            throw new UnrecoverableEntryException("Incorrect ProtectionParameter");
508        }
509
510        String clName = Security.getProperty("auth.login.defaultCallbackHandler");
511        if (clName == null) {
512            throw new UnrecoverableEntryException("Default CallbackHandler was not defined");
513
514        }
515
516        try {
517            Class<?> cl = Class.forName(clName);
518            CallbackHandler cbHand = (CallbackHandler) cl.newInstance();
519            PasswordCallback[] pwCb = { new PasswordCallback("password: ", true) };
520            cbHand.handle(pwCb);
521            return pwCb[0].getPassword();
522        } catch (Exception e) {
523            throw new UnrecoverableEntryException(e.toString());
524        }
525    }
526}
527