Source code: com/ssttr/crypto/DH.java
1 /*
2 * DH.java
3 *
4 * This code is considered to be Public Domain as it was originaly posted anonymously to
5 * Usenet. See the comments below for details.
6 *
7 * Created on July 3, 2003, 9:24 PM
8 */
9
10 package com.ssttr.crypto;
11
12 import com.ssttr.util.Strings;
13
14 /**
15 * This class performs the basic essential functions for Diffie-Hellman key genereration.
16 * Specifily, given a generator (g), exponent (x), and prime modulus (m), it performs:
17 * <code>g<sup>x</sup> mod m</code>. Of course this could also be done just as easily
18 * with the java.math.BigInteger.modPow() method, but that class is rarely included
19 * in J2ME and embeded JVMs this can still be usefull.
20 * <p>
21 * It is worth noting that this class has been tested to reliably work on key sizes >= 64 bits.
22 * It will often fail on a keys < 64 bits. This could (in theory) be fixed but since key sizes
23 * less than 64 bits are generally considered worthless (due to the ability to brute force the
24 * entire keyspace in short order) this has been classified as a featurenotbug.
25 * <p>
26 * To generate a shared private key:<br>
27 * Jack performs:<br>
28 * <pre>
29 * int keySize = 128;
30 * DH dh = new DH(keySize);
31 * Random rnd = new Random();
32 * String gen = "3";
33 * String jacksExp = Long.toString( Math.abs(rnd.nextLong()), 16 ).toUpperCase();
34 * String prime = "FFFFFFFFFFFFFFFFFFFFFFFFFFFFFF61";
35 * String publicKey = dh.generateKey( gen, jacksExp, prime);
36 * </pre>
37 * <br>
38 * Then sends keySize, gen, prime, and publicKey to Jill (but exp MUST be kept secret!).<br>
39 * Then Jill:<br>
40 * <pre>
41 * DH dh = new DH(jacksKeySize);
42 * Random rnd = new Random();
43 * String jillsExp = Long.toString( Math.abs(rnd.nextLong()), 16 ).toUpperCase();
44 * String publicKey = dh.generateKey( jacksGen, jillsExp, jacksPrime);
45 * String jillsSecretKey = dh.generateKey( jacksPublicKey, jillsExp, jacksPrime);
46 * </pre>
47 * <br>
48 * And sends publicKey to Jack.<br>
49 * Who then:<br>
50 * <pre>
51 * String jacksSecretKey = dh.generateKey( jillsPublicKey, jacksExp, prime);
52 * </pre>
53 * Now if everything worked properly (and it should have) jacksSecretKey == jillsSecretKey
54 * and they can now use a shared-secret algorithm (like {@link TEA}) to communicate.
55 * <p>
56 * Most of the code for this class was blatently stollen (and subsequently ported to Java) from
57 * <a href="http://www.cypherspace.org/~adam/rsa/dh-in-C.html">http://www.cypherspace.org/~adam/rsa/dh-in-C.html</a>.
58 * Which is copied here in case the page goes down:
59 * <p>
60 * <p>
61 * <xmp>
62 * Diffie-Hellman Key Exchange
63 * This 10 line C program was written by an anonymous poster to sci.crypt. Possibly the poster is located in the US,
64 * though there is no way of telling. Anyway nobody's code includes a beautifully compacted big num implementation,
65 * so all you need is a C compiler.
66 *
67 * If you are trying this out with the example nobody gives in his message you should note that you must change the
68 * initialisation of S=129 on the first line to be S=3 (size of key in bytes + 1) - nobody says this in the text,
69 * it just caught me out first time.
70 *
71 * I posted a follow up to this message to sci.crypt in which I mail dropped nobody a message using the 1024 bit
72 * Diffie-Hellman key exchange he/she initiated. This message includes the Diffie-Hellman in perl I wrote (it's
73 * very similar to RSA in it's use of modular exponentiation) inspired by nobody's code.
74 *
75 * My perl-diffie-hellman plus examples of use (inspired by nobody's C code)
76 *
77 * Here is nobody's posting to sci.crypt (it was cross posted to talk.politics.crypto and alt.anonymous.messages also):
78 *
79 * Newsgroups: sci.crypt,talk.politics.crypto,alt.anonymous.messages
80 * From: nobody@replay.com (Name withheld by request)
81 * Organization: Replay and Company UnLimited.
82 * X-Warning: This message was forwarded by an Anonymous Remailer.
83 * X-Comment: Replay does not necessarily approve of the contents of this posting.
84 * X-Comment: Please report inappropriate use to <postmaster@replay.com>
85 * Subject: Export-a-crypto-system: Diffie-Hellman
86 * X-Posting-Host: xs1.xs4all.nl
87 * Date: Sun, 2 Apr 1995 02:25:08 +0000
88 * Sender: usenet@demon.co.uk
89 * Lines: 88
90 *
91 * Inspired by Adam Back's RSA code...
92 *
93 * The export-a-crypto-system sig II - Diffie-Hellman in 10 lines of C:
94 *
95 * #include <stdio.h> // Usage: dh base exponent modulus
96 * typedef unsigned char u;u m[1024],g[1024],e[1024],b[1024];int n,v,d,z,S=129;a(
97 * u *x,u *y,int o){d=0;for(v=S;v--;){d+=x[v]+y[v]*o;x[v]=d;d=d>>8;}}s(u *x){for(
98 * v=0;(v<S-1)&&(x[v]==m[v]);)v++;if(x[v]>=m[v])a(x,m,-1);}r(u *x){d=0;for(v=0;v<
99 * S;){d|=x[v];x[v++]=d/2;d=(d&1)<<8;}}M(u *x,u *y){u X[1024],Y[1024];bcopy(x,X,S
100 * );bcopy(y,Y,S);bzero(x,S);for(z=S*8;z--;){if(X[S-1]&1){a(x,Y,1);s(x);}r(X);a(Y
101 * ,Y,1);s(Y);}}h(char *x,u *y){bzero(y,S);for(n=0;x[n]>0;n++){for(z=4;z--;)a(y,y
102 * ,1);x[n]|=32;y[S-1]|=x[n]-48-(x[n]>96)*39;}}p(u *x){for(n=0;!x[n];)n++;for(;n<
103 * S;n++)printf("%c%c",48+x[n]/16+(x[n]>159)*7,48+(x[n]&15)+7*((x[n]&15)>9));
104 * printf("\n");}main(int c,char **v){h(v[1],g);h(v[2],e);h(v[3],m);bzero(b,S);b[
105 * S-1]=1;for(n=S*8;n--;){if(e[S-1]&1)M(b,g);M(g,g);r(e);}p(b);}
106 * </code>
107 *
108 * To compile it, type: gcc dh.c -o dh
109
110 * The program is somewhat slow, but it works, and it's almost small enough to
111 * attach to your posts as a signature. It's set up for 1024-bit numbers, but
112 * you can allow bigger numbers by setting the value of the S variable. To
113 * allow for carry digits, the value set for S must be one greater than the
114 * maximum length in bytes of the modulus that you wish to use. The time to
115 * calculate a modular exponent is roughly proportional to the cube of the
116 * number of bits, so if a 1024-bit number takes 5 minutes, a 2048-bit number
117 * will take 40 minutes, and a 4096-bit number would take about 5 hours.
118 * All numbers are entered and printed in hexadecimal. Because of the minimal
119 * size, the program doesn't check for human error; it may do strange things
120 * if you give it bad data.
121 *
122 * For example purposes, the numbers used below are small, any practical
123 * use would require numbers of 512-1024 bits in length.
124 *
125 * To generate a key, Joe selects a public generator (3 in this example), a
126 * public prime modulus (10001 hexadecimal), and a secret exponent (9A2E hex).
127 * Joe then calculates the following:
128 *
129 * joe% dh 3 9A2E 10001
130 * C366
131 *
132 * This is his public key. Joe sends these three numbers (3,10001,C366)
133 * to Alice.
134 *
135 * To encrypt a message to Joe, Alice picks a secret random number (4C20 in
136 * this example) and using Joe's generator and modulus, calculates:
137 *
138 * alice% dh 3 4C20 10001
139 * 6246
140 *
141 * She sends this result to Joe. Alice then takes Joe's public key and her
142 * secret random number and calculates:
143 *
144 * alice% dh C366 4C20 10001
145 * DED4
146 *
147 * She uses this result as a session key to encrypt her message to Joe.
148 *
149 * To decrypt the message, Joe uses the number Alice sent him, and his
150 * secret key to calculate:
151 *
152 * joe% dh 6246 9A2E 10001
153 * DED4
154 *
155 * Joe now has the session key and can decrypt Alice's message.
156 *
157 * An eavesdropper sees Joe send Alice three numbers (3,10001,C366), and
158 * sees Alice send Joe '6246'. But the eavesdropper can not use these
159 * numbers to calculate the secret session key (DED4), and thus can not
160 * decrypt the message.
161 *
162 *
163 * You can use this program to do RSA also, but generating a key is more
164 * difficult.
165 *
166 * I wonder if the USA ITAR law covers integer math programs?
167 *
168 *
169 * Here is my key:
170 *
171 * g=2
172 * X=28D4FEFED5CC1ACDBE7D550813A1518DB56812855014EFDDD9E038A15BE52FD524D6FFA9C9E3
173 * 1E59DDFEF705EAB75DD031908F10B584F5A2495BC2C1433ACB6FA065FB0850ECCF139993BA4564
174 * B97A289FBCD59524FC30F377060366077378288F93173AC508D1DC5B4D9DAE0AFECFFA9E5494C5
175 * 3EED74C4E29DEBAF0C08B720
176 * m=DE9B707D4C5A4633C0290C95FF30A605AEB7AE864FF48370F13CF01D49ADB9F23D19A439F753
177 * EE7703CF342D87F431105C843C78CA4DF639931F3458FAE8A94D1687E99A76ED99D0BA87189F42
178 * FD31AD8262C54A8CF5914AE6C28C540D714A5F6087A171FB74F4814C6F968D72386EF356A05180
179 * C3BEC7DDD5EF6FE76B0531C3
180 * </xmp>
181 *
182 * <p>
183 * @author anonymous
184 */
185 public class DH
186 {
187 Object lock = new Object();
188
189 byte[] m,g,e,b,key,tmp;
190
191 int n,v,d,z,S,keySize;
192
193 /**
194 * Creates a new instance of DH. Each instance of DH is threadsafe in that
195 * it uses a lock to guarentee that all calls to generateKey() are serialized.
196 *
197 * @param keySize The size of the key(s) to be generated, in bits.
198 *
199 * @throws IllegalArgumentException if keySize < 64
200 */
201 public DH (final int keySize)
202 {
203 if( keySize < 64 )
204 throw new IllegalArgumentException("keySize < 64");
205
206 this.keySize = keySize;
207 S = keySize / 8 + 1;
208 }
209
210 private final void initialize()
211 {
212 m = new byte[S];
213 g = new byte[S];
214 e = new byte[S];
215 b = new byte[S];
216 key = new byte[S*2];
217 }
218
219 public String generateKey (String generator, String exponent, String prime)
220 {
221 synchronized ( lock )
222 {
223 initialize();
224
225 h (generator.getBytes(),g);
226 h (exponent.getBytes(),e);
227 h (prime.getBytes(),m);
228
229 generateKey();
230
231 tmp = trim(b);
232 final String k = Strings.toHex(tmp);
233 // Save a little memory
234 m = g = e = b = key = tmp = null;
235 return k;
236
237 /*
238 p (b);
239
240 tmp = new byte[(S-1)*2];
241 System.arraycopy(key,0,tmp,0,tmp.length);
242
243 final String k = new String(tmp);
244 // Save a little memory
245 m = g = e = b = key = tmp = null;
246
247 return k;
248 */
249 }
250 }
251
252 public byte[] generateKey (byte[] generator, byte[] exponent, byte[] prime)
253 {
254 synchronized ( lock )
255 {
256 initialize();
257
258 int l;
259 l = Math.min(S,generator.length);
260 System.arraycopy( generator, 0, g, S - l, l );
261 l = Math.min(S,exponent.length);
262 System.arraycopy( exponent, 0, e, S - l, l );
263 l = Math.min(S,prime.length);
264 System.arraycopy( prime, 0, m, S - l, l );
265
266 generateKey();
267
268 final byte[] k = trim(b);
269 // Save a little memory
270 m = g = e = b = key = tmp = null;
271
272 return k;
273 }
274 }
275
276 private final void generateKey()
277 {
278 bzero (b,S);
279
280 b[S-1] = 1;
281
282 for( n = keySize; n-- > 0; )
283 {
284 if( (e[S-1] & 1) != 0 )
285 M (b,g);
286 M (g,g);
287 r (e);
288 }
289 }
290
291 private void s (byte[] x)
292 {
293 for( v = 0; (v < S - 1) && (x[v] == m[v]); v++ );
294
295 if( (x[v] & 0xFF) >= (m[v] & 0xFF) )
296 a (x,m,-1);
297 }
298
299 private void r (byte[] x)
300 {
301 d = 0;
302 for( v = 0; v < S; )
303 {
304 d |= (x[v] & 0xFF);
305 x[v++] = (byte)(d / 2);
306 d = ( d & 1 ) << 8;
307 }
308 }
309
310 private void M (byte[] x,byte[] y)
311 {
312 byte[] X = new byte[S], Y = new byte[S];
313
314 System.arraycopy (x,0,X,0,S);
315 System.arraycopy (y,0,Y,0,S);
316 bzero (x,S);
317
318 for( z = keySize; z > 0; z-- )
319 {
320 if( (X[S-1] & 1) != 0 )
321 {
322 a (x,Y,1);
323 s (x);
324 }
325 r (X);
326 a (Y,Y,1);
327 s (Y);
328 }
329 }
330
331 private void a ( byte[] x,byte[] y,int o)
332 {
333 d = 0;
334 for( v = S - 1; v >= 0; v -- )
335 {
336 d += (x[v] & 0xFF) + (y[v] & 0xFF) * o;
337 x[v] = (byte)d;
338 d >>= 8;
339 }
340 }
341
342 private void h (byte[] x,byte[] y)
343 {
344 int j;
345 bzero (y,S);
346 for( n = 0; n < x.length && x[n] > 0; n++ )
347 {
348 for( z = 4; z-- > 0; )
349 a (y,y,1);
350 x[n] |= 0x20;
351 j = x[n] & 0xFF;
352 y[S-1] |= (byte)(j - 48 - (j > 0x60 ? 39 : 0));
353 }
354 }
355 /*
356 private void p (byte[] x)
357 {
358 for( n = 0; x[n] == 0;)
359 n++;
360
361 bzero(key,key.length);
362
363 int i = 0, j;
364
365 for( ; n < S; n++ )
366 {
367 j = x[n] & 0xFF;
368 key[i++] = (byte)(48 + j / 0x10 + ( j > 0x9f ? 0x7 : 0) );
369 key[i++] = (byte)(48 + (j & 0xF) + ( ( (j & 0xf) > 0x9) ? 0x7 : 0) );
370 }
371 }
372 */
373 private void bzero(final byte[] a, int len)
374 {
375 while( --len >= 0 )
376 a[len] = 0;
377 }
378
379 private static byte[] trim(final byte[] bytes)
380 {
381 int i;
382 for( i = 0; i < bytes.length && bytes[i] == 0; i ++ );
383
384 byte[] tmp = new byte[ bytes.length - i ];
385 System.arraycopy( bytes, i, tmp, 0, tmp.length );
386
387 return tmp;
388 }
389
390 }