|
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 } |