PageRenderTime 42ms CodeModel.GetById 18ms RepoModel.GetById 0ms app.codeStats 0ms

/modules/quercus/src/com/caucho/quercus/lib/mcrypt/Mcrypt.java

https://github.com/GEFFROY/Quercus
Java | 393 lines | 250 code | 54 blank | 89 comment | 77 complexity | 033c1c73c4485c7899d58cdbf842fdae MD5 | raw file
  1. /*
  2. * Copyright (c) 1998-2010 Caucho Technology -- all rights reserved
  3. *
  4. * This file is part of Resin(R) Open Source
  5. *
  6. * Each copy or derived work must preserve the copyright notice and this
  7. * notice unmodified.
  8. *
  9. * Resin Open Source is free software; you can redistribute it and/or modify
  10. * it under the terms of the GNU General Public License as published by
  11. * the Free Software Foundation; either version 2 of the License, or
  12. * (at your option) any later version.
  13. *
  14. * Resin Open Source is distributed in the hope that it will be useful,
  15. * but WITHOUT ANY WARRANTY; without even the implied warranty of
  16. * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE, or any warranty
  17. * of NON-INFRINGEMENT. See the GNU General Public License for more
  18. * details.
  19. *
  20. * You should have received a copy of the GNU General Public License
  21. * along with Resin Open Source; if not, write to the
  22. *
  23. * Free Software Foundation, Inc.
  24. * 59 Temple Place, Suite 330
  25. * Boston, MA 02111-1307 USA
  26. *
  27. * @author Scott Ferguson
  28. */
  29. package com.caucho.quercus.lib.mcrypt;
  30. import com.caucho.quercus.QuercusRuntimeException;
  31. import com.caucho.quercus.env.ArrayValue;
  32. import com.caucho.quercus.env.ArrayValueImpl;
  33. import com.caucho.quercus.env.Env;
  34. import com.caucho.quercus.env.Value;
  35. import com.caucho.util.L10N;
  36. import javax.crypto.Cipher;
  37. import javax.crypto.spec.IvParameterSpec;
  38. import javax.crypto.spec.SecretKeySpec;
  39. import java.security.Key;
  40. import java.util.logging.Logger;
  41. /**
  42. * Encryption class
  43. */
  44. public class Mcrypt {
  45. private static final L10N L = new L10N(Mcrypt.class);
  46. private static final Logger log =
  47. Logger.getLogger(Mcrypt.class.getName());
  48. private final String _algorithm;
  49. private final String _mode;
  50. private final Cipher _cipher;
  51. private Key _key;
  52. private IvParameterSpec _iv;
  53. Mcrypt(Env env, String algorithm, String mode)
  54. throws Exception
  55. {
  56. _algorithm = algorithm;
  57. _mode = mode.toUpperCase();
  58. String transformation = getTransformation(algorithm, mode);
  59. if (transformation == null)
  60. throw new QuercusRuntimeException(L.l("'{0}' is an unknown algorithm",
  61. algorithm));
  62. _cipher = Cipher.getInstance(transformation);
  63. }
  64. /**
  65. * Uninitialize data.
  66. */
  67. public boolean deinit()
  68. {
  69. return false;
  70. }
  71. /**
  72. * Encrypts data.
  73. */
  74. public byte []decrypt(byte []data)
  75. {
  76. try {
  77. _cipher.init(Cipher.DECRYPT_MODE, _key, _iv);
  78. int blockSize = _cipher.getBlockSize();
  79. return _cipher.doFinal(data);
  80. } catch (Exception e) {
  81. throw new RuntimeException(e);
  82. }
  83. }
  84. /**
  85. * Encrypts data.
  86. */
  87. public byte []encrypt(byte []data)
  88. {
  89. try {
  90. _cipher.init(Cipher.ENCRYPT_MODE, _key, _iv);
  91. if (isPadded())
  92. data = pad(data);
  93. return _cipher.doFinal(data);
  94. } catch (Exception e) {
  95. throw new RuntimeException(e);
  96. }
  97. }
  98. /**
  99. * Returns the block size.
  100. */
  101. public int get_block_size()
  102. {
  103. return _cipher.getBlockSize();
  104. }
  105. /**
  106. * Returns the initialization vector size.
  107. */
  108. public String get_algorithms_name()
  109. {
  110. return _algorithm;
  111. }
  112. /**
  113. * Returns the initialization vector size.
  114. */
  115. public int get_iv_size()
  116. {
  117. if (_mode.equals("OFB"))
  118. return _cipher.getBlockSize();
  119. else if (_mode.equals("CFB"))
  120. return _cipher.getBlockSize();
  121. else if (_mode.equals("CBC"))
  122. return _cipher.getBlockSize();
  123. else if (_mode.equals("ECB"))
  124. return _cipher.getBlockSize();
  125. else
  126. return 0;
  127. }
  128. /**
  129. * Returns the supported key sizes
  130. */
  131. public Value get_supported_key_sizes()
  132. {
  133. ArrayValue value = new ArrayValueImpl();
  134. // php/1q0c - can mix any key size with any block size
  135. if (McryptModule.MCRYPT_RIJNDAEL_128.equals(_algorithm)) {
  136. value.put(128 / 8);
  137. value.put(192 / 8);
  138. value.put(256 / 8);
  139. }
  140. else if (McryptModule.MCRYPT_RIJNDAEL_192.equals(_algorithm)) {
  141. value.put(128 / 8);
  142. value.put(192 / 8);
  143. value.put(256 / 8);
  144. }
  145. else if (McryptModule.MCRYPT_RIJNDAEL_256.equals(_algorithm)) {
  146. value.put(128 / 8);
  147. value.put(192 / 8);
  148. value.put(256 / 8);
  149. }
  150. else if (McryptModule.MCRYPT_3DES.equals(_algorithm)) {
  151. value.put(24);
  152. }
  153. else if (McryptModule.MCRYPT_DES.equals(_algorithm)) {
  154. value.put(8);
  155. }
  156. return value;
  157. }
  158. /**
  159. * Returns the maximum key size in bytes.
  160. */
  161. public int get_key_size()
  162. {
  163. // php/1q0s - php mcrypt allows up to 256 bit keys for Rijndael of any
  164. // block size. The number in the algorithm string is the block size.
  165. // Note that when they say "Rijndael", they are not referring to AES,
  166. // which has a fixed block size of 128 bits.
  167. if (McryptModule.MCRYPT_RIJNDAEL_128.equals(_algorithm))
  168. return 256 / 8;
  169. else if (McryptModule.MCRYPT_RIJNDAEL_192.equals(_algorithm))
  170. return 256 / 8;
  171. else if (McryptModule.MCRYPT_RIJNDAEL_256.equals(_algorithm))
  172. return 256 / 8;
  173. else if (McryptModule.MCRYPT_3DES.equals(_algorithm))
  174. return 24;
  175. else if (McryptModule.MCRYPT_BLOWFISH.equals(_algorithm))
  176. return 56;
  177. else
  178. return get_block_size();
  179. }
  180. private boolean isPadKey()
  181. {
  182. if (McryptModule.MCRYPT_BLOWFISH.equals(_algorithm))
  183. return false;
  184. else
  185. return true;
  186. }
  187. /**
  188. * Pads a key up to the next accepted size for the given cipher.
  189. * Follows php mcrypt behavior php/1q0x
  190. **/
  191. private byte []padKey(byte []keyData)
  192. {
  193. int keySize = get_key_size();
  194. int len = keyData.length;
  195. if (McryptModule.MCRYPT_RIJNDAEL_128.equals(_algorithm)
  196. || McryptModule.MCRYPT_RIJNDAEL_192.equals(_algorithm)
  197. || McryptModule.MCRYPT_RIJNDAEL_256.equals(_algorithm)) {
  198. if (len <= 16)
  199. keySize = 16;
  200. else if (len <= 24)
  201. keySize = 24;
  202. else if (len <= 32)
  203. keySize = 32;
  204. else
  205. throw new QuercusRuntimeException(
  206. L.l("Key too large for algorithm ({0} > 32)", len));
  207. }
  208. if (len == keySize)
  209. return keyData;
  210. else {
  211. byte []paddedKey = new byte[keySize];
  212. System.arraycopy(keyData, 0, paddedKey, 0, len);
  213. return paddedKey;
  214. }
  215. }
  216. /**
  217. * Returns the initialization vector size.
  218. */
  219. public String get_modes_name()
  220. {
  221. return _mode;
  222. }
  223. /**
  224. * Initialize the crypt.
  225. */
  226. public int init(byte []keyBytesArg, byte []iv)
  227. {
  228. byte []keyBytes;
  229. if (isPadKey())
  230. keyBytes = padKey(keyBytesArg);
  231. else
  232. keyBytes = keyBytesArg;
  233. _key = new SecretKeySpec(keyBytes, getAlgorithm(_algorithm));
  234. if (iv == null)
  235. _iv = null;
  236. else if (_mode.equals("CBC") || _mode.equals("CFB") || _mode.equals("OFB"))
  237. _iv = new IvParameterSpec(iv);
  238. else
  239. _iv = null;
  240. return 0;
  241. }
  242. /**
  243. * Returns true for block algorithms
  244. */
  245. public boolean is_block_algorithm()
  246. {
  247. if (_algorithm.equals(McryptModule.MCRYPT_BLOWFISH))
  248. return false;
  249. else
  250. return true;
  251. }
  252. /**
  253. * Returns true for block algorithms
  254. */
  255. public boolean is_block_algorithm_mode()
  256. {
  257. return _mode.equals("CBC") || _mode.equals("CFB") || _mode.equals("OFB");
  258. }
  259. /**
  260. * Returns true for block algorithms
  261. */
  262. public boolean is_block_mode()
  263. {
  264. return _mode.equals("CBC") || _mode.equals("ECB");
  265. }
  266. private byte []pad(byte []data)
  267. {
  268. int blockSize = get_block_size();
  269. int len = data.length;
  270. int offset = len % blockSize;
  271. if (offset == 0)
  272. return data;
  273. else {
  274. byte []pad = new byte[len + blockSize - offset];
  275. System.arraycopy(data, 0, pad, 0, data.length);
  276. return pad;
  277. }
  278. }
  279. private boolean isPadded()
  280. {
  281. if (_mode.equals("CFB") || _mode.equals("OFB"))
  282. return false;
  283. return true;
  284. }
  285. /**
  286. * Closes the mcrypt.
  287. */
  288. public void close()
  289. {
  290. }
  291. private static String getTransformation(String algorithm, String mode)
  292. throws Exception
  293. {
  294. mode = mode.toUpperCase();
  295. // php/1q02 & php/1q0s
  296. // mcrypt thinks OFB == OFB8 while Java thinks OFB == OFB64
  297. if (mode.equals("OFB"))
  298. mode = "OFB8";
  299. // XXX Sun and BC don't provide 192 and 256 Rijndael at
  300. // this time, probably because they were rejected by the AES
  301. // standard, but mcrypt does. Using numbers in the mode part just
  302. // selects the mode block size, not the cipher block size.
  303. if (McryptModule.MCRYPT_RIJNDAEL_128.equals(algorithm))
  304. return "AES/" + mode + "/NoPadding";
  305. else if (McryptModule.MCRYPT_RIJNDAEL_192.equals(algorithm))
  306. return "Rijndael192/" + mode + "/NoPadding";
  307. else if (McryptModule.MCRYPT_RIJNDAEL_256.equals(algorithm))
  308. return "Rijndael256/" + mode + "/NoPadding";
  309. else if (McryptModule.MCRYPT_DES.equals(algorithm))
  310. return "DES/" + mode + "/NoPadding";
  311. else if (McryptModule.MCRYPT_3DES.equals(algorithm))
  312. return "DESede/" + mode + "/NoPadding";
  313. else if (McryptModule.MCRYPT_BLOWFISH.equals(algorithm)) {
  314. // php/1q0t, #2561
  315. return "Blowfish/" + mode + "/NoPadding";
  316. }
  317. else if (McryptModule.MCRYPT_ARCFOUR.equals(algorithm)
  318. || McryptModule.MCRYPT_RC4.equals(algorithm))
  319. return "ARCFOUR/" + mode + "/NoPadding";
  320. else
  321. return algorithm + '/' + mode + "/NoPadding";
  322. }
  323. private static String getAlgorithm(String algorithm)
  324. {
  325. if (McryptModule.MCRYPT_RIJNDAEL_128.equals(algorithm))
  326. return "AES";
  327. else if (McryptModule.MCRYPT_RIJNDAEL_192.equals(algorithm)
  328. || McryptModule.MCRYPT_RIJNDAEL_256.equals(algorithm))
  329. return "Rijndael";
  330. else if (McryptModule.MCRYPT_3DES.equals(algorithm))
  331. return "DESede";
  332. else
  333. return algorithm;
  334. }
  335. public String toString()
  336. {
  337. return "Mcrypt[" + _algorithm + ", " + _mode + "]";
  338. }
  339. }