Implement new API

This commit is contained in:
Marko Zivanovic
2015-03-12 12:30:39 +01:00
parent 258a3a7b31
commit 7ad028c966
12 changed files with 253 additions and 48 deletions
+7 -1
View File
@@ -58,7 +58,13 @@
<dependency> <dependency>
<groupId>rs.in.zivanovic</groupId> <groupId>rs.in.zivanovic</groupId>
<artifactId>j-password-obfuscator</artifactId> <artifactId>j-password-obfuscator</artifactId>
<version>1.0.0</version> <version>1.0.1-SNAPSHOT</version>
</dependency>
<dependency>
<groupId>junit</groupId>
<artifactId>junit</artifactId>
<version>4.10</version>
<scope>test</scope>
</dependency> </dependency>
</dependencies> </dependencies>
@@ -23,41 +23,31 @@
*/ */
package rs.in.zivanovic.obfuscator; package rs.in.zivanovic.obfuscator;
import com.beust.jcommander.JCommander; import java.util.concurrent.Callable;
import java.util.HashMap;
import java.util.Map;
import java.util.Map.Entry;
/** /**
* Main entry point. * Obfuscator command line utility main class.
*/ */
public class Main { public class Main {
private Main() { private Main() {
} }
/**
* Entry point.
*
* @param args command line arguments
*/
public static void main(String[] args) { public static void main(String[] args) {
JCommander jc = new JCommander();
Map<String, Runnable> commands = new HashMap<>();
Map<String, String[]> aliases = new HashMap<>();
commands.put("o", new ObfuscateCommand());
aliases.put("o", new String[]{"ob", "obfuscate"});
commands.put("d", new DeObfuscateCommand());
aliases.put("d", new String[]{"deob", "deobfuscate"});
for (Entry<String, Runnable> e : commands.entrySet()) {
String[] a = aliases.get(e.getKey());
jc.addCommand(e.getKey(), e.getValue(), a);
}
try { try {
jc.parse(args); ParsedCommandLine pcl = new ParsedCommandLine(args);
if (commands.keySet().contains(jc.getParsedCommand())) { Callable<String> command = pcl.getCommand();
Runnable r = commands.get(jc.getParsedCommand()); if (command == null) {
r.run(); System.out.println(pcl.getHelpText());
} else { } else {
jc.usage(); System.out.println(command.call());
} }
} catch (RuntimeException ex) { } catch (Exception ex) {
System.err.println("ERROR: " + ex.getMessage()); System.err.println("ERROR: " + ex.getMessage());
} }
} }
@@ -26,14 +26,14 @@ package rs.in.zivanovic.obfuscator;
import com.beust.jcommander.Parameter; import com.beust.jcommander.Parameter;
import com.beust.jcommander.Parameters; import com.beust.jcommander.Parameters;
import com.google.common.base.Joiner; import com.google.common.base.Joiner;
import java.nio.charset.StandardCharsets;
import java.util.List; import java.util.List;
import java.util.concurrent.Callable;
/** /**
* Implementation of the obfuscate command. * Implementation of the obfuscate command.
*/ */
@Parameters(commandDescription = "Obfuscate sensitive data") @Parameters(commandDescription = "Obfuscate sensitive data")
public class ObfuscateCommand implements Runnable { public class ObfuscateCommand implements Callable<String> {
@Parameter(names = {"-k", "--key"}, description = "Master key to use for obfuscation", required = true) @Parameter(names = {"-k", "--key"}, description = "Master key to use for obfuscation", required = true)
private String masterKey; private String masterKey;
@@ -43,14 +43,12 @@ public class ObfuscateCommand implements Runnable {
private int version = 1; private int version = 1;
@Parameter(description = "data to obfuscate", required = true) @Parameter(description = "data to obfuscate", required = true)
private List<String> data; private List<String> params;
@Override @Override
public void run() { public String call() {
JPasswordObfuscator jpo = new JPasswordObfuscator(); String data = Joiner.on(' ').join(params);
byte[] dataBytes = Joiner.on(' ').join(data).getBytes(StandardCharsets.UTF_8); return new Obfuscated(masterKey.toCharArray(), data, version).toString();
String s = jpo.obfuscate(masterKey.toCharArray(), dataBytes, version);
System.out.println(s);
} }
} }
@@ -0,0 +1,94 @@
/*
* The MIT License
*
* Copyright 2015 Marko Zivanovic <marko@zivanovic.in.rs>.
*
* Permission is hereby granted, free of charge, to any person obtaining a copy
* of this software and associated documentation files (the "Software"), to deal
* in the Software without restriction, including without limitation the rights
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
* copies of the Software, and to permit persons to whom the Software is
* furnished to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included in
* all copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
* THE SOFTWARE.
*/
package rs.in.zivanovic.obfuscator;
import com.beust.jcommander.JCommander;
import java.util.HashMap;
import java.util.Map;
import java.util.concurrent.Callable;
/**
* Wrapper around command line arguments that performs parsing and returns callable that executes required command.
*/
public class ParsedCommandLine {
private static final Map<String, Callable<String>> COMMANDS = new HashMap<>();
private static final Map<String, String[]> ALIASES = new HashMap<>();
private final Callable<String> command;
private final StringBuilder usage = new StringBuilder();
static {
COMMANDS.put("o", new ObfuscateCommand());
ALIASES.put("o", new String[]{"ob", "obfuscate"});
COMMANDS.put("u", new UnobfuscateCommand());
ALIASES.put("u", new String[]{"unob", "unobfuscate"});
}
/**
* Parse command line arguments and prepare command to execute and usage help text.
*
* @param args command line arguments to parse
*/
public ParsedCommandLine(String[] args) {
JCommander jc = new JCommander();
addCommands(jc);
jc.usage(usage);
this.command = parse(jc, args);
}
/**
* Get the command to execute.
*
* @return command to execute
*/
public Callable<String> getCommand() {
return command;
}
/**
* Get usage help text.
*
* @return usage help text
*/
public String getHelpText() {
return usage.toString();
}
private void addCommands(JCommander jc) {
for (Map.Entry<String, Callable<String>> e : COMMANDS.entrySet()) {
String[] a = ALIASES.get(e.getKey());
jc.addCommand(e.getKey(), e.getValue(), a);
}
}
private Callable<String> parse(JCommander jc, String[] args) {
Callable ret = null;
jc.parse(args);
if (COMMANDS.keySet().contains(jc.getParsedCommand())) {
ret = COMMANDS.get(jc.getParsedCommand());
}
return ret;
}
}
@@ -26,27 +26,25 @@ package rs.in.zivanovic.obfuscator;
import com.beust.jcommander.Parameter; import com.beust.jcommander.Parameter;
import com.beust.jcommander.Parameters; import com.beust.jcommander.Parameters;
import com.google.common.base.Joiner; import com.google.common.base.Joiner;
import java.nio.charset.StandardCharsets;
import java.util.List; import java.util.List;
import java.util.concurrent.Callable;
/** /**
* Implementation of the de-obfuscate command. * Implementation of the un-obfuscate command.
*/ */
@Parameters(commandDescription = "De-obfuscate sensitive data") @Parameters(commandDescription = "Un-obfuscate sensitive data")
public class DeObfuscateCommand implements Runnable { public class UnobfuscateCommand implements Callable<String> {
@Parameter(names = {"-k", "--key"}, description = "Master key to use for de-obfuscation", required = true) @Parameter(names = {"-k", "--key"}, description = "Master key to use for un-obfuscation", required = true)
private String masterKey; private String masterKey;
@Parameter(description = "obfuscated string to de-obfuscate", required = true) @Parameter(description = "obfuscated string to un-obfuscate", required = true)
private List<String> data; private List<String> params;
@Override @Override
public void run() { public String call() {
JPasswordObfuscator jpo = new JPasswordObfuscator(); String obfuscated = Joiner.on(' ').skipNulls().join(params);
String obfuscated = Joiner.on(' ').skipNulls().join(data); return new Unobfuscated(masterKey.toCharArray(), obfuscated).asString();
byte[] deObfuscated = jpo.deObfuscate(masterKey.toCharArray(), obfuscated);
System.out.println(new String(deObfuscated, StandardCharsets.UTF_8));
} }
} }
@@ -0,0 +1,52 @@
/*
* The MIT License
*
* Copyright 2015 Marko Zivanovic <marko@zivanovic.in.rs>.
*
* Permission is hereby granted, free of charge, to any person obtaining a copy
* of this software and associated documentation files (the "Software"), to deal
* in the Software without restriction, including without limitation the rights
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
* copies of the Software, and to permit persons to whom the Software is
* furnished to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included in
* all copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
* THE SOFTWARE.
*/
package rs.in.zivanovic.obfuscator;
import com.google.common.base.Splitter;
import com.google.common.collect.Iterables;
import java.util.concurrent.Callable;
import static org.hamcrest.CoreMatchers.*;
import static org.junit.Assert.*;
import org.junit.Test;
/**
* Obfuscator command-line utility unit tests.
*/
public class ObfuscateCommandTest {
private String run(String args) throws Exception {
Iterable<String> a = Splitter.on(' ').split(args);
ParsedCommandLine pcl = new ParsedCommandLine(Iterables.toArray(a, String.class));
Callable<String> c = pcl.getCommand();
return c.call();
}
@Test
public void testObfuscateUnobfuscate() throws Exception {
String o = run("o -k test test");
String d = run("u -k test " + o);
assertThat("test", equalTo(d));
}
}
@@ -40,7 +40,7 @@ public class Obfuscated {
/** /**
* Build new obfuscation wrapper using latest obfuscation algorithm version. * Build new obfuscation wrapper using latest obfuscation algorithm version.
* *
* @param key master key to use for obfuscation * @param key master key to use for obfuscation
* @param data data to obfuscate * @param data data to obfuscate
*/ */
public Obfuscated(char[] key, String data) { public Obfuscated(char[] key, String data) {
@@ -50,7 +50,18 @@ public class Obfuscated {
/** /**
* Build new obfuscation wrapper using latest obfuscation algorithm version. * Build new obfuscation wrapper using latest obfuscation algorithm version.
* *
* @param key master key to use for obfuscation * @param key master key to use for obfuscation
* @param data data to obfuscate
* @param version version of the obfuscation algorithm to use
*/
public Obfuscated(char[] key, String data, int version) {
this(key, data.getBytes(StandardCharsets.UTF_8), version);
}
/**
* Build new obfuscation wrapper using latest obfuscation algorithm version.
*
* @param key master key to use for obfuscation
* @param data data to obfuscate * @param data data to obfuscate
*/ */
public Obfuscated(char[] key, byte[] data) { public Obfuscated(char[] key, byte[] data) {
@@ -60,8 +71,8 @@ public class Obfuscated {
/** /**
* Build new obfuscation wrapper using specified obfuscation algorithm version. * Build new obfuscation wrapper using specified obfuscation algorithm version.
* *
* @param key master key to use for obfuscation * @param key master key to use for obfuscation
* @param data data to obfuscate * @param data data to obfuscate
* @param version version of the obfuscation algorithm to use * @param version version of the obfuscation algorithm to use
*/ */
public Obfuscated(char[] key, byte[] data, int version) { public Obfuscated(char[] key, byte[] data, int version) {
@@ -36,6 +36,13 @@ public class ObfuscatedData {
private final byte[] salt; private final byte[] salt;
private final byte[] cipherText; private final byte[] cipherText;
/**
* Build new obfuscated data container.
*
* @param version version of the algorithm used to obfuscate data
* @param salt random salt bytes
* @param cipherText obfuscated data
*/
public ObfuscatedData(int version, byte[] salt, byte[] cipherText) { public ObfuscatedData(int version, byte[] salt, byte[] cipherText) {
this.version = version; this.version = version;
this.salt = Arrays.copyOf(salt, salt.length); this.salt = Arrays.copyOf(salt, salt.length);
@@ -48,6 +55,13 @@ public class ObfuscatedData {
Base64.toBase64String(cipherText)); Base64.toBase64String(cipherText));
} }
/**
* Parse string containing obfuscated data.
*
* @param obfuscatedString obfuscated string to parse
*
* @return parsed data
*/
public static ObfuscatedData fromString(String obfuscatedString) { public static ObfuscatedData fromString(String obfuscatedString) {
String[] parts = obfuscatedString.split("\\$"); String[] parts = obfuscatedString.split("\\$");
if (parts.length != 5) { if (parts.length != 5) {
@@ -28,6 +28,11 @@ package rs.in.zivanovic.obfuscator.impl;
*/ */
public class ObfuscatorException extends RuntimeException { public class ObfuscatorException extends RuntimeException {
/**
* Wrap throwable into obfuscator exception.
*
* @param cause throwable to wrap
*/
public ObfuscatorException(Throwable cause) { public ObfuscatorException(Throwable cause) {
super(cause); super(cause);
} }
@@ -40,6 +40,15 @@ public class PBEObfuscatorImpl implements Obfuscator {
private final int iterations; private final int iterations;
private final int saltLen; private final int saltLen;
/**
* Build new PBE-based obfuscator with specified parameters.
*
* @param version version number for this set of parameters
* @param algo encryption algorithm to use
* @param provider crypto provider
* @param iterations number of key derivation rounds
* @param saltLen length of random salt in bytes
*/
public PBEObfuscatorImpl(int version, String algo, String provider, int iterations, int saltLen) { public PBEObfuscatorImpl(int version, String algo, String provider, int iterations, int saltLen) {
this.algo = algo; this.algo = algo;
this.provider = provider; this.provider = provider;
@@ -41,6 +41,10 @@ public class V1ObfuscatorImpl extends PBEObfuscatorImpl {
private static final int SALT_LEN = 8; private static final int SALT_LEN = 8;
private static final int ITERATION_COUNT = 16_000; private static final int ITERATION_COUNT = 16_000;
/**
* Build new PBE-based obfuscator using PBEWithSHA256And128BitAES-CBC-BC with 8 bytes random salt and 16000
* iterations.
*/
public V1ObfuscatorImpl() { public V1ObfuscatorImpl() {
super(VERSION, ALGO, PROVIDER, ITERATION_COUNT, SALT_LEN); super(VERSION, ALGO, PROVIDER, ITERATION_COUNT, SALT_LEN);
} }
@@ -51,6 +51,20 @@ public class NewApiTest {
assertThat(unob, equalTo(dataS)); assertThat(unob, equalTo(dataS));
} }
@Test
public void testObfuscateUnobfuscateArraysV1() {
String o = new Obfuscated(key, dataBA, 1).toString();
byte[] unob = new Unobfuscated(key, o).asByteArray();
assertThat(unob, equalTo(dataBA));
}
@Test
public void testObfuscateUnobfuscateStringsV1() {
String o = new Obfuscated(key, dataS, 1).toString();
String unob = new Unobfuscated(key, o).asString();
assertThat(unob, equalTo(dataS));
}
@Test(expected = NullPointerException.class) @Test(expected = NullPointerException.class)
public void testUnobfuscationClearsData() { public void testUnobfuscationClearsData() {
String o = new Obfuscated(key, dataBA).toString(); String o = new Obfuscated(key, dataBA).toString();
@@ -59,4 +73,14 @@ public class NewApiTest {
unob.asByteArray(); unob.asByteArray();
} }
@Test(expected = IllegalArgumentException.class)
public void testObfuscatedInvalidVersion() {
String o = new Obfuscated(key, dataS, -1).toString();
}
@Test(expected = IllegalArgumentException.class)
public void testUnobfuscatedInvalidVersion() {
String o = new Unobfuscated(key, "$rizobf$-1$F6Krv4H91RE=$NjytmuPjmfZbwQjqUWcbDg==").asString();
}
} }