1 /* 2 * Copyright (C) 2008 The Android Open Source Project 3 * 4 * Licensed under the Eclipse Public License, Version 1.0 (the "License"); 5 * you may not use this file except in compliance with the License. 6 * You may obtain a copy of the License at 7 * 8 * http://www.eclipse.org/org/documents/epl-v10.php 9 * 10 * Unless required by applicable law or agreed to in writing, software 11 * distributed under the License is distributed on an "AS IS" BASIS, 12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 * See the License for the specific language governing permissions and 14 * limitations under the License. 15 */ 16 17 package com.android.ide.eclipse.adt.internal.wizards.export; 18 19 import com.android.ide.eclipse.adt.internal.project.ProjectHelper; 20 import com.android.ide.eclipse.adt.internal.wizards.export.ExportWizard.ExportWizardPage; 21 22 import org.eclipse.core.resources.IProject; 23 import org.eclipse.jface.wizard.IWizardPage; 24 import org.eclipse.swt.SWT; 25 import org.eclipse.swt.events.ModifyEvent; 26 import org.eclipse.swt.events.ModifyListener; 27 import org.eclipse.swt.events.VerifyEvent; 28 import org.eclipse.swt.events.VerifyListener; 29 import org.eclipse.swt.layout.GridData; 30 import org.eclipse.swt.layout.GridLayout; 31 import org.eclipse.swt.widgets.Composite; 32 import org.eclipse.swt.widgets.Label; 33 import org.eclipse.swt.widgets.Text; 34 35 import java.util.List; 36 37 /** 38 * Key creation page. 39 */ 40 final class KeyCreationPage extends ExportWizardPage { 41 42 private final ExportWizard mWizard; 43 private Text mAlias; 44 private Text mKeyPassword; 45 private Text mKeyPassword2; 46 private Text mCnField; 47 private boolean mDisableOnChange = false; 48 private Text mOuField; 49 private Text mOField; 50 private Text mLField; 51 private Text mStField; 52 private Text mCField; 53 private String mDName; 54 private int mValidity = 0; 55 private List<String> mExistingAliases; 56 57 58 protected KeyCreationPage(ExportWizard wizard, String pageName) { 59 super(pageName); 60 mWizard = wizard; 61 62 setTitle("Key Creation"); 63 setDescription(""); // TODO? 64 } 65 66 @Override 67 public void createControl(Composite parent) { 68 Composite composite = new Composite(parent, SWT.NULL); 69 composite.setLayoutData(new GridData(GridData.FILL_BOTH)); 70 GridLayout gl = new GridLayout(2, false); 71 composite.setLayout(gl); 72 73 GridData gd; 74 75 new Label(composite, SWT.NONE).setText("Alias:"); 76 mAlias = new Text(composite, SWT.BORDER); 77 mAlias.setLayoutData(gd = new GridData(GridData.FILL_HORIZONTAL)); 78 79 new Label(composite, SWT.NONE).setText("Password:"); 80 mKeyPassword = new Text(composite, SWT.BORDER | SWT.PASSWORD); 81 mKeyPassword.setLayoutData(gd = new GridData(GridData.FILL_HORIZONTAL)); 82 mKeyPassword.addVerifyListener(sPasswordVerifier); 83 84 new Label(composite, SWT.NONE).setText("Confirm:"); 85 mKeyPassword2 = new Text(composite, SWT.BORDER | SWT.PASSWORD); 86 mKeyPassword2.setLayoutData(gd = new GridData(GridData.FILL_HORIZONTAL)); 87 mKeyPassword2.addVerifyListener(sPasswordVerifier); 88 89 new Label(composite, SWT.NONE).setText("Validity (years):"); 90 final Text validityText = new Text(composite, SWT.BORDER); 91 validityText.setLayoutData(gd = new GridData(GridData.FILL_HORIZONTAL)); 92 validityText.addVerifyListener(new VerifyListener() { 93 @Override 94 public void verifyText(VerifyEvent e) { 95 // check for digit only. 96 for (int i = 0 ; i < e.text.length(); i++) { 97 char letter = e.text.charAt(i); 98 if (letter < '0' || letter > '9') { 99 e.doit = false; 100 return; 101 } 102 } 103 } 104 }); 105 106 new Label(composite, SWT.SEPARATOR | SWT.HORIZONTAL).setLayoutData( 107 gd = new GridData(GridData.FILL_HORIZONTAL)); 108 gd.horizontalSpan = 2; 109 110 new Label(composite, SWT.NONE).setText("First and Last Name:"); 111 mCnField = new Text(composite, SWT.BORDER); 112 mCnField.setLayoutData(gd = new GridData(GridData.FILL_HORIZONTAL)); 113 114 new Label(composite, SWT.NONE).setText("Organizational Unit:"); 115 mOuField = new Text(composite, SWT.BORDER); 116 mOuField.setLayoutData(gd = new GridData(GridData.FILL_HORIZONTAL)); 117 118 new Label(composite, SWT.NONE).setText("Organization:"); 119 mOField = new Text(composite, SWT.BORDER); 120 mOField.setLayoutData(gd = new GridData(GridData.FILL_HORIZONTAL)); 121 122 new Label(composite, SWT.NONE).setText("City or Locality:"); 123 mLField = new Text(composite, SWT.BORDER); 124 mLField.setLayoutData(gd = new GridData(GridData.FILL_HORIZONTAL)); 125 126 new Label(composite, SWT.NONE).setText("State or Province:"); 127 mStField = new Text(composite, SWT.BORDER); 128 mStField.setLayoutData(gd = new GridData(GridData.FILL_HORIZONTAL)); 129 130 new Label(composite, SWT.NONE).setText("Country Code (XX):"); 131 mCField = new Text(composite, SWT.BORDER); 132 mCField.setLayoutData(gd = new GridData(GridData.FILL_HORIZONTAL)); 133 134 // Show description the first time 135 setErrorMessage(null); 136 setMessage(null); 137 setControl(composite); 138 139 mAlias.addModifyListener(new ModifyListener() { 140 @Override 141 public void modifyText(ModifyEvent e) { 142 mWizard.setKeyAlias(mAlias.getText().trim()); 143 onChange(); 144 } 145 }); 146 mKeyPassword.addModifyListener(new ModifyListener() { 147 @Override 148 public void modifyText(ModifyEvent e) { 149 mWizard.setKeyPassword(mKeyPassword.getText()); 150 onChange(); 151 } 152 }); 153 mKeyPassword2.addModifyListener(new ModifyListener() { 154 @Override 155 public void modifyText(ModifyEvent e) { 156 onChange(); 157 } 158 }); 159 160 validityText.addModifyListener(new ModifyListener() { 161 @Override 162 public void modifyText(ModifyEvent e) { 163 try { 164 mValidity = Integer.parseInt(validityText.getText()); 165 } catch (NumberFormatException e2) { 166 // this should only happen if the text field is empty due to the verifyListener. 167 mValidity = 0; 168 } 169 mWizard.setValidity(mValidity); 170 onChange(); 171 } 172 }); 173 174 ModifyListener dNameListener = new ModifyListener() { 175 @Override 176 public void modifyText(ModifyEvent e) { 177 onDNameChange(); 178 } 179 }; 180 181 mCnField.addModifyListener(dNameListener); 182 mOuField.addModifyListener(dNameListener); 183 mOField.addModifyListener(dNameListener); 184 mLField.addModifyListener(dNameListener); 185 mStField.addModifyListener(dNameListener); 186 mCField.addModifyListener(dNameListener); 187 } 188 189 @Override 190 void onShow() { 191 // fill the texts with information loaded from the project. 192 if ((mProjectDataChanged & (DATA_PROJECT | DATA_KEYSTORE)) != 0) { 193 // reset the keystore/alias from the content of the project 194 IProject project = mWizard.getProject(); 195 196 // disable onChange for now. we'll call it once at the end. 197 mDisableOnChange = true; 198 199 String alias = ProjectHelper.loadStringProperty(project, ExportWizard.PROPERTY_ALIAS); 200 if (alias != null) { 201 mAlias.setText(alias); 202 } 203 204 // get the existing list of keys if applicable 205 if (mWizard.getKeyCreationMode()) { 206 mExistingAliases = mWizard.getExistingAliases(); 207 } else { 208 mExistingAliases = null; 209 } 210 211 // reset the passwords 212 mKeyPassword.setText(""); //$NON-NLS-1$ 213 mKeyPassword2.setText(""); //$NON-NLS-1$ 214 215 // enable onChange, and call it to display errors and enable/disable pageCompleted. 216 mDisableOnChange = false; 217 onChange(); 218 } 219 } 220 221 @Override 222 public IWizardPage getPreviousPage() { 223 if (mWizard.getKeyCreationMode()) { // this means we create a key from an existing store 224 return mWizard.getKeySelectionPage(); 225 } 226 227 return mWizard.getKeystoreSelectionPage(); 228 } 229 230 @Override 231 public IWizardPage getNextPage() { 232 return mWizard.getKeyCheckPage(); 233 } 234 235 /** 236 * Handles changes and update the error message and calls {@link #setPageComplete(boolean)}. 237 */ 238 private void onChange() { 239 if (mDisableOnChange) { 240 return; 241 } 242 243 setErrorMessage(null); 244 setMessage(null); 245 246 if (mAlias.getText().trim().length() == 0) { 247 setErrorMessage("Enter key alias."); 248 setPageComplete(false); 249 return; 250 } else if (mExistingAliases != null) { 251 // we cannot use indexOf, because we need to do a case-insensitive check 252 String keyAlias = mAlias.getText().trim(); 253 for (String alias : mExistingAliases) { 254 if (alias.equalsIgnoreCase(keyAlias)) { 255 setErrorMessage("Key alias already exists in keystore."); 256 setPageComplete(false); 257 return; 258 } 259 } 260 } 261 262 String value = mKeyPassword.getText(); 263 if (value.length() == 0) { 264 setErrorMessage("Enter key password."); 265 setPageComplete(false); 266 return; 267 } else if (value.length() < 6) { 268 setErrorMessage("Key password is too short - must be at least 6 characters."); 269 setPageComplete(false); 270 return; 271 } 272 273 if (value.equals(mKeyPassword2.getText()) == false) { 274 setErrorMessage("Key passwords don't match."); 275 setPageComplete(false); 276 return; 277 } 278 279 if (mValidity == 0) { 280 setErrorMessage("Key certificate validity is required."); 281 setPageComplete(false); 282 return; 283 } else if (mValidity < 25) { 284 setMessage("A 25 year certificate validity is recommended.", WARNING); 285 } else if (mValidity > 1000) { 286 setErrorMessage("Key certificate validity must be between 1 and 1000 years."); 287 setPageComplete(false); 288 return; 289 } 290 291 if (mDName == null || mDName.length() == 0) { 292 setErrorMessage("At least one Certificate issuer field is required to be non-empty."); 293 setPageComplete(false); 294 return; 295 } 296 297 setPageComplete(true); 298 } 299 300 /** 301 * Handles changes in the DName fields. 302 */ 303 private void onDNameChange() { 304 StringBuilder sb = new StringBuilder(); 305 306 buildDName("CN", mCnField, sb); 307 buildDName("OU", mOuField, sb); 308 buildDName("O", mOField, sb); 309 buildDName("L", mLField, sb); 310 buildDName("ST", mStField, sb); 311 buildDName("C", mCField, sb); 312 313 mDName = sb.toString(); 314 mWizard.setDName(mDName); 315 316 onChange(); 317 } 318 319 /** 320 * Builds the distinguished name string with the provided {@link StringBuilder}. 321 * @param prefix the prefix of the entry. 322 * @param textField The {@link Text} field containing the entry value. 323 * @param sb the string builder containing the dname. 324 */ 325 private void buildDName(String prefix, Text textField, StringBuilder sb) { 326 if (textField != null) { 327 String value = textField.getText().trim(); 328 if (value.length() > 0) { 329 if (sb.length() > 0) { 330 sb.append(","); 331 } 332 333 sb.append(prefix); 334 sb.append('='); 335 sb.append(value); 336 } 337 } 338 } 339 } 340