From 7ad028c9661b2b0a1b5e596da5f864879be22728 Mon Sep 17 00:00:00 2001 From: Marko Zivanovic Date: Thu, 12 Mar 2015 12:30:39 +0100 Subject: [PATCH] Implement new API --- jPasswordObfuscator-bin/pom.xml | 8 +- .../java/rs/in/zivanovic/obfuscator/Main.java | 36 +++---- .../obfuscator/ObfuscateCommand.java | 14 ++- .../obfuscator/ParsedCommandLine.java | 94 +++++++++++++++++++ ...teCommand.java => UnobfuscateCommand.java} | 22 ++--- .../obfuscator/ObfuscateCommandTest.java | 52 ++++++++++ .../in/zivanovic/obfuscator/Obfuscated.java | 19 +++- .../obfuscator/impl/ObfuscatedData.java | 14 +++ .../obfuscator/impl/ObfuscatorException.java | 5 + .../obfuscator/impl/PBEObfuscatorImpl.java | 9 ++ .../obfuscator/impl/V1ObfuscatorImpl.java | 4 + .../in/zivanovic/obfuscator/NewApiTest.java | 24 +++++ 12 files changed, 253 insertions(+), 48 deletions(-) create mode 100644 jPasswordObfuscator-bin/src/main/java/rs/in/zivanovic/obfuscator/ParsedCommandLine.java rename jPasswordObfuscator-bin/src/main/java/rs/in/zivanovic/obfuscator/{DeObfuscateCommand.java => UnobfuscateCommand.java} (68%) create mode 100644 jPasswordObfuscator-bin/src/test/java/rs/in/zivanovic/obfuscator/ObfuscateCommandTest.java diff --git a/jPasswordObfuscator-bin/pom.xml b/jPasswordObfuscator-bin/pom.xml index bae10af..e0ce0ca 100644 --- a/jPasswordObfuscator-bin/pom.xml +++ b/jPasswordObfuscator-bin/pom.xml @@ -58,7 +58,13 @@ rs.in.zivanovic j-password-obfuscator - 1.0.0 + 1.0.1-SNAPSHOT + + + junit + junit + 4.10 + test diff --git a/jPasswordObfuscator-bin/src/main/java/rs/in/zivanovic/obfuscator/Main.java b/jPasswordObfuscator-bin/src/main/java/rs/in/zivanovic/obfuscator/Main.java index 97fc6b4..70af81b 100644 --- a/jPasswordObfuscator-bin/src/main/java/rs/in/zivanovic/obfuscator/Main.java +++ b/jPasswordObfuscator-bin/src/main/java/rs/in/zivanovic/obfuscator/Main.java @@ -23,41 +23,31 @@ */ package rs.in.zivanovic.obfuscator; -import com.beust.jcommander.JCommander; -import java.util.HashMap; -import java.util.Map; -import java.util.Map.Entry; +import java.util.concurrent.Callable; /** - * Main entry point. + * Obfuscator command line utility main class. */ public class Main { private Main() { } + /** + * Entry point. + * + * @param args command line arguments + */ public static void main(String[] args) { - JCommander jc = new JCommander(); - Map commands = new HashMap<>(); - Map 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 e : commands.entrySet()) { - String[] a = aliases.get(e.getKey()); - jc.addCommand(e.getKey(), e.getValue(), a); - } - try { - jc.parse(args); - if (commands.keySet().contains(jc.getParsedCommand())) { - Runnable r = commands.get(jc.getParsedCommand()); - r.run(); + ParsedCommandLine pcl = new ParsedCommandLine(args); + Callable command = pcl.getCommand(); + if (command == null) { + System.out.println(pcl.getHelpText()); } else { - jc.usage(); + System.out.println(command.call()); } - } catch (RuntimeException ex) { + } catch (Exception ex) { System.err.println("ERROR: " + ex.getMessage()); } } diff --git a/jPasswordObfuscator-bin/src/main/java/rs/in/zivanovic/obfuscator/ObfuscateCommand.java b/jPasswordObfuscator-bin/src/main/java/rs/in/zivanovic/obfuscator/ObfuscateCommand.java index b596844..64f76c1 100644 --- a/jPasswordObfuscator-bin/src/main/java/rs/in/zivanovic/obfuscator/ObfuscateCommand.java +++ b/jPasswordObfuscator-bin/src/main/java/rs/in/zivanovic/obfuscator/ObfuscateCommand.java @@ -26,14 +26,14 @@ package rs.in.zivanovic.obfuscator; import com.beust.jcommander.Parameter; import com.beust.jcommander.Parameters; import com.google.common.base.Joiner; -import java.nio.charset.StandardCharsets; import java.util.List; +import java.util.concurrent.Callable; /** * Implementation of the obfuscate command. */ @Parameters(commandDescription = "Obfuscate sensitive data") -public class ObfuscateCommand implements Runnable { +public class ObfuscateCommand implements Callable { @Parameter(names = {"-k", "--key"}, description = "Master key to use for obfuscation", required = true) private String masterKey; @@ -43,14 +43,12 @@ public class ObfuscateCommand implements Runnable { private int version = 1; @Parameter(description = "data to obfuscate", required = true) - private List data; + private List params; @Override - public void run() { - JPasswordObfuscator jpo = new JPasswordObfuscator(); - byte[] dataBytes = Joiner.on(' ').join(data).getBytes(StandardCharsets.UTF_8); - String s = jpo.obfuscate(masterKey.toCharArray(), dataBytes, version); - System.out.println(s); + public String call() { + String data = Joiner.on(' ').join(params); + return new Obfuscated(masterKey.toCharArray(), data, version).toString(); } } diff --git a/jPasswordObfuscator-bin/src/main/java/rs/in/zivanovic/obfuscator/ParsedCommandLine.java b/jPasswordObfuscator-bin/src/main/java/rs/in/zivanovic/obfuscator/ParsedCommandLine.java new file mode 100644 index 0000000..8687db1 --- /dev/null +++ b/jPasswordObfuscator-bin/src/main/java/rs/in/zivanovic/obfuscator/ParsedCommandLine.java @@ -0,0 +1,94 @@ +/* + * The MIT License + * + * Copyright 2015 Marko Zivanovic . + * + * 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> COMMANDS = new HashMap<>(); + private static final Map ALIASES = new HashMap<>(); + private final Callable 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 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> e : COMMANDS.entrySet()) { + String[] a = ALIASES.get(e.getKey()); + jc.addCommand(e.getKey(), e.getValue(), a); + } + } + + private Callable parse(JCommander jc, String[] args) { + Callable ret = null; + jc.parse(args); + if (COMMANDS.keySet().contains(jc.getParsedCommand())) { + ret = COMMANDS.get(jc.getParsedCommand()); + } + return ret; + } + +} diff --git a/jPasswordObfuscator-bin/src/main/java/rs/in/zivanovic/obfuscator/DeObfuscateCommand.java b/jPasswordObfuscator-bin/src/main/java/rs/in/zivanovic/obfuscator/UnobfuscateCommand.java similarity index 68% rename from jPasswordObfuscator-bin/src/main/java/rs/in/zivanovic/obfuscator/DeObfuscateCommand.java rename to jPasswordObfuscator-bin/src/main/java/rs/in/zivanovic/obfuscator/UnobfuscateCommand.java index c66dfe8..1269836 100644 --- a/jPasswordObfuscator-bin/src/main/java/rs/in/zivanovic/obfuscator/DeObfuscateCommand.java +++ b/jPasswordObfuscator-bin/src/main/java/rs/in/zivanovic/obfuscator/UnobfuscateCommand.java @@ -26,27 +26,25 @@ package rs.in.zivanovic.obfuscator; import com.beust.jcommander.Parameter; import com.beust.jcommander.Parameters; import com.google.common.base.Joiner; -import java.nio.charset.StandardCharsets; 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") -public class DeObfuscateCommand implements Runnable { +@Parameters(commandDescription = "Un-obfuscate sensitive data") +public class UnobfuscateCommand implements Callable { - @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; - @Parameter(description = "obfuscated string to de-obfuscate", required = true) - private List data; + @Parameter(description = "obfuscated string to un-obfuscate", required = true) + private List params; @Override - public void run() { - JPasswordObfuscator jpo = new JPasswordObfuscator(); - String obfuscated = Joiner.on(' ').skipNulls().join(data); - byte[] deObfuscated = jpo.deObfuscate(masterKey.toCharArray(), obfuscated); - System.out.println(new String(deObfuscated, StandardCharsets.UTF_8)); + public String call() { + String obfuscated = Joiner.on(' ').skipNulls().join(params); + return new Unobfuscated(masterKey.toCharArray(), obfuscated).asString(); } } diff --git a/jPasswordObfuscator-bin/src/test/java/rs/in/zivanovic/obfuscator/ObfuscateCommandTest.java b/jPasswordObfuscator-bin/src/test/java/rs/in/zivanovic/obfuscator/ObfuscateCommandTest.java new file mode 100644 index 0000000..4d79d35 --- /dev/null +++ b/jPasswordObfuscator-bin/src/test/java/rs/in/zivanovic/obfuscator/ObfuscateCommandTest.java @@ -0,0 +1,52 @@ +/* + * The MIT License + * + * Copyright 2015 Marko Zivanovic . + * + * 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 a = Splitter.on(' ').split(args); + ParsedCommandLine pcl = new ParsedCommandLine(Iterables.toArray(a, String.class)); + Callable 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)); + } + +} diff --git a/jPasswordObfuscator-lib/src/main/java/rs/in/zivanovic/obfuscator/Obfuscated.java b/jPasswordObfuscator-lib/src/main/java/rs/in/zivanovic/obfuscator/Obfuscated.java index e450c4f..db61385 100644 --- a/jPasswordObfuscator-lib/src/main/java/rs/in/zivanovic/obfuscator/Obfuscated.java +++ b/jPasswordObfuscator-lib/src/main/java/rs/in/zivanovic/obfuscator/Obfuscated.java @@ -40,7 +40,7 @@ public class Obfuscated { /** * 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 */ public Obfuscated(char[] key, String data) { @@ -50,7 +50,18 @@ public class Obfuscated { /** * 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 */ public Obfuscated(char[] key, byte[] data) { @@ -60,8 +71,8 @@ public class Obfuscated { /** * Build new obfuscation wrapper using specified obfuscation algorithm version. * - * @param key master key to use for obfuscation - * @param data data to obfuscate + * @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) { diff --git a/jPasswordObfuscator-lib/src/main/java/rs/in/zivanovic/obfuscator/impl/ObfuscatedData.java b/jPasswordObfuscator-lib/src/main/java/rs/in/zivanovic/obfuscator/impl/ObfuscatedData.java index 5e5c34c..b6d023c 100644 --- a/jPasswordObfuscator-lib/src/main/java/rs/in/zivanovic/obfuscator/impl/ObfuscatedData.java +++ b/jPasswordObfuscator-lib/src/main/java/rs/in/zivanovic/obfuscator/impl/ObfuscatedData.java @@ -36,6 +36,13 @@ public class ObfuscatedData { private final byte[] salt; 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) { this.version = version; this.salt = Arrays.copyOf(salt, salt.length); @@ -48,6 +55,13 @@ public class ObfuscatedData { Base64.toBase64String(cipherText)); } + /** + * Parse string containing obfuscated data. + * + * @param obfuscatedString obfuscated string to parse + * + * @return parsed data + */ public static ObfuscatedData fromString(String obfuscatedString) { String[] parts = obfuscatedString.split("\\$"); if (parts.length != 5) { diff --git a/jPasswordObfuscator-lib/src/main/java/rs/in/zivanovic/obfuscator/impl/ObfuscatorException.java b/jPasswordObfuscator-lib/src/main/java/rs/in/zivanovic/obfuscator/impl/ObfuscatorException.java index 0c9f7f0..1e8c810 100644 --- a/jPasswordObfuscator-lib/src/main/java/rs/in/zivanovic/obfuscator/impl/ObfuscatorException.java +++ b/jPasswordObfuscator-lib/src/main/java/rs/in/zivanovic/obfuscator/impl/ObfuscatorException.java @@ -28,6 +28,11 @@ package rs.in.zivanovic.obfuscator.impl; */ public class ObfuscatorException extends RuntimeException { + /** + * Wrap throwable into obfuscator exception. + * + * @param cause throwable to wrap + */ public ObfuscatorException(Throwable cause) { super(cause); } diff --git a/jPasswordObfuscator-lib/src/main/java/rs/in/zivanovic/obfuscator/impl/PBEObfuscatorImpl.java b/jPasswordObfuscator-lib/src/main/java/rs/in/zivanovic/obfuscator/impl/PBEObfuscatorImpl.java index 724ad25..dda02d4 100644 --- a/jPasswordObfuscator-lib/src/main/java/rs/in/zivanovic/obfuscator/impl/PBEObfuscatorImpl.java +++ b/jPasswordObfuscator-lib/src/main/java/rs/in/zivanovic/obfuscator/impl/PBEObfuscatorImpl.java @@ -40,6 +40,15 @@ public class PBEObfuscatorImpl implements Obfuscator { private final int iterations; 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) { this.algo = algo; this.provider = provider; diff --git a/jPasswordObfuscator-lib/src/main/java/rs/in/zivanovic/obfuscator/impl/V1ObfuscatorImpl.java b/jPasswordObfuscator-lib/src/main/java/rs/in/zivanovic/obfuscator/impl/V1ObfuscatorImpl.java index a228111..ce47ffc 100644 --- a/jPasswordObfuscator-lib/src/main/java/rs/in/zivanovic/obfuscator/impl/V1ObfuscatorImpl.java +++ b/jPasswordObfuscator-lib/src/main/java/rs/in/zivanovic/obfuscator/impl/V1ObfuscatorImpl.java @@ -41,6 +41,10 @@ public class V1ObfuscatorImpl extends PBEObfuscatorImpl { private static final int SALT_LEN = 8; 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() { super(VERSION, ALGO, PROVIDER, ITERATION_COUNT, SALT_LEN); } diff --git a/jPasswordObfuscator-lib/src/test/java/rs/in/zivanovic/obfuscator/NewApiTest.java b/jPasswordObfuscator-lib/src/test/java/rs/in/zivanovic/obfuscator/NewApiTest.java index 43acece..56406c0 100644 --- a/jPasswordObfuscator-lib/src/test/java/rs/in/zivanovic/obfuscator/NewApiTest.java +++ b/jPasswordObfuscator-lib/src/test/java/rs/in/zivanovic/obfuscator/NewApiTest.java @@ -51,6 +51,20 @@ public class NewApiTest { 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(); @@ -59,4 +73,14 @@ public class NewApiTest { 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(); + } + }