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.jarsigner;
019:
020: import java.io.IOException;
021: import java.net.URI;
022: import java.net.URISyntaxException;
023: import java.security.KeyStore;
024: import java.security.KeyStoreException;
025: import java.security.NoSuchAlgorithmException;
026: import java.security.Provider;
027: import java.security.Security;
028: import java.security.UnrecoverableKeyException;
029: import java.util.logging.Logger;
030:
031: /**
032: * The class to parse the program arguments.
033: */
034: class ArgParser {
035: // options names to compare to //
036: final static String sVerify = "-verify";
037:
038: final static String sKeyStore = "-keystore";
039:
040: final static String sStoreType = "-storetype";
041:
042: final static String sStorePass = "-storepass";
043:
044: final static String sKeyPass = "-keypass";
045:
046: final static String sSigFile = "-sigfile";
047:
048: final static String sSignedJAR = "-signedjar";
049:
050: final static String sCerts = "-certs";
051:
052: final static String sVerbose = "-verbose";
053:
054: final static String sInternalSF = "-internalsf";
055:
056: final static String sSectionsOnly = "-sectionsonly";
057:
058: final static String sProvider = "-providerclass";
059:
060: final static String sProviderName = "-providername";
061:
062: final static String sCertProvider = "-certproviderclass";
063:
064: final static String sCertProviderName = "-certprovidername";
065:
066: final static String sSigProvider = "-sigproviderclass";
067:
068: final static String sSigProviderName = "-sigprovidername";
069:
070: final static String sKSProvider = "-ksproviderclass";
071:
072: final static String sKSProviderName = "-ksprovidername";
073:
074: final static String sMDProvider = "-mdproviderclass";
075:
076: final static String sMDProviderName = "-mdprovidername";
077:
078: final static String sTSA = "-tsa";
079:
080: final static String sTSAcert = "-tsacert";
081:
082: final static String sProxy = "-proxy";
083:
084: final static String sProxyType = "-proxytype";
085:
086: final static String sSilent = "-silent";
087:
088: final static String sAltSigner = "-altsigner";
089:
090: final static String sAltSignerPath = "-altsignerpath";
091:
092: /**
093: * @param args
094: * @param param
095: * @return new instance of JSParameters if param is null or updated param
096: * object if it is non-null. Returns null if args is null or
097: * zero-sized, an unknown option is found or an expected option
098: * value is not given or not of an expected type. If null is
099: * returned, the param object contents is not defined.
100: *
101: * @throws JarSignerException
102: * @throws IOException
103: * @throws KeyStoreException
104: * @throws UnrecoverableKeyException
105: * @throws NoSuchAlgorithmException
106: */
107: static JSParameters parseArgs(String[] args, JSParameters param)
108: throws JarSignerException, IOException, KeyStoreException,
109: UnrecoverableKeyException, NoSuchAlgorithmException {
110: if (args == null) {
111: return null;
112: }
113: if (args.length == 0) {
114: return null;
115: }
116: if (param == null) {
117: param = new JSParameters();
118: } else {
119: // clean the param
120: param.setDefault();
121: }
122:
123: try {
124: for (int i = 0; i < args.length; i++) {
125: if (args[i].equalsIgnoreCase(sVerify)) {
126: param.setVerify(true);
127: continue;
128: }
129: if (args[i].equalsIgnoreCase(sKeyStore)) {
130: param.setStoreURI(args[++i]);
131: continue;
132: }
133: if (args[i].equalsIgnoreCase(sStoreType)) {
134: param.setStoreType(args[++i]);
135: continue;
136: }
137: if (args[i].equalsIgnoreCase(sStorePass)) {
138: param.setStorePass(args[++i].toCharArray());
139: continue;
140: }
141: if (args[i].equalsIgnoreCase(sKeyPass)) {
142: param.setKeyPass(args[++i].toCharArray());
143: continue;
144: }
145: if (args[i].equalsIgnoreCase(sSigFile)) {
146: param.setSigFileName(args[++i]);
147: continue;
148: }
149: if (args[i].equalsIgnoreCase(sSignedJAR)) {
150: param.setSignedJARName(args[++i]);
151: continue;
152: }
153: if (args[i].equalsIgnoreCase(sCerts)) {
154: param.setCerts(true);
155: continue;
156: }
157: if (args[i].equalsIgnoreCase(sVerbose)) {
158: param.setVerbose(true);
159: continue;
160: }
161: if (args[i].equalsIgnoreCase(sSilent)) {
162: param.setSilent(true);
163: continue;
164: }
165: if (args[i].equalsIgnoreCase(sInternalSF)) {
166: param.setInternalSF(true);
167: continue;
168: }
169: if (args[i].equalsIgnoreCase(sSectionsOnly)) {
170: param.setSectionsOnly(true);
171: continue;
172: }
173: if (args[i].equalsIgnoreCase(sProvider)) {
174: param.setProvider(args[++i]);
175: addProvider(args[i]);
176: continue;
177: }
178: if (args[i].equalsIgnoreCase(sProviderName)) {
179: param.setProviderName(args[++i]);
180: continue;
181: }
182: if (args[i].equalsIgnoreCase(sCertProvider)) {
183: param.setCertProvider(args[++i]);
184: addProvider(args[i]);
185: continue;
186: }
187: if (args[i].equalsIgnoreCase(sCertProviderName)) {
188: param.setCertProviderName(args[++i]);
189: continue;
190: }
191: if (args[i].equalsIgnoreCase(sSigProvider)) {
192: param.setSigProvider(args[++i]);
193: addProvider(args[i]);
194: continue;
195: }
196: if (args[i].equalsIgnoreCase(sSigProviderName)) {
197: param.setSigProviderName(args[++i]);
198: continue;
199: }
200: if (args[i].equalsIgnoreCase(sKSProvider)) {
201: param.setKsProvider(args[++i]);
202: addProvider(args[i]);
203: continue;
204: }
205: if (args[i].equalsIgnoreCase(sKSProviderName)) {
206: param.setKsProviderName(args[++i]);
207: continue;
208: }
209: if (args[i].equalsIgnoreCase(sMDProvider)) {
210: param.setMdProvider(args[++i]);
211: addProvider(args[i]);
212: continue;
213: }
214: if (args[i].equalsIgnoreCase(sMDProviderName)) {
215: param.setMdProviderName(args[++i]);
216: continue;
217: }
218: if (args[i].equalsIgnoreCase(sTSA)) {
219: try {
220: param.setTsaURI(new URI(args[++i]));
221: } catch (URISyntaxException e) {
222: throw new JarSignerException("Argument "
223: + args[i] + " is not an URI");
224: }
225: continue;
226: }
227: if (args[i].equalsIgnoreCase(sTSAcert)) {
228: param.setTsaCertAlias(args[++i]);
229: continue;
230: }
231: if (args[i].equalsIgnoreCase(sProxy)) {
232: int colonPos = args[++i].lastIndexOf(':');
233: if (colonPos == -1) {
234: param.setProxy(args[i]);
235: continue;
236: }
237:
238: String proxy = args[i].substring(0, colonPos);
239: int port;
240: try {
241: port = Integer.parseInt(args[i].substring(
242: colonPos + 1, args[i].length()));
243: } catch (NumberFormatException e) {
244: throw new JarSignerException(
245: "Proxy port must be an integer value.");
246: }
247: param.setProxy(proxy);
248: param.setProxyPort(port);
249: continue;
250: }
251: if (args[i].equalsIgnoreCase(sAltSigner)) {
252: param.setAltSigner(args[++i]);
253: continue;
254: }
255: if (args[i].equalsIgnoreCase(sAltSignerPath)) {
256: param.setAltSignerPath(args[++i]);
257: continue;
258: }
259:
260: if ((param.isVerify() && i == args.length - 1)
261: || (!param.isVerify() && i == args.length - 2)) {
262: param.setJarURIorPath(args[i]);
263: continue;
264: }
265: if (!param.isVerify() && i == args.length - 1) {
266: param.setAlias(args[i]);
267: continue;
268: }
269:
270: System.out.println("Illegal option: " + args[i]);
271: return null;
272: }
273: } catch (ArrayIndexOutOfBoundsException e) {
274: // ignore the last option if its value is not provided
275: }
276:
277: // set specific provider names the same as the main provider name
278: // if their values are not set.
279: String providerName = param.getProviderName();
280: if (providerName != null) {
281: if (param.getCertProviderName() == null) {
282: param.setCertProviderName(providerName);
283: }
284: if (param.getSigProviderName() == null) {
285: param.setSigProviderName(providerName);
286: }
287: if (param.getKsProviderName() == null) {
288: param.setKsProviderName(providerName);
289: }
290: if (param.getMdProviderName() == null) {
291: param.setMdProviderName(providerName);
292: }
293: }
294:
295: // if the store password is not given, prompt for it
296: if (param.getStorePass() == null) {
297: param.setStorePass(UserInteractor
298: .getDataFromUser("Enter keystore password: "));
299: // check the password
300: param.getKeyStore();
301: }
302:
303: if (param.getAlias() == null && !param.isVerify()) {
304: param.setAlias(new String(UserInteractor
305: .getDataFromUser("Enter alias name: ")));
306: }
307: if (!param.getKeyStore().containsAlias(param.getAlias())) {
308: throw new JarSignerException("The alias "
309: + param.getAlias() + " does not exist in keystore");
310: }
311:
312: // if key password is not given, try to inplace it with store password
313: if (param.getKeyPass() == null) {
314: param.setKeyPass(tryStorePassAsKeyPass(param.getKeyStore(),
315: param.getAlias(), param.getStorePass()));
316: }
317:
318: // TODO: if decide to implement such abilities, remove this code
319: if (param.isInternalSF() || param.isSectionsOnly()
320: || param.getAltSigner() != null
321: || param.getAltSignerPath() != null) {
322: Logger
323: .getLogger(JSParameters.loggerName)
324: .warning(
325: "Options "
326: + sAltSigner
327: + ", "
328: + sAltSignerPath
329: + ", "
330: + sInternalSF
331: + ", "
332: + sSectionsOnly
333: + " are currently ignored since they eliminate "
334: + "useful optimizations. ");
335: }
336:
337: return param;
338: }
339:
340: // Method tries to get the key, associated with alias, using the storePass.
341: // If it can be recovered using the password, storePass is returned,
342: // otherwise - the password is prompted for. Another attempt to recover the
343: // key with entered password. If it is ok, it is returned, otherwise
344: // UnrecoverableKeyException is thrown.
345: private static char[] tryStorePassAsKeyPass(KeyStore keyStore,
346: String alias, char[] storePass) throws KeyStoreException,
347: IOException, UnrecoverableKeyException,
348: NoSuchAlgorithmException {
349: try {
350: // try to get a key with keystore password
351: // if succeed set key password same as that for keystore
352: keyStore.getKey(alias, storePass);
353:
354: // will not come here if exception is thrown
355: return storePass;
356: } catch (UnrecoverableKeyException e) {
357: // if key password is not equal to store password, ask for it.
358: char[] keyPass = UserInteractor
359: .getDataFromUser("Enter key password for <" + alias
360: + ">: ");
361: // if the new password is incorrect an exception will be thrown
362: try {
363: keyStore.getKey(alias, keyPass);
364: } catch (NoSuchAlgorithmException nsae) {
365: throw new NoSuchAlgorithmException(
366: "Cannot find the algorithm to recover the key. ",
367: e);
368: }
369: return keyPass;
370: } catch (NoSuchAlgorithmException e) {
371: throw new NoSuchAlgorithmException(
372: "Cannot find the algorithm to recover the key. ", e);
373: }
374: }
375:
376: // method for adding providers to java.security.Security
377: static int addProvider(String provider) throws JarSignerException {
378: try {
379: return Security.addProvider(Class.forName(provider)
380: .asSubclass(Provider.class).newInstance());
381: } catch (Exception e) {
382: throw new JarSignerException("Failed to load the provider "
383: + provider, e);
384: }
385: }
386:
387: }
|