001: package tide.export;
002:
003: import snow.utils.ProcessUtils;
004: import snow.utils.StringGobblerLineListener;
005: import snow.utils.storage.FileUtils;
006: import snow.utils.StreamGobbler;
007: import tide.editor.*;
008: import tide.project.*;
009: import snow.utils.gui.*;
010: import javax.swing.*;
011: import javax.swing.border.*;
012: import java.awt.*;
013: import java.awt.event.*;
014: import snow.Basics;
015: import java.security.*;
016: import java.io.*;
017: import java.util.*;
018:
019: /**
020: * panel to specify keytool location, alias and password.
021: * With key generation option and jar file signature (another).
022: */
023: public class JarSignerPanel extends JPanel {
024: // vector (to be combobox model)
025: final private Vector<String> aliases = new Vector<String>();
026:
027: private final FileField keyStoreNameField = new FileField("",
028: false, "Choose the keystore file", JFileChooser.FILES_ONLY);
029: private final JPasswordField keyStorePasswordField = new JPasswordField(
030: "", 8);
031: private final JComboBox keyStoreAliasComboBox = new JComboBox(
032: aliases);
033: private final JButton signSomeFile = new JButton("Sign a jar file");
034: private final JButton keyStoreInfos = new JButton("Infos");
035:
036: private final JPanel contentPanel = new JPanel();
037:
038: public JarSignerPanel(final JDialog parent) {
039: super (new BorderLayout(0, 0));
040: add(contentPanel, BorderLayout.CENTER);
041:
042: this .keyStoreNameField.setComponentWidth(300);
043: keyStoreNameField.setAutoColorized();
044: keyStoreAliasComboBox
045: .setPreferredSize(new Dimension(120,
046: (int) keyStoreNameField.getPreferredSize()
047: .getHeight()));
048:
049: // initialization
050: readValuesFromIniFile();
051: setOpaque(false);
052: int fs = UIManager.getFont("TextField.font").getSize();
053:
054: contentPanel.setBorder(BorderFactory.createRaisedBevelBorder());
055: contentPanel.setLayout(new BoxLayout(contentPanel,
056: BoxLayout.Y_AXIS));
057:
058: JButton createKeysButton = new JButton(
059: "Generate a new keypair...");
060: createKeysButton.setMargin(new Insets(0, 2, 0, 2));
061: createKeysButton.setFocusPainted(false);
062: createKeysButton.addActionListener(new ActionListener() {
063: public void actionPerformed(ActionEvent ee) {
064: KeystoreGenerator gen = new KeystoreGenerator(parent);
065: gen.pack();
066: gen.setLocationRelativeTo(parent);
067: gen.setVisible(true);
068: if (gen.wasSuccessfulyTerminated) {
069: keyStoreNameField.setPath(gen.getKeystoreLocation()
070: .getAbsolutePath());
071: keyStorePasswordField.setText(new String(gen
072: .getKeystorePassword()));
073: searchAliasInKeyStore(keyStoreNameField.getPath(),
074: gen.getKeystorePassword());
075: saveSettingsInInifile();
076: }
077: }
078: });
079:
080: JPanel jarSignerPanel0 = new JPanel(new FlowLayout(
081: FlowLayout.LEFT, fs / 2, fs / 4));
082: contentPanel.add(jarSignerPanel0);
083: jarSignerPanel0.setOpaque(false);
084: jarSignerPanel0.add(createKeysButton);
085:
086: signSomeFile.setMargin(new Insets(0, 2, 0, 2));
087: signSomeFile.setFocusPainted(false);
088: signSomeFile.addActionListener(new ActionListener() {
089: public void actionPerformed(ActionEvent ee) {
090: if (getKeyStorePath() == null
091: || !getKeyStorePath().exists()) {
092: JOptionPane.showMessageDialog(null,
093: "Please select a valid keystore path",
094: "Error: no keystore",
095: JOptionPane.ERROR_MESSAGE);
096: return;
097: }
098:
099: JFileChooser fs2 = new JFileChooser();
100:
101: if (MainEditorFrame.instance != null) {
102: fs2
103: .setCurrentDirectory(new File(
104: MainEditorFrame.instance
105: .getActualProject()
106: .getProperty(
107: "keystore_signAnotherJarPath",
108: "")));
109: }
110:
111: fs2.setDialogTitle("Choose the jar file to sign");
112: fs2
113: .setFileFilter(new javax.swing.filechooser.FileFilter() {
114: public String getDescription() {
115: return "Jar files";
116: }
117:
118: public boolean accept(java.io.File file) {
119: if (file.isDirectory())
120: return true;
121: return file.getName().toLowerCase()
122: .endsWith(".jar");
123: }
124: });
125: int rep = fs2.showOpenDialog(parent);
126: if (rep == JFileChooser.APPROVE_OPTION) {
127: final File file = fs2.getSelectedFile();
128: if (MainEditorFrame.instance != null) {
129: MainEditorFrame.instance.getActualProject()
130: .setProperty(
131: "keystore_signAnotherJarPath",
132: file.getAbsolutePath());
133: }
134:
135: final ProgressModalDialog pmd = new ProgressModalDialog(
136: MainEditorFrame.instance,
137: "Signing Jar file", true);
138: pmd.start();
139: Thread t = new Thread() {
140: public void run() {
141: try {
142: pmd.setProgressSection("Signing "
143: + file);
144: pmd
145: .setProgressCommentAndKeepIndeterminate("");
146:
147: Process proc = JarSigner.signJarFile_(
148: file, getKeyStorePath(),
149: getKeyStorePassword(),
150: getKeyStoreAlias());
151: pmd.setProcessToOptionalKill(proc);
152: // just drain, ignore outputs !
153: //String procstack = ProcessUtils.readWholeProcessStack( proc, -1 );
154:
155: StreamGobbler sg3 = new StreamGobbler(
156: proc.getInputStream(),
157: //(Writer)null, //(MainEditorFrame.debug ? System.out : null),
158: System.out, "JarSigner_");
159: sg3.start();
160:
161: sg3
162: .setLineListener(new StringGobblerLineListener() {
163: public String lineToMatch() {
164: return null;
165: }
166:
167: public void lineMatch(
168: String matchedLine) {
169: }
170:
171: public void lineRead(
172: String line) {
173: if (line
174: .startsWith("Warning")
175: || line
176: .startsWith("Error")) {
177: MainEditorFrame.instance.outputPanels.toolsOutputPanel.doc
178: .appendErrorLine(line);
179: MainEditorFrame.instance.outputPanels
180: .selectToolsTab(true);
181: }
182: }
183:
184: });
185:
186: final StreamGobbler sg4;
187: if (MainEditorFrame.instance == null) {
188: sg4 = new StreamGobbler(proc
189: .getErrorStream(),
190: System.err, "JarSigner#");
191: } else {
192: sg4 = new StreamGobbler(
193: proc.getErrorStream(),
194: MainEditorFrame.instance.outputPanels.toolsOutputPanel.doc
195: .createWriterForThisDocument(true),
196: "JarSigner#");
197: }
198: sg4.start();
199:
200: sg3.join();
201: sg4.join();
202:
203: int err = proc.waitFor();
204: if (err != 0)
205: throw new Exception(
206: "Proc terminated with err code "
207: + err);
208:
209: } catch (Exception e) {
210: JOptionPane.showMessageDialog(parent,
211: "" + e.getMessage(),
212: "Jar signing failed",
213: JOptionPane.ERROR_MESSAGE);
214: e.printStackTrace();
215: } finally {
216: pmd.closeDialog();
217: }
218: }
219: };
220: t.setName("signing a jar file");
221: t.start();
222:
223: }
224: }
225: });
226:
227: jarSignerPanel0.add(signSomeFile);
228:
229: GUIUtils.makeNiceButton(keyStoreInfos);
230: jarSignerPanel0.add(keyStoreInfos);
231: keyStoreInfos.addActionListener(new ActionListener() {
232: public void actionPerformed(ActionEvent ee) {
233: if (getKeyStorePath() == null
234: || !getKeyStorePath().exists()) {
235: JOptionPane.showMessageDialog(null,
236: "Please select a valid keystore path",
237: "Error: no keystore",
238: JOptionPane.ERROR_MESSAGE);
239: return;
240: }
241:
242: try {
243: System.out.println("Reading keystore infos");
244:
245: final ProjectSettings actualProject = MainEditorFrame.instance
246: .getActualProject();
247: String out = ProcessUtils.readWholeProcessStack(
248: actualProject.getKeyTool_TOOL()
249: .getAbsolutePath(),
250: "-J-Duser.language=en", "-list",
251: "-v", // "-rfc", accepts NOT both (1.6.0_03)
252: //"-alias", getKeyStoreAlias(), // NO alias => prints the whole info
253: "-storepass", new String(
254: getKeyStorePassword()),
255: "-keystore", getKeyStorePath()
256: .getAbsolutePath());
257:
258: JOptionPane.showMessageDialog(JarSignerPanel.this ,
259: new JScrollPane(new JTextArea(out.trim())));
260:
261: } catch (Exception ex) {
262: ex.printStackTrace();
263: JOptionPane.showMessageDialog(null, ""
264: + ex.getMessage(),
265: "Error: cannot read keystore infos",
266: JOptionPane.ERROR_MESSAGE);
267: }
268: }
269: });
270:
271: JPanel ip1 = new JPanel();
272: GridLayout3 gl1 = new GridLayout3(2, ip1);
273: contentPanel.add(ip1);
274:
275: gl1.add("Keystore ");
276: gl1.add(this .keyStoreNameField);
277:
278: // notified on enter and setPath
279: keyStoreNameField.addActionListener(new ActionListener() {
280: public void actionPerformed(ActionEvent ee) {
281: File file = keyStoreNameField.getPath();
282: if (file != null && !file.getName().equals("")) {
283: searchAliasInKeyStore(file, keyStorePasswordField
284: .getPassword());
285: saveSettingsInInifile();
286: } else {
287: keyStoreAliasComboBox.setEnabled(false);
288: }
289: }
290: });
291:
292: /*
293: keyStoreNameField.getTextField().addKeyListener(new KeyAdapter()
294: {
295: @Override public void keyPressed(KeyEvent ke)
296: {
297: File file = keyStoreNameField.getPath();
298: if(file!=null && !file.equals(""))
299: {
300: searchAliasInKeyStore(file, keyStorePasswordField.getPassword());
301: saveSettingsInInifile();
302: }
303: else
304: {
305: keyStoreAliasComboBox.setEnabled(false);
306: }
307: }
308: });*/
309:
310: keyStorePasswordField.addActionListener(new ActionListener() {
311: public void actionPerformed(ActionEvent ee) {
312: searchAliasInKeyStore(keyStoreNameField.getPath(),
313: keyStorePasswordField.getPassword());
314: saveSettingsInInifile();
315: }
316: });
317:
318: gl1.add("Password ");
319: JPanel panp = new JPanel(new FlowLayout(FlowLayout.LEFT, 0, 0));
320: gl1.add(panp);
321: panp.add(keyStorePasswordField);
322: panp.add(new JLabel(" Alias "));
323: panp.add(keyStoreAliasComboBox);
324:
325: }
326:
327: /** null if ok.
328: */
329: public String isKeystoreAndPasswordOK() {
330: File keyStoreLocation = keyStoreNameField.getPath();
331: if (!keyStoreLocation.exists())
332: return "Keystore not found";
333:
334: char[] password = this .getKeyStorePassword();
335: String ali = getKeyStoreAlias();
336:
337: FileInputStream fis = null;
338: try {
339: KeyStore ks = KeyStore.getInstance("JKS");
340:
341: fis = new FileInputStream(keyStoreLocation);
342: ks.load(fis, password);
343: Enumeration als = ks.aliases();
344: while (als.hasMoreElements()) {
345: String alias = (String) als.nextElement();
346: if (ali.equals(alias))
347: return null; // OK
348: }
349: return "Alias " + ali + " not found in keystore! ";
350: } catch (IOException ioex) {
351: if (ioex.getCause() instanceof UnrecoverableKeyException) {
352: return "BAD PASSWORD.";
353: }
354: return "Keystore error: " + ioex.getMessage();
355: } catch (Exception er) {
356: return "Keystore error: " + er.getMessage();
357: } finally {
358: FileUtils.closeIgnoringExceptions(fis);
359: }
360: }
361:
362: /**
363: * try to read alias in the keystore, write
364: * "CANNOT READ ALIASES" if none found
365: */
366: private void searchAliasInKeyStore(File keyStoreLocation,
367: char[] password) {
368: FileInputStream fis = null;
369: keyStorePasswordField.setForeground(UIManager
370: .getColor("PasswordField.foreground"));
371: try {
372: aliases.clear();
373: KeyStore ks = KeyStore.getInstance("JKS");
374: if (!keyStoreLocation.exists()) {
375: return;
376: }
377:
378: fis = new FileInputStream(keyStoreLocation);
379: ks.load(fis, password);
380: Enumeration als = ks.aliases();
381: while (als.hasMoreElements()) {
382: String alias = (String) als.nextElement();
383: //System.out.println("======= Alias ="+alias);
384: aliases.add(alias);
385: }
386: } catch (IOException ioex) {
387: if (ioex.getCause() instanceof UnrecoverableKeyException) {
388: System.out.println("BAD PASSWORD.");
389: keyStorePasswordField.setForeground(Color.red);
390: }
391: } catch (Exception ignored) {
392: Basics.ignore(ignored);
393: } finally {
394: FileUtils.closeIgnoringExceptions(fis);
395: }
396:
397: if (keyStoreAliasComboBox.getModel().getSize() > 0) {
398:
399: keyStoreAliasComboBox.setSelectedIndex(0);
400: keyStoreAliasComboBox.setEnabled(true);
401: } else {
402: aliases.add("CANNOT READ ALIASES");
403: keyStoreAliasComboBox.setSelectedIndex(0);
404: keyStoreAliasComboBox.setEnabled(false);
405: // keyStoreAliasComboBox.setEnabled(false);
406: }
407: keyStoreAliasComboBox.updateUI();
408:
409: keyStoreAliasComboBox.setPreferredSize(new Dimension(
410: (int) keyStoreAliasComboBox.getPreferredSize()
411: .getWidth(), (int) keyStoreNameField
412: .getPreferredSize().getHeight()));
413: }
414:
415: public void readValuesFromIniFile() {
416: if (MainEditorFrame.instance == null)
417: return;
418: ProjectSettings actualProject = MainEditorFrame.instance
419: .getActualProject();
420: keyStorePasswordField.setText(actualProject.getProperty(
421: "keystore_password", "")); // TODO: encrypt... ??
422: keyStoreNameField.setPath(actualProject.getProperty(
423: "keystore_field", new File(actualProject
424: .getSources_Home().getParentFile(),
425: "/dev/keystore").getAbsolutePath()));
426:
427: keyStoreNameField.offerRememberedGlobalCompletion(
428: MainEditorFrame.instance.globalProperties,
429: "knownKeystores");
430: }
431:
432: public void saveSettingsInInifile() {
433: if (MainEditorFrame.instance == null)
434: return;
435: ProjectSettings actualProject = MainEditorFrame.instance
436: .getActualProject();
437: actualProject.setProperty("keystore_password", new String(
438: keyStorePasswordField.getPassword()));
439: actualProject.setProperty("keystore_field", keyStoreNameField
440: .getPath().getAbsolutePath());
441: // global remember
442: keyStoreNameField.rememberPathForGlobalCompletion(
443: MainEditorFrame.instance.globalProperties,
444: "knownKeystores");
445: }
446:
447: public void setEditEnabled(boolean is) {
448: keyStoreNameField.setEditable(is);
449: keyStorePasswordField.setEditable(is);
450: this .keyStoreAliasComboBox.setEditable(is); // allow writing any string
451: this .keyStoreAliasComboBox.setEnabled(is);
452: signSomeFile.setEnabled(is);
453:
454: }
455:
456: /** Prefer this method to a global setVisble, because of a gridlayout buggy behaviour.
457: */
458: public void makeVisible(boolean v) {
459: contentPanel.setVisible(v);
460: }
461:
462: public void updateFields() {
463: // update
464: searchAliasInKeyStore(keyStoreNameField.getPath(),
465: keyStorePasswordField.getPassword());
466: }
467:
468: /** null if none. */
469: public File getKeyStorePath() {
470: return keyStoreNameField.getPath();
471: }
472:
473: public String getKeyStoreAlias() {
474: Object it = this .keyStoreAliasComboBox.getSelectedItem();
475: if (it == null)
476: return "";
477: return (String) it;
478: }
479:
480: public char[] getKeyStorePassword() {
481: return this.keyStorePasswordField.getPassword();
482: }
483:
484: }
|