1 package net
.rizon
.acid
.util
;
3 import java
.net
.Inet4Address
;
4 import java
.net
.Inet6Address
;
5 import java
.net
.InetAddress
;
6 import java
.net
.UnknownHostException
;
7 import java
.security
.MessageDigest
;
8 import java
.security
.NoSuchAlgorithmException
;
11 * CloakGenerator that generates a cloaked host from an input IP or hostname.
15 public class CloakGenerator
18 protected String
[] keys
;
19 protected String hostPrefix
;
20 protected MessageDigest digest
;
22 public static boolean validateCloakKey(final String key
)
24 boolean hasLowerCase
= false;
25 boolean hasUpperCase
= false;
26 boolean hasDigit
= false;
28 for (int i
= 0; i
< key
.length(); i
++)
30 char c
= key
.charAt(i
);
31 if (!hasLowerCase
&& Character
.isLowerCase(c
))
35 if (!hasUpperCase
&& Character
.isUpperCase(c
))
39 if (!hasDigit
&& Character
.isDigit(c
))
43 if (hasLowerCase
&& hasUpperCase
&& hasDigit
)
52 public CloakGenerator(String
[] keys
, String hostPrefix
)
56 throw new IllegalArgumentException("There must be 3 keys for the cloaking system.");
60 this.hostPrefix
= hostPrefix
;
64 this.digest
= MessageDigest
.getInstance("MD5");
66 catch (NoSuchAlgorithmException ex
)
68 throw new RuntimeException("Unable to load MD5 digest", ex
);
73 * * Cloak an arbitrary host. Will do detection to figure out what kind
78 public String
cloak(final String host
)
81 if (host
.contains(":"))
85 return this.cloak(Inet6Address
.getByName(host
));
87 catch (UnknownHostException e
)
93 else if (host
.contains("."))
95 for (char c
: host
.toCharArray())
97 if (c
!= '.' && !Character
.isDigit(c
)) {
99 return this.cloakHost(host
);
105 return this.cloak(Inet4Address
.getByName(host
));
107 catch (UnknownHostException e
)
115 return this.cloakHost(host
);
120 * Cloak an internet address.
122 private String
cloak(InetAddress addr
)
124 if (addr
instanceof Inet4Address
)
126 return this.cloakIPv4((Inet4Address
) addr
);
128 else if (addr
instanceof Inet6Address
)
130 return this.cloakIPv6((Inet6Address
) addr
);
136 * Cloak an IPv4 address.
138 * Cloak format: A.B.C.D -> ALPHA.BETA.GAMMA.IP
140 * ALPHA: downmix(md5(md5(keys[1]:A.B.C.D:keys[2])keys[0]))
141 * BETA: downmix(md5(md5(keys[2]:A.B.C:keys[0])keys[1]))
142 * GAMMA: downmix(md5(md5(keys[0]:A.B:keys[1])keys[2]))
144 private String
cloakIPv4(Inet4Address addr
)
146 /* Gather IP pieces. */
147 byte[] bytePieces
= addr
.getAddress();
148 int[] pieces
= new int[bytePieces
.length
];
149 for (int i
= 0; i
< bytePieces
.length
; i
++)
151 pieces
[i
] = bytePieces
[i
] & 0xFF;
154 /* Generate alpha. */
155 String alphaInput
= String
.format("%d.%d.%d.%d", pieces
[0], pieces
[1], pieces
[2], pieces
[3]);
156 String alpha
= this.stage(alphaInput
, this.keys
[1], this.keys
[2], this.keys
[0]);
159 String betaInput
= String
.format("%d.%d.%d", pieces
[0], pieces
[1], pieces
[2]);
160 String beta
= this.stage(betaInput
, this.keys
[2], this.keys
[0], this.keys
[1]);
162 /* Generate gamma. */
163 String gammaInput
= String
.format("%d.%d", pieces
[0], pieces
[1]);
164 String gamma
= this.stage(gammaInput
, this.keys
[0], this.keys
[1], this.keys
[2]);
166 return alpha
+ "." + beta
+ "." + gamma
+ ".IP";
170 * Cloak an IPv6 address.
172 * Cloak format: a:b:c:d:e:f:g:h -> ALPHA:BETA:GAMMA:IP
174 * ALPHA: downmix(md5(md5(keys[1]:a:b:c:d:e:f:g:h:keys[2])keys[0]))
175 * BETA: downmix(md5(md5(keys[2]:a:b:c:d:e:f:g:keys[0])keys[1]))
176 * GAMMA: downmix(md5(md5(keys[0]:a:b:c:d:keys[1])keys[2]))
181 public String
cloakIPv6(Inet6Address addr
)
183 /* Gather IP pieces. */
184 byte[] bytePieces
= addr
.getAddress();
185 int[] pieces
= new int[bytePieces
.length
/ 2];
186 for (int i
= 0; i
< bytePieces
.length
; i
+= 2)
188 pieces
[i
/ 2] = ((bytePieces
[i
] & 0xFF) << 8) | (bytePieces
[i
+ 1] & 0xFF);
191 /* Generate alpha. */
192 String alphaInput
= String
.format("%x:%x:%x:%x:%x:%x:%x:%x:%x",
193 pieces
[0], pieces
[1], pieces
[2], pieces
[3], pieces
[4], pieces
[5], pieces
[6], pieces
[7], pieces
[8]);
194 String alpha
= this.stage(alphaInput
, this.keys
[1], this.keys
[2], this.keys
[0]);
197 String betaInput
= String
.format("%x:%x:%x:%x:%x:%x:%x",
198 pieces
[0], pieces
[1], pieces
[2], pieces
[3], pieces
[4], pieces
[5], pieces
[6]);
199 String beta
= this.stage(betaInput
, this.keys
[2], this.keys
[0], this.keys
[1]);
201 /* Generate gamma. */
202 String gammaInput
= String
.format("%x:%x:%x:%x", pieces
[0], pieces
[1], pieces
[2], pieces
[3]);
203 String gamma
= this.stage(gammaInput
, this.keys
[0], this.keys
[1], this.keys
[2]);
205 return alpha
+ ":" + beta
+ ":" + gamma
+ ":IP";
209 * Cloak a reverse DNS host.
211 * Cloak format: my.host.com -> PREFIX-ALPHA.host.com localhost ->
214 * ALPHA: downmix(md5(md5(keys[0]:my.host.com:keys[1])keys[2]))
216 private String
cloakHost(String host
)
218 int dot
= host
.indexOf('.');
219 String alpha
= this.stage(host
, this.keys
[0], this.keys
[1], this.keys
[2]);
221 while (dot
!= -1 && ((host
.length() - 1) > dot
) && !Character
.isAlphabetic(host
.charAt(dot
+ 1)))
223 host
= host
.substring(dot
+ 1);
224 dot
= host
.indexOf('.');
227 if (dot
== -1 || dot
== host
.length() - 1)
229 return this.hostPrefix
+ alpha
;
232 return this.hostPrefix
+ alpha
+ host
.substring(dot
);
236 * Perform a downmix(md5(md5(key1:host:key2)key3)) stage.
242 private String
stage(String input
, String key1
, String key2
, String key3
)
244 String salted
= String
.format("%s:%s:%s", key1
, input
, key2
);
245 byte[] firstMD5
= this.md5sum(salted
);
246 byte[] secondMD5
= this.md5sum(firstMD5
, key3
.getBytes());
247 return this.downmix(secondMD5
);
251 * Mix the 128 bits of the MD5 hash into a single 32-bit value.
253 private String
downmix(byte[] input
)
255 byte[] res
= new byte[4];
256 res
[0] = (byte) (input
[0] ^ input
[1] ^ input
[2] ^ input
[3]);
257 res
[1] = (byte) (input
[4] ^ input
[5] ^ input
[6] ^ input
[7]);
258 res
[2] = (byte) (input
[8] ^ input
[9] ^ input
[10] ^ input
[11]);
259 res
[3] = (byte) (input
[12] ^ input
[13] ^ input
[14] ^ input
[15]);
261 int r
= (((int) res
[0] & 0xFFFFFFFF) << 24)
262 + (((int) res
[1] & 0xFFFFFFFF) << 16)
263 + (((int) res
[2] & 0xFFFFFFFF) << 8)
264 + ((int) res
[3] & 0xFFFFFFFF);
265 return String
.format("%X", r
);
268 private byte[] md5sum(String input
)
270 return this.md5sum(input
.getBytes());
273 private byte[] md5sum(byte[]... input
)
276 for (byte[] b
: input
)
278 this.digest
.update(b
);
280 return this.digest
.digest();