Skip to content

Commit 4fea0fa

Browse files
committed
keyTool.changeKeystorePassword and changeKeyPassword
Signed-off-by: David Matějček <[email protected]>
1 parent e6c2014 commit 4fea0fa

File tree

3 files changed

+138
-193
lines changed
  • nucleus

3 files changed

+138
-193
lines changed

nucleus/admin/server-mgmt/src/main/java/com/sun/enterprise/admin/servermgmt/KeystoreManager.java

Lines changed: 59 additions & 177 deletions
Original file line numberDiff line numberDiff line change
@@ -18,122 +18,45 @@
1818
package com.sun.enterprise.admin.servermgmt;
1919

2020
import com.sun.enterprise.admin.servermgmt.pe.PEFileLayout;
21-
import com.sun.enterprise.universal.glassfish.ASenvPropertyReader;
22-
import com.sun.enterprise.universal.io.SmartFile;
23-
import com.sun.enterprise.universal.process.ProcessManager;
24-
import com.sun.enterprise.universal.process.ProcessManagerException;
25-
import com.sun.enterprise.universal.process.ProcessUtils;
2621
import com.sun.enterprise.util.OS;
2722
import com.sun.enterprise.util.i18n.StringManager;
2823
import com.sun.enterprise.util.net.NetUtils;
2924

3025
import java.io.File;
31-
import java.io.FileInputStream;
3226
import java.io.IOException;
27+
import java.lang.System.Logger;
28+
import java.security.Key;
3329
import java.security.KeyStore;
30+
import java.security.UnrecoverableKeyException;
3431
import java.util.ArrayList;
3532
import java.util.Arrays;
36-
import java.util.Enumeration;
33+
import java.util.Collections;
3734
import java.util.List;
3835
import java.util.regex.Pattern;
3936

4037
import org.glassfish.main.jdke.security.KeyTool;
4138

42-
import static com.sun.enterprise.admin.servermgmt.SLogger.UNHANDLED_EXCEPTION;
43-
import static com.sun.enterprise.admin.servermgmt.SLogger.getLogger;
4439
import static com.sun.enterprise.admin.servermgmt.domain.DomainConstants.KEYSTORE_FILE;
4540
import static com.sun.enterprise.admin.servermgmt.domain.DomainConstants.TRUSTSTORE_FILE;
46-
import static java.util.logging.Level.SEVERE;
47-
import static org.glassfish.embeddable.GlassFishVariable.JAVA_ROOT;
41+
import static java.lang.System.Logger.Level.WARNING;
4842

