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.File;
021: import java.io.IOException;
022: import java.io.InputStreamReader;
023: import java.security.KeyStore;
024: import java.security.KeyStoreException;
025: import java.security.NoSuchAlgorithmException;
026: import java.security.NoSuchProviderException;
027: import java.security.UnrecoverableKeyException;
028: import java.security.cert.CertificateException;
029:
030: /**
031: * The class to interact with the user - parse the program arguments, ask for
032: * confirmations, and necessary parameters which haven't been set in the command
033: * line.
034: */
035:
036: class ArgumentsParser {
037: // used to get additional data prompted
038: static InputStreamReader in = new InputStreamReader(System.in);
039:
040: // buffer for the data read
041: static char[] readData = new char[256];
042:
043: // number of symbols read
044: static int charsRead;
045:
046: // minimum password length permitted
047: static int minPwdLength = 6;
048:
049: // maximum number of attempts to set the password
050: static int maxNrOfAttempts = 3;
051:
052: // length of the "\r\n" which is added to the end of the line,
053: // when ENTER is pressed.
054: private static int newLineLength = 2;
055:
056: // options names to compare to //
057: // commands
058: final static String sGenkey = "-genkey";
059:
060: final static String sSelfcert = "-selfcert";
061:
062: final static String sImport = "-import";
063:
064: final static String sExport = "-export";
065:
066: final static String sStorepasswd = "-storepasswd";
067:
068: final static String sKeypasswd = "-keypasswd";
069:
070: final static String sCertreq = "-certreq";
071:
072: final static String sCheck = "-checkcrl";
073:
074: final static String sConvert = "-convert";
075:
076: final static String sVerify = "-verify";
077:
078: final static String sPrintcert = "-printcert";
079:
080: final static String sKeyclone = "-keyclone";
081:
082: final static String sDelete = "-delete";
083:
084: final static String sList = "-list";
085:
086: final static String sHelp = "-help";
087:
088: // additional options
089: final static String sKeystore = "-keystore";
090:
091: final static String sStoretype = "-storetype";
092:
093: final static String sProvider = "-provider";
094:
095: final static String sCertProvider = "-certprovider";
096:
097: final static String sKeyProvider = "-keyprovider";
098:
099: final static String sMdProvider = "-mdprovider";
100:
101: final static String sSigProvider = "-sigprovider";
102:
103: final static String sKsProvider = "-ksprovider";
104:
105: final static String sConvKsProvider = "-convprovider";
106:
107: final static String sStorepass = "-storepass";
108:
109: final static String sAlias = "-alias";
110:
111: final static String sKeyalg = "-keyalg";
112:
113: final static String sKeysize = "-keysize";
114:
115: final static String sSigalg = "-sigalg";
116:
117: final static String sDname = "-dname";
118:
119: final static String sKeypass = "-keypass";
120:
121: final static String sValidity = "-validity";
122:
123: final static String sV = "-v";
124:
125: final static String sJ = "-J";
126:
127: final static String sFile = "-file";
128:
129: final static String sNoprompt = "-noprompt";
130:
131: final static String sTrustcacerts = "-trustcacerts";
132:
133: final static String sRfc = "-rfc";
134:
135: final static String sNew = "-new";
136:
137: final static String sIssuerAlias = "-issuer";
138:
139: final static String sIssuerPass = "-issuerpass";
140:
141: final static String sSecretkey = "-secretkey";
142:
143: final static String sX509Version = "-x509version";
144:
145: final static String sCertSerial = "-certserial";
146:
147: final static String sDestAlias = "-dest";
148:
149: final static String sCRLfile = "-crlfile";
150:
151: final static String sCA = "-ca";
152:
153: final static String sConvStorePath = "-convkeystore";
154:
155: final static String sConvStorePass = "-convstorepass";
156:
157: final static String sConvStoreType = "-convtype";
158:
159: final static String sConvKeyEntries = "-convkeys";
160:
161: final static String sCAcertsPath = "-cacerts";
162:
163: final static String sCAcertsPass = "-cacertspass";
164:
165: /**
166: * The method finds known options in args which is usually taken from
167: * command line and sets the corresponding fields of the returned
168: * KeytoolParameters object to given values.
169: *
170: * @param args -
171: * String array to parse.
172: * @return null if args is null or zero-sized, one of the elements of args
173: * is null or empty, an unknown option is found or an expected
174: * option value is not given or not of an expected type.
175: * @throws IOException
176: * @throws NumberFormatException
177: * @throws KeytoolException
178: */
179:
180: static KeytoolParameters parseArgs(String[] args)
181: throws NumberFormatException, KeytoolException, IOException {
182: if (args == null || args.length == 0) {
183: return null;
184: }
185: KeytoolParameters param = new KeytoolParameters();
186:
187: // look for known options and get their values.
188: try {
189: for (int i = 0; i < args.length; i++) {
190:
191: // commands
192: if (args[i].compareToIgnoreCase(sGenkey) == 0) {
193: param.setCommand(Command.GENKEY);
194: continue;
195: }
196: if (args[i].compareToIgnoreCase(sSelfcert) == 0) {
197: param.setCommand(Command.SELFCERT);
198: continue;
199: }
200: if (args[i].compareToIgnoreCase(sImport) == 0) {
201: param.setCommand(Command.IMPORT);
202: continue;
203: }
204: if (args[i].compareToIgnoreCase(sExport) == 0) {
205: param.setCommand(Command.EXPORT);
206: continue;
207: }
208: if (args[i].compareToIgnoreCase(sStorepasswd) == 0) {
209: param.setCommand(Command.STOREPASSWD);
210: continue;
211: }
212: if (args[i].compareToIgnoreCase(sKeypasswd) == 0) {
213: param.setCommand(Command.KEYPASSWD);
214: continue;
215: }
216: if (args[i].compareToIgnoreCase(sCertreq) == 0) {
217: param.setCommand(Command.CERTREQ);
218: continue;
219: }
220: if (args[i].compareToIgnoreCase(sCheck) == 0) {
221: param.setCommand(Command.CHECK);
222: continue;
223: }
224: if (args[i].compareToIgnoreCase(sConvert) == 0) {
225: param.setCommand(Command.CONVERT);
226: continue;
227: }
228: if (args[i].compareToIgnoreCase(sVerify) == 0) {
229: param.setCommand(Command.VERIFY);
230: continue;
231: }
232: if (args[i].compareToIgnoreCase(sPrintcert) == 0) {
233: param.setCommand(Command.PRINTCERT);
234: continue;
235: }
236: if (args[i].compareToIgnoreCase(sKeyclone) == 0) {
237: param.setCommand(Command.KEYCLONE);
238: continue;
239: }
240: if (args[i].compareToIgnoreCase(sDelete) == 0) {
241: param.setCommand(Command.DELETE);
242: continue;
243: }
244: if (args[i].compareToIgnoreCase(sList) == 0) {
245: param.setCommand(Command.LIST);
246: continue;
247: }
248: if (args[i].compareToIgnoreCase(sHelp) == 0) {
249: param.setCommand(Command.HELP);
250: if (args.length == i + 2) {
251: param.setHelpTopic(args[++i]);
252: }
253: continue;
254: }
255:
256: // additional options
257: if (args[i].compareToIgnoreCase(sKeystore) == 0) {
258: param.setStorePath(args[++i]);
259: continue;
260: }
261: if (args[i].compareToIgnoreCase(sStoretype) == 0) {
262: param.setStoreType(args[++i]);
263: continue;
264: }
265: if (args[i].compareToIgnoreCase(sProvider) == 0) {
266: param.setProvider(args[++i]);
267: continue;
268: }
269: if (args[i].compareToIgnoreCase(sCertProvider) == 0) {
270: param.setCertProvider(args[++i]);
271: continue;
272: }
273: if (args[i].compareToIgnoreCase(sKeyProvider) == 0) {
274: param.setKeyProvider(args[++i]);
275: continue;
276: }
277: if (args[i].compareToIgnoreCase(sMdProvider) == 0) {
278: param.setMdProvider(args[++i]);
279: continue;
280: }
281: if (args[i].compareToIgnoreCase(sSigProvider) == 0) {
282: param.setSigProvider(args[++i]);
283: continue;
284: }
285: if (args[i].compareToIgnoreCase(sKsProvider) == 0) {
286: param.setKsProvider(args[++i]);
287: continue;
288: }
289: if (args[i].compareToIgnoreCase(sConvKsProvider) == 0) {
290: param.setConvKsProvider(args[++i]);
291: continue;
292: }
293: if (args[i].compareToIgnoreCase(sAlias) == 0) {
294: param.setAlias(args[++i]);
295: continue;
296: }
297: if (args[i].compareToIgnoreCase(sKeyalg) == 0) {
298: param.setKeyAlg(args[++i]);
299: continue;
300: }
301: if (args[i].compareToIgnoreCase(sSigalg) == 0) {
302: param.setSigAlg(args[++i]);
303: continue;
304: }
305: if (args[i].compareToIgnoreCase(sDname) == 0) {
306: param.setDName(args[++i]);
307: continue;
308: }
309: if (args[i].compareToIgnoreCase(sFile) == 0) {
310: param.setFileName(args[++i]);
311: continue;
312: }
313: if (args[i].compareToIgnoreCase(sIssuerAlias) == 0) {
314: param.setIssuerAlias(args[++i]);
315: continue;
316: }
317: if (args[i].compareToIgnoreCase(sStorepass) == 0) {
318: param.setStorePass(args[++i].toCharArray());
319: continue;
320: }
321: if (args[i].compareToIgnoreCase(sKeypass) == 0) {
322: param.setKeyPass(args[++i].toCharArray());
323: continue;
324: }
325: if (args[i].compareToIgnoreCase(sIssuerPass) == 0) {
326: param.setIssuerPass(args[++i].toCharArray());
327: continue;
328: }
329: if (args[i].compareToIgnoreCase(sCRLfile) == 0) {
330: param.setCrlFile(args[++i]);
331: continue;
332: }
333: if (args[i].compareToIgnoreCase(sDestAlias) == 0) {
334: param.setDestAlias(args[++i]);
335: continue;
336: }
337: if (args[i].compareToIgnoreCase(sNew) == 0) {
338: param.setNewPasswd(args[++i].toCharArray());
339: continue;
340: }
341: if (args[i].compareToIgnoreCase(sConvStorePath) == 0) {
342: param.setConvertedKeyStorePath(args[++i]);
343: continue;
344: }
345: if (args[i].compareToIgnoreCase(sConvStoreType) == 0) {
346: param.setConvertedKeyStoreType(args[++i]);
347: continue;
348: }
349: if (args[i].compareToIgnoreCase(sConvStorePass) == 0) {
350: param.setConvertedKeyStorePass(args[++i]
351: .toCharArray());
352: continue;
353: }
354: if (args[i].compareToIgnoreCase(sCAcertsPath) == 0) {
355: param.setCacertsPath(args[++i]);
356: continue;
357: }
358: if (args[i].compareToIgnoreCase(sCAcertsPass) == 0) {
359: param.setCacertsPass(args[++i].toCharArray());
360: continue;
361: }
362: if (args[i].compareToIgnoreCase(sKeysize) == 0) {
363:
364: param.setKeySize((new Integer(args[++i]))
365: .intValue());
366: if (param.getKeySize() <= 0) {
367: throw new KeytoolException("Key size"
368: + " must be more than zero.");
369: }
370: continue;
371: }
372: if (args[i].compareToIgnoreCase(sValidity) == 0) {
373: param.setValidity((new Integer(args[++i]))
374: .intValue());
375: if (param.getValidity() <= 0) {
376: throw new KeytoolException("Validity"
377: + " must be more than zero.");
378: }
379: continue;
380: }
381: if (args[i].compareToIgnoreCase(sX509Version) == 0) {
382: param.setX509version((new Integer(args[++i]))
383: .intValue());
384: if (param.getX509version() < 1
385: || param.getX509version() > 3) {
386: throw new KeytoolException(
387: "Certificate version must be "
388: + "1, 2 or 3");
389: }
390: continue;
391: }
392: if (args[i].compareToIgnoreCase(sCertSerial) == 0) {
393: param.setCertSerialNr((new Integer(args[++i]))
394: .intValue());
395: if (param.getCertSerialNr() <= 0) {
396: throw new KeytoolException(
397: "Certificate serial number"
398: + " must be more than zero.");
399: }
400: continue;
401: }
402:
403: // flags
404: if (args[i].compareToIgnoreCase(sNoprompt) == 0) {
405: param.setNoPrompt(true);
406: continue;
407: }
408: if (args[i].compareToIgnoreCase(sTrustcacerts) == 0) {
409: param.setTrustCACerts(true);
410: continue;
411: }
412: if (args[i].compareToIgnoreCase(sRfc) == 0) {
413: param.setRfc(true);
414: continue;
415: }
416: if (args[i].compareToIgnoreCase(sV) == 0) {
417: param.setVerbose(true);
418: continue;
419: }
420: if (args[i].compareToIgnoreCase(sSecretkey) == 0) {
421: param.setSecretKey(true);
422: continue;
423: }
424: if (args[i].compareToIgnoreCase(sCA) == 0) {
425: param.setCA(true);
426: continue;
427: }
428: if (args[i].compareToIgnoreCase(sConvKeyEntries) == 0) {
429: param.setConvertKeyEntries(true);
430: continue;
431: }
432:
433: System.out.println("Illegal option: " + args[i]);
434: return null;
435: }
436: } catch (ArrayIndexOutOfBoundsException e) {
437: // ignore the last option if its value is not provided
438: }
439:
440: Command cmd = param.getCommand();
441:
442: // check whether -v and -rfc options are used separately with -list.
443: if (cmd == Command.LIST && param.isRfc() && param.isVerbose()) {
444: throw new KeytoolException(
445: "There must not be both -v and -rfc "
446: + "options specified");
447: }
448:
449: // skip the store password setting if -printcert or -help commands were
450: // given.
451: if (cmd == Command.PRINTCERT || cmd == Command.HELP) {
452: return param;
453: }
454:
455: // if the store password has not been entered, prompt for it
456: if (param.getStorePass() == null) {
457: // get path to the store
458: String storePath = (param.getStorePath() != null) ? param
459: .getStorePath()
460: : KeytoolParameters.defaultKeystorePath;
461: // get store password
462: String prompt = "Enter keystore password: ";
463: System.out.print(prompt);
464: charsRead = in.read(readData);
465: char[] storePass;
466: File storeFile = new File(storePath);
467: if (storeFile.isDirectory()) {
468: throw new KeytoolException("The keystore path "
469: + storePath + " points to a directory.");
470: }
471: // Allow short passwords to unlock existing stores and
472: // disallow passwords shorter than minPwdLength symbols for new
473: // ones.
474: // Check whether the file exists
475: if (!storeFile.exists()) {
476: // check of password length and additional prompts for
477: // password are made here
478: storePass = promptLongerPassword(prompt);
479: } else {
480: // if the store exists don't check the length
481: storePass = new char[charsRead - newLineLength];// remove "\r\n"
482: System.arraycopy(readData, 0, storePass, 0, charsRead
483: - newLineLength);
484: }
485: if (storePass.length != 0) {
486: param.setStorePass(storePass);
487: } else {
488: param.setStorePass(null);
489: }
490: }
491:
492: return param;
493: }
494:
495: /**
496: * Checks if the needed values are set and, if not, prompts for them.
497: *
498: * This method must be called after the keystore is loaded.
499: *
500: * @param param
501: * @return
502: * @throws KeytoolException
503: * @throws UnrecoverableKeyException
504: * @throws NoSuchAlgorithmException
505: * @throws IOException
506: * @throws KeyStoreException
507: * @throws NoSuchProviderException
508: * @throws CertificateException
509: */
510: static void getAdditionalParameters(KeytoolParameters param)
511: throws KeytoolException, IOException, KeyStoreException,
512: UnrecoverableKeyException, NoSuchAlgorithmException,
513: CertificateException, NoSuchProviderException {
514: // this method must be called after the keystore is loaded.
515: KeyStore keyStore = param.getKeyStore();
516:
517: // set the alias to "mykey" if it's not set up
518: Command command = param.getCommand();
519: if (param.getAlias() == null
520: && (command == Command.KEYCLONE
521: || command == Command.EXPORT
522: || command == Command.CERTREQ
523: || command == Command.GENKEY
524: || command == Command.SELFCERT
525: || command == Command.IMPORT || command == Command.KEYPASSWD)) {
526: param.setAlias("mykey");
527: }
528: String alias = param.getAlias();
529:
530: // check if the alias exists
531: if (command == Command.CERTREQ
532: || command == Command.DELETE
533: || command == Command.EXPORT
534: || command == Command.KEYCLONE
535: || command == Command.KEYPASSWD
536: || command == Command.SELFCERT
537: || (command == Command.LIST && param.getAlias() != null)) {
538: if (!keyStore.containsAlias(param.getAlias())) {
539: throw new KeytoolException("Alias <" + alias
540: + "> doesn't exist");
541: }
542: } else if (command == Command.GENKEY) {
543: if (keyStore.containsAlias(param.getAlias())) {
544: throw new KeytoolException(
545: "Key(s) not generated, alias <" + alias
546: + "> already exists.");
547: }
548: }
549:
550: // if the key password has not been entered and the password is required
551: // to get the key (it is not a password for a newly created entry)
552: if (param.getKeyPass() == null
553: && (command == Command.KEYCLONE
554: || command == Command.EXPORT
555: || command == Command.CERTREQ
556: || command == Command.KEYPASSWD
557: || command == Command.SELFCERT
558: // if keystore contains alias, import of a certificate reply
559: // is considered, otherwise password is unnecessary.
560: || (command == Command.IMPORT && keyStore
561: .containsAlias(alias)))) {
562: param.setKeyPass(tryStorePassAsKeyPass(keyStore, alias,
563: param.getStorePass()));
564: }
565:
566: switch (command) {
567: case GENKEY:
568: // if the distinguished name is not specified, get the
569: // necessary data
570: if (param.getDName() == null && !param.isSecretKey()) {
571: param.setDName(getDistinguishedName());
572: }
573: // if the key password has not been entered (and can equal store
574: // password)
575: if (param.getKeyPass() == null) {
576: param.setKeyPass(getNewPassword(null, alias, param
577: .getStorePass()));
578: }
579:
580: String issuerAlias = param.getIssuerAlias();
581: // if the newly generated certificate should be signed with
582: // another certificate chain from the keystore.
583: if (issuerAlias != null && !param.isSecretKey()) {
584: // Check if the issuer password was entered. If not, try storepass.
585: // If it's not ok, prompt the user.
586: if (param.getIssuerPass() == null) {
587: param.setIssuerPass(tryStorePassAsKeyPass(keyStore,
588: issuerAlias, param.getStorePass()));
589: }
590: }
591:
592: break;
593:
594: case KEYCLONE:
595: // prompt for a destination alias, if one is not specified
596: if (param.getDestAlias() == null) {
597: System.out.print("Enter destination alias name: ");
598: charsRead = in.read(readData);
599: if (charsRead <= newLineLength) {
600: throw new KeytoolException(
601: "Must specify destination alias");
602: } else {
603: param.setDestAlias(new String(readData).substring(
604: 0, charsRead - newLineLength));
605: }
606: }
607: // if the password for a newly created entry is not specified,
608: // ask for it.
609: if (param.getNewPasswd() == null) {
610: param.setNewPasswd(getNewPassword(alias, param
611: .getDestAlias(), param.getKeyPass()));
612: }
613: break;
614: case DELETE:
615: // prompt for an alias to delete, if one is not specified
616: if (alias == null) {
617: System.out.print("Enter alias name: ");
618: charsRead = in.read(readData);
619: if (charsRead <= newLineLength) {
620: throw new KeytoolException("Must specify alias");
621: } else {
622: param.setAlias(new String(readData).substring(0,
623: charsRead - newLineLength));
624: }
625: }
626: break;
627: case STOREPASSWD:
628: case KEYPASSWD:
629: String prompt;
630: String promptReenter;
631: // prompt for a new password, if it is not specified
632: if (command == Command.KEYPASSWD) {
633: prompt = "Enter new key password for <" + alias + ">: ";
634: promptReenter = "Re-enter new keystore password for <"
635: + alias + ">: ";
636: } else { // if param.getCommand() == Command.STOREPASSWD
637: // prompt for a new store password, if it is not specified
638: prompt = "Enter new keystore password: ";
639: promptReenter = "Re-enter new keystore password: ";
640: }
641:
642: // if the new password is not entered
643: if (param.getNewPasswd() == null) {
644: System.out.print(prompt);
645: charsRead = in.read(readData);
646: char[] password = promptLongerPassword(prompt);
647: System.out.print(promptReenter);
648: charsRead = in.read(readData);
649: if (charsRead == password.length + newLineLength) {
650: for (int i = 0; i < password.length; i++) {
651: if (readData[i] != password[i]) {
652: throw new KeytoolException(
653: "Passwords do not match");
654: }
655: }
656: param.setNewPasswd(password);
657: } else {
658: throw new KeytoolException("Passwords do not match");
659: }
660: // if entered a short password in the command line
661: } else if (param.getNewPasswd().length < minPwdLength) {
662: throw new KeytoolException(
663: "The password must be at least " + minPwdLength
664: + " characters");
665: }
666:
667: break;
668: case LIST:
669: if (alias != null) {
670: // This check is not where the same thing for other
671: // commands done, because (alias != null) check is
672: // necessary.
673: if (keyStore.entryInstanceOf(alias,
674: KeyStore.SecretKeyEntry.class)
675: && param.getKeyPass() == null) {
676: param.setKeyPass(tryStorePassAsKeyPass(keyStore,
677: alias, param.getStorePass()));
678: }
679: }
680: break;
681: }// switch (param.getCommand())
682:
683: }
684:
685: /**
686: * The method prompts user to enter data to initialize an X.500
687: * Distinguished Name to create a new certificate. It gets the data asks if
688: * it is correct, and if it is returns the String representing the
689: * distinguished name, if the data entered is not correct prompts to enter
690: * it again.
691: *
692: * @return - String representing the distinguished names
693: */
694: private static String getDistinguishedName() throws IOException,
695: KeytoolException {
696: // X.500 principal: CN, OU, O, L, ST, C;
697: String[] dnFields = { "CN=", ", OU=", ", O=", ", L=", ", ST=",
698: ", C=" };
699: // the flag is set to true, when the user confirms that
700: // the data he (or she) entered is correct.
701: boolean isCorrect = false;
702: // X.500 Distinguished Name. It will look like:
703: // "CN=user_name, OU=org_unit, O=organization, L=city, ST=state,
704: // C=com"
705: StringBuffer dname = new StringBuffer(256);
706: // the flag is set to true when there are spaces and/or commas in
707: // the fields of the distinguished name
708: boolean needQuotes = false;
709:
710: // data that user enters is saved here
711: StringBuffer[] dnFieldsData = new StringBuffer[] {
712: new StringBuffer("Unknown"),
713: new StringBuffer("Unknown"),
714: new StringBuffer("Unknown"),
715: new StringBuffer("Unknown"),
716: new StringBuffer("Unknown"),
717: new StringBuffer("Unknown") };
718:
719: // prompts to show to user when asking to enter some data
720: String[] prompts = { "Enter your first and last name: ",
721: "Enter the name of your organizational unit: ",
722: "Enter the name of your organization: ",
723: "Enter the name of your city or locality: ",
724: "Enter the name of your state or province: ",
725: "Enter the two-letter country code for the unit: ",
726: "Is the information you entered correct? [no]: " };
727:
728: // do it until user confirms that the data he entered is true.
729: while (!isCorrect) {
730: // clear dname if it is not empty
731: if (dname.length() > 0) {
732: dname.delete(0, dname.length());
733: }
734: for (int i = 0; i < dnFieldsData.length; i++) {
735: // ask the user to enter info
736: System.out.println(prompts[i]);
737: // print the current value of the field
738: System.out.print("[" + dnFieldsData[i] + "]: ");
739: dname.append(dnFields[i]);
740: charsRead = in.read(readData);
741: // if something was entered put the new value to
742: // dnFieldsData
743: // else don't change what was entered before.
744: if (charsRead > newLineLength) {
745: // check whether quotes are needed.
746: needQuotes = false;
747: for (int j = 0; j < charsRead - newLineLength; j++) {
748: if (readData[j] == ',' || readData[j] == ' ') {
749: needQuotes = true;
750: break;
751: }
752: }
753: // if quotes are not needed
754: if (!needQuotes) {
755: // copy the read data into the StringBuffer.
756: // don't need the '\r' and \n' in the end
757: dnFieldsData[i].insert(0, readData, 0,
758: charsRead - newLineLength);
759: dnFieldsData[i].delete(charsRead
760: - newLineLength, dnFieldsData[i]
761: .length());
762: } else {// if quotes are needed, add them to the begin
763: // and to the end
764: dnFieldsData[i].insert(0, '\"');
765: dnFieldsData[i].insert(1, readData, 0,
766: charsRead - newLineLength);
767: dnFieldsData[i].insert(charsRead - 1, '\"');
768: dnFieldsData[i].delete(charsRead,
769: dnFieldsData[i].length());
770: }
771: }
772: dname.append(dnFieldsData[i]);
773: }
774: // print the distinguished name with the fields filled
775: System.out.println(dname);
776: // confirm, if the user enters 'y' or "yes"
777: // any other input results in asking the questions again
778: isCorrect = getConfirmation(prompts[prompts.length - 1],
779: true);
780: }
781: // save the data when got the confirmation from the user
782: return new String(dname);
783: }
784:
785: /**
786: * The method should be called only after the password was entered and put into
787: * readData. charsRead also shouldn't be changed after the password was
788: * entered and before the method is called. If charsRead is less than
789: * minPwdLength + newLineLength, the method just copies the password from
790: * readData into a newly created char array; otherwise it prompts for a
791: * longer password for maxNrOfAttempts times.
792: *
793: * @param prompt
794: * @return new password of length equal or longer than minPwdLength
795: * @throws IOException
796: * @throws KeytoolException
797: */
798: private static char[] promptLongerPassword(String prompt)
799: throws IOException, KeytoolException {
800: int cntAttempts = 0;
801: while (charsRead < minPwdLength + newLineLength) {
802: System.out.println("The password must be at least "
803: + minPwdLength + " characters");
804: System.out.print(prompt);
805: charsRead = in.read(readData);
806: ++cntAttempts;
807: if (cntAttempts >= maxNrOfAttempts) {
808: throw new KeytoolException("Too many failures. "
809: + "Please, try again later.");
810: }
811: }
812: char[] password = new char[charsRead - newLineLength];
813: System.arraycopy(readData, 0, password, 0, charsRead
814: - newLineLength);
815: return password;
816: }
817:
818: /**
819: * Does all work to get from the user a password for a newly created (cloned
820: * or generated) key.
821: *
822: * @param -
823: * srcAlias is the alias of the entry to clone, or if it is null,
824: * the keystore password will be prompted to use.
825: * @param -
826: * destAlias is the alias of the newly created entry.
827: * @param -
828: * srcPass is the password to be used with a new entry if the
829: * user doesn't enter a new one.
830: *
831: * @return - char array representing the password for the entry. It can be
832: * equal to the keystore password or the password of a cloned key.
833: */
834: private static char[] getNewPassword(String srcAlias,
835: String destAlias, char[] srcPass) throws IOException,
836: KeytoolException {
837: if (destAlias == null) {
838: return null;
839: }
840: String prompt = "Enter key password for <" + destAlias + ">: ";
841: System.out.print(prompt);
842: if (srcAlias == null) {
843: System.out.print("(Press RETURN if same as for keystore) ");
844: } else {
845: System.out.print("(Press RETURN if same as for <"
846: + srcAlias + ">) ");
847: }
848: charsRead = in.read(readData);
849: char[] destPass;
850: // if RETURN was pressed
851: if (charsRead <= newLineLength) {
852: destPass = new char[srcPass.length];
853: System.arraycopy(srcPass, 0, destPass, 0, srcPass.length);
854: } else {// if some password was entered
855: destPass = promptLongerPassword(prompt);
856: }
857: return destPass;
858: }
859:
860: /**
861: * Prints a promt. Reads what the user enters. If the user has entered
862: * 'y'/"yes" or 'n'/"no" (case insensitively) the method returns
863: * respectively true or false. Depending on acceptAnother parameter the
864: * method can return false if anything except 'y' or "yes" is entered, or it
865: * can prompt for a correct answer. If only ENTER is pressed false is
866: * returned.
867: *
868: * @param promt -
869: * text printed to ask the user for a confirmation
870: * @param acceptAnother -
871: * if set to true, the method returns true if and only if the
872: * user enters 'y' or "yes"; if set to false prompts to reenter
873: * the answer from user until 'y'/"yes" or 'n'/"no" is entered.
874: * @return true if the user confirms the request, false - otherwise.
875: * @throws IOException
876: */
877: static boolean getConfirmation(String promt, boolean acceptAnother)
878: throws IOException, KeytoolException {
879: int counter = 0;
880: while (counter++ < 100) {
881: System.out.print(promt);
882: charsRead = in.read(readData);
883: // if pressed ENTER return the default value
884: if (charsRead == newLineLength) {
885: return false;
886: }
887: // confirm, if the user enters 'y' or "yes"
888: if ((charsRead == newLineLength + 1 && (readData[0] == 'y' || readData[0] == 'Y'))
889: || (charsRead == newLineLength + 3 && "yes"
890: .equalsIgnoreCase(new String(readData)
891: .substring(0, 3)))) {
892: return true;
893: } else if (acceptAnother) {
894: return false;
895: } else {
896: // if entered 'n' or "no"
897: if (readData[0] == 'n'
898: || readData[0] == 'N'
899: && ((charsRead == newLineLength + 1) || (charsRead == newLineLength + 2
900: && readData[0] == 'o' || readData[0] == 'O'))) {
901: return false;
902: } else {
903: System.out
904: .println("Wrong answer, please, try again");
905: }
906: }
907: }
908: throw new KeytoolException("Too many failures. ");
909: }
910:
911: // method tries to get the key, associated with alias, using the storePass,
912: // if it can be recovered using the password storePass is returned,
913: // otherwise - the password is prompted for. Another attempt to recover the
914: // key with entered password. If it is ok, it is returned, otherwise
915: // UnrecoverableKeyException is thrown.
916: private static char[] tryStorePassAsKeyPass(KeyStore keyStore,
917: String alias, char[] storePass) throws KeyStoreException,
918: IOException, UnrecoverableKeyException,
919: NoSuchAlgorithmException {
920: try {
921: // try to get a key with keystore password
922: // if succeed set key password same as that for keystore
923: keyStore.getKey(alias, storePass);
924:
925: // will not come here if exception is thrown
926: return storePass;
927: } catch (UnrecoverableKeyException e) {
928: // if key password is not equal to store password, ask for it.
929: System.out
930: .print("Enter key password for <" + alias + ">: ");
931: charsRead = in.read(readData);
932: char[] keyPass = new char[charsRead - newLineLength];
933: System.arraycopy(readData, 0, keyPass, 0, charsRead
934: - newLineLength);
935: // if the new password is incorrect an exception will be thrown
936: try {
937: keyStore.getKey(alias, keyPass);
938: } catch (NoSuchAlgorithmException nsae) {
939: throw new NoSuchAlgorithmException(
940: "Cannot find the algorithm to recover the key. ",
941: e);
942: }
943: return keyPass;
944: } catch (NoSuchAlgorithmException e) {
945: throw new NoSuchAlgorithmException(
946: "Cannot find the algorithm to recover the key. ", e);
947: }
948: }
949:
950: }
|