Merge pull request #2 from zmarko/new-api

New api implementation (#1)
This commit is contained in:
Marko Živanović
2015-03-12 12:38:16 +01:00
18 changed files with 517 additions and 84 deletions
+3 -5
View File
@@ -13,7 +13,7 @@ Maven dependency is available at:
<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.1.0</version>
</dependency> </dependency>
## Typical usage ## Typical usage
@@ -23,8 +23,7 @@ Maven dependency is available at:
Say we want to obfuscate our super secret data string "Hello World" using the secret key "key". Say we want to obfuscate our super secret data string "Hello World" using the secret key "key".
We'd use the following snippet: We'd use the following snippet:
JPasswordObfuscator obfuscator = new JPasswordObfuscator(); String obfuscated = new Obfuscated("key".toCharArray(), "Hello World").toString();
String obfuscated = obfuscator.obfuscate("key".toCharArray(), "Hello World".getBytes(StandardCharsets.UTF_8));
System.out.println(obfuscated); System.out.println(obfuscated);
output will resemble this: output will resemble this:
@@ -40,8 +39,7 @@ As with all secret keys, it is recommended to use random, or, at least, not easi
When we want to un-obfuscate data: When we want to un-obfuscate data:
JPasswordObfuscator obfuscator = new JPasswordObfuscator(); String original = new Unobfuscated("key".toCharArray(), "$rizobf$1$l7IwmuwEZnY=$F5K7LeIP0u1cSluV3wBXqQ==").asString();
String original = obfuscator.deObfuscate("key".toCharArray(), "$rizobf$1$l7IwmuwEZnY=$F5K7LeIP0u1cSluV3wBXqQ==");
System.out.println(original); System.out.println(original);
and we get our original super secret data back: and we get our original super secret data back:
+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.1.0-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));
}
}
+3 -3
View File
@@ -3,7 +3,7 @@
<modelVersion>4.0.0</modelVersion> <modelVersion>4.0.0</modelVersion>
<groupId>rs.in.zivanovic</groupId> <groupId>rs.in.zivanovic</groupId>
<artifactId>j-password-obfuscator</artifactId> <artifactId>j-password-obfuscator</artifactId>
<version>1.0.1-SNAPSHOT</version> <version>1.1.0-SNAPSHOT</version>
<packaging>jar</packaging> <packaging>jar</packaging>
<name>JPasswordObfuscator library</name> <name>JPasswordObfuscator library</name>
@@ -51,7 +51,7 @@
<dependency> <dependency>
<groupId>junit</groupId> <groupId>junit</groupId>
<artifactId>junit</artifactId> <artifactId>junit</artifactId>
<version>4.10</version> <version>4.12</version>
<scope>test</scope> <scope>test</scope>
</dependency> </dependency>
</dependencies> </dependencies>
@@ -117,7 +117,7 @@
<plugin> <plugin>
<groupId>org.apache.maven.plugins</groupId> <groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-gpg-plugin</artifactId> <artifactId>maven-gpg-plugin</artifactId>
<version>1.5</version> <version>1.6</version>
<executions> <executions>
<execution> <execution>
<id>sign-artifacts</id> <id>sign-artifacts</id>
@@ -29,7 +29,7 @@ import rs.in.zivanovic.obfuscator.impl.Obfuscator;
import rs.in.zivanovic.obfuscator.impl.V1ObfuscatorImpl; import rs.in.zivanovic.obfuscator.impl.V1ObfuscatorImpl;
/** /**
* Obfuscate and de-obfuscate sensitive data. * Obfuscate and un-obfuscate sensitive data.
* *
* This method is useful for protecting potentially sensitive data from casual onlookers. It is NOT secure against * This method is useful for protecting potentially sensitive data from casual onlookers. It is NOT secure against
* attackers with access to source code or live systems. * attackers with access to source code or live systems.
@@ -44,7 +44,7 @@ public final class JPasswordObfuscator {
* @param masterKey master key to use for obfuscation * @param masterKey master key to use for obfuscation
* @param data data to obfuscate * @param data data to obfuscate
* *
* @return string containing obfuscated data; use {@link #deObfuscate} to get secret data from this string * @return string containing obfuscated data; use {@link #unObfuscate} to get secret data from this string
*/ */
public String obfuscate(char[] masterKey, byte[] data) { public String obfuscate(char[] masterKey, byte[] data) {
return obfuscate(masterKey, data, 1); return obfuscate(masterKey, data, 1);
@@ -57,7 +57,7 @@ public final class JPasswordObfuscator {
* @param data data to obfuscate * @param data data to obfuscate
* @param version obfuscation algorithm version to use * @param version obfuscation algorithm version to use
* *
* @return string containing obfuscated data; use {@link #deObfuscate} to get secret data from this string * @return string containing obfuscated data; use {@link #unObfuscate} to get secret data from this string
*/ */
public String obfuscate(char[] masterKey, byte[] data, int version) { public String obfuscate(char[] masterKey, byte[] data, int version) {
Objects.requireNonNull(masterKey); Objects.requireNonNull(masterKey);
@@ -71,20 +71,20 @@ public final class JPasswordObfuscator {
} }
/** /**
* De-obfuscate string generated with {@link #obfuscate} method. * Un-obfuscate string generated with {@link #obfuscate} method.
* *
* @param masterKey master key to use for de-obfuscation; must match the key used for obfuscation * @param masterKey master key to use for un-obfuscation; must match the key used for obfuscation
* @param obfuscatedString obfuscated string generated using one of {@link #obfuscate} methods * @param obfuscatedString obfuscated string generated using one of {@link #obfuscate} methods
* *
* @return original, de-obfuscated data * @return original, un-obfuscated data
*/ */
public byte[] deObfuscate(char[] masterKey, String obfuscatedString) { public byte[] unObfuscate(char[] masterKey, String obfuscatedString) {
Objects.requireNonNull(masterKey); Objects.requireNonNull(masterKey);
Objects.requireNonNull(obfuscatedString); Objects.requireNonNull(obfuscatedString);
ObfuscatedData ob = ObfuscatedData.fromString(obfuscatedString); ObfuscatedData ob = ObfuscatedData.fromString(obfuscatedString);
switch (ob.getVersion()) { switch (ob.getVersion()) {
case 1: case 1:
return v1Obfuscator.deObfuscate(masterKey, ob); return v1Obfuscator.unObfuscate(masterKey, ob);
default: default:
throw new IllegalArgumentException("Unsupported version: " + ob.getVersion()); throw new IllegalArgumentException("Unsupported version: " + ob.getVersion());
} }
@@ -0,0 +1,96 @@
/*
* 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 java.nio.charset.StandardCharsets;
import java.util.Objects;
import rs.in.zivanovic.obfuscator.impl.ObfuscatedData;
import rs.in.zivanovic.obfuscator.impl.V1ObfuscatorImpl;
/**
* Wrapper around sensitive data that performs obfuscation using secret key and generate output string. For the sake of
* security, you should zero your secret key data as soon as possible after constructing Obfuscated object. Objects of
* this class do not retain any sensitive data after being constructed.
*/
public class Obfuscated {
private final ObfuscatedData obfuscatedData;
/**
* Build new obfuscation wrapper using latest obfuscation algorithm version.
*
* @param key master key to use for obfuscation
* @param data data to obfuscate
*/
public Obfuscated(char[] key, String data) {
this(key, data.getBytes(StandardCharsets.UTF_8), 1);
}
/**
* Build new obfuscation wrapper using latest obfuscation algorithm version.
*
* @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
*/
public Obfuscated(char[] key, byte[] data) {
this(key, data, 1);
}
/**
* Build new obfuscation wrapper using specified obfuscation algorithm version.
*
* @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, byte[] data, int version) {
Objects.requireNonNull(key);
Objects.requireNonNull(data);
Objects.requireNonNull(version);
switch (version) {
case 1:
this.obfuscatedData = new V1ObfuscatorImpl().obfuscate(key, data);
break;
default:
throw new IllegalArgumentException("Unsupported version: " + version);
}
}
@Override
public String toString() {
return obfuscatedData.toString();
}
}
@@ -0,0 +1,83 @@
/*
* 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 java.nio.charset.StandardCharsets;
import java.util.Arrays;
import java.util.Objects;
import rs.in.zivanovic.obfuscator.impl.ObfuscatedData;
import rs.in.zivanovic.obfuscator.impl.ObfuscatorException;
import rs.in.zivanovic.obfuscator.impl.V1ObfuscatorImpl;
/**
* Wrapper around obfuscated data that performs un-obfuscation and generates output in various formats. For the sake of
* security, you should zero your secret key data as soon as possible after constructing Unobfuscated object. Objects of
* this class only retain un-obfuscated data after being constructed. First call to {@link #asByteArray()} or
* {@link #asString()} methods will return copy of and clear the un-obfuscated data; further calls will throw
* {@link ObfuscatorException}.
*/
public class Unobfuscated {
private byte[] data;
/**
* Build new wrapper around obfuscated data.
*
* @param key master key to use for un-obfuscation
* @param obfuscatedData obfuscated data to wrap
*/
public Unobfuscated(char[] key, String obfuscatedData) {
ObfuscatedData od = ObfuscatedData.fromString(obfuscatedData);
switch (od.getVersion()) {
case 1:
this.data = new V1ObfuscatorImpl().unObfuscate(key, od);
break;
default:
throw new IllegalArgumentException("Unsupported version: " + od.getVersion());
}
}
/**
* Return un-obfuscated data as byte array.
*
* @return un-obfuscated data
*/
public byte[] asByteArray() {
Objects.requireNonNull(data);
byte[] ret = Arrays.copyOf(data, data.length);
Arrays.fill(data, (byte) 0);
data = null;
return ret;
}
/**
* Return un-obfuscated data as string.
*
* @return un-obfuscated data
*/
public String asString() {
return new String(asByteArray(), StandardCharsets.UTF_8);
}
}
@@ -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) {
@@ -24,7 +24,7 @@
package rs.in.zivanovic.obfuscator.impl; package rs.in.zivanovic.obfuscator.impl;
/** /**
* Obfuscator/de-obfuscator interface. * Obfuscator/un-obfuscator interface.
*/ */
public interface Obfuscator { public interface Obfuscator {
@@ -39,12 +39,12 @@ public interface Obfuscator {
ObfuscatedData obfuscate(char[] masterKey, byte[] data); ObfuscatedData obfuscate(char[] masterKey, byte[] data);
/** /**
* De-obfuscate data using supplied master key. * un-obfuscate data using supplied master key.
* *
* @param masterKey master key to use for data de-obfuscation; must match master key used for obfuscation * @param masterKey master key to use for data un-obfuscation; must match master key used for obfuscation
* @param data string obfuscated with {@link #obfuscate} to de-obfuscate * @param data string obfuscated with {@link #obfuscate} to un-obfuscate
* *
* @return * @return
*/ */
byte[] deObfuscate(char[] masterKey, ObfuscatedData data); byte[] unObfuscate(char[] masterKey, ObfuscatedData data);
} }
@@ -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;
@@ -60,7 +69,7 @@ public class PBEObfuscatorImpl implements Obfuscator {
} }
@Override @Override
public byte[] deObfuscate(char[] masterKey, ObfuscatedData ob) { public byte[] unObfuscate(char[] masterKey, ObfuscatedData ob) {
try { try {
return crypto(Cipher.DECRYPT_MODE, ob.getCipherText(), masterKey, ob.getSalt()); return crypto(Cipher.DECRYPT_MODE, ob.getCipherText(), masterKey, ob.getSalt());
} catch (GeneralSecurityException ex) { } catch (GeneralSecurityException ex) {
@@ -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);
} }
@@ -40,11 +40,11 @@ public class JPasswordObfuscatorTest {
private final JPasswordObfuscator engine = new JPasswordObfuscator(); private final JPasswordObfuscator engine = new JPasswordObfuscator();
@Test @Test
public void testObfuscateDeObfuscate() { public void testObfuscateUnObfuscate() {
String ob = engine.obfuscate(masterKey, data); String ob = engine.obfuscate(masterKey, data);
System.out.println(ob); System.out.println(ob);
byte[] deob = engine.deObfuscate(masterKey, ob); byte[] unob = engine.unObfuscate(masterKey, ob);
assertArrayEquals(data, deob); assertArrayEquals(data, unob);
} }
@Test(expected = IllegalArgumentException.class) @Test(expected = IllegalArgumentException.class)
@@ -53,10 +53,10 @@ public class JPasswordObfuscatorTest {
} }
@Test(expected = RuntimeException.class) @Test(expected = RuntimeException.class)
public void testObfuscateDeObfuscateWrongMasterKey() { public void testObfuscateUnObfuscateWrongMasterKey() {
String ob = engine.obfuscate(masterKey1, data); String ob = engine.obfuscate(masterKey1, data);
byte[] deob = engine.deObfuscate(masterKey2, ob); byte[] unob = engine.unObfuscate(masterKey2, ob);
assertThat(data, not(equalTo(deob))); assertThat(data, not(equalTo(unob)));
} }
@Test @Test
@@ -83,33 +83,33 @@ public class JPasswordObfuscatorTest {
} }
@Test(expected = NullPointerException.class) @Test(expected = NullPointerException.class)
public void testDeobfuscateNullData() { public void testUnobfuscateNullData() {
engine.deObfuscate(masterKey, null); engine.unObfuscate(masterKey, null);
} }
@Test(expected = RuntimeException.class) @Test(expected = RuntimeException.class)
public void testDeobfuscateGarbageData() { public void testUnobfuscateGarbageData() {
engine.deObfuscate(masterKey, "23499999puchn-p23948hfrn-[8f2333331xd"); engine.unObfuscate(masterKey, "23499999puchn-p23948hfrn-[8f2333331xd");
} }
@Test(expected = IllegalArgumentException.class) @Test(expected = IllegalArgumentException.class)
public void testDeobfuscateInvalidSignature() { public void testUnobfuscateInvalidSignature() {
engine.deObfuscate(masterKey, "$INV$1$v1hACmQULBk=$ggRxqVe1fUMvUnzpLA6CoQ=="); engine.unObfuscate(masterKey, "$INV$1$v1hACmQULBk=$ggRxqVe1fUMvUnzpLA6CoQ==");
} }
@Test(expected = IllegalArgumentException.class) @Test(expected = IllegalArgumentException.class)
public void testDeobfuscateTruncated() { public void testUnobfuscateTruncated() {
engine.deObfuscate(masterKey, "$rizobf$1$v1hACmQULBk=$"); engine.unObfuscate(masterKey, "$rizobf$1$v1hACmQULBk=$");
} }
@Test(expected = RuntimeException.class) @Test(expected = RuntimeException.class)
public void testDeobfuscateInvalidData() { public void testUnobfuscateInvalidData() {
engine.deObfuscate(masterKey, "$rizobf$1$v1hACmQULBk=$ggRxqVe1fUMvUnzpLA6Coq"); engine.unObfuscate(masterKey, "$rizobf$1$v1hACmQULBk=$ggRxqVe1fUMvUnzpLA6Coq");
} }
@Test(expected = IllegalArgumentException.class) @Test(expected = IllegalArgumentException.class)
public void testDeobfuscateInvalidVersion() { public void testUnobfuscateInvalidVersion() {
engine.deObfuscate(masterKey, "$rizobf$211$v1hACmQULBk=$ggRxqVe1fUMvUnzpLA6Coq"); engine.unObfuscate(masterKey, "$rizobf$211$v1hACmQULBk=$ggRxqVe1fUMvUnzpLA6Coq");
} }
} }
@@ -0,0 +1,86 @@
/*
* 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 java.nio.charset.StandardCharsets;
import static org.hamcrest.CoreMatchers.*;
import static org.junit.Assert.*;
import org.junit.Test;
/**
* New API ({@link Obfuscated} and {@link Unobfuscated}) tests.
*/
public class NewApiTest {
private static final char[] key = "key".toCharArray();
private static final String dataS = "data";
private static final byte[] dataBA = dataS.getBytes(StandardCharsets.UTF_8);
@Test
public void testObfuscateUnobfuscateArrays() {
String o = new Obfuscated(key, dataBA).toString();
byte[] unob = new Unobfuscated(key, o).asByteArray();
assertThat(unob, equalTo(dataBA));
}
@Test
public void testObfuscateUnobfuscateStrings() {
String o = new Obfuscated(key, dataS).toString();
String unob = new Unobfuscated(key, o).asString();
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)
public void testUnobfuscationClearsData() {
String o = new Obfuscated(key, dataBA).toString();
Unobfuscated unob = new Unobfuscated(key, o);
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();
}
}