4943
/**
5044
* @author kebbs
5145
*/
5246
public class KeystoreManager {
5347

54-
private static final String KEYTOOL_CMD;
55-
private static final String KEYTOOL_EXE_NAME = OS.isWindows() ? "keytool.exe" : "keytool";
48+
private static final Logger LOG = System.getLogger(KeystoreManager.class.getName());
5649
private static final String CERTIFICATE_DN_PREFIX = "CN=";
5750
private static final String CERTIFICATE_DN_SUFFIX = ",OU=GlassFish,O=Eclipse Foundation";
5851
public static final String CERTIFICATE_ALIAS = "s1as";
5952
public static final String INSTANCE_SECURE_ADMIN_ALIAS = "glassfish-instance";
6053
public static final String DEFAULT_MASTER_PASSWORD = "changeit";
61-
private static final String SKID_EXTENSION_SYSTEM_PROPERTY = "-J-Dsun.security.internal.keytool.skid";
6254
private static final String INSTANCE_CN_SUFFIX = "-instance";
6355

6456
private static final StringManager _strMgr = StringManager.getManager(KeystoreManager.class);
6557
private PEFileLayout fileLayout;
6658

6759

68-
static {
69-
// Byron Nevins, July 2011
70-
String nonFinalKeyTool = KEYTOOL_EXE_NAME; // at the end we set the final
71-
String javaroot = new ASenvPropertyReader().getProps().get(JAVA_ROOT.getPropertyName());
72-
File keyToolBin = new File(new File(javaroot, "bin"), KEYTOOL_EXE_NAME);
73-
74-
if (keyToolBin.canExecute()) {
75-
nonFinalKeyTool = SmartFile.sanitize(keyToolBin.getPath());
76-
} else {
77-
// Can't find it in a JDK. Maybe it is in the PATH?
78-
keyToolBin = ProcessUtils.getExe(KEYTOOL_EXE_NAME);
79-
80-
if (keyToolBin != null && keyToolBin.canExecute()) {
81-
nonFinalKeyTool = keyToolBin.getPath();
82-
}
83-
}
84-
85-
KEYTOOL_CMD = nonFinalKeyTool;
86-
}
87-
88-
private static class KeytoolExecutor {
89-
90-
private ProcessManager process;
91-
92-
public KeytoolExecutor(String[] args, int timeoutInSeconds) {
93-
this.process = createProcessManager(args);
94-
process.setTimeout(timeoutInSeconds * 1000);
95-
}
96-
97-
public KeytoolExecutor(String[] args, int timeoutInSeconds, String[] inputLines) {
98-
this.process = createProcessManager(args);
99-
process.setTimeout(timeoutInSeconds * 1000);
100-
process.setStdinLines(Arrays.asList(inputLines));
101-
}
102-
103-
private static ProcessManager createProcessManager(String[] args) {
104-
final String[] command;
105-
if (args[0].equals(KEYTOOL_CMD)) {
106-
command = Arrays.copyOf(args, args.length);
107-
} else {
108-
command = new String[args.length + 1];
109-
command[0] = KEYTOOL_CMD;
110-
System.arraycopy(args, 0, command, 1, args.length);
111-
}
112-
return new ProcessManager(command);
113-
}
114-
115-
public void execute(String keystoreErrorMsg, File keystoreName) throws RepositoryException {
116-
try {
117-
if (process.execute() != 0) {
118-
throw new RepositoryException(_strMgr.getString(keystoreErrorMsg, keystoreName)
119-
+ process.getStderr() + " " + process.getStdout());
120-
}
121-
} catch (ProcessManagerException ex) {
122-
throw new RepositoryException(
123-
_strMgr.getString(keystoreErrorMsg, keystoreName) + process.getStderr() + " " + process.getStdout(),
124-
ex);
125-
}
126-
}
127-
}
128-
129-
130-
131-
/**
132-
* Creates a new instance of RepositoryManager
133-
*/
134-
public KeystoreManager() {
135-
}
136-
13760
protected static String getCertificateDN(RepositoryConfig cfg, final String CNSuffix) {
13861
String cn = getCNFromCfg(cfg);
13962
if (cn == null) {
@@ -158,26 +81,27 @@ protected PEFileLayout getFileLayout(RepositoryConfig config) {
15881

15982
/**
16083
* Create the default SSL key store using keytool to generate a self signed certificate.
84+
* @param keyStore
16185
*
16286
* @param config
16387
* @param masterPassword
164-
* @throws IOException
88+
* @throws DomainException
16589
*/
166-
protected void createKeyStore(File keystore, RepositoryConfig config, String masterPassword) throws DomainException {
90+
protected void createKeyStore(File keyStore, RepositoryConfig config, String masterPassword) throws DomainException {
16791
// Generate a new self signed certificate with s1as as the alias
16892
// Create the default self signed cert
16993
final String dasCertDN = getDASCertDN(config);
17094
System.out.println(_strMgr.getString("CertificateDN", dasCertDN));
17195
try {
172-
final KeyTool keyTool = new KeyTool(keystore, masterPassword.toCharArray());
96+
final KeyTool keyTool = new KeyTool(keyStore, masterPassword.toCharArray());
17397
keyTool.generateKeyPair(CERTIFICATE_ALIAS, dasCertDN, "RSA", 3650);
17498

17599
// Generate a new self signed certificate with glassfish-instance as the alias
176100
// Create the default self-signed cert for instances to use for SSL auth.
177101
final String instanceCertDN = getInstanceCertDN(config);
178102
keyTool.generateKeyPair(INSTANCE_SECURE_ADMIN_ALIAS, instanceCertDN, "RSA", 3650);
179103
} catch (IOException e) {
180-
throw new DomainException(_strMgr.getString("SomeProblemWithKeytool", e.getMessage()), e);
104+
throw new DomainException(_strMgr.getString("SomeProblemWithKeytool", keyStore), e);
181105
}
182106
}
183107

@@ -191,7 +115,7 @@ protected void copyCertificatesToTrustStore(File configRoot, DomainConfig config
191115
keyTool.copyCertificate(CERTIFICATE_ALIAS, trustStore);
192116
keyTool.copyCertificate(INSTANCE_SECURE_ADMIN_ALIAS, trustStore);
193117
} catch (IOException e) {
194-
throw new DomainException(_strMgr.getString("SomeProblemWithKeytool", e.getMessage()), e);
118+
throw new DomainException(_strMgr.getString("SomeProblemWithKeytool", keyStore), e);
195119
}
196120
}
197121

@@ -200,18 +124,18 @@ protected void copyCertificatesToTrustStore(File configRoot, DomainConfig config
200124
*
201125
* @param oldPassword the old keystore password
202126
* @param newPassword the new keystore password
203-
* @param keystore the keystore whose password is to be changed.
204-
* @throws RepositoryException
127+
* @param keyStore the keystore whose password is to be changed.
128+
* @throws DomainException
205129
*/
206-
protected void changeKeystorePassword(String oldPassword, String newPassword, File keystore) throws RepositoryException {
207-
if (!oldPassword.equals(newPassword)) {
208-
// Change truststore password from the default
209-
String[] keytoolCmd = {
210-
"-storepasswd",
211-
"-keystore", keystore.getAbsolutePath(), };
212-
213-
KeytoolExecutor keytoolExecutor = new KeytoolExecutor(keytoolCmd, 30, new String[] { oldPassword, newPassword, newPassword });
214-
keytoolExecutor.execute("keyStorePasswordNotChanged", keystore);
130+
protected void changeKeystorePassword(String oldPassword, String newPassword, File keyStore) throws DomainException {
131+
if (oldPassword.equals(newPassword)) {
132+
return;
133+
}
134+
try {
135+
final KeyTool keyTool = new KeyTool(keyStore, oldPassword.toCharArray());
136+
keyTool.changeKeyStorePassword(newPassword.toCharArray());
137+
} catch (IOException e) {
138+
throw new DomainException(_strMgr.getString("keyStorePasswordNotChanged", keyStore), e);
215139
}
216140
}
217141

@@ -226,65 +150,39 @@ protected void changeKeystorePassword(String oldPassword, String newPassword, Fi
226150
* @param storePassword the keystore password
227151
* @param oldKeyPassword the old password for the s1as alias
228152
* @param newKeyPassword the new password for the s1as alias
229-
* @throws RepositoryException
153+
* @throws DomainException
230154
*/
231-
protected void changeS1ASAliasPassword(RepositoryConfig config, String storePassword, String oldKeyPassword, String newKeyPassword) throws RepositoryException {
232-
if (!storePassword.equals(oldKeyPassword) && !oldKeyPassword.equals(newKeyPassword)) {
233-
final PEFileLayout layout = getFileLayout(config);
234-
final File keystore = layout.getKeyStore();
235-
236-
// First see if the alias exists. The user could have deleted it. Any failure in the
237-
// command indicates that the alias does not exist, so we return without error.
238-
String keyStoreType = System.getProperty("javax.net.ssl.keyStoreType");
239-
if (keyStoreType == null) {
240-
keyStoreType = KeyStore.getDefaultType();
241-
}
242-
243-
// Add code to change all the aliases that exist rather then change s1as only
244-
List<String> aliases = new ArrayList<>();
245-
FileInputStream is = null;
246-
try {
247-
KeyStore keyStore = KeyStore.getInstance(keyStoreType);
248-
is = new FileInputStream(keystore);
249-
keyStore.load(is, storePassword.toCharArray());
250-
Enumeration<String> all = keyStore.aliases();
251-
while (all.hasMoreElements()) {
252-
aliases.add(all.nextElement());
155+
protected void changeKeyPasswords(RepositoryConfig config, String storePassword, String oldKeyPassword,
156+
String newKeyPassword) throws DomainException {
157+
if (storePassword.equals(oldKeyPassword) || oldKeyPassword.equals(newKeyPassword)) {
158+
return;
159+
}
160+
final PEFileLayout layout = getFileLayout(config);
161+
final File keystore = layout.getKeyStore();
162+
try {
163+
KeyStore keyStore = KeyStore.getInstance(keystore, storePassword.toCharArray());
164+
List<String> aliases = Collections.list(keyStore.aliases());
165+
List<String> keyAliases = new ArrayList<>();
166+
for (String alias : aliases) {
167+
Key key;
168+
try {
169+
key = keyStore.getKey(alias, oldKeyPassword.toCharArray());
170+
} catch (UnrecoverableKeyException e) {
171+
LOG.log(WARNING,
172+
"Key entry with alias {0} in a key store {1} could not be recovered with provided key password.",
173+
alias, keystore);
174+
continue;
253175
}
254-
} catch (Exception e) {
255-
aliases.add(CERTIFICATE_ALIAS);
256-
} finally {
257-
if (is != null) {
258-
try {
259-
is.close();
260-
} catch (IOException ex) {
261-
getLogger().log(SEVERE, UNHANDLED_EXCEPTION, ex);
262-
}
176+
if (key != null) {
177+
keyAliases.add(alias);
263178
}
264179
}
265-
266-
String[] keytoolCmd = {
267-
"-list",
268-
"-keystore", keystore.getAbsolutePath(),
269-
"-alias", CERTIFICATE_ALIAS, };
270-
271-
KeytoolExecutor keytoolExecutor = new KeytoolExecutor(keytoolCmd, 30, new String[] { storePassword });
272-
try {
273-
keytoolExecutor.execute("s1asKeyPasswordNotChanged", keystore);
274-
} catch (RepositoryException ex) {
275-
return;
276-
}
277-
278-
// Change truststore password from the default
279-
for (String alias : aliases) {
280-
keytoolCmd = new String[] {
281-
"-keypasswd",
282-
"-keystore", keystore.getAbsolutePath(),
283-
"-alias", alias, };
284-
285-
keytoolExecutor = new KeytoolExecutor(keytoolCmd, 30, new String[] { storePassword, oldKeyPassword, newKeyPassword, newKeyPassword });
286-
keytoolExecutor.execute("s1asKeyPasswordNotChanged", keystore);
180+
KeyTool keyTool = new KeyTool(keystore, storePassword.toCharArray());
181+
for (String alias : keyAliases) {
182+
keyTool.changeKeyPassword(alias, oldKeyPassword.toCharArray(), newKeyPassword.toCharArray());
287183
}
184+
} catch (Exception e) {
185+
throw new DomainException(_strMgr.getString("s1asKeyPasswordNotChanged", keystore), e);
288186
}
289187
}
290188

@@ -294,34 +192,18 @@ protected void changeS1ASAliasPassword(RepositoryConfig config, String storePass
294192
* own key/truststore
295193
*
296194
* @param config
297-
* @param storePassword
298-
* @param oldKeyPassword
299-
* @param newKeyPassword
195+
* @param oldPassword
196+
* @param newPassword
300197
*/
301-
protected void changeSSLCertificateDatabasePassword(RepositoryConfig config, String oldPassword, String newPassword) throws RepositoryException {
198+
protected void changeSSLCertificateDatabasePassword(RepositoryConfig config, String oldPassword, String newPassword) throws DomainException {
302199
final PEFileLayout layout = getFileLayout(config);
303200
File keystore = layout.getKeyStore();
304201
File truststore = layout.getTrustStore();
305202

306203
if (keystore.exists()) {
307204
// Change the password on the keystore
308205
changeKeystorePassword(oldPassword, newPassword, keystore);
309-
310-
// Change the s1as alias password in the keystore...
311-
//
312-
// The assumption here is that the keystore password is not the same as the key password.
313-
// This is due to the fact that the keystore password should first be changed followed next
314-
// by the key password. The end result is that the keystore and s1as key both have
315-
// the same passwords. This function will tolerate deletion of the s1as alias, but
316-
// it will not tolerate changing the s1as key from something other than the
317-
// database password.
318-
try {
319-
changeS1ASAliasPassword(config, newPassword, oldPassword, newPassword);
320-
} catch (Exception ex) {
321-
// For now we eat all exceptions and dump to the log if the password
322-
// alias could not be changed.
323-
getLogger().log(SEVERE, UNHANDLED_EXCEPTION, ex);
324-
}
206+
changeKeyPasswords(config, newPassword, oldPassword, newPassword);
325207
}
326208

327209
if (truststore.exists()) {
@@ -372,6 +254,10 @@ private static String getCNFromCfg(RepositoryConfig cfg) {
372254
return value;
373255
}
374256

257+
private static String getCNFromOption(String option) {
258+
return getValueFromOptionForName(option, "CN", true);
259+
}
260+
375261
/**
376262
* Returns CN if valid and non-blank. Returns null otherwise.
377263
*
@@ -397,8 +283,4 @@ private static String getValueFromOptionForName(String option, String name, bool
397283

398284
return null;
399285
}
400-
401-
private static String getCNFromOption(String option) {
402-
return getValueFromOptionForName(option, "CN", true);
403-
}
404286
}

0 commit comments

Comments
 (0)