001: /*
002: * Licensed to the Apache Software Foundation (ASF) under one or more
003: * contributor license agreements. See the NOTICE file distributed with
004: * this work for additional information regarding copyright ownership.
005: * The ASF licenses this file to You under the Apache License, Version 2.0
006: * (the "License"); you may not use this file except in compliance with
007: * the License. You may obtain a copy of the License at
008: *
009: * http://www.apache.org/licenses/LICENSE-2.0
010: *
011: * Unless required by applicable law or agreed to in writing, software
012: * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
013: * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
014: * License for the specific language governing permissions and limitations under
015: * the License.
016: */
017:
018: package org.apache.harmony.tools.keytool;
019:
020: import java.io.FileNotFoundException;
021: import java.io.IOException;
022: import java.security.KeyStore;
023: import java.security.KeyStoreException;
024: import java.security.NoSuchAlgorithmException;
025: import java.security.NoSuchProviderException;
026: import java.security.PrivateKey;
027: import java.security.PublicKey;
028: import java.security.UnrecoverableKeyException;
029: import java.security.cert.CertPathBuilderException;
030: import java.security.cert.CertificateException;
031: import java.security.cert.X509Certificate;
032: import java.util.Arrays;
033: import java.util.Collection;
034:
035: /**
036: * Class for importing certificates - adding to trusted certificates or
037: * Certificate Signing Request (CSR) replies from certificate authorities
038: * (CAs). CSR reply can be a single X.509 certificate or a PKCS#7-formatted
039: * certificate chain containing X.509 certificates. X.509 v1, v2 and v3
040: * certificates are supported. Certificates to import can be in binary (DER) or
041: * printable (PEM) encoding format.
042: */
043: public class CertImporter {
044:
045: /**
046: * Reads an X.509 certificate or a PKCS#7 formatted certificate chain from
047: * the file specified in param and puts it into the entry identified by the
048: * supplied alias. If the input file is not specified, the certificates are
049: * read from the standard input.
050: *
051: * @param param
052: * @throws KeytoolException
053: * @throws IOException
054: * @throws CertPathBuilderException
055: * @throws UnrecoverableKeyException
056: * @throws NoSuchAlgorithmException
057: * @throws CertificateException
058: * @throws FileNotFoundException
059: * @throws NoSuchProviderException
060: * @throws KeyStoreException
061: */
062: static void importCert(KeytoolParameters param)
063: throws FileNotFoundException, CertificateException,
064: NoSuchAlgorithmException, UnrecoverableKeyException,
065: CertPathBuilderException, IOException, KeytoolException,
066: NoSuchProviderException, KeyStoreException {
067:
068: String alias = param.getAlias();
069: KeyStore keyStore = param.getKeyStore();
070: boolean contains = keyStore.containsAlias(alias);
071: String certProvider = (param.getCertProvider() != null) ? param
072: .getCertProvider() : param.getProvider();
073:
074: // if the alias already exists, try to import the certificate as
075: // a cert reply
076: if (contains
077: && keyStore.entryInstanceOf(alias,
078: KeyStore.PrivateKeyEntry.class)) {
079: // read the certificates
080: Collection<X509Certificate> certCollection = CertReader
081: .readCerts(param.getFileName(), false, certProvider);
082:
083: importReply(param, certCollection);
084: } else if (!contains) { // import a trusted certificate
085: // read the certificate
086: Collection<X509Certificate> trustedCert = CertReader
087: .readCerts(param.getFileName(), true, certProvider);
088:
089: importTrusted(param, trustedCert.iterator().next());
090: } else {// if the existing entry is not a private key entry
091: throw new KeytoolException(
092: "Failed to import the certificate. \nAlias <"
093: + alias
094: + "> already exists and is not a private key entry");
095: }
096: }
097:
098: /**
099: * Imports a Certificate Signing Request (CSR) reply - single X.509
100: * certificate or PKCS#7 formatted certificate chain, consisting of X.509
101: * certificates.
102: *
103: * @param param
104: * @throws FileNotFoundException
105: * @throws CertificateException
106: * @throws IOException
107: * @throws KeyStoreException
108: * @throws NoSuchAlgorithmException
109: * @throws UnrecoverableKeyException
110: * @throws CertPathBuilderException
111: * @throws InvalidAlgorithmParameterException
112: * @throws KeytoolException
113: * @throws NoSuchProviderException
114: */
115: private static void importReply(KeytoolParameters param,
116: Collection<X509Certificate> certCollection)
117: throws FileNotFoundException, CertificateException,
118: IOException, KeyStoreException, NoSuchAlgorithmException,
119: UnrecoverableKeyException, CertPathBuilderException,
120: KeytoolException, NoSuchProviderException {
121: if (certCollection.size() == 1) {
122: importSingleX509Reply(param, certCollection.iterator()
123: .next());
124: } else if (certCollection.size() > 0) {
125: importCertChain(param, certCollection);
126: }
127: }
128:
129: /**
130: * Imports a single X.509 certificate Certificate Signing Request (CSR)
131: * reply.
132: *
133: * @param param
134: * @param newCert
135: * @throws CertificateException
136: * @throws FileNotFoundException
137: * @throws IOException
138: * @throws KeyStoreException
139: * @throws NoSuchAlgorithmException
140: * @throws UnrecoverableKeyException
141: * @throws CertPathBuilderException
142: * @throws KeytoolException
143: * @throws NoSuchProviderException
144: */
145:
146: private static void importSingleX509Reply(KeytoolParameters param,
147: X509Certificate newCert) throws CertificateException,
148: FileNotFoundException, IOException, KeyStoreException,
149: NoSuchAlgorithmException, UnrecoverableKeyException,
150: CertPathBuilderException, KeytoolException,
151: NoSuchProviderException {
152:
153: String alias = param.getAlias();
154: KeyStore keyStore = param.getKeyStore();
155:
156: // the certificate to be replaced with certificate reply.
157: X509Certificate csrCert = (X509Certificate) keyStore
158: .getCertificate(alias);
159: // quit if public keys of the imported certificate and csrCert don't
160: // match
161: PublicKey publicKey = csrCert.getPublicKey();
162: if (!Arrays.equals(publicKey.getEncoded(), newCert
163: .getPublicKey().getEncoded())) {
164: throw new KeytoolException("Public keys don't match.");
165: }
166: // quit if the certificates are identical
167: if (newCert.equals(csrCert)) {
168: throw new KeytoolException(
169: "Certificate reply is identical to the "
170: + "certificate in keystore");
171: }
172:
173: // save the private key to put it in a newly created entry
174: PrivateKey privateKey;
175: try {
176: privateKey = (PrivateKey) keyStore.getKey(alias, param
177: .getKeyPass());
178: } catch (NoSuchAlgorithmException e) {
179: throw new NoSuchAlgorithmException(
180: "Cannot find the algorithm to recover the key. ", e);
181: }
182:
183: X509Certificate[] newChain = CertChainVerifier
184: .buildFullCertPath(param, newCert);
185:
186: // changing the certificate chain //
187: // remove the entry with old certificate chain
188: keyStore.deleteEntry(alias);
189:
190: // set the new certificate chain
191: keyStore.setKeyEntry(alias, privateKey, param.getKeyPass(),
192: newChain);
193: param.setNeedSaveKS(true);
194: System.out
195: .println("Certificate reply is successfully installed into the keystore.");
196: }
197:
198: /**
199: * Imports an X.509 certificate into a trusted certificate entry.
200: *
201: * @param param
202: * @throws KeytoolException
203: * @throws IOException
204: * @throws CertPathBuilderException
205: * @throws CertificateException
206: * @throws NoSuchAlgorithmException
207: * @throws KeyStoreException
208: * @throws NoSuchProviderException
209: */
210: private static void importTrusted(KeytoolParameters param,
211: X509Certificate newCert) throws NoSuchAlgorithmException,
212: CertificateException, CertPathBuilderException,
213: IOException, KeytoolException, KeyStoreException,
214: NoSuchProviderException {
215: String alias = param.getAlias();
216: KeyStore keyStore = param.getKeyStore();
217:
218: // should the certificate be added to the store or not
219: boolean addIt = false;
220:
221: // if "-noprompt" option has been specified, don't make
222: // additional checks.
223: if (param.isNoPrompt()) {
224: addIt = true;
225: } else {
226: // search for an equal certificate in the keystore
227: String equalCertName = keyStore
228: .getCertificateAlias(newCert);
229:
230: if (equalCertName != null) {
231: // if an equal certificate exists in the store
232: System.out
233: .println("The certificate already exists in the "
234: + "keystore under alias <"
235: + equalCertName + ">");
236: // ask if a duplicating certificate should be added to the
237: // store
238: addIt = ArgumentsParser.getConfirmation(
239: "Do you still want to add it? [no] ", false);
240: } else {
241: try {
242: if (CertChainVerifier.buildCertPath(param, newCert) != null) {
243: addIt = true;
244: }
245: } catch (Exception e) {
246: // if the certificate chain cannot be built
247: // print it and ask if it should be trusted or not.
248: String mdProvider = (param.getMdProvider() != null) ? param
249: .getMdProvider()
250: : param.getProvider();
251: KeyStoreCertPrinter.printX509CertDetailed(newCert,
252: mdProvider);
253: addIt = ArgumentsParser.getConfirmation(
254: "Trust this certificate? [no] ", false);
255: }
256: }
257: }
258: if (addIt) {
259: keyStore.setCertificateEntry(alias, newCert);
260: param.setNeedSaveKS(true);
261: System.out
262: .println("The certificate is added to the keystore\n");
263: } else {
264: System.out
265: .println("The certificate is not added to the keystore\n");
266: }
267: }
268:
269: /**
270: * Imports a PKCS#7-formatted certificate chain as a CSR reply. The
271: * certificate chain is firstly ordered. After that top-level certificate of
272: * the chain is checked to be a trusted one. If it is not a trusted
273: * certificate, the user is asked if the certificate should be trusted. If
274: * the certificate is considered to be trusted, old certificate chain,
275: * associated with param.getAlias() is replaced with the new one.
276: * Certificates can be in DER or PEM format.
277: *
278: * @param param
279: * @param newCerts
280: * @throws Exception
281: * @throws NoSuchAlgorithmException
282: * @throws KeytoolException
283: * @throws KeyStoreException
284: * @throws IOException
285: * @throws UnrecoverableKeyException
286: * @throws NoSuchProviderException
287: * @throws CertificateException
288: */
289: private static void importCertChain(KeytoolParameters param,
290: Collection<X509Certificate> newCerts)
291: throws NoSuchAlgorithmException, KeytoolException,
292: KeyStoreException, IOException, UnrecoverableKeyException,
293: NoSuchProviderException, CertificateException {
294:
295: String alias = param.getAlias();
296: KeyStore keyStore = param.getKeyStore();
297: // get the public key of the certificate, associated with alias,
298: // to import reply to.
299: PublicKey publicKey = keyStore.getCertificate(alias)
300: .getPublicKey();
301: // order the certificate chain
302: X509Certificate[] orderedChain = CertChainVerifier.orderChain(
303: newCerts, publicKey);
304: // get the top-level certificate in the chain
305: X509Certificate lastInChain = orderedChain[orderedChain.length - 1];
306:
307: // should the chain be added to the keystore or not
308: boolean needAddChain;
309: // try to build a chain of trust beginning with the top certificate
310: needAddChain = CertChainVerifier.isTrusted(param, lastInChain);
311:
312: if (!needAddChain) {
313: // If couldn't build full cert path for some reason,
314: // ask user if the certificate should be trusted.
315: System.out
316: .println("Top-level certificate in the reply chain:\n");
317: String mdProvider = (param.getMdProvider() != null) ? param
318: .getMdProvider() : param.getProvider();
319: KeyStoreCertPrinter.printX509CertDetailed(lastInChain,
320: mdProvider);
321: needAddChain = ArgumentsParser
322: .getConfirmation(
323: "... is not trusted.\n"
324: + "Do you still want to install the reply? [no]: ",
325: false);
326:
327: if (!needAddChain) {
328: System.out.println("The certificate reply is " + "not "
329: + "installed into the keystore.");
330: return;
331: }
332: }
333:
334: // replacing old certificate chain with the new one
335: char[] keyPassword = param.getKeyPass();
336: PrivateKey privateKey = (PrivateKey) keyStore.getKey(alias,
337: keyPassword);
338: keyStore.deleteEntry(alias);
339: keyStore.setKeyEntry(alias, privateKey, keyPassword,
340: orderedChain);
341: param.setNeedSaveKS(true);
342: System.out.println("The certificate reply is "
343: + "successfully " + "installed into the keystore.");
344: }
345:
346: }
|