Java tutorial
/* * MODIFICATIONS to the original source have been made by * Scott Douglass <scott@swdouglass.com> * * Copyright 2009 Scott Douglass <scott@swdouglass.com> * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. * * The original copyright notice and license terms are below. */ // // (C) Copyright 2007 VeriSign, Inc. All Rights Reserved. // // VeriSign, Inc. shall have no responsibility, financial or // otherwise, for any consequences arising out of the use of // this material. The program material is provided on an "AS IS" // BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either // express or implied. // // Distributed under an Apache License // http://www.apache.org/licenses/LICENSE-2.0 // package com.swdouglass.joid; import java.security.SecureRandom; import java.math.BigInteger; import java.security.NoSuchAlgorithmException; import org.apache.commons.logging.Log; import org.apache.commons.logging.LogFactory; /** * Implements the underlying Diffie-Hellman cryptography. */ public class DiffieHellman { private BigInteger modulus; private BigInteger generator; private BigInteger privateKey; private BigInteger publicKey; private final static Log log = LogFactory.getLog(DiffieHellman.class); private DiffieHellman() { } /** * The default modulus defined in the specification. */ public static final BigInteger DEFAULT_MODULUS = new BigInteger( "1551728981814736974712322577637155399157248019669" + "154044797077953140576293785419175806512274236981" + "889937278161526466314385615958256881888899512721" + "588426754199503412587065565498035801048705376814" + "767265132557470407658574792912915723345106432450" + "947150072296210941943497839259847603755949858482" + "53359305585439638443"); /** * The default generator defined in the specification. */ public static final BigInteger DEFAULT_GENERATOR = BigInteger.valueOf(2); /** * Returns a Diffie-Hellman instance using default modulus and * generator. Note that each call to this method will return an instance * with random private key. * @return a DiffieHellman instance using modulus * ${#DiffieHellman.DEFAULT_MODULUS}, and generator * ${#DiffieHellman.DEFAULT_GENERATOR}. */ public static DiffieHellman getDefault() { BigInteger p = DiffieHellman.DEFAULT_MODULUS; BigInteger g = DiffieHellman.DEFAULT_GENERATOR; return new DiffieHellman(p, g); } private static SecureRandom random; static { try { random = SecureRandom.getInstance("SHA1PRNG"); } catch (NoSuchAlgorithmException e) { throw new RuntimeException("No secure random available!"); } } /** * Returns the private key. * @return the private key. */ public BigInteger getPrivateKey() { return privateKey; } /** * Returns the public key. * @return the public key. */ public BigInteger getPublicKey() { return publicKey; } /** * Creates a DiffieHellman instance. * * @param mod the modulus to use. If null, use {@link #DEFAULT_MODULUS}. * @param gen the generator to use. If null, use * {@link #DEFAULT_GENERATOR}. */ public DiffieHellman(BigInteger mod, BigInteger gen) { modulus = (mod != null ? mod : DiffieHellman.DEFAULT_MODULUS); generator = (gen != null ? gen : DiffieHellman.DEFAULT_GENERATOR); int bits = modulus.bitLength(); BigInteger max = modulus.subtract(BigInteger.ONE); while (true) { BigInteger pkey = new BigInteger(bits, random); if (pkey.compareTo(max) >= 0) { //too large continue; } else if (pkey.compareTo(BigInteger.ONE) <= 0) {//too small continue; } privateKey = pkey; publicKey = generator.modPow(privateKey, modulus); break; } } /** * Recreates a DiffieHellman instance. * * @param privateKey the private key. Cannot be null. * @param modulus the modulus to use. Cannot be be null. */ public static DiffieHellman recreate(BigInteger privateKey, BigInteger modulus) { if (privateKey == null || modulus == null) { throw new IllegalArgumentException("Null parameter"); } DiffieHellman dh = new DiffieHellman(); dh.setPrivateKey(privateKey); dh.setModulus(modulus); return dh; } private void setPrivateKey(BigInteger privateKey) { this.privateKey = privateKey; } private void setModulus(BigInteger modulus) { this.modulus = modulus; } /** * Returns the shared secret. * * @param composite the composite number (public key) with which this * instance shares a secret. * @return the shared secret. */ public BigInteger getSharedSecret(BigInteger composite) { return composite.modPow(privateKey, modulus); } /** * Returns the shared secret SHA-1 hashed and XOR 'encrypted'. * * @param otherPublic the other party's public modulus; cannot be null. * @param secret the key to XOR encrypt with. * @return the encrypted secret. * @throws IllegalArgumentException if <code>otherPublic</code> is null. * @throws RuntimeException if length of <code>secret</code> is * incorrect. Big TODO here to make this error reporting better. */ public byte[] xorSecret(BigInteger otherPublic, byte[] secret) throws NoSuchAlgorithmException { if (otherPublic == null) { throw new IllegalArgumentException("otherPublic cannot be null"); } BigInteger shared = getSharedSecret(otherPublic); byte[] hashed; if (secret.length == 32) { hashed = Crypto.sha256(shared.toByteArray()); } else { hashed = Crypto.sha1(shared.toByteArray()); } if (secret.length != hashed.length) { log.warn("invalid secret byte[] length: secret=" + secret.length + ", hashed=" + hashed.length); throw new RuntimeException("nyi"); } byte[] result = new byte[secret.length]; for (int i = 0; i < result.length; i++) { result[i] = (byte) (hashed[i] ^ secret[i]); } return result; } }