Add new API

This commit is contained in:
Marko Zivanovic
2015-03-11 23:30:21 +01:00
parent eddb894072
commit b16158d5e4
7 changed files with 262 additions and 32 deletions
@@ -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,85 @@
/*
* 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
*/
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);
}
}
@@ -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);
} }
@@ -60,7 +60,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) {
@@ -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,62 @@
/*
* 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(expected = NullPointerException.class)
public void testUnobfuscationClearsData() {
String o = new Obfuscated(key, dataBA).toString();
Unobfuscated unob = new Unobfuscated(key, o);
unob.asByteArray();
unob.asByteArray();
}
}