mirror of
https://github.com/scm-manager/scm-manager.git
synced 2026-03-14 00:00:20 +01:00
#979 split implementation of ScmClientConfigFileHandler in order to create new more secure implementation
This commit is contained in:
@@ -0,0 +1,60 @@
|
||||
/**
|
||||
* Copyright (c) 2010, Sebastian Sdorra
|
||||
* All rights reserved.
|
||||
*
|
||||
* Redistribution and use in source and binary forms, with or without
|
||||
* modification, are permitted provided that the following conditions are met:
|
||||
*
|
||||
* 1. Redistributions of source code must retain the above copyright notice,
|
||||
* this list of conditions and the following disclaimer.
|
||||
* 2. Redistributions in binary form must reproduce the above copyright notice,
|
||||
* this list of conditions and the following disclaimer in the documentation
|
||||
* and/or other materials provided with the distribution.
|
||||
* 3. Neither the name of SCM-Manager; nor the names of its
|
||||
* contributors may be used to endorse or promote products derived from this
|
||||
* software without specific prior written permission.
|
||||
*
|
||||
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
|
||||
* AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
|
||||
* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
|
||||
* DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE FOR ANY
|
||||
* DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
|
||||
* (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
|
||||
* LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON
|
||||
* ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
|
||||
* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
|
||||
* SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||
*
|
||||
* http://bitbucket.org/sdorra/scm-manager
|
||||
*
|
||||
*/
|
||||
|
||||
|
||||
package sonia.scm.cli.config;
|
||||
|
||||
import java.io.InputStream;
|
||||
import java.io.OutputStream;
|
||||
|
||||
/**
|
||||
* The CipherStreamHandler is able to encrypt and decrypt streams.
|
||||
*/
|
||||
public interface CipherStreamHandler {
|
||||
|
||||
/**
|
||||
* Decrypts the given input stream.
|
||||
*
|
||||
* @param inputStream encrypted input stream
|
||||
*
|
||||
* @return raw input stream
|
||||
*/
|
||||
InputStream decrypt(InputStream inputStream);
|
||||
|
||||
/**
|
||||
* Encrypts the given output stream.
|
||||
*
|
||||
* @param outputStream raw output stream
|
||||
*
|
||||
* @return encrypting output stream
|
||||
*/
|
||||
OutputStream encrypt(OutputStream outputStream);
|
||||
}
|
||||
@@ -0,0 +1,57 @@
|
||||
/**
|
||||
* Copyright (c) 2010, Sebastian Sdorra
|
||||
* All rights reserved.
|
||||
*
|
||||
* Redistribution and use in source and binary forms, with or without
|
||||
* modification, are permitted provided that the following conditions are met:
|
||||
*
|
||||
* 1. Redistributions of source code must retain the above copyright notice,
|
||||
* this list of conditions and the following disclaimer.
|
||||
* 2. Redistributions in binary form must reproduce the above copyright notice,
|
||||
* this list of conditions and the following disclaimer in the documentation
|
||||
* and/or other materials provided with the distribution.
|
||||
* 3. Neither the name of SCM-Manager; nor the names of its
|
||||
* contributors may be used to endorse or promote products derived from this
|
||||
* software without specific prior written permission.
|
||||
*
|
||||
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
|
||||
* AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
|
||||
* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
|
||||
* DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE FOR ANY
|
||||
* DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
|
||||
* (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
|
||||
* LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON
|
||||
* ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
|
||||
* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
|
||||
* SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||
*
|
||||
* http://bitbucket.org/sdorra/scm-manager
|
||||
*
|
||||
*/
|
||||
|
||||
package sonia.scm.cli.config;
|
||||
|
||||
/**
|
||||
* KeyStore is able to read and write keys.
|
||||
*/
|
||||
public interface KeyStore {
|
||||
|
||||
/**
|
||||
* Writes the given secret key to the store.
|
||||
*
|
||||
* @param secretKey secret key to write
|
||||
*/
|
||||
void set(String secretKey);
|
||||
|
||||
/**
|
||||
* Reads the secret key from the store. The method returns {@code null} if no secret key was stored.
|
||||
*
|
||||
* @return secret key of {@code null}
|
||||
*/
|
||||
String get();
|
||||
|
||||
/**
|
||||
* Removes the secret key from store.
|
||||
*/
|
||||
void remove();
|
||||
}
|
||||
@@ -0,0 +1,64 @@
|
||||
/**
|
||||
* Copyright (c) 2010, Sebastian Sdorra
|
||||
* All rights reserved.
|
||||
*
|
||||
* Redistribution and use in source and binary forms, with or without
|
||||
* modification, are permitted provided that the following conditions are met:
|
||||
*
|
||||
* 1. Redistributions of source code must retain the above copyright notice,
|
||||
* this list of conditions and the following disclaimer.
|
||||
* 2. Redistributions in binary form must reproduce the above copyright notice,
|
||||
* this list of conditions and the following disclaimer in the documentation
|
||||
* and/or other materials provided with the distribution.
|
||||
* 3. Neither the name of SCM-Manager; nor the names of its
|
||||
* contributors may be used to endorse or promote products derived from this
|
||||
* software without specific prior written permission.
|
||||
*
|
||||
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
|
||||
* AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
|
||||
* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
|
||||
* DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE FOR ANY
|
||||
* DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
|
||||
* (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
|
||||
* LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON
|
||||
* ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
|
||||
* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
|
||||
* SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||
*
|
||||
* http://bitbucket.org/sdorra/scm-manager
|
||||
*
|
||||
*/
|
||||
|
||||
package sonia.scm.cli.config;
|
||||
|
||||
import java.util.prefs.Preferences;
|
||||
|
||||
/**
|
||||
* KeyStore implementation with uses {@link Preferences}.
|
||||
*/
|
||||
public class PrefsKeyStore implements KeyStore {
|
||||
|
||||
private static final String PREF_SECRET_KEY = "scm.client.key";
|
||||
|
||||
private final Preferences preferences;
|
||||
|
||||
public PrefsKeyStore() {
|
||||
// we use ScmClientConfigFileHandler as base for backward compatibility
|
||||
preferences = Preferences.userNodeForPackage(ScmClientConfigFileHandler.class);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void set(String secretKey) {
|
||||
preferences.put(PREF_SECRET_KEY, secretKey);
|
||||
}
|
||||
|
||||
@Override
|
||||
public String get() {
|
||||
return preferences.get(PREF_SECRET_KEY, null);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void remove() {
|
||||
preferences.remove(PREF_SECRET_KEY);
|
||||
}
|
||||
}
|
||||
@@ -66,7 +66,7 @@ public class ScmClientConfig
|
||||
* Constructs ...
|
||||
*
|
||||
*/
|
||||
private ScmClientConfig()
|
||||
ScmClientConfig()
|
||||
{
|
||||
this.serverConfigMap = new HashMap<String, ServerConfig>();
|
||||
}
|
||||
|
||||
@@ -35,38 +35,17 @@ package sonia.scm.cli.config;
|
||||
|
||||
//~--- non-JDK imports --------------------------------------------------------
|
||||
|
||||
import sonia.scm.util.IOUtil;
|
||||
import sonia.scm.security.KeyGenerator;
|
||||
import sonia.scm.security.UUIDKeyGenerator;
|
||||
import sonia.scm.util.Util;
|
||||
|
||||
//~--- JDK imports ------------------------------------------------------------
|
||||
|
||||
import java.io.File;
|
||||
import java.io.FileInputStream;
|
||||
import java.io.FileOutputStream;
|
||||
import java.io.InputStream;
|
||||
import java.io.OutputStream;
|
||||
|
||||
import java.security.InvalidAlgorithmParameterException;
|
||||
import java.security.InvalidKeyException;
|
||||
import java.security.NoSuchAlgorithmException;
|
||||
import java.security.spec.InvalidKeySpecException;
|
||||
|
||||
import java.util.UUID;
|
||||
import java.util.prefs.Preferences;
|
||||
|
||||
import javax.crypto.Cipher;
|
||||
import javax.crypto.CipherInputStream;
|
||||
import javax.crypto.CipherOutputStream;
|
||||
import javax.crypto.NoSuchPaddingException;
|
||||
import javax.crypto.SecretKey;
|
||||
import javax.crypto.SecretKeyFactory;
|
||||
import javax.crypto.spec.PBEKeySpec;
|
||||
import javax.crypto.spec.PBEParameterSpec;
|
||||
|
||||
import javax.xml.bind.JAXBContext;
|
||||
import javax.xml.bind.JAXBException;
|
||||
import javax.xml.bind.Marshaller;
|
||||
import javax.xml.bind.Unmarshaller;
|
||||
import java.io.*;
|
||||
|
||||
//~--- JDK imports ------------------------------------------------------------
|
||||
|
||||
/**
|
||||
*
|
||||
@@ -81,62 +60,66 @@ public class ScmClientConfigFileHandler
|
||||
/** Field description */
|
||||
public static final String ENV_CONFIG_FILE = "SCM_CLI_CONFIG";
|
||||
|
||||
/** Field description */
|
||||
public static final String PREF_SECRET_KEY = "scm.client.key";
|
||||
|
||||
/** Field description */
|
||||
public static final String SALT = "AE16347F";
|
||||
|
||||
/** Field description */
|
||||
public static final int SPEC_ITERATION = 12;
|
||||
|
||||
/** Field description */
|
||||
private static final String CIPHER_NAME = "PBEWithMD5AndDES";
|
||||
|
||||
//~--- constructors ---------------------------------------------------------
|
||||
|
||||
private final KeyStore keyStore;
|
||||
private final KeyGenerator keyGenerator;
|
||||
private final File file;
|
||||
private final JAXBContext context;
|
||||
|
||||
private final CipherStreamHandler cipherStreamHandler;
|
||||
|
||||
|
||||
/**
|
||||
* Constructs ...
|
||||
*
|
||||
*/
|
||||
public ScmClientConfigFileHandler()
|
||||
{
|
||||
prefs = Preferences.userNodeForPackage(ScmClientConfigFileHandler.class);
|
||||
key = prefs.get(PREF_SECRET_KEY, null);
|
||||
public ScmClientConfigFileHandler() {
|
||||
this(new PrefsKeyStore(), new UUIDKeyGenerator(), getDefaultConfigFile());
|
||||
}
|
||||
|
||||
if (Util.isEmpty(key))
|
||||
{
|
||||
key = createNewKey();
|
||||
prefs.put(PREF_SECRET_KEY, key);
|
||||
ScmClientConfigFileHandler(KeyStore keyStore, KeyGenerator keyGenerator, File file) {
|
||||
this.keyStore = keyStore;
|
||||
this.keyGenerator = keyGenerator;
|
||||
this.file = file;
|
||||
|
||||
String key = keyStore.get();
|
||||
|
||||
if (Util.isEmpty(key)) {
|
||||
key = keyGenerator.createKey();
|
||||
keyStore.set(key);
|
||||
}
|
||||
|
||||
try
|
||||
{
|
||||
cipherStreamHandler = new WeakCipherStreamHandler(key.toCharArray());
|
||||
|
||||
try {
|
||||
context = JAXBContext.newInstance(ScmClientConfig.class);
|
||||
}
|
||||
catch (JAXBException ex)
|
||||
{
|
||||
throw new ScmConfigException(
|
||||
"could not create JAXBContext for ScmClientConfig", ex);
|
||||
} catch (JAXBException ex) {
|
||||
throw new ScmConfigException("could not create JAXBContext for ScmClientConfig", ex);
|
||||
}
|
||||
}
|
||||
|
||||
//~--- methods --------------------------------------------------------------
|
||||
|
||||
private static File getDefaultConfigFile() {
|
||||
String configPath = System.getenv(ENV_CONFIG_FILE);
|
||||
|
||||
if (Util.isNotEmpty(configPath)){
|
||||
return new File(configPath);
|
||||
}
|
||||
return new File(System.getProperty("user.home"), DEFAULT_CONFIG_NAME);
|
||||
}
|
||||
|
||||
/**
|
||||
* Method description
|
||||
*
|
||||
*/
|
||||
public void delete()
|
||||
{
|
||||
File configFile = getConfigFile();
|
||||
|
||||
if (configFile.exists() &&!configFile.delete())
|
||||
{
|
||||
public void delete() {
|
||||
if (file.exists() &&!file.delete()) {
|
||||
throw new ScmConfigException("could not delete config file");
|
||||
}
|
||||
|
||||
prefs.remove(PREF_SECRET_KEY);
|
||||
keyStore.remove();
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -145,33 +128,16 @@ public class ScmClientConfigFileHandler
|
||||
*
|
||||
* @return
|
||||
*/
|
||||
public ScmClientConfig read()
|
||||
{
|
||||
public ScmClientConfig read() {
|
||||
ScmClientConfig config = null;
|
||||
File configFile = getConfigFile();
|
||||
|
||||
if (configFile.exists())
|
||||
{
|
||||
InputStream input = null;
|
||||
|
||||
try
|
||||
{
|
||||
Cipher c = createCipher(Cipher.DECRYPT_MODE);
|
||||
|
||||
input = new CipherInputStream(new FileInputStream(configFile), c);
|
||||
|
||||
if (file.exists()) {
|
||||
try (InputStream input = cipherStreamHandler.decrypt(new FileInputStream(file))) {
|
||||
Unmarshaller um = context.createUnmarshaller();
|
||||
|
||||
config = (ScmClientConfig) um.unmarshal(input);
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
} catch (IOException | JAXBException ex) {
|
||||
throw new ScmConfigException("could not read config file", ex);
|
||||
}
|
||||
finally
|
||||
{
|
||||
IOUtil.close(input);
|
||||
}
|
||||
}
|
||||
|
||||
return config;
|
||||
@@ -183,124 +149,12 @@ public class ScmClientConfigFileHandler
|
||||
*
|
||||
* @param config
|
||||
*/
|
||||
public void write(ScmClientConfig config)
|
||||
{
|
||||
File configFile = getConfigFile();
|
||||
OutputStream output = null;
|
||||
|
||||
try
|
||||
{
|
||||
Cipher c = createCipher(Cipher.ENCRYPT_MODE);
|
||||
|
||||
output = new CipherOutputStream(new FileOutputStream(configFile), c);
|
||||
|
||||
public void write(ScmClientConfig config) {
|
||||
try (OutputStream output = cipherStreamHandler.encrypt(new FileOutputStream(file))) {
|
||||
Marshaller m = context.createMarshaller();
|
||||
|
||||
m.marshal(config, output);
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
} catch (IOException | JAXBException ex) {
|
||||
throw new ScmConfigException("could not write config file", ex);
|
||||
}
|
||||
finally
|
||||
{
|
||||
IOUtil.close(output);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Method description
|
||||
*
|
||||
*
|
||||
* @param mode
|
||||
*
|
||||
* @return
|
||||
*
|
||||
*
|
||||
* @throws InvalidAlgorithmParameterException
|
||||
* @throws InvalidKeyException
|
||||
* @throws InvalidKeySpecException
|
||||
* @throws NoSuchAlgorithmException
|
||||
* @throws NoSuchPaddingException
|
||||
*/
|
||||
private Cipher createCipher(int mode)
|
||||
throws NoSuchAlgorithmException, NoSuchPaddingException,
|
||||
InvalidKeySpecException, InvalidKeyException,
|
||||
InvalidAlgorithmParameterException
|
||||
{
|
||||
SecretKey sk = createSecretKey();
|
||||
Cipher cipher = Cipher.getInstance(CIPHER_NAME);
|
||||
PBEParameterSpec spec = new PBEParameterSpec(SALT.getBytes(),
|
||||
SPEC_ITERATION);
|
||||
|
||||
cipher.init(mode, sk, spec);
|
||||
|
||||
return cipher;
|
||||
}
|
||||
|
||||
/**
|
||||
* Method description
|
||||
*
|
||||
*
|
||||
* @return
|
||||
*/
|
||||
private String createNewKey()
|
||||
{
|
||||
return UUID.randomUUID().toString();
|
||||
}
|
||||
|
||||
/**
|
||||
* Method description
|
||||
*
|
||||
*
|
||||
* @return
|
||||
*
|
||||
* @throws InvalidKeySpecException
|
||||
* @throws NoSuchAlgorithmException
|
||||
*/
|
||||
private SecretKey createSecretKey()
|
||||
throws NoSuchAlgorithmException, InvalidKeySpecException
|
||||
{
|
||||
PBEKeySpec keySpec = new PBEKeySpec(key.toCharArray());
|
||||
SecretKeyFactory factory = SecretKeyFactory.getInstance(CIPHER_NAME);
|
||||
|
||||
return factory.generateSecret(keySpec);
|
||||
}
|
||||
|
||||
//~--- get methods ----------------------------------------------------------
|
||||
|
||||
/**
|
||||
* Method description
|
||||
*
|
||||
*
|
||||
* @return
|
||||
*/
|
||||
private File getConfigFile()
|
||||
{
|
||||
File configFile = null;
|
||||
String configPath = System.getenv(ENV_CONFIG_FILE);
|
||||
|
||||
if (Util.isEmpty(configPath))
|
||||
{
|
||||
configFile = new File(System.getProperty("user.home"),
|
||||
DEFAULT_CONFIG_NAME);
|
||||
}
|
||||
else
|
||||
{
|
||||
configFile = new File(configPath);
|
||||
}
|
||||
|
||||
return configFile;
|
||||
}
|
||||
|
||||
//~--- fields ---------------------------------------------------------------
|
||||
|
||||
/** Field description */
|
||||
private JAXBContext context;
|
||||
|
||||
/** Field description */
|
||||
private String key;
|
||||
|
||||
/** Field description */
|
||||
private Preferences prefs;
|
||||
}
|
||||
|
||||
@@ -0,0 +1,109 @@
|
||||
/**
|
||||
* Copyright (c) 2010, Sebastian Sdorra
|
||||
* All rights reserved.
|
||||
*
|
||||
* Redistribution and use in source and binary forms, with or without
|
||||
* modification, are permitted provided that the following conditions are met:
|
||||
*
|
||||
* 1. Redistributions of source code must retain the above copyright notice,
|
||||
* this list of conditions and the following disclaimer.
|
||||
* 2. Redistributions in binary form must reproduce the above copyright notice,
|
||||
* this list of conditions and the following disclaimer in the documentation
|
||||
* and/or other materials provided with the distribution.
|
||||
* 3. Neither the name of SCM-Manager; nor the names of its
|
||||
* contributors may be used to endorse or promote products derived from this
|
||||
* software without specific prior written permission.
|
||||
*
|
||||
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
|
||||
* AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
|
||||
* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
|
||||
* DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE FOR ANY
|
||||
* DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
|
||||
* (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
|
||||
* LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON
|
||||
* ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
|
||||
* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
|
||||
* SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||
*
|
||||
* http://bitbucket.org/sdorra/scm-manager
|
||||
*
|
||||
*/
|
||||
|
||||
package sonia.scm.cli.config;
|
||||
|
||||
import javax.crypto.*;
|
||||
import javax.crypto.spec.PBEKeySpec;
|
||||
import javax.crypto.spec.PBEParameterSpec;
|
||||
import java.io.InputStream;
|
||||
import java.io.OutputStream;
|
||||
import java.security.InvalidAlgorithmParameterException;
|
||||
import java.security.InvalidKeyException;
|
||||
import java.security.NoSuchAlgorithmException;
|
||||
import java.security.spec.InvalidKeySpecException;
|
||||
|
||||
/**
|
||||
* Weak implementation of {@link CipherStreamHandler}.
|
||||
*
|
||||
* @see <a href="https://bitbucket.org/sdorra/scm-manager/issues/978/iteration-count-for-password-based">Issue 978</a>
|
||||
* @see <a href="https://bitbucket.org/sdorra/scm-manager/issues/979/constant-salts-for-pbe-are-insecure">Issue 979</a>
|
||||
*/
|
||||
public class WeakCipherStreamHandler implements CipherStreamHandler {
|
||||
|
||||
private static final String SALT = "AE16347F";
|
||||
private static final int SPEC_ITERATION = 12;
|
||||
private static final String CIPHER_NAME = "PBEWithMD5AndDES";
|
||||
|
||||
private final char[] secretKey;
|
||||
|
||||
/**
|
||||
* Creates a new handler with the given secret key.
|
||||
*
|
||||
* @param secretKey secret key
|
||||
*/
|
||||
public WeakCipherStreamHandler(char[] secretKey) {
|
||||
this.secretKey = secretKey;
|
||||
}
|
||||
|
||||
@Override
|
||||
public InputStream decrypt(InputStream inputStream) {
|
||||
try {
|
||||
Cipher c = createCipher(Cipher.DECRYPT_MODE);
|
||||
return new CipherInputStream(inputStream, c);
|
||||
} catch (Exception ex) {
|
||||
throw new ScmConfigException("could not encrypt output stream", ex);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public OutputStream encrypt(OutputStream outputStream) {
|
||||
try {
|
||||
Cipher c = createCipher(Cipher.ENCRYPT_MODE);
|
||||
return new CipherOutputStream(outputStream, c);
|
||||
} catch (Exception ex) {
|
||||
throw new ScmConfigException("could not encrypt output stream", ex);
|
||||
}
|
||||
}
|
||||
|
||||
private Cipher createCipher(int mode)
|
||||
throws NoSuchAlgorithmException, NoSuchPaddingException,
|
||||
InvalidKeySpecException, InvalidKeyException,
|
||||
InvalidAlgorithmParameterException
|
||||
{
|
||||
SecretKey sk = createSecretKey();
|
||||
Cipher cipher = Cipher.getInstance(CIPHER_NAME);
|
||||
PBEParameterSpec spec = new PBEParameterSpec(SALT.getBytes(), SPEC_ITERATION);
|
||||
|
||||
cipher.init(mode, sk, spec);
|
||||
|
||||
return cipher;
|
||||
}
|
||||
|
||||
private SecretKey createSecretKey()
|
||||
throws NoSuchAlgorithmException, InvalidKeySpecException
|
||||
{
|
||||
PBEKeySpec keySpec = new PBEKeySpec(secretKey);
|
||||
SecretKeyFactory factory = SecretKeyFactory.getInstance(CIPHER_NAME);
|
||||
|
||||
return factory.generateSecret(keySpec);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,98 @@
|
||||
/**
|
||||
* Copyright (c) 2010, Sebastian Sdorra
|
||||
* All rights reserved.
|
||||
*
|
||||
* Redistribution and use in source and binary forms, with or without
|
||||
* modification, are permitted provided that the following conditions are met:
|
||||
*
|
||||
* 1. Redistributions of source code must retain the above copyright notice,
|
||||
* this list of conditions and the following disclaimer.
|
||||
* 2. Redistributions in binary form must reproduce the above copyright notice,
|
||||
* this list of conditions and the following disclaimer in the documentation
|
||||
* and/or other materials provided with the distribution.
|
||||
* 3. Neither the name of SCM-Manager; nor the names of its
|
||||
* contributors may be used to endorse or promote products derived from this
|
||||
* software without specific prior written permission.
|
||||
*
|
||||
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
|
||||
* AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
|
||||
* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
|
||||
* DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE FOR ANY
|
||||
* DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
|
||||
* (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
|
||||
* LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON
|
||||
* ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
|
||||
* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
|
||||
* SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||
*
|
||||
* http://bitbucket.org/sdorra/scm-manager
|
||||
*
|
||||
*/
|
||||
|
||||
package sonia.scm.cli.config;
|
||||
|
||||
import org.junit.Rule;
|
||||
import org.junit.Test;
|
||||
import org.junit.rules.TemporaryFolder;
|
||||
import sonia.scm.security.UUIDKeyGenerator;
|
||||
|
||||
import java.io.File;
|
||||
import java.io.IOException;
|
||||
|
||||
import static org.junit.Assert.assertEquals;
|
||||
import static org.junit.Assert.assertFalse;
|
||||
import static org.junit.Assert.assertTrue;
|
||||
|
||||
public class ScmClientConfigFileHandlerTest {
|
||||
|
||||
@Rule
|
||||
public TemporaryFolder temporaryFolder = new TemporaryFolder();
|
||||
|
||||
@Test
|
||||
public void testClientConfigFileHandler() throws IOException {
|
||||
File configFile = temporaryFolder.newFile();
|
||||
|
||||
ScmClientConfigFileHandler handler = new ScmClientConfigFileHandler(
|
||||
new InMemoryKeyStore(), new UUIDKeyGenerator(), configFile
|
||||
);
|
||||
|
||||
ScmClientConfig config = new ScmClientConfig();
|
||||
ServerConfig defaultConfig = config.getDefaultConfig();
|
||||
defaultConfig.setServerUrl("http://localhost:8080/scm");
|
||||
defaultConfig.setUsername("scmadmin");
|
||||
defaultConfig.setPassword("admin123");
|
||||
handler.write(config);
|
||||
|
||||
assertTrue(configFile.exists());
|
||||
|
||||
config = handler.read();
|
||||
defaultConfig = config.getDefaultConfig();
|
||||
assertEquals("http://localhost:8080/scm", defaultConfig.getServerUrl());
|
||||
assertEquals("scmadmin", defaultConfig.getUsername());
|
||||
assertEquals("admin123", defaultConfig.getPassword());
|
||||
|
||||
handler.delete();
|
||||
|
||||
assertFalse(configFile.exists());
|
||||
}
|
||||
|
||||
private static class InMemoryKeyStore implements KeyStore {
|
||||
|
||||
private String secretKey;
|
||||
|
||||
@Override
|
||||
public void set(String secretKey) {
|
||||
this.secretKey = secretKey;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String get() {
|
||||
return secretKey;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void remove() {
|
||||
this.secretKey = null;
|
||||
}
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user