mirror of
https://github.com/scm-manager/scm-manager.git
synced 2026-03-10 06:10:19 +01:00
merge with branch issue-979
This commit is contained in:
@@ -0,0 +1,114 @@
|
||||
/**
|
||||
* 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 com.google.common.base.Charsets;
|
||||
|
||||
import javax.crypto.Cipher;
|
||||
import javax.crypto.CipherInputStream;
|
||||
import javax.crypto.CipherOutputStream;
|
||||
import javax.crypto.spec.IvParameterSpec;
|
||||
import javax.crypto.spec.SecretKeySpec;
|
||||
import java.io.IOException;
|
||||
import java.io.InputStream;
|
||||
import java.io.OutputStream;
|
||||
import java.security.SecureRandom;
|
||||
|
||||
/**
|
||||
* Implementation of {@link CipherStreamHandler} which uses AES. This version is used since version 1.60 for the
|
||||
* cli client encryption.
|
||||
*
|
||||
* @author Sebastian Sdorra
|
||||
* @since 1.60
|
||||
*/
|
||||
public class AesCipherStreamHandler implements CipherStreamHandler {
|
||||
|
||||
private static final String CIPHER_ALGORITHM = "AES/CBC/PKCS5PADDING";
|
||||
private static final String SECRET_KEY_ALGORITHM = "AES";
|
||||
private static final int IV_LENGTH = 16;
|
||||
|
||||
private final SecureRandom random = new SecureRandom();
|
||||
|
||||
private final byte[] secretKey;
|
||||
|
||||
AesCipherStreamHandler(String secretKey) {
|
||||
this.secretKey = secretKey.getBytes(Charsets.UTF_8);
|
||||
}
|
||||
|
||||
@Override
|
||||
public OutputStream encrypt(OutputStream outputStream) throws IOException {
|
||||
Cipher cipher = createCipherForEncryption();
|
||||
outputStream.write(cipher.getIV());
|
||||
return new CipherOutputStream(outputStream, cipher);
|
||||
}
|
||||
|
||||
@Override
|
||||
public InputStream decrypt(InputStream inputStream) throws IOException {
|
||||
Cipher cipher = createCipherForDecryption(inputStream);
|
||||
return new CipherInputStream(inputStream, cipher);
|
||||
}
|
||||
|
||||
private Cipher createCipherForDecryption(InputStream inputStream) throws IOException {
|
||||
byte[] iv = createEmptyIvArray();
|
||||
inputStream.read(iv);
|
||||
return createCipher(Cipher.DECRYPT_MODE, iv);
|
||||
}
|
||||
|
||||
private byte[] createEmptyIvArray() {
|
||||
return new byte[IV_LENGTH];
|
||||
}
|
||||
|
||||
private Cipher createCipherForEncryption() {
|
||||
byte[] iv = generateIV();
|
||||
return createCipher(Cipher.ENCRYPT_MODE, iv);
|
||||
}
|
||||
|
||||
private byte[] generateIV() {
|
||||
// use 12 byte as described at nist
|
||||
// https://nvlpubs.nist.gov/nistpubs/Legacy/SP/nistspecialpublication800-38d.pdf
|
||||
byte[] iv = createEmptyIvArray();
|
||||
random.nextBytes(iv);
|
||||
return iv;
|
||||
}
|
||||
|
||||
private Cipher createCipher(int mode, byte[] iv) {
|
||||
try {
|
||||
Cipher cipher = Cipher.getInstance(CIPHER_ALGORITHM);
|
||||
IvParameterSpec ivParameterSpec = new IvParameterSpec(iv);
|
||||
cipher.init(mode, new SecretKeySpec(secretKey, SECRET_KEY_ALGORITHM), ivParameterSpec);
|
||||
return cipher;
|
||||
} catch (Exception ex) {
|
||||
throw new ScmConfigException("failed to create cipher", ex);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -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.io.IOException;
|
||||
import java.io.InputStream;
|
||||
import java.io.OutputStream;
|
||||
|
||||
/**
|
||||
* The CipherStreamHandler is able to encrypt and decrypt streams.
|
||||
*
|
||||
* @author Sebastian Sdorra
|
||||
* @since 1.60
|
||||
*/
|
||||
public interface CipherStreamHandler {
|
||||
|
||||
/**
|
||||
* Decrypts the given input stream.
|
||||
*
|
||||
* @param inputStream encrypted input stream
|
||||
*
|
||||
* @return raw input stream
|
||||
*/
|
||||
InputStream decrypt(InputStream inputStream) throws IOException;
|
||||
|
||||
/**
|
||||
* Encrypts the given output stream.
|
||||
*
|
||||
* @param outputStream raw output stream
|
||||
*
|
||||
* @return encrypting output stream
|
||||
*/
|
||||
OutputStream encrypt(OutputStream outputStream) throws IOException;
|
||||
}
|
||||
@@ -0,0 +1,151 @@
|
||||
/**
|
||||
* 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 com.google.common.annotations.VisibleForTesting;
|
||||
import com.google.common.base.Charsets;
|
||||
import com.google.common.base.Preconditions;
|
||||
import com.google.common.base.Strings;
|
||||
import sonia.scm.security.KeyGenerator;
|
||||
|
||||
import javax.xml.bind.JAXB;
|
||||
import java.io.*;
|
||||
import java.util.Arrays;
|
||||
|
||||
/**
|
||||
* Util methods for configuration files.
|
||||
*
|
||||
* @author Sebastian Sdorra
|
||||
* @since 1.60
|
||||
*/
|
||||
final class ConfigFiles {
|
||||
|
||||
private static final KeyGenerator keyGenerator = new SecureRandomKeyGenerator();
|
||||
|
||||
// SCM Config Version 2
|
||||
@VisibleForTesting
|
||||
static final byte[] VERSION_IDENTIFIER = "SCV2".getBytes(Charsets.US_ASCII);
|
||||
|
||||
private ConfigFiles() {
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns {@code true} if the file is encrypted with the v2 format.
|
||||
*
|
||||
* @param file configuration file
|
||||
*
|
||||
* @return {@code true} for format v2
|
||||
*
|
||||
* @throws IOException
|
||||
*/
|
||||
static boolean isFormatV2(File file) throws IOException {
|
||||
try (InputStream input = new FileInputStream(file)) {
|
||||
byte[] bytes = new byte[VERSION_IDENTIFIER.length];
|
||||
input.read(bytes);
|
||||
return Arrays.equals(VERSION_IDENTIFIER, bytes);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Decrypt and parse v1 configuration file.
|
||||
*
|
||||
* @param secretKeyStore key store
|
||||
* @param file configuration file
|
||||
*
|
||||
* @return client configuration
|
||||
*
|
||||
* @throws IOException
|
||||
*/
|
||||
static ScmClientConfig parseV1(SecretKeyStore secretKeyStore, File file) throws IOException {
|
||||
String secretKey = secretKey(secretKeyStore);
|
||||
CipherStreamHandler cipherStreamHandler = new WeakCipherStreamHandler(secretKey);
|
||||
return decrypt(cipherStreamHandler, new FileInputStream(file));
|
||||
}
|
||||
|
||||
/**
|
||||
* Decrypt and parse v12configuration file.
|
||||
*
|
||||
* @param secretKeyStore key store
|
||||
* @param file configuration file
|
||||
*
|
||||
* @return client configuration
|
||||
*
|
||||
* @throws IOException
|
||||
*/
|
||||
static ScmClientConfig parseV2(SecretKeyStore secretKeyStore, File file) throws IOException {
|
||||
String secretKey = secretKey(secretKeyStore);
|
||||
CipherStreamHandler cipherStreamHandler = new AesCipherStreamHandler(secretKey);
|
||||
try (InputStream input = new FileInputStream(file)) {
|
||||
input.skip(VERSION_IDENTIFIER.length);
|
||||
return decrypt(cipherStreamHandler, input);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Store encrypt and write the configuration to the given file.
|
||||
* Note the method uses always the latest available format.
|
||||
*
|
||||
* @param secretKeyStore key store
|
||||
* @param config configuration
|
||||
* @param file configuration file
|
||||
*
|
||||
* @throws IOException
|
||||
*/
|
||||
static void store(SecretKeyStore secretKeyStore, ScmClientConfig config, File file) throws IOException {
|
||||
String secretKey = keyGenerator.createKey();
|
||||
CipherStreamHandler cipherStreamHandler = new AesCipherStreamHandler(secretKey);
|
||||
try (OutputStream output = new FileOutputStream(file)) {
|
||||
output.write(VERSION_IDENTIFIER);
|
||||
encrypt(cipherStreamHandler, output, config);
|
||||
}
|
||||
secretKeyStore.set(secretKey);
|
||||
}
|
||||
|
||||
private static String secretKey(SecretKeyStore secretKeyStore) {
|
||||
String secretKey = secretKeyStore.get();
|
||||
Preconditions.checkState(!Strings.isNullOrEmpty(secretKey), "no stored secret key found");
|
||||
return secretKey;
|
||||
}
|
||||
|
||||
private static ScmClientConfig decrypt(CipherStreamHandler cipherStreamHandler, InputStream input) throws IOException {
|
||||
try ( InputStream decryptedInputStream = cipherStreamHandler.decrypt(input) ) {
|
||||
return JAXB.unmarshal(decryptedInputStream, ScmClientConfig.class);
|
||||
}
|
||||
}
|
||||
|
||||
private static void encrypt(CipherStreamHandler cipherStreamHandler, OutputStream output, ScmClientConfig clientConfig) throws IOException {
|
||||
try ( OutputStream encryptedOutputStream = cipherStreamHandler.encrypt(output) ) {
|
||||
JAXB.marshal(clientConfig, encryptedOutputStream);
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,138 @@
|
||||
/**
|
||||
* 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 com.google.common.annotations.VisibleForTesting;
|
||||
import com.google.common.base.Charsets;
|
||||
import com.google.common.base.Strings;
|
||||
import com.google.common.io.BaseEncoding;
|
||||
|
||||
import javax.crypto.BadPaddingException;
|
||||
import javax.crypto.Cipher;
|
||||
import javax.crypto.IllegalBlockSizeException;
|
||||
import javax.crypto.NoSuchPaddingException;
|
||||
import javax.crypto.spec.SecretKeySpec;
|
||||
import java.security.InvalidKeyException;
|
||||
import java.security.NoSuchAlgorithmException;
|
||||
import java.security.SecureRandom;
|
||||
|
||||
/**
|
||||
* The EncryptionSecretKeyStoreWrapper is a wrapper around the {@link SecretKeyStore} interface. The wrapper will
|
||||
* encrypt the passed secret keys, before they are written to the underlying {@link SecretKeyStore} implementation. The
|
||||
* wrapper will also honor old unencrypted keys.
|
||||
*
|
||||
* @author Sebastian Sdorra
|
||||
* @since 1.60
|
||||
*/
|
||||
public class EncryptionSecretKeyStoreWrapper implements SecretKeyStore {
|
||||
|
||||
private static final String ALGORITHM = "AES";
|
||||
|
||||
private static final SecureRandom random = new SecureRandom();
|
||||
|
||||
// i know storing the key directly in the class is far away from a best practice, but this is a chicken egg type
|
||||
// of problem. We need a key to encrypt the stored keys, however encrypting the keys with a static defined key
|
||||
// is better as storing them as plain text.
|
||||
private static final byte[] SECRET_KEY = new byte[]{ 0x50, 0x61, 0x41, 0x67, 0x55, 0x43, 0x48, 0x7a, 0x48, 0x59,
|
||||
0x7a, 0x57, 0x6b, 0x34, 0x54, 0x62
|
||||
};
|
||||
|
||||
@VisibleForTesting
|
||||
static final String ENCRYPTED_PREFIX = "SKV2:";
|
||||
|
||||
private SecretKeyStore wrappedSecretKeyStore;
|
||||
|
||||
EncryptionSecretKeyStoreWrapper(SecretKeyStore wrappedSecretKeyStore) {
|
||||
this.wrappedSecretKeyStore = wrappedSecretKeyStore;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void set(String secretKey) {
|
||||
String encrypted = encrypt(secretKey);
|
||||
wrappedSecretKeyStore.set(ENCRYPTED_PREFIX.concat(encrypted));
|
||||
}
|
||||
|
||||
private String encrypt(String value) {
|
||||
try {
|
||||
Cipher cipher = createCipher(Cipher.ENCRYPT_MODE);
|
||||
byte[] raw = cipher.doFinal(value.getBytes(Charsets.UTF_8));
|
||||
return encode(raw);
|
||||
} catch (IllegalBlockSizeException | BadPaddingException ex) {
|
||||
throw new ScmConfigException("failed to encrypt key", ex);
|
||||
}
|
||||
}
|
||||
|
||||
private String encode(byte[] raw) {
|
||||
return BaseEncoding.base64().encode(raw);
|
||||
}
|
||||
|
||||
@Override
|
||||
public String get() {
|
||||
String value = wrappedSecretKeyStore.get();
|
||||
if (Strings.nullToEmpty(value).startsWith(ENCRYPTED_PREFIX)) {
|
||||
String encrypted = value.substring(ENCRYPTED_PREFIX.length());
|
||||
return decrypt(encrypted);
|
||||
}
|
||||
return value;
|
||||
}
|
||||
|
||||
private String decrypt(String encoded) {
|
||||
try {
|
||||
Cipher cipher = createCipher(Cipher.DECRYPT_MODE);
|
||||
byte[] raw = decode(encoded);
|
||||
return new String(cipher.doFinal(raw), Charsets.UTF_8);
|
||||
} catch (IllegalBlockSizeException | BadPaddingException ex) {
|
||||
throw new ScmConfigException("failed to decrypt key", ex);
|
||||
}
|
||||
}
|
||||
|
||||
private byte[] decode(String encoded) {
|
||||
return BaseEncoding.base64().decode(encoded);
|
||||
}
|
||||
|
||||
private Cipher createCipher(int mode) {
|
||||
try {
|
||||
Cipher cipher = Cipher.getInstance(ALGORITHM);
|
||||
SecretKeySpec secretKeySpec = new SecretKeySpec(SECRET_KEY, "AES");
|
||||
cipher.init(mode, secretKeySpec, random);
|
||||
return cipher;
|
||||
} catch (NoSuchAlgorithmException | NoSuchPaddingException | InvalidKeyException ex) {
|
||||
throw new ScmConfigException("failed to create key", ex);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void remove() {
|
||||
wrappedSecretKeyStore.remove();
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,67 @@
|
||||
/**
|
||||
* 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;
|
||||
|
||||
/**
|
||||
* SecretKeyStore implementation with uses {@link Preferences}.
|
||||
*
|
||||
* @author Sebastian Sdorra
|
||||
* @since 1.60
|
||||
*/
|
||||
public class PrefsSecretKeyStore implements SecretKeyStore {
|
||||
|
||||
private static final String PREF_SECRET_KEY = "scm.client.key";
|
||||
|
||||
private final Preferences preferences;
|
||||
|
||||
PrefsSecretKeyStore() {
|
||||
// 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,12 @@ package sonia.scm.cli.config;
|
||||
|
||||
//~--- non-JDK imports --------------------------------------------------------
|
||||
|
||||
import sonia.scm.util.IOUtil;
|
||||
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.io.IOException;
|
||||
|
||||
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;
|
||||
//~--- JDK imports ------------------------------------------------------------
|
||||
|
||||
/**
|
||||
*
|
||||
@@ -81,62 +55,46 @@ 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 SecretKeyStore secretKeyStore;
|
||||
private final File file;
|
||||
|
||||
|
||||
/**
|
||||
* Constructs ...
|
||||
*
|
||||
* Constructs a new ScmClientConfigFileHandler with a encrypted {@link java.util.prefs.Preferences} based
|
||||
* {@link SecretKeyStore} and a determined default location.
|
||||
*/
|
||||
public ScmClientConfigFileHandler()
|
||||
{
|
||||
prefs = Preferences.userNodeForPackage(ScmClientConfigFileHandler.class);
|
||||
key = prefs.get(PREF_SECRET_KEY, null);
|
||||
public ScmClientConfigFileHandler() {
|
||||
this(new EncryptionSecretKeyStoreWrapper(new PrefsSecretKeyStore()), getDefaultConfigFile());
|
||||
}
|
||||
|
||||
if (Util.isEmpty(key))
|
||||
{
|
||||
key = createNewKey();
|
||||
prefs.put(PREF_SECRET_KEY, key);
|
||||
}
|
||||
|
||||
try
|
||||
{
|
||||
context = JAXBContext.newInstance(ScmClientConfig.class);
|
||||
}
|
||||
catch (JAXBException ex)
|
||||
{
|
||||
throw new ScmConfigException(
|
||||
"could not create JAXBContext for ScmClientConfig", ex);
|
||||
}
|
||||
ScmClientConfigFileHandler(SecretKeyStore secretKeyStore, File file) {
|
||||
this.secretKeyStore = secretKeyStore;
|
||||
this.file = file;
|
||||
}
|
||||
|
||||
//~--- 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);
|
||||
secretKeyStore.remove();
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -145,162 +103,42 @@ 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);
|
||||
|
||||
Unmarshaller um = context.createUnmarshaller();
|
||||
|
||||
config = (ScmClientConfig) um.unmarshal(input);
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
throw new ScmConfigException("could not read config file", ex);
|
||||
}
|
||||
finally
|
||||
{
|
||||
IOUtil.close(input);
|
||||
}
|
||||
if (file.exists()) {
|
||||
config = readFromFile();
|
||||
}
|
||||
|
||||
return config;
|
||||
}
|
||||
|
||||
private ScmClientConfig readFromFile() {
|
||||
ScmClientConfig config;
|
||||
try {
|
||||
if (ConfigFiles.isFormatV2(file)) {
|
||||
config = ConfigFiles.parseV2(secretKeyStore, file);
|
||||
} else {
|
||||
config = ConfigFiles.parseV1(secretKeyStore, file);
|
||||
ConfigFiles.store(secretKeyStore, config, file);
|
||||
}
|
||||
} catch (IOException ex) {
|
||||
throw new ScmConfigException("could not read config file", ex);
|
||||
}
|
||||
return config;
|
||||
}
|
||||
|
||||
/**
|
||||
* Method description
|
||||
*
|
||||
*
|
||||
* @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);
|
||||
|
||||
Marshaller m = context.createMarshaller();
|
||||
|
||||
m.marshal(config, output);
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
public void write(ScmClientConfig config) {
|
||||
try {
|
||||
ConfigFiles.store(secretKeyStore, config, file);
|
||||
} catch (IOException 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,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;
|
||||
|
||||
/**
|
||||
* SecretKeyStore is able to read and write secret keys.
|
||||
*
|
||||
* @author Sebastian Sdorra
|
||||
* @since 1.60
|
||||
*/
|
||||
public interface SecretKeyStore {
|
||||
|
||||
/**
|
||||
* 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 or {@code null}
|
||||
*/
|
||||
String get();
|
||||
|
||||
/**
|
||||
* Removes the secret key from store.
|
||||
*/
|
||||
void remove();
|
||||
}
|
||||
@@ -0,0 +1,69 @@
|
||||
/**
|
||||
* 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 com.google.common.annotations.VisibleForTesting;
|
||||
import sonia.scm.security.KeyGenerator;
|
||||
|
||||
import java.security.SecureRandom;
|
||||
import java.util.Locale;
|
||||
|
||||
/**
|
||||
* Create keys by using {@link SecureRandom}. The SecureRandomKeyGenerator produces aes compatible keys.
|
||||
* Warning the class is not thread safe.
|
||||
*
|
||||
* @author Sebastian Sdorra
|
||||
* @since 1.60
|
||||
*/
|
||||
public class SecureRandomKeyGenerator implements KeyGenerator {
|
||||
|
||||
private SecureRandom random = new SecureRandom();
|
||||
|
||||
// key length 16 for aes128
|
||||
@VisibleForTesting
|
||||
static final int KEY_LENGTH = 16;
|
||||
|
||||
private static final String UPPER = "ABCDEFGHIJKLMNOPQRSTUVWXYZ";
|
||||
private static final String LOWER = UPPER.toLowerCase(Locale.ENGLISH);
|
||||
private static final String DIGITS = "0123456789";
|
||||
private static final char[] ALL = (UPPER + LOWER + DIGITS).toCharArray();
|
||||
|
||||
@Override
|
||||
public String createKey() {
|
||||
char[] key = new char[KEY_LENGTH];
|
||||
for (int idx = 0; idx < KEY_LENGTH; ++idx) {
|
||||
key[idx] = ALL[random.nextInt(ALL.length)];
|
||||
}
|
||||
return new String(key);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,113 @@
|
||||
/**
|
||||
* 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}. This is the old implementation, which was used in versions prior
|
||||
* 1.60.
|
||||
*
|
||||
* @author Sebastian Sdorra
|
||||
* @since 1.60
|
||||
*
|
||||
* @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
|
||||
*/
|
||||
WeakCipherStreamHandler(String secretKey) {
|
||||
this.secretKey = secretKey.toCharArray();
|
||||
}
|
||||
|
||||
@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,68 @@
|
||||
/**
|
||||
* 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 com.google.common.base.Charsets;
|
||||
import com.google.common.io.ByteStreams;
|
||||
import org.junit.Test;
|
||||
import sonia.scm.security.KeyGenerator;
|
||||
|
||||
import java.io.*;
|
||||
|
||||
import static org.junit.Assert.assertEquals;
|
||||
|
||||
public class AesCipherStreamHandlerTest {
|
||||
|
||||
private final KeyGenerator keyGenerator = new SecureRandomKeyGenerator();
|
||||
|
||||
@Test
|
||||
public void testEncryptAndDecrypt() throws IOException {
|
||||
AesCipherStreamHandler cipherStreamHandler = new AesCipherStreamHandler(keyGenerator.createKey());
|
||||
|
||||
// douglas adams
|
||||
String content = "If you try and take a cat apart to see how it works, the first thing you have on your hands is a nonworking cat.";
|
||||
|
||||
// encrypt
|
||||
ByteArrayOutputStream output = new ByteArrayOutputStream();
|
||||
OutputStream encryptedOutput = cipherStreamHandler.encrypt(output);
|
||||
encryptedOutput.write(content.getBytes(Charsets.UTF_8));
|
||||
encryptedOutput.close();
|
||||
|
||||
InputStream input = new ByteArrayInputStream(output.toByteArray());
|
||||
input = cipherStreamHandler.decrypt(input);
|
||||
byte[] decrypted = ByteStreams.toByteArray(input);
|
||||
|
||||
assertEquals(content, new String(decrypted, Charsets.UTF_8));
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,96 @@
|
||||
/**
|
||||
* 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 com.google.common.base.Charsets;
|
||||
import com.google.common.io.ByteStreams;
|
||||
|
||||
import javax.xml.bind.JAXB;
|
||||
import java.io.*;
|
||||
|
||||
import static org.junit.Assert.assertEquals;
|
||||
|
||||
final class ClientConfigurationTests {
|
||||
|
||||
private ClientConfigurationTests() {
|
||||
}
|
||||
|
||||
static void testCipherStream(CipherStreamHandler cipherStreamHandler, String content) throws IOException {
|
||||
byte[] encrypted = encrypt(cipherStreamHandler, content);
|
||||
String decrypted = decrypt(cipherStreamHandler, encrypted);
|
||||
assertEquals(content, decrypted);
|
||||
}
|
||||
|
||||
|
||||
static byte[] encrypt(CipherStreamHandler cipherStreamHandler, String content) throws IOException {
|
||||
ByteArrayOutputStream output = new ByteArrayOutputStream();
|
||||
OutputStream encryptedOutput = cipherStreamHandler.encrypt(output);
|
||||
encryptedOutput.write(content.getBytes(Charsets.UTF_8));
|
||||
encryptedOutput.close();
|
||||
return output.toByteArray();
|
||||
}
|
||||
|
||||
static String decrypt(CipherStreamHandler cipherStreamHandler, byte[] encrypted) throws IOException {
|
||||
InputStream input = new ByteArrayInputStream(encrypted);
|
||||
input = cipherStreamHandler.decrypt(input);
|
||||
byte[] decrypted = ByteStreams.toByteArray(input);
|
||||
input.close();
|
||||
|
||||
return new String(decrypted, Charsets.UTF_8);
|
||||
}
|
||||
|
||||
static void assertSampleConfig(ScmClientConfig config) {
|
||||
ServerConfig defaultConfig;
|
||||
defaultConfig = config.getDefaultConfig();
|
||||
|
||||
assertEquals("http://localhost:8080/scm", defaultConfig.getServerUrl());
|
||||
assertEquals("admin", defaultConfig.getUsername());
|
||||
assertEquals("admin123", defaultConfig.getPassword());
|
||||
}
|
||||
|
||||
static ScmClientConfig createSampleConfig() {
|
||||
ScmClientConfig config = new ScmClientConfig();
|
||||
ServerConfig defaultConfig = config.getDefaultConfig();
|
||||
defaultConfig.setServerUrl("http://localhost:8080/scm");
|
||||
defaultConfig.setUsername("admin");
|
||||
defaultConfig.setPassword("admin123");
|
||||
return config;
|
||||
}
|
||||
|
||||
static void encrypt(CipherStreamHandler cipherStreamHandler, ScmClientConfig config, File file) throws IOException {
|
||||
try (OutputStream output = cipherStreamHandler.encrypt(new FileOutputStream(file))) {
|
||||
JAXB.marshal(config, output);
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,105 @@
|
||||
/**
|
||||
* 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 com.google.common.base.Charsets;
|
||||
import com.google.common.io.Files;
|
||||
import org.junit.Rule;
|
||||
import org.junit.Test;
|
||||
import org.junit.rules.TemporaryFolder;
|
||||
|
||||
import java.io.ByteArrayOutputStream;
|
||||
import java.io.File;
|
||||
import java.io.IOException;
|
||||
|
||||
import static org.junit.Assert.*;
|
||||
|
||||
public class ConfigFilesTest {
|
||||
|
||||
@Rule
|
||||
public TemporaryFolder temporaryFolder = new TemporaryFolder();
|
||||
|
||||
@Test
|
||||
public void testIsFormatV2() throws IOException {
|
||||
byte[] content = "The door was the way to... to... The Door was The Way".getBytes(Charsets.UTF_8);
|
||||
|
||||
File fileV1 = temporaryFolder.newFile();
|
||||
Files.write(content, fileV1);
|
||||
|
||||
assertFalse(ConfigFiles.isFormatV2(fileV1));
|
||||
|
||||
File fileV2 = temporaryFolder.newFile();
|
||||
|
||||
ByteArrayOutputStream baos = new ByteArrayOutputStream();
|
||||
baos.write(ConfigFiles.VERSION_IDENTIFIER);
|
||||
baos.write(content);
|
||||
Files.write(baos.toByteArray(), fileV2);
|
||||
|
||||
assertTrue(ConfigFiles.isFormatV2(fileV2));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testParseV1() throws IOException {
|
||||
InMemorySecretKeyStore keyStore = createKeyStore();
|
||||
WeakCipherStreamHandler handler = new WeakCipherStreamHandler(keyStore.get());
|
||||
|
||||
ScmClientConfig config = ClientConfigurationTests.createSampleConfig();
|
||||
File file = temporaryFolder.newFile();
|
||||
ClientConfigurationTests.encrypt(handler, config, file);
|
||||
|
||||
config = ConfigFiles.parseV1(keyStore, file);
|
||||
ClientConfigurationTests.assertSampleConfig(config);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void storeAndParseV2() throws IOException {
|
||||
InMemorySecretKeyStore keyStore = new InMemorySecretKeyStore();
|
||||
ScmClientConfig config = ClientConfigurationTests.createSampleConfig();
|
||||
File file = temporaryFolder.newFile();
|
||||
|
||||
ConfigFiles.store(keyStore, config, file);
|
||||
|
||||
String key = keyStore.get();
|
||||
assertNotNull(key);
|
||||
|
||||
config = ConfigFiles.parseV2(keyStore, file);
|
||||
ClientConfigurationTests.assertSampleConfig(config);
|
||||
}
|
||||
|
||||
private InMemorySecretKeyStore createKeyStore() {
|
||||
String secretKey = new SecureRandomKeyGenerator().createKey();
|
||||
InMemorySecretKeyStore keyStore = new InMemorySecretKeyStore();
|
||||
keyStore.set(secretKey);
|
||||
return keyStore;
|
||||
}
|
||||
|
||||
}
|
||||
@@ -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 org.junit.Test;
|
||||
|
||||
import static org.junit.Assert.*;
|
||||
|
||||
public class EncryptionSecretKeyStoreWrapperTest {
|
||||
|
||||
private SecretKeyStore secretKeyStore = new InMemorySecretKeyStore();
|
||||
|
||||
@Test
|
||||
public void testEncryptionKeyStoreWrapper() {
|
||||
EncryptionSecretKeyStoreWrapper wrapper = new EncryptionSecretKeyStoreWrapper(secretKeyStore);
|
||||
wrapper.set("mysecretkey");
|
||||
|
||||
assertEquals("mysecretkey", wrapper.get());
|
||||
assertTrue(secretKeyStore.get().startsWith(EncryptionSecretKeyStoreWrapper.ENCRYPTED_PREFIX));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testEncryptionKeyStoreWrapperWithOldUnencryptedKey() {
|
||||
secretKeyStore.set("mysecretkey");
|
||||
EncryptionSecretKeyStoreWrapper wrapper = new EncryptionSecretKeyStoreWrapper(secretKeyStore);
|
||||
assertEquals("mysecretkey", wrapper.get());
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,53 @@
|
||||
/**
|
||||
* 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;
|
||||
|
||||
public class InMemorySecretKeyStore implements SecretKeyStore {
|
||||
|
||||
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;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,138 @@
|
||||
/**
|
||||
* 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 com.google.common.io.Files;
|
||||
import com.google.common.io.Resources;
|
||||
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 java.net.URL;
|
||||
|
||||
import static org.junit.Assert.*;
|
||||
|
||||
public class ScmClientConfigFileHandlerTest {
|
||||
|
||||
@Rule
|
||||
public TemporaryFolder temporaryFolder = new TemporaryFolder();
|
||||
|
||||
@Test
|
||||
public void testClientConfigFileHandler() throws IOException {
|
||||
File configFile = temporaryFolder.newFile();
|
||||
|
||||
ScmClientConfigFileHandler handler = new ScmClientConfigFileHandler(
|
||||
new EncryptionSecretKeyStoreWrapper(new InMemorySecretKeyStore()), 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());
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testClientConfigFileHandlerWithOldConfiguration() throws IOException {
|
||||
File configFile = temporaryFolder.newFile();
|
||||
|
||||
// old implementation has used uuids as keys
|
||||
String key = new UUIDKeyGenerator().createKey();
|
||||
|
||||
WeakCipherStreamHandler weakCipherStreamHandler = new WeakCipherStreamHandler(key);
|
||||
ScmClientConfig clientConfig = ClientConfigurationTests.createSampleConfig();
|
||||
ClientConfigurationTests.encrypt(weakCipherStreamHandler, clientConfig, configFile);
|
||||
|
||||
assertFalse(ConfigFiles.isFormatV2(configFile));
|
||||
|
||||
SecretKeyStore secretKeyStore = new EncryptionSecretKeyStoreWrapper(new InMemorySecretKeyStore());
|
||||
secretKeyStore.set(key);
|
||||
|
||||
ScmClientConfigFileHandler handler = new ScmClientConfigFileHandler(
|
||||
secretKeyStore, configFile
|
||||
);
|
||||
|
||||
ScmClientConfig config = handler.read();
|
||||
ClientConfigurationTests.assertSampleConfig(config);
|
||||
|
||||
// ensure key has changed
|
||||
assertNotEquals(key, secretKeyStore.get());
|
||||
|
||||
// ensure config rewritten with v2
|
||||
assertTrue(ConfigFiles.isFormatV2(configFile));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testClientConfigFileHandlerWithRealMigration() throws IOException {
|
||||
URL resource = Resources.getResource("sonia/scm/cli/config/scm-cli-config.enc.xml");
|
||||
byte[] bytes = Resources.toByteArray(resource);
|
||||
|
||||
File configFile = temporaryFolder.newFile();
|
||||
Files.write(bytes, configFile);
|
||||
|
||||
String key = "358e018a-0c3c-4339-8266-3874e597305f";
|
||||
SecretKeyStore secretKeyStore = new EncryptionSecretKeyStoreWrapper(new InMemorySecretKeyStore());
|
||||
secretKeyStore.set(key);
|
||||
|
||||
ScmClientConfigFileHandler handler = new ScmClientConfigFileHandler(
|
||||
secretKeyStore, configFile
|
||||
);
|
||||
|
||||
ScmClientConfig config = handler.read();
|
||||
ServerConfig defaultConfig = config.getDefaultConfig();
|
||||
assertEquals("http://hitchhicker.com/scm", defaultConfig.getServerUrl());
|
||||
assertEquals("tricia", defaultConfig.getUsername());
|
||||
assertEquals("trillian123", defaultConfig.getPassword());
|
||||
|
||||
// ensure key has changed
|
||||
assertNotEquals(key, secretKeyStore.get());
|
||||
|
||||
// ensure config rewritten with v2
|
||||
assertTrue(ConfigFiles.isFormatV2(configFile));
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,47 @@
|
||||
/**
|
||||
* 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.Test;
|
||||
|
||||
import static org.junit.Assert.*;
|
||||
|
||||
public class SecureRandomKeyGeneratorTest {
|
||||
|
||||
@Test
|
||||
public void testCreateKey() {
|
||||
SecureRandomKeyGenerator keyGenerator = new SecureRandomKeyGenerator();
|
||||
assertNotNull(keyGenerator.createKey());
|
||||
assertEquals(SecureRandomKeyGenerator.KEY_LENGTH, keyGenerator.createKey().length());
|
||||
}
|
||||
|
||||
}
|
||||
Binary file not shown.
Reference in New Issue
Block a user