/* * Copyright 2001-2013 Geert Bevin (gbevin[remove] at uwyn dot com) * Licensed under the Apache License, Version 2.0 (the "License") * Obfuscation code Copyright (c) 1998 Mort Bay Consulting (Australia) Pty. Ltd. */ package com.uwyn.rife.tools; import com.uwyn.rife.datastructures.EnumClass; import java.security.MessageDigest; import java.security.NoSuchAlgorithmException; public class StringEncryptor extends EnumClass<String> { private static final String IDENTIFIER_HEX_SUFFIX = "HEX"; public static final String IDENTIFIER_OBF = "OBF"; public static final String IDENTIFIER_MD5 = "MD5"; public static final String IDENTIFIER_MD5HEX = IDENTIFIER_MD5 + IDENTIFIER_HEX_SUFFIX; public static final String IDENTIFIER_SHA = "SHA"; public static final String IDENTIFIER_SHAHEX = IDENTIFIER_SHA + IDENTIFIER_HEX_SUFFIX; public static final String IDENTIFIER_WHIRLPOOL = "WRP"; public static final String IDENTIFIER_WHIRLPOOLHEX = IDENTIFIER_WHIRLPOOL + IDENTIFIER_HEX_SUFFIX; private static final String PREFIX_SEPERATOR_SUFFIX = ":"; private static final String PREFIX_OBF = IDENTIFIER_OBF + PREFIX_SEPERATOR_SUFFIX; private static final String PREFIX_MD5 = IDENTIFIER_MD5 + PREFIX_SEPERATOR_SUFFIX; private static final String PREFIX_MD5HEX = IDENTIFIER_MD5HEX + PREFIX_SEPERATOR_SUFFIX; private static final String PREFIX_SHA = IDENTIFIER_SHA + PREFIX_SEPERATOR_SUFFIX; private static final String PREFIX_SHAHEX = IDENTIFIER_SHAHEX + PREFIX_SEPERATOR_SUFFIX; private static final String PREFIX_WHIRLPOOL = IDENTIFIER_WHIRLPOOL + PREFIX_SEPERATOR_SUFFIX; private static final String PREFIX_WHIRLPOOLHEX = IDENTIFIER_WHIRLPOOLHEX + PREFIX_SEPERATOR_SUFFIX; public static final StringEncryptor OBF = new StringEncryptor(PREFIX_OBF); public static final StringEncryptor MD5 = new StringEncryptor(PREFIX_MD5); public static final StringEncryptor MD5HEX = new StringEncryptor(PREFIX_MD5HEX); public static final StringEncryptor SHA = new StringEncryptor(PREFIX_SHA); public static final StringEncryptor SHAHEX = new StringEncryptor(PREFIX_SHAHEX); public static final StringEncryptor WHIRLPOOL = new StringEncryptor(PREFIX_WHIRLPOOL); public static final StringEncryptor WHIRLPOOLHEX = new StringEncryptor(PREFIX_WHIRLPOOLHEX); private StringEncryptor(String identifier) { super(identifier); } public static StringEncryptor getEncryptor(String identifier) { return getMember(StringEncryptor.class, identifier + PREFIX_SEPERATOR_SUFFIX); } private static String encodeBase64(byte[] bytes) { return Base64.encodeToString(bytes, false); } private static String encodeHex(byte[] bytes) { StringBuilder sb = new StringBuilder(); for (byte b : bytes) { sb.append(Integer.toHexString((b & 0xFF) | 0x100).substring(1, 3)); } return sb.toString(); } public static String autoEncrypt(String value) throws NoSuchAlgorithmException { if (null == value) throw new IllegalArgumentException("value can't be null"); if (value.startsWith(PREFIX_OBF)) { return PREFIX_OBF + obfuscate(value.substring(PREFIX_OBF.length())); } else { boolean encode_base64; String prefix; byte[] bytes; if (value.startsWith(PREFIX_SHA) || value.startsWith(PREFIX_SHAHEX)) { if (value.startsWith(PREFIX_SHA)) { prefix = PREFIX_SHA; encode_base64 = true; } else { prefix = PREFIX_SHAHEX; encode_base64 = false; } MessageDigest digest = MessageDigest.getInstance("SHA"); digest.update(value.substring(prefix.length()).getBytes()); bytes = digest.digest(); } else if (value.startsWith(PREFIX_WHIRLPOOL) || value.startsWith(PREFIX_WHIRLPOOLHEX)) { if (value.startsWith(PREFIX_WHIRLPOOL)) { prefix = PREFIX_WHIRLPOOL; encode_base64 = true; } else { prefix = PREFIX_WHIRLPOOLHEX; encode_base64 = false; } Whirlpool whirlpool = new Whirlpool(); whirlpool.NESSIEinit(); whirlpool.NESSIEadd(value.substring(prefix.length())); byte[] digest = new byte[Whirlpool.DIGESTBYTES]; whirlpool.NESSIEfinalize(digest); bytes = digest; } else if (value.startsWith(PREFIX_MD5) || value.startsWith(PREFIX_MD5HEX)) { if (value.startsWith(PREFIX_MD5)) { prefix = PREFIX_MD5; encode_base64 = true; } else { prefix = PREFIX_MD5HEX; encode_base64 = false; } MessageDigest digest = MessageDigest.getInstance("MD5"); digest.update(value.substring(prefix.length()).getBytes()); bytes = digest.digest(); } else { return value; } if (encode_base64) { value = prefix + encodeBase64(bytes); } else { value = prefix + encodeHex(bytes); } } return value; } public static boolean matches(String checkedValue, String encryptedValue) throws NoSuchAlgorithmException { if (null == checkedValue) throw new IllegalArgumentException("checkedValue can't be null"); if (null == encryptedValue) throw new IllegalArgumentException("encryptedValue can't be null"); return encryptedValue.equals(adaptiveEncrypt(checkedValue, encryptedValue)); } public static String adaptiveEncrypt(String clearValue, String encryptedValue) throws NoSuchAlgorithmException { if (null == clearValue) throw new IllegalArgumentException("clearValue can't be null"); if (null == encryptedValue) throw new IllegalArgumentException("encryptedValue can't be null"); if (encryptedValue.startsWith(PREFIX_OBF)) { clearValue = PREFIX_OBF + clearValue; } else if (encryptedValue.startsWith(PREFIX_WHIRLPOOL)) { clearValue = PREFIX_WHIRLPOOL + clearValue; } else if (encryptedValue.startsWith(PREFIX_WHIRLPOOLHEX)) { clearValue = PREFIX_WHIRLPOOLHEX + clearValue; } else if (encryptedValue.startsWith(PREFIX_MD5)) { clearValue = PREFIX_MD5 + clearValue; } else if (encryptedValue.startsWith(PREFIX_MD5HEX)) { clearValue = PREFIX_MD5HEX + clearValue; } else if (encryptedValue.startsWith(PREFIX_SHA)) { clearValue = PREFIX_SHA + clearValue; } else if (encryptedValue.startsWith(PREFIX_SHAHEX)) { clearValue = PREFIX_SHAHEX + clearValue; } return autoEncrypt(clearValue); } public static String obfuscate(String value) { if (null == value) throw new IllegalArgumentException("value can't be null"); StringBuilder buffer = new StringBuilder(); byte[] bytes = value.getBytes(); for (int i = 0; i < bytes.length; i++) { byte b1 = bytes[i]; byte b2 = bytes[value.length() - (i + 1)]; int i1 = (int)b1 + (int)b2 + 127; int i2 = (int)b1 - (int)b2 + 127; int i0 = i1 * 256 + i2; String x = Integer.toString(i0, 36); switch (x.length()) { case 1: buffer.append('0'); case 2: buffer.append('0'); case 3: buffer.append('0'); default: buffer.append(x); } } return buffer.toString(); } public static String deobfuscate(String value) { if (null == value) throw new IllegalArgumentException("value can't be null"); if (value.startsWith(PREFIX_OBF)) { value = value.substring(PREFIX_OBF.length()); } byte[] bytes = new byte[value.length() / 2]; int l = 0; for (int i = 0; i < value.length(); i += 4) { String x = value.substring(i, i + 4); int i0 = Integer.parseInt(x, 36); int i1 = (i0 / 256); int i2 = (i0 % 256); bytes[l++] = (byte)((i1 + i2 - 254) / 2); } return new String(bytes, 0, l); } public static void main(String[] arguments) { boolean valid_arguments = true; if (arguments.length < 1 || arguments.length > 3) { valid_arguments = false; } else if (!arguments[0].startsWith("-")) { if (arguments.length > 1) { valid_arguments = false; } } else { if (!arguments[0].equals("-e") && !arguments[0].equals("-d") && !arguments[0].equals("-c")) { valid_arguments = false; } else if (!arguments[0].equals("-c") && 3 == arguments.length) { valid_arguments = false; } } if (!valid_arguments) { System.err.println("Usage : java " + StringEncryptor.class.getName() + " [-edc] string {encrypted}"); System.err.println("Encrypts strings for usage with RIFE."); System.err.println(" -e encrypt a string (default)"); System.err.println(" -d decrypt a string if the algorithm support it"); System.err.println(" -c check the validity of the string against an encrypted version"); System.exit(1); } try { if (1 == arguments.length) { System.err.println(autoEncrypt(arguments[0])); System.exit(0); } else if (arguments[0].equals("-e")) { System.err.println(autoEncrypt(arguments[1])); System.exit(0); } if (arguments[0].equals("-d")) { if (arguments[1].startsWith(PREFIX_OBF)) { System.err.println(deobfuscate(arguments[1])); } else { System.err.println("ERROR: the algorithm doesn't support decoding."); } System.exit(0); } if (arguments[0].equals("-c")) { if (matches(arguments[1], arguments[2])) { System.err.println("VALID: the strings match."); } else { System.err.println("INVALID: the strings don't match."); } System.exit(0); } } catch (NoSuchAlgorithmException e) { e.printStackTrace(); } } public String encrypt(String value) throws NoSuchAlgorithmException { if (null == value) throw new IllegalArgumentException("value can't be null"); return autoEncrypt(identifier + value); } }