/* * Copyright 2000-2012 JetBrains s.r.o. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.jetbrains.android.exportSignedPackage; import com.intellij.ide.passwordSafe.PasswordSafe; import com.intellij.ide.passwordSafe.PasswordSafeException; import com.intellij.ide.wizard.CommitStepException; import com.intellij.openapi.diagnostic.Logger; import com.intellij.openapi.project.Project; import com.intellij.openapi.ui.TextFieldWithBrowseButton; import com.intellij.ui.components.JBCheckBox; import org.jetbrains.android.compiler.artifact.ApkSigningSettingsForm; import org.jetbrains.android.util.AndroidBundle; import org.jetbrains.android.util.AndroidUiUtil; import org.jetbrains.android.util.AndroidUtils; import org.jetbrains.annotations.NotNull; import org.jetbrains.annotations.Nullable; import javax.swing.*; import java.io.File; import java.io.FileInputStream; import java.io.IOException; import java.security.KeyStore; import java.security.PrivateKey; import java.security.cert.Certificate; import java.security.cert.X509Certificate; import java.util.Arrays; /** * @author Eugene.Kudelevsky */ class KeystoreStep extends ExportSignedPackageWizardStep implements ApkSigningSettingsForm { private static final Logger LOG = Logger.getInstance("#org.jetbrains.android.exportSignedPackage.KeystoreStep"); private static final String KEY_STORE_PASSWORD_KEY = "KEY_STORE_PASSWORD"; private static final String KEY_PASSWORD_KEY = "KEY_PASSWORD"; private JPanel myContentPanel; private JPasswordField myKeyStorePasswordField; private JPasswordField myKeyPasswordField; private TextFieldWithBrowseButton.NoPathCompletion myKeyAliasField; private JTextField myKeyStorePathField; private JButton myCreateKeyStoreButton; private JButton myLoadKeyStoreButton; private JBCheckBox myRememberPasswordCheckBox; private final ExportSignedPackageWizard myWizard; private final boolean myUseGradleForSigning; public KeystoreStep(ExportSignedPackageWizard wizard, boolean useGradleForSigning) { myWizard = wizard; myUseGradleForSigning = useGradleForSigning; final Project project = wizard.getProject(); final GenerateSignedApkSettings settings = GenerateSignedApkSettings.getInstance(project); myKeyStorePathField.setText(settings.KEY_STORE_PATH); myKeyAliasField.setText(settings.KEY_ALIAS); myRememberPasswordCheckBox.setSelected(settings.REMEMBER_PASSWORDS); if (settings.REMEMBER_PASSWORDS) { final PasswordSafe passwordSafe = PasswordSafe.getInstance(); try { String password = passwordSafe.getPassword(project, KeystoreStep.class, makePasswordKey( KEY_STORE_PASSWORD_KEY, settings.KEY_STORE_PATH, null)); if (password != null) { myKeyStorePasswordField.setText(password); } password = passwordSafe.getPassword(project, KeystoreStep.class, makePasswordKey( KEY_PASSWORD_KEY, settings.KEY_STORE_PATH, settings.KEY_ALIAS)); if (password != null) { myKeyPasswordField.setText(password); } } catch (PasswordSafeException e) { LOG.debug(e); myKeyStorePasswordField.setText(""); myKeyPasswordField.setText(""); } } AndroidUiUtil.initSigningSettingsForm(project, this); } private static String makePasswordKey(@NotNull String prefix, @NotNull String keyStorePath, @Nullable String keyAlias) { return prefix + "__" + keyStorePath + (keyAlias != null ? "__" + keyAlias : ""); } @Override public JComponent getPreferredFocusedComponent() { if (myKeyStorePathField.getText().length() == 0) { return myKeyStorePathField; } else if (myKeyStorePasswordField.getPassword().length == 0) { return myKeyStorePasswordField; } else if (myKeyAliasField.getText().length() == 0) { return myKeyAliasField; } else if (myKeyPasswordField.getPassword().length == 0) { return myKeyPasswordField; } return null; } @Override public JComponent getComponent() { return myContentPanel; } @Override public String getHelpId() { return "reference.android.reference.extract.signed.package.specify.keystore"; } @Override protected void commitForNext() throws CommitStepException { final String keyStoreLocation = myKeyStorePathField.getText().trim(); if (keyStoreLocation.length() == 0) { throw new CommitStepException(AndroidBundle.message("android.export.package.specify.keystore.location.error")); } final char[] keyStorePassword = myKeyStorePasswordField.getPassword(); if (keyStorePassword.length == 0) { throw new CommitStepException(AndroidBundle.message("android.export.package.specify.key.store.password.error")); } final String keyAlias = myKeyAliasField.getText().trim(); if (keyAlias.length() == 0) { throw new CommitStepException(AndroidBundle.message("android.export.package.specify.key.alias.error")); } final char[] keyPassword = myKeyPasswordField.getPassword(); if (keyPassword.length == 0) { throw new CommitStepException(AndroidBundle.message("android.export.package.specify.key.password.error")); } if (myUseGradleForSigning) { myWizard.setGradleSigningInfo(new GradleSigningInfo(keyStoreLocation, keyStorePassword, keyAlias, keyPassword)); } else { final KeyStore keyStore = loadKeyStore(new File(keyStoreLocation)); if (keyStore == null) { throw new CommitStepException(AndroidBundle.message("android.export.package.keystore.error.title")); } loadKeyAndSaveToWizard(keyStore, keyAlias, keyPassword); } final Project project = myWizard.getProject(); final GenerateSignedApkSettings settings = GenerateSignedApkSettings.getInstance(project); settings.KEY_STORE_PATH = keyStoreLocation; settings.KEY_ALIAS = keyAlias; final boolean rememberPasswords = myRememberPasswordCheckBox.isSelected(); settings.REMEMBER_PASSWORDS = rememberPasswords; final PasswordSafe passwordSafe = PasswordSafe.getInstance(); final String keyStorePasswordKey = makePasswordKey(KEY_STORE_PASSWORD_KEY, keyStoreLocation, null); final String keyPasswordKey = makePasswordKey(KEY_PASSWORD_KEY, keyStoreLocation, keyAlias); try { if (rememberPasswords) { passwordSafe.storePassword(project, KeystoreStep.class, keyStorePasswordKey, new String(keyStorePassword)); passwordSafe.storePassword(project, KeystoreStep.class, keyPasswordKey, new String(keyPassword)); } else { passwordSafe.removePassword(project, KeystoreStep.class, keyStorePasswordKey); passwordSafe.removePassword(project, KeystoreStep.class, keyPasswordKey); } } catch (PasswordSafeException e) { LOG.debug(e); throw new CommitStepException("Cannot store passwords: " + e.getMessage()); } } private KeyStore loadKeyStore(File keystoreFile) throws CommitStepException { final char[] password = myKeyStorePasswordField.getPassword(); FileInputStream fis = null; AndroidUtils.checkPassword(password); if (!keystoreFile.isFile()) { throw new CommitStepException(AndroidBundle.message("android.cannot.find.file.error", keystoreFile.getPath())); } final KeyStore keyStore; try { keyStore = KeyStore.getInstance(KeyStore.getDefaultType()); //noinspection IOResourceOpenedButNotSafelyClosed fis = new FileInputStream(keystoreFile); keyStore.load(fis, password); } catch (Exception e) { throw new CommitStepException(e.getMessage()); } finally { if (fis != null) { try { fis.close(); } catch (IOException ignored) { } } Arrays.fill(password, '\0'); } return keyStore; } private void loadKeyAndSaveToWizard(KeyStore keyStore, String alias, char[] keyPassword) throws CommitStepException { KeyStore.PrivateKeyEntry entry; try { assert keyStore != null; entry = (KeyStore.PrivateKeyEntry)keyStore.getEntry(alias, new KeyStore.PasswordProtection(keyPassword)); } catch (Exception e) { throw new CommitStepException("Error: " + e.getMessage()); } if (entry == null) { throw new CommitStepException(AndroidBundle.message("android.extract.package.cannot.find.key.error", alias)); } PrivateKey privateKey = entry.getPrivateKey(); Certificate certificate = entry.getCertificate(); if (privateKey == null || certificate == null) { throw new CommitStepException(AndroidBundle.message("android.extract.package.cannot.find.key.error", alias)); } myWizard.setPrivateKey(privateKey); myWizard.setCertificate((X509Certificate)certificate); } @Override public JButton getLoadKeyStoreButton() { return myLoadKeyStoreButton; } @Override public JTextField getKeyStorePathField() { return myKeyStorePathField; } @Override public JPanel getPanel() { return myContentPanel; } @Override public JButton getCreateKeyStoreButton() { return myCreateKeyStoreButton; } @Override public JPasswordField getKeyStorePasswordField() { return myKeyStorePasswordField; } @Override public TextFieldWithBrowseButton getKeyAliasField() { return myKeyAliasField; } @Override public JPasswordField getKeyPasswordField() { return myKeyPasswordField; } }