src/net/java/otr4j/crypto/OtrCryptoEngineImpl.java
changeset 895 b2e1b45382a4
parent 810 0ff0059f2ec3
equal deleted inserted replaced
894:5315a5713dd5 895:b2e1b45382a4
       
     1 /*
       
     2  * otr4j, the open source java otr library.
       
     3  *
       
     4  * Distributable under LGPL license.
       
     5  * See terms of license at gnu.org.
       
     6  */
       
     7 package net.java.otr4j.crypto;
       
     8 
       
     9 import java.io.IOException;
       
    10 import java.math.BigInteger;
       
    11 import java.nio.ByteBuffer;
       
    12 import java.security.InvalidKeyException;
       
    13 import java.security.KeyFactory;
       
    14 import java.security.KeyPair;
       
    15 import java.security.MessageDigest;
       
    16 import java.security.NoSuchAlgorithmException;
       
    17 import java.security.PrivateKey;
       
    18 import java.security.PublicKey;
       
    19 import java.security.SecureRandom;
       
    20 import java.security.interfaces.DSAParams;
       
    21 import java.security.interfaces.DSAPrivateKey;
       
    22 import java.security.interfaces.DSAPublicKey;
       
    23 
       
    24 import javax.crypto.KeyAgreement;
       
    25 import javax.crypto.interfaces.DHPrivateKey;
       
    26 import javax.crypto.interfaces.DHPublicKey;
       
    27 import javax.crypto.spec.DHPrivateKeySpec;
       
    28 import javax.crypto.spec.DHPublicKeySpec;
       
    29 import javax.crypto.spec.SecretKeySpec;
       
    30 
       
    31 import net.java.otr4j.io.SerializationUtils;
       
    32 
       
    33 import org.bouncycastle2.crypto.AsymmetricCipherKeyPair;
       
    34 import org.bouncycastle2.crypto.BufferedBlockCipher;
       
    35 import org.bouncycastle2.crypto.engines.AESFastEngine;
       
    36 import org.bouncycastle2.crypto.generators.DHKeyPairGenerator;
       
    37 import org.bouncycastle2.crypto.modes.SICBlockCipher;
       
    38 import org.bouncycastle2.crypto.params.DHKeyGenerationParameters;
       
    39 import org.bouncycastle2.crypto.params.DHParameters;
       
    40 import org.bouncycastle2.crypto.params.DHPrivateKeyParameters;
       
    41 import org.bouncycastle2.crypto.params.DHPublicKeyParameters;
       
    42 import org.bouncycastle2.crypto.params.DSAParameters;
       
    43 import org.bouncycastle2.crypto.params.DSAPrivateKeyParameters;
       
    44 import org.bouncycastle2.crypto.params.DSAPublicKeyParameters;
       
    45 import org.bouncycastle2.crypto.params.KeyParameter;
       
    46 import org.bouncycastle2.crypto.params.ParametersWithIV;
       
    47 import org.bouncycastle2.crypto.signers.DSASigner;
       
    48 import org.bouncycastle2.util.BigIntegers;
       
    49 
       
    50 /**
       
    51  * 
       
    52  * @author George Politis
       
    53  * 
       
    54  */
       
    55 public class OtrCryptoEngineImpl implements OtrCryptoEngine {
       
    56 
       
    57 	public KeyPair generateDHKeyPair() throws OtrCryptoException {
       
    58 
       
    59 		// Generate a AsymmetricCipherKeyPair using BC.
       
    60 		DHParameters dhParams = new DHParameters(MODULUS, GENERATOR, null,
       
    61 				DH_PRIVATE_KEY_MINIMUM_BIT_LENGTH);
       
    62 		DHKeyGenerationParameters params = new DHKeyGenerationParameters(
       
    63 				new SecureRandom(), dhParams);
       
    64 		DHKeyPairGenerator kpGen = new DHKeyPairGenerator();
       
    65 
       
    66 		kpGen.init(params);
       
    67 		AsymmetricCipherKeyPair pair = kpGen.generateKeyPair();
       
    68 
       
    69 		// Convert this AsymmetricCipherKeyPair to a standard JCE KeyPair.
       
    70 		DHPublicKeyParameters pub = (DHPublicKeyParameters) pair.getPublic();
       
    71 		DHPrivateKeyParameters priv = (DHPrivateKeyParameters) pair
       
    72 				.getPrivate();
       
    73 
       
    74 		try {
       
    75 			KeyFactory keyFac = KeyFactory.getInstance("DH");
       
    76 
       
    77 			DHPublicKeySpec pubKeySpecs = new DHPublicKeySpec(pub.getY(),
       
    78 					MODULUS, GENERATOR);
       
    79 			DHPublicKey pubKey = (DHPublicKey) keyFac
       
    80 					.generatePublic(pubKeySpecs);
       
    81 
       
    82 			DHParameters dhParameters = priv.getParameters();
       
    83 			DHPrivateKeySpec privKeySpecs = new DHPrivateKeySpec(priv.getX(),
       
    84 					dhParameters.getP(), dhParameters.getG());
       
    85 			DHPrivateKey privKey = (DHPrivateKey) keyFac
       
    86 					.generatePrivate(privKeySpecs);
       
    87 
       
    88 			return new KeyPair(pubKey, privKey);
       
    89 		} catch (Exception e) {
       
    90 			throw new OtrCryptoException(e);
       
    91 		}
       
    92 	}
       
    93 
       
    94 	public DHPublicKey getDHPublicKey(byte[] mpiBytes)
       
    95 			throws OtrCryptoException {
       
    96 		return getDHPublicKey(new BigInteger(mpiBytes));
       
    97 	}
       
    98 
       
    99 	public DHPublicKey getDHPublicKey(BigInteger mpi) throws OtrCryptoException {
       
   100 		DHPublicKeySpec pubKeySpecs = new DHPublicKeySpec(mpi, MODULUS,
       
   101 				GENERATOR);
       
   102 		try {
       
   103 			KeyFactory keyFac = KeyFactory.getInstance("DH");
       
   104 			return (DHPublicKey) keyFac.generatePublic(pubKeySpecs);
       
   105 		} catch (Exception e) {
       
   106 			throw new OtrCryptoException(e);
       
   107 		}
       
   108 	}
       
   109 
       
   110 	public byte[] sha256Hmac(byte[] b, byte[] key) throws OtrCryptoException {
       
   111 		return this.sha256Hmac(b, key, 0);
       
   112 	}
       
   113 
       
   114 	public byte[] sha256Hmac(byte[] b, byte[] key, int length)
       
   115 			throws OtrCryptoException {
       
   116 
       
   117 		SecretKeySpec keyspec = new SecretKeySpec(key, "HmacSHA256");
       
   118 		javax.crypto.Mac mac;
       
   119 		try {
       
   120 			mac = javax.crypto.Mac.getInstance("HmacSHA256");
       
   121 		} catch (NoSuchAlgorithmException e) {
       
   122 			throw new OtrCryptoException(e);
       
   123 		}
       
   124 		try {
       
   125 			mac.init(keyspec);
       
   126 		} catch (InvalidKeyException e) {
       
   127 			throw new OtrCryptoException(e);
       
   128 		}
       
   129 
       
   130 		byte[] macBytes = mac.doFinal(b);
       
   131 
       
   132 		if (length > 0) {
       
   133 			byte[] bytes = new byte[length];
       
   134 			ByteBuffer buff = ByteBuffer.wrap(macBytes);
       
   135 			buff.get(bytes);
       
   136 			return bytes;
       
   137 		} else {
       
   138 			return macBytes;
       
   139 		}
       
   140 	}
       
   141 
       
   142 	public byte[] sha1Hmac(byte[] b, byte[] key, int length)
       
   143 			throws OtrCryptoException {
       
   144 
       
   145 		try {
       
   146 			SecretKeySpec keyspec = new SecretKeySpec(key, "HmacSHA1");
       
   147 			javax.crypto.Mac mac = javax.crypto.Mac.getInstance("HmacSHA1");
       
   148 			mac.init(keyspec);
       
   149 
       
   150 			byte[] macBytes = mac.doFinal(b);
       
   151 
       
   152 			if (length > 0) {
       
   153 				byte[] bytes = new byte[length];
       
   154 				ByteBuffer buff = ByteBuffer.wrap(macBytes);
       
   155 				buff.get(bytes);
       
   156 				return bytes;
       
   157 			} else {
       
   158 				return macBytes;
       
   159 			}
       
   160 		} catch (Exception e) {
       
   161 			throw new OtrCryptoException(e);
       
   162 		}
       
   163 	}
       
   164 
       
   165 	public byte[] sha256Hmac160(byte[] b, byte[] key) throws OtrCryptoException {
       
   166 		return sha256Hmac(b, key, 20);
       
   167 	}
       
   168 
       
   169 	public byte[] sha256Hash(byte[] b) throws OtrCryptoException {
       
   170 		try {
       
   171 			MessageDigest sha256 = MessageDigest.getInstance("SHA-256");
       
   172 			sha256.update(b, 0, b.length);
       
   173 			return sha256.digest();
       
   174 		} catch (Exception e) {
       
   175 			throw new OtrCryptoException(e);
       
   176 		}
       
   177 	}
       
   178 
       
   179 	public byte[] sha1Hash(byte[] b) throws OtrCryptoException {
       
   180 		try {
       
   181 			MessageDigest sha256 = MessageDigest.getInstance("SHA-1");
       
   182 			sha256.update(b, 0, b.length);
       
   183 			return sha256.digest();
       
   184 		} catch (Exception e) {
       
   185 			throw new OtrCryptoException(e);
       
   186 		}
       
   187 	}
       
   188 
       
   189 	public byte[] aesDecrypt(byte[] key, byte[] ctr, byte[] b)
       
   190 			throws OtrCryptoException {
       
   191 
       
   192 		AESFastEngine aesDec = new AESFastEngine();
       
   193 		SICBlockCipher sicAesDec = new SICBlockCipher(aesDec);
       
   194 		BufferedBlockCipher bufSicAesDec = new BufferedBlockCipher(sicAesDec);
       
   195 
       
   196 		// Create initial counter value 0.
       
   197 		if (ctr == null)
       
   198 			ctr = ZERO_CTR;
       
   199 		bufSicAesDec.init(false, new ParametersWithIV(new KeyParameter(key),
       
   200 				ctr));
       
   201 		byte[] aesOutLwDec = new byte[b.length];
       
   202 		int done = bufSicAesDec.processBytes(b, 0, b.length, aesOutLwDec, 0);
       
   203 		try {
       
   204 			bufSicAesDec.doFinal(aesOutLwDec, done);
       
   205 		} catch (Exception e) {
       
   206 			throw new OtrCryptoException(e);
       
   207 		}
       
   208 
       
   209 		return aesOutLwDec;
       
   210 	}
       
   211 
       
   212 	public byte[] aesEncrypt(byte[] key, byte[] ctr, byte[] b)
       
   213 			throws OtrCryptoException {
       
   214 
       
   215 		AESFastEngine aesEnc = new AESFastEngine();
       
   216 		SICBlockCipher sicAesEnc = new SICBlockCipher(aesEnc);
       
   217 		BufferedBlockCipher bufSicAesEnc = new BufferedBlockCipher(sicAesEnc);
       
   218 
       
   219 		// Create initial counter value 0.
       
   220 		if (ctr == null)
       
   221 			ctr = ZERO_CTR;
       
   222 		bufSicAesEnc.init(true,
       
   223 				new ParametersWithIV(new KeyParameter(key), ctr));
       
   224 		byte[] aesOutLwEnc = new byte[b.length];
       
   225 		int done = bufSicAesEnc.processBytes(b, 0, b.length, aesOutLwEnc, 0);
       
   226 		try {
       
   227 			bufSicAesEnc.doFinal(aesOutLwEnc, done);
       
   228 		} catch (Exception e) {
       
   229 			throw new OtrCryptoException(e);
       
   230 		}
       
   231 		return aesOutLwEnc;
       
   232 	}
       
   233 
       
   234 	public BigInteger generateSecret(PrivateKey privKey, PublicKey pubKey)
       
   235 			throws OtrCryptoException {
       
   236 		try {
       
   237 			KeyAgreement ka = KeyAgreement.getInstance("DH");
       
   238 			ka.init(privKey);
       
   239 			ka.doPhase(pubKey, true);
       
   240 			byte[] sb = ka.generateSecret();
       
   241 			BigInteger s = new BigInteger(1, sb);
       
   242 			return s;
       
   243 
       
   244 		} catch (Exception e) {
       
   245 			throw new OtrCryptoException(e);
       
   246 		}
       
   247 	}
       
   248 
       
   249 	public byte[] sign(byte[] b, PrivateKey privatekey)
       
   250 			throws OtrCryptoException {
       
   251 
       
   252 		if (!(privatekey instanceof DSAPrivateKey))
       
   253 			throw new IllegalArgumentException();
       
   254 
       
   255 		DSAParams dsaParams = ((DSAPrivateKey) privatekey).getParams();
       
   256 		DSAParameters bcDSAParameters = new DSAParameters(dsaParams.getP(),
       
   257 				dsaParams.getQ(), dsaParams.getG());
       
   258 
       
   259 		DSAPrivateKey dsaPrivateKey = (DSAPrivateKey) privatekey;
       
   260 		DSAPrivateKeyParameters bcDSAPrivateKeyParms = new DSAPrivateKeyParameters(
       
   261 				dsaPrivateKey.getX(), bcDSAParameters);
       
   262 
       
   263 		DSASigner dsaSigner = new DSASigner();
       
   264 		dsaSigner.init(true, bcDSAPrivateKeyParms);
       
   265 
       
   266 		BigInteger q = dsaParams.getQ();
       
   267 
       
   268 		// Ian: Note that if you can get the standard DSA implementation you're
       
   269 		// using to not hash its input, you should be able to pass it ((256-bit
       
   270 		// value) mod q), (rather than truncating the 256-bit value) and all
       
   271 		// should be well.
       
   272 		// ref: Interop problems with libotr - DSA signature
       
   273 		BigInteger bmpi = new BigInteger(1, b);
       
   274 		BigInteger[] rs = dsaSigner.generateSignature(BigIntegers
       
   275 				.asUnsignedByteArray(bmpi.mod(q)));
       
   276 
       
   277 		int siglen = q.bitLength() / 4;
       
   278 		int rslen = siglen / 2;
       
   279 		byte[] rb = BigIntegers.asUnsignedByteArray(rs[0]);
       
   280 		byte[] sb = BigIntegers.asUnsignedByteArray(rs[1]);
       
   281 
       
   282 		// Create the final signature array, padded with zeros if necessary.
       
   283 		byte[] sig = new byte[siglen];
       
   284 		Boolean writeR = false;
       
   285 		Boolean writeS = false;
       
   286 		for (int i = 0; i < siglen; i++) {
       
   287 			if (i < rslen) {
       
   288 				if (!writeR)
       
   289 					writeR = rb.length >= rslen - i;
       
   290 				sig[i] = (writeR) ? rb[i] : (byte) 0x0;
       
   291 			} else {
       
   292 				int j = i - rslen; // Rebase.
       
   293 				if (!writeS)
       
   294 					writeS = sb.length >= rslen - j;
       
   295 				sig[i] = (writeS) ? sb[j] : (byte) 0x0;
       
   296 			}
       
   297 		}
       
   298 		return sig;
       
   299 	}
       
   300 
       
   301 	public boolean verify(byte[] b, PublicKey pubKey, byte[] rs)
       
   302 			throws OtrCryptoException {
       
   303 
       
   304 		if (!(pubKey instanceof DSAPublicKey))
       
   305 			throw new IllegalArgumentException();
       
   306 
       
   307 		DSAParams dsaParams = ((DSAPublicKey) pubKey).getParams();
       
   308 		int qlen = dsaParams.getQ().bitLength() / 8;
       
   309 		ByteBuffer buff = ByteBuffer.wrap(rs);
       
   310 		byte[] r = new byte[qlen];
       
   311 		buff.get(r);
       
   312 		byte[] s = new byte[qlen];
       
   313 		buff.get(s);
       
   314 		return verify(b, pubKey, r, s);
       
   315 	}
       
   316 
       
   317 	private Boolean verify(byte[] b, PublicKey pubKey, byte[] r, byte[] s)
       
   318 			throws OtrCryptoException {
       
   319 		Boolean result = verify(b, pubKey, new BigInteger(1, r),
       
   320 				new BigInteger(1, s));
       
   321 		return result;
       
   322 	}
       
   323 
       
   324 	private Boolean verify(byte[] b, PublicKey pubKey, BigInteger r,
       
   325 			BigInteger s) throws OtrCryptoException {
       
   326 
       
   327 		if (!(pubKey instanceof DSAPublicKey))
       
   328 			throw new IllegalArgumentException();
       
   329 
       
   330 		DSAParams dsaParams = ((DSAPublicKey) pubKey).getParams();
       
   331 
       
   332 		BigInteger q = dsaParams.getQ();
       
   333 		DSAParameters bcDSAParams = new DSAParameters(dsaParams.getP(), q,
       
   334 				dsaParams.getG());
       
   335 
       
   336 		DSAPublicKey dsaPrivateKey = (DSAPublicKey) pubKey;
       
   337 		DSAPublicKeyParameters dsaPrivParms = new DSAPublicKeyParameters(
       
   338 				dsaPrivateKey.getY(), bcDSAParams);
       
   339 
       
   340 		// Ian: Note that if you can get the standard DSA implementation you're
       
   341 		// using to not hash its input, you should be able to pass it ((256-bit
       
   342 		// value) mod q), (rather than truncating the 256-bit value) and all
       
   343 		// should be well.
       
   344 		// ref: Interop problems with libotr - DSA signature
       
   345 		DSASigner dsaSigner = new DSASigner();
       
   346 		dsaSigner.init(false, dsaPrivParms);
       
   347 
       
   348 		BigInteger bmpi = new BigInteger(1, b);
       
   349 		Boolean result = dsaSigner.verifySignature(BigIntegers
       
   350 				.asUnsignedByteArray(bmpi.mod(q)), r, s);
       
   351 		return result;
       
   352 	}
       
   353 
       
   354 	public String getFingerprint(PublicKey pubKey) throws OtrCryptoException {
       
   355 		byte[] b;
       
   356 		try {
       
   357 			byte[] bRemotePubKey = SerializationUtils.writePublicKey(pubKey);
       
   358 
       
   359 			if (pubKey.getAlgorithm().equals("DSA")) {
       
   360 				byte[] trimmed = new byte[bRemotePubKey.length - 2];
       
   361 				System.arraycopy(bRemotePubKey, 2, trimmed, 0, trimmed.length);
       
   362 				b = new OtrCryptoEngineImpl().sha1Hash(trimmed);
       
   363 			} else
       
   364 				b = new OtrCryptoEngineImpl().sha1Hash(bRemotePubKey);
       
   365 		} catch (IOException e) {
       
   366 			throw new OtrCryptoException(e);
       
   367 		}
       
   368 		return this.byteArrayToHexString(b);
       
   369 	}
       
   370 
       
   371 	private String byteArrayToHexString(byte in[]) {
       
   372 		byte ch = 0x00;
       
   373 		int i = 0;
       
   374 		if (in == null || in.length <= 0)
       
   375 			return null;
       
   376 		String pseudo[] = { "0", "1", "2", "3", "4", "5", "6", "7", "8", "9",
       
   377 				"A", "B", "C", "D", "E", "F" };
       
   378 		StringBuffer out = new StringBuffer(in.length * 2);
       
   379 		while (i < in.length) {
       
   380 			ch = (byte) (in[i] & 0xF0);
       
   381 			ch = (byte) (ch >>> 4);
       
   382 			ch = (byte) (ch & 0x0F);
       
   383 			out.append(pseudo[(int) ch]);
       
   384 			ch = (byte) (in[i] & 0x0F);
       
   385 			out.append(pseudo[(int) ch]);
       
   386 			i++;
       
   387 		}
       
   388 
       
   389 		String rslt = new String(out);
       
   390 		return rslt;
       
   391 
       
   392 	}
       
   393 }