| package org.jetbrains.android.exportSignedPackage; |
| |
| import com.android.jarutils.DebugKeyProvider; |
| import com.android.jarutils.KeystoreHelper; |
| import com.intellij.ide.wizard.CommitStepException; |
| import com.intellij.openapi.diagnostic.Logger; |
| import com.intellij.openapi.project.Project; |
| import com.intellij.openapi.ui.Messages; |
| import org.jetbrains.android.util.AndroidBundle; |
| import org.jetbrains.android.util.AndroidUtils; |
| import org.jetbrains.annotations.NotNull; |
| import org.jetbrains.annotations.Nullable; |
| |
| import javax.swing.*; |
| import java.awt.*; |
| 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.HashSet; |
| import java.util.List; |
| import java.util.Set; |
| |
| /** |
| * @author Eugene.Kudelevsky |
| */ |
| public abstract class NewKeyForm { |
| private static final Logger LOG = Logger.getInstance("#org.jetbrains.android.exportSignedPackage.NewKeyForm"); |
| |
| private JPanel myContentPanel; |
| private JTextField myAliasField; |
| private JPasswordField myKeyPasswordField; |
| private JPasswordField myConfirmKeyPasswordField; |
| private JSpinner myValiditySpinner; |
| private JTextField myFirstAndLastNameField; |
| private JTextField myOrganizationUnitField; |
| private JTextField myCityField; |
| private JTextField myStateOrProvinceField; |
| private JTextField myCountryCodeField; |
| private JPanel myCertificatePanel; |
| private JTextField myOrganizationField; |
| |
| private KeyStore myKeyStore; |
| private PrivateKey myPrivateKey; |
| private X509Certificate myCertificate; |
| |
| public NewKeyForm() { |
| myValiditySpinner.setModel(new SpinnerNumberModel(25, 1, 1000, 1)); |
| } |
| |
| private int getValidity() { |
| SpinnerNumberModel model = (SpinnerNumberModel)myValiditySpinner.getModel(); |
| return model.getNumber().intValue(); |
| } |
| |
| public void init() { |
| myAliasField.setText(generateAlias()); |
| } |
| |
| public JPanel getContentPanel() { |
| return myContentPanel; |
| } |
| |
| private boolean findNonEmptyCertificateField() { |
| for (Component component : myCertificatePanel.getComponents()) { |
| if (component instanceof JTextField) { |
| if (((JTextField)component).getText().trim().length() > 0) { |
| return true; |
| } |
| } |
| } |
| return false; |
| } |
| |
| public void createKey() throws CommitStepException { |
| if (getKeyAlias().length() == 0) { |
| throw new CommitStepException(AndroidBundle.message("android.export.package.specify.key.alias.error")); |
| } |
| AndroidUtils.checkNewPassword(myKeyPasswordField, myConfirmKeyPasswordField); |
| if (!findNonEmptyCertificateField()) { |
| throw new CommitStepException(AndroidBundle.message("android.export.package.specify.certificate.field.error")); |
| } |
| doCreateKey(); |
| } |
| |
| @NotNull |
| private String generateAlias() { |
| List<String> aliasList = getExistingKeyAliasList(); |
| String prefix = "key"; |
| if (aliasList == null) { |
| return prefix + '0'; |
| } |
| Set<String> aliasSet = new HashSet<String>(); |
| for (String alias : aliasList) { |
| aliasSet.add(alias.toLowerCase()); |
| } |
| for (int i = 0; ; i++) { |
| String alias = prefix + i; |
| if (!aliasSet.contains(alias)) { |
| return alias; |
| } |
| } |
| } |
| |
| @Nullable |
| protected abstract List<String> getExistingKeyAliasList(); |
| |
| private static void buildDName(StringBuilder builder, String prefix, JTextField textField) { |
| if (textField != null) { |
| String value = textField.getText().trim(); |
| if (value.length() > 0) { |
| if (builder.length() > 0) { |
| builder.append(","); |
| } |
| builder.append(prefix); |
| builder.append('='); |
| builder.append(value); |
| } |
| } |
| } |
| |
| private String getDName() { |
| StringBuilder builder = new StringBuilder(); |
| buildDName(builder, "CN", myFirstAndLastNameField); |
| buildDName(builder, "OU", myOrganizationUnitField); |
| buildDName(builder, "O", myOrganizationField); |
| buildDName(builder, "L", myCityField); |
| buildDName(builder, "ST", myStateOrProvinceField); |
| buildDName(builder, "C", myCountryCodeField); |
| return builder.toString(); |
| } |
| |
| |
| |
| private void doCreateKey() throws CommitStepException { |
| String keystoreLocation = getKeyStoreLocation(); |
| String keystorePassword = new String(getKeyStorePassword()); |
| String keyPassword = new String(getKeyPassword()); |
| String keyAlias = getKeyAlias(); |
| String dname = getDName(); |
| assert dname != null; |
| |
| if (keystorePassword.indexOf('"') >= 0 || keyPassword.indexOf('"') >= 0) { |
| throw new CommitStepException("Passwords cannot contain quote character"); |
| } |
| |
| boolean createdStore = false; |
| final StringBuilder errorBuilder = new StringBuilder(); |
| final StringBuilder outBuilder = new StringBuilder(); |
| try { |
| createdStore = KeystoreHelper |
| .createNewStore(keystoreLocation, null, keystorePassword, keyAlias, keyPassword, dname, getValidity(), |
| new DebugKeyProvider.IKeyGenOutput() { |
| @Override |
| public void err(String message) { |
| errorBuilder.append(message).append('\n'); |
| LOG.info("Error: " + message); |
| } |
| |
| @Override |
| public void out(String message) { |
| outBuilder.append(message).append('\n'); |
| LOG.info(message); |
| } |
| }); |
| } |
| catch (Exception e) { |
| LOG.info(e); |
| errorBuilder.append(e.getMessage()).append('\n'); |
| } |
| normalizeBuilder(errorBuilder); |
| normalizeBuilder(outBuilder); |
| |
| if (createdStore) { |
| if (errorBuilder.length() > 0) { |
| String prefix = AndroidBundle.message("android.create.new.key.error.prefix"); |
| Messages.showErrorDialog(myContentPanel, prefix + '\n' + errorBuilder.toString()); |
| } |
| } |
| else { |
| if (errorBuilder.length() > 0) { |
| throw new CommitStepException(errorBuilder.toString()); |
| } |
| if (outBuilder.length() > 0) { |
| throw new CommitStepException(outBuilder.toString()); |
| } |
| throw new CommitStepException(AndroidBundle.message("android.cannot.create.new.key.error")); |
| } |
| loadKeystoreAndKey(keystoreLocation, keystorePassword, keyAlias, keyPassword); |
| } |
| |
| @NotNull |
| public char[] getKeyPassword() { |
| return myKeyPasswordField.getPassword(); |
| } |
| |
| @NotNull |
| public String getKeyAlias() { |
| return myAliasField.getText().trim(); |
| } |
| |
| @NotNull |
| protected abstract Project getProject(); |
| |
| @NotNull |
| protected abstract char[] getKeyStorePassword(); |
| |
| @NotNull |
| protected abstract String getKeyStoreLocation(); |
| |
| private void loadKeystoreAndKey(String keystoreLocation, String keystorePassword, String keyAlias, String keyPassword) |
| throws CommitStepException { |
| FileInputStream fis = null; |
| try { |
| KeyStore keyStore = KeyStore.getInstance(KeyStore.getDefaultType()); |
| fis = new FileInputStream(new File(keystoreLocation)); |
| keyStore.load(fis, keystorePassword.toCharArray()); |
| myKeyStore = keyStore; |
| KeyStore.PrivateKeyEntry entry = (KeyStore.PrivateKeyEntry)keyStore.getEntry( |
| keyAlias, new KeyStore.PasswordProtection(keyPassword.toCharArray())); |
| if (entry == null) { |
| throw new CommitStepException(AndroidBundle.message("android.extract.package.cannot.find.key.error", keyAlias)); |
| } |
| 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", keyAlias)); |
| } |
| myPrivateKey = privateKey; |
| myCertificate = (X509Certificate)certificate; |
| } |
| catch (Exception e) { |
| throw new CommitStepException("Error: " + e.getMessage()); |
| } |
| finally { |
| if (fis != null) { |
| try { |
| fis.close(); |
| } |
| catch (IOException ignored) { |
| } |
| } |
| } |
| } |
| |
| private static void normalizeBuilder(StringBuilder builder) { |
| if (builder.length() > 0) { |
| builder.deleteCharAt(builder.length() - 1); |
| } |
| } |
| |
| @Nullable |
| public KeyStore getKeyStore() { |
| return myKeyStore; |
| } |
| |
| @Nullable |
| public PrivateKey getPrivateKey() { |
| return myPrivateKey; |
| } |
| |
| @Nullable |
| public X509Certificate getCertificate() { |
| return myCertificate; |
| } |
| } |