Source code: com/traxel/crypto/CipherPartner.java
1 /* $Id: CipherPartner.java,v 1.6 2001/04/02 08:42:50 cvsbob Exp $ */
2
3 /*
4 * CipherPartner.java, class for synching Diffie Hellman partners.
5 * Copyright (C) 2001 Robert Bushman.
6 *
7 * I reserve the right to release this program under seperate license.
8 * If you require a special license grant contact Robert Bushman.
9 *
10 * This program is free software; you can redistribute it and/or
11 * modify it under the terms of the GNU General Public License
12 * as published by the Free Software Foundation; either version 2
13 * of the License, or (at your option) any later version.
14 *
15 * This program is distributed in the hope that it will be useful,
16 * but WITHOUT ANY WARRANTY; without even the implied warranty of
17 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
18 * GNU General Public License for more details.
19 *
20 * You should have received a copy of the GNU General Public License
21 * along with this program; if not, write to the Free Software
22 * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA
23 * 02111-1307, USA.
24 */
25
26 package com.traxel.crypto;
27
28 import java.io.FileInputStream;
29 import java.io.FileNotFoundException;
30 import java.io.FileOutputStream;
31 import java.io.IOException;
32 import java.math.BigInteger;
33 import java.security.InvalidAlgorithmParameterException;
34 import java.security.InvalidKeyException;
35 import java.security.KeyFactory;
36 import java.security.KeyPair;
37 import java.security.KeyPairGenerator;
38 import java.security.NoSuchAlgorithmException;
39 import java.security.spec.InvalidKeySpecException;
40 import java.security.spec.X509EncodedKeySpec;
41 import javax.crypto.Cipher;
42 import javax.crypto.KeyAgreement;
43 import javax.crypto.SecretKey;
44 import javax.crypto.NoSuchPaddingException;
45 import javax.crypto.interfaces.DHPublicKey;
46 import javax.crypto.interfaces.DHPrivateKey;
47 import javax.crypto.spec.DHParameterSpec;
48 import javax.crypto.spec.IvParameterSpec;
49
50 /**
51 * <b>NOTE:</b> TEST_PARAMS are not intended for production use.
52 * <br><br>
53 * <b>Test Usage</b>
54 * <pre>
55 * // DHParameterSpecs can be safely published to all parties.
56 * alice = new CipherPartner( "ALICE", CipherPartner.TEST_PARAMS );
57 * bob = new CipherPartner( "BOB", CipherPartner.TEST_PARAMS );
58 *
59 * alicePublicEncoded = alice.getPublicEncoded();
60 *
61 * // transmit alicePublicEncoded to bob
62 * // Alternately, alice could load from disk and bob could
63 * // already have a copy of alice's public key.
64 *
65 * // bob initializes only from alices key, and generates IV
66 * bob.initFromPartner( alicePublicEncoded );
67 * bobPublicEncoded = bob.getPublicEncoded();
68 * bobIv = bob.getIv();
69 *
70 * // transmit bobPublicEncoded and bobIV to alice
71 *
72 * // alice requires bobPublicEncoded and bobIv
73 * alice.initFromPartner( bobPublicEncoded, bobIv );
74 * aliceEncrypt = alice.getEncryptCipher();
75 * aliceDecrypt = alice.getDecryptCipher();
76 *
77 * bobEncrypt = alice.getEncrypt();
78 * bobDecrypt = alice.getDecrypt();
79 * </pre>
80 *
81 * @author Robert Bushman
82 */
83
84 public class CipherPartner {
85
86 // -----------------------------------------------------------
87 // CONSTANTS
88 // -----------------------------------------------------------
89
90 public static final BigInteger TEST_G = new BigInteger
91 ("1db17639cdf96bc4eabba19454f0b7e5bd4e14862889a725c96eb61048dcd676" +
92 "ceb303d586e30f060dbafd8a571a39c4d823982117da5cc4e0f89c77388b7a08" +
93 "896362429b94a18a327604eb7ff227bffbc83459ade299e57b5f77b50fb04525" +
94 "0934938efa145511166e3197373e1b5b1e52de713eb49792bedde722c6717abf",
95 16);
96 public static final BigInteger TEST_P = new BigInteger
97 ("a00e283b3c624e5b2b4d9fbc2653b5185d99499b00fd1bf244c6f0bb817b4d1c" +
98 "451b2958d62a0f8a38caef059fb5ecd25d75ed9af403f5b5bdab97a642902f82" +
99 "4e3c13789fed95fa106ddfe0ff4a707c85e2eb77d49e68f2808bcea18ce128b1" +
100 "78cd287c6bc00efa9a1ad2a673fe0dceace53166f75b81d6709d5f8af7c66bb7",
101 16);
102 public static final
103 DHParameterSpec TEST_PARAMS = new DHParameterSpec( TEST_P, TEST_G );
104
105 public static final String PUBLIC_ALGORITHM = "DH";
106 public static final String PRIVATE_ALGORITHM = "Blowfish";
107 public static final String PRIVATE_TRANSFORM = "Blowfish/OFB8/NoPadding";
108 public static final String SHA1 = "SHA1";
109 public static final String DIGEST_ALGORITHM = SHA1;
110
111 public static final String DEFAULT_PROVIDER_NAME =
112 "org.bouncycastle.jce.provider.BouncyCastleProvider";
113
114 // ----------------------------------------------------------
115 // CONSTRUCTORS AND INITIALIZERS
116 // ----------------------------------------------------------
117
118 /**
119 * This is typically called by a parent process that is
120 * spawning a number of child processes, all of which
121 * share a common Diffie Hellman key set.
122 */
123 public CipherPartner( String name, CipherPartner copyFrom ) {
124 KeyAgreement dhKeyAgree;
125
126 try {
127 setName( name );
128 setDhKeyPair( copyFrom.getDhKeyPair() );
129 dhKeyAgree = KeyAgreement.getInstance( PUBLIC_ALGORITHM );
130 dhKeyAgree.init( getDhKeyPair().getPrivate() );
131 setDhKeyAgree( dhKeyAgree );
132 } catch( Exception e ) {
133 e.printStackTrace();
134 System.exit( 1 );
135 }
136 }
137
138 /**
139 * This is the first partner initialized. It does not require
140 * a partner public key because it is the first partner. The
141 * other partner *must* be instantiated with this partner's
142 * public key.
143 */
144 public CipherPartner( String name, DHParameterSpec dhParams ) {
145 KeyPairGenerator dhKeyPairGen;
146 KeyPair dhKeyPair;
147 KeyAgreement dhKeyAgree;
148
149 setName( name );
150 try {
151 dhKeyAndAgree( dhParams );
152 } catch( Exception e ) {
153 e.printStackTrace();
154 System.exit( 1 );
155 }
156 }
157
158 public CipherPartner( String name,
159 DHPublicKey pubKey,
160 DHPrivateKey privKey ) {
161
162 KeyPair dhKeyPair;
163 KeyAgreement dhKeyAgree;
164
165 setName( name );
166 try {
167 dhKeyPair = new KeyPair( pubKey, privKey );
168 dhKeyAgree = KeyAgreement.getInstance( PUBLIC_ALGORITHM );
169 dhKeyAgree.init( dhKeyPair.getPrivate() );
170
171 setDhKeyPair( dhKeyPair );
172 setDhKeyAgree( dhKeyAgree );
173
174 System.out.println( "Partner Inititalized From Keys" );
175 } catch( Exception e ) {
176 e.printStackTrace();
177 System.exit( 1 );
178 }
179 }
180
181 public CipherPartner( String name,
182 String pubKeyFile,
183 String privKeyFile )
184 throws FileNotFoundException,
185 SecurityException,
186 IOException,
187 InvalidKeyException,
188 InvalidKeySpecException {
189
190 FileInputStream pubIn;
191 FileInputStream privIn;
192 DHPublicKey pubKey;
193 DHPrivateKey privKey;
194 KeyPair dhKeyPair;
195 KeyAgreement dhKeyAgree;
196
197 try {
198 pubIn = new FileInputStream( pubKeyFile );
199 privIn = new FileInputStream( privKeyFile );
200 pubKey = DHUtil.pubKeyFromBase16( pubIn );
201 privKey = DHUtil.privKeyFromBase16( privIn );
202 dhKeyPair = new KeyPair( pubKey, privKey );
203 dhKeyAgree = KeyAgreement.getInstance( PUBLIC_ALGORITHM );
204 dhKeyAgree.init( dhKeyPair.getPrivate() );
205
206 setDhKeyPair( dhKeyPair );
207 setDhKeyAgree( dhKeyAgree );
208 setName( name );
209 } catch( NoSuchAlgorithmException e ) {
210 e.printStackTrace();
211 System.out.println
212 ( "JCE Provider Not Set Or Diffie Hellman Unavailable" );
213 System.out.println
214 ( "Make sure you have Bouncy Castle installed properly." );
215 System.out.println
216 ( "Make sure all Bouncy Castle jars are in your classpath." );
217 System.exit( 1 );
218 }
219
220 System.out.println( "Partner Inititalized From Keys" );
221 }
222
223 // ----------------------------------------------------------
224 // PUBLIC API
225 // ----------------------------------------------------------
226
227 /**
228 * This is used when an IV has not yet been generated.
229 * This is the first partner to receive the other partner's
230 * key.
231 */
232 public void initFromPartner( byte[] partnerPublicEncoded )
233 throws InvalidKeyException,
234 InvalidKeySpecException,
235 InvalidAlgorithmParameterException {
236
237
238 KeyFactory dhKeyFac;
239 X509EncodedKeySpec partner509;
240 DHPublicKey partnerPublic;
241 DHParameterSpec partnerDhSpec;
242
243 SecretKey blowfishKey;
244 Cipher decryptCipher;
245 Cipher encryptCipher;
246 byte[] iv;
247 IvParameterSpec ivSpec;
248
249 try {
250 dhKeyFac = KeyFactory.getInstance( PUBLIC_ALGORITHM );
251 partner509 = new X509EncodedKeySpec( partnerPublicEncoded );
252 partnerPublic = (DHPublicKey)dhKeyFac.generatePublic( partner509 );
253
254 getDhKeyAgree().doPhase( partnerPublic, true );
255
256 blowfishKey = getDhKeyAgree().generateSecret( PRIVATE_ALGORITHM );
257 decryptCipher = Cipher.getInstance( PRIVATE_TRANSFORM );
258 encryptCipher = Cipher.getInstance( PRIVATE_TRANSFORM );
259
260 encryptCipher.init( Cipher.ENCRYPT_MODE, blowfishKey );
261 iv = encryptCipher.getIV();
262 ivSpec = new IvParameterSpec( iv );
263 decryptCipher.init( Cipher.DECRYPT_MODE, blowfishKey, ivSpec );
264
265 setBlowfishKey( blowfishKey );
266 setEncryptCipher( encryptCipher );
267 setDecryptCipher( decryptCipher );
268 setIv( iv );
269 } catch( NoSuchAlgorithmException e ) {
270 e.printStackTrace();
271 System.exit( 1 );
272 } catch( NoSuchPaddingException e ) {
273 e.printStackTrace();
274 System.exit( 1 );
275 }
276 }
277
278 public void initFromPartner( byte[] partnerPublicEncoded,
279 byte[] partnerIv )
280 throws InvalidKeyException,
281 InvalidAlgorithmParameterException,
282 InvalidKeySpecException {
283 IvParameterSpec partnerIvSpec;
284 KeyFactory keyFac;
285 X509EncodedKeySpec partner509;
286 DHPublicKey partnerPublic;
287
288 SecretKey blowfishKey;
289 Cipher encryptCipher;
290 Cipher decryptCipher;
291
292 try {
293 partnerIvSpec = new IvParameterSpec( partnerIv );
294
295 keyFac = KeyFactory.getInstance( PUBLIC_ALGORITHM );
296 partner509 = new X509EncodedKeySpec( partnerPublicEncoded );
297 partnerPublic = (DHPublicKey)keyFac.generatePublic( partner509 );
298
299 getDhKeyAgree().doPhase( partnerPublic, true );
300
301 blowfishKey = getDhKeyAgree().generateSecret( PRIVATE_ALGORITHM );
302 encryptCipher = Cipher.getInstance( PRIVATE_TRANSFORM );
303 decryptCipher = Cipher.getInstance( PRIVATE_TRANSFORM );
304 encryptCipher.init( Cipher.ENCRYPT_MODE, blowfishKey, partnerIvSpec );
305 decryptCipher.init( Cipher.DECRYPT_MODE, blowfishKey, partnerIvSpec );
306
307 setBlowfishKey( blowfishKey );
308 setEncryptCipher( encryptCipher );
309 setDecryptCipher( decryptCipher );
310 setIv( partnerIv );
311 } catch( NoSuchAlgorithmException e ) {
312 e.printStackTrace();
313 System.exit( 1 );
314 } catch( NoSuchPaddingException e ) {
315 e.printStackTrace();
316 System.exit( 1 );
317 }
318 }
319
320 // KEY EXPORT, TRANSFER AND AUTHENTICATION
321
322 public String getPublicBase16() {
323 byte[] encoded = getPublicEncoded();
324 BigInteger bigInt = new BigInteger( encoded );
325 return( bigInt.toString( 16 ) );
326 }
327
328 public String getPrivateBase16() {
329 byte[] encoded = getPrivateEncoded();
330 BigInteger bigInt = new BigInteger( encoded );
331 return( bigInt.toString( 16 ) );
332 }
333
334 public String getPublicSha1Base16() {
335 byte[] sha1Bytes;
336 BigInteger bigInt;
337
338 sha1Bytes = Sha1Util.getSha1( getPublicEncoded() );
339 bigInt = new BigInteger( sha1Bytes );
340
341 return( bigInt.toString( 16 ) );
342 }
343
344 public static boolean verify( String publicBase16, String sha1Base16 ) {
345 BigInteger publicBigInt;
346
347 publicBigInt = new BigInteger( publicBase16, 16 );
348 return( Sha1Util.verify( publicBigInt.toByteArray(), sha1Base16 ) );
349 }
350
351 public void exportDhKeys( String pubKeyFile,
352 String privKeyFile,
353 String pubSha1File )
354 throws FileNotFoundException, IOException, Exception {
355
356 FileOutputStream pubOut;
357 FileOutputStream privOut;
358 FileOutputStream pubSha1Out;
359
360 pubOut = new FileOutputStream( pubKeyFile );
361 privOut = new FileOutputStream( privKeyFile );
362 pubSha1Out = new FileOutputStream( pubSha1File );
363 pubOut.write( getPublicBase16().getBytes() );
364 privOut.write( getPrivateBase16().getBytes() );
365 pubSha1Out.write( getPublicSha1Base16().getBytes() );
366 }
367
368 // ---------------------------------------------------------
369 // INTERNAL API
370 // ---------------------------------------------------------
371
372 protected void dhKeyAndAgree( DHParameterSpec dhSpec )
373 throws InvalidAlgorithmParameterException {
374 KeyPairGenerator dhKeyPairGen;
375 KeyPair dhKeyPair;
376 KeyAgreement dhKeyAgree;
377
378 try {
379 dhKeyPairGen = KeyPairGenerator.getInstance( PUBLIC_ALGORITHM );
380 dhKeyPairGen.initialize( dhSpec );
381 dhKeyPair = dhKeyPairGen.generateKeyPair();
382 dhKeyAgree = KeyAgreement.getInstance( PUBLIC_ALGORITHM );
383 dhKeyAgree.init( dhKeyPair.getPrivate() );
384
385 setDhKeyPair( dhKeyPair );
386 setDhKeyAgree( dhKeyAgree );
387 } catch( NoSuchAlgorithmException e ) {
388 e.printStackTrace();
389 System.exit( 1 );
390 } catch( InvalidKeyException e ) {
391 e.printStackTrace();
392 System.err.println( "This should never happen" );
393 System.exit( 1 );
394 }
395 }
396
397 // -----------------------------------------------------------
398 // INSTANCE PARAMETERS AND ACCESSORS
399 // ----------------------------------------------------------
400
401 // PARAMETERS
402 private String _name;
403 private KeyPair _dhKeyPair;
404 private KeyAgreement _dhKeyAgree;
405 private SecretKey _blowfishKey;
406 private Cipher _encryptCipher;
407 private Cipher _decryptCipher;
408 private byte[] _iv;
409
410 // SETTERS
411 protected void setName( String name ) { _name = name; }
412 protected void setDhKeyPair( KeyPair dhKeyPair ) { _dhKeyPair = dhKeyPair; }
413 protected void setDhKeyAgree( KeyAgreement dhKeyAgree ) {
414 _dhKeyAgree = dhKeyAgree;
415 }
416 protected void setBlowfishKey( SecretKey blowfishKey ) {
417 _blowfishKey = blowfishKey;
418 }
419 protected void setEncryptCipher( Cipher encryptCipher ) {
420 _encryptCipher = encryptCipher;
421 }
422 protected void setDecryptCipher( Cipher decryptCipher ) {
423 _decryptCipher = decryptCipher;
424 }
425 protected void setIv( byte[] iv ) { _iv = iv; }
426
427 // GETTERS
428 protected KeyPair getDhKeyPair() { return( _dhKeyPair ); }
429 protected KeyAgreement getDhKeyAgree() { return( _dhKeyAgree ); }
430 protected SecretKey getBlowfishKey() { return( _blowfishKey ); }
431
432 public String getName() { return( _name ); }
433 public Cipher getEncryptCipher() { return( _encryptCipher ); }
434 public Cipher getDecryptCipher() { return( _decryptCipher ); }
435 public byte[] getIv() { return( _iv ); }
436
437 // CONVENIENCE METHODS
438 public byte[] getPublicEncoded() {
439 return( getDhKeyPair().getPublic().getEncoded() );
440 }
441 public byte[] getPrivateEncoded() {
442 return( getDhKeyPair().getPrivate().getEncoded() );
443 }
444
445 // ----------------------------------------------------------
446 // RUNTIME
447 // ----------------------------------------------------------
448
449 public static void main( String[] args ) throws Exception {
450 CipherPartner alice;
451 CipherPartner bob;
452
453 byte[] alicePublicEncoded;
454 byte[] bobPublicEncoded;
455 byte[] bobIv;
456
457 CryptoUtil.initializeProvider();
458
459 alice = new CipherPartner( "ALICE", CipherPartner.TEST_PARAMS );
460 bob = new CipherPartner( "BOB", CipherPartner.TEST_PARAMS );
461
462 // transmit alicePublicEncoded to bob
463 alicePublicEncoded = alice.getPublicEncoded();
464 bob.initFromPartner( alicePublicEncoded );
465
466 // transmit bobPublicEncoded and bobIV to alice
467 bobPublicEncoded = bob.getPublicEncoded();
468 bobIv = bob.getIv();
469
470 alice.initFromPartner( bobPublicEncoded, bobIv );
471
472 System.out.println
473 ( "Stream Test: " + CryptoUtil.streamTest
474 ( alice.getEncryptCipher(), bob.getDecryptCipher() ) );
475 System.out.println
476 ( "Stream Test: " + CryptoUtil.streamTest
477 ( bob.getEncryptCipher(), alice.getDecryptCipher() ) );
478
479 System.out.println( "Alice Pub Hex\n" + alice.getPublicBase16() );
480 System.out.println( "Alice Priv Hex\n" + alice.getPrivateBase16() );
481 System.out.println( "Alice Sha1 Hex\n" + alice.getPublicSha1Base16() );
482 System.out.println
483 ( "Verify: " + Sha1Util.verify( alice.getPublicEncoded(),
484 alice.getPublicSha1Base16() ) );
485
486 System.out.println( "Bob Pub Hex\n" + bob.getPublicBase16() );
487 System.out.println( "Bob Priv Hex\n" + bob.getPrivateBase16() );
488 System.out.println( "Bob Sha1 Hex\n" + bob.getPublicSha1Base16() );
489 System.out.println
490 ( "Verify: " + Sha1Util.verify( bob.getPublicEncoded(),
491 bob.getPublicSha1Base16() ) );
492
493 System.out.println
494 ( "Cross Verify (should fail): "
495 + Sha1Util.verify( bob.getPublicEncoded(),
496 alice.getPublicSha1Base16() ) );
497 }
498 }