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

/projects/jruby-1.7.3/src/org/jruby/ext/openssl/Cipher.java

https://gitlab.com/essere.lab.public/qualitas.class-corpus
Java | 805 lines | 666 code | 85 blank | 54 comment | 144 complexity | c1c3f60adff67b0e20e4aa9acd6b6fa1 MD5 | raw file
  1. /***** BEGIN LICENSE BLOCK *****
  2. * Version: EPL 1.0/GPL 2.0/LGPL 2.1
  3. *
  4. * The contents of this file are subject to the Common Public
  5. * License Version 1.0 (the "License"); you may not use this file
  6. * except in compliance with the License. You may obtain a copy of
  7. * the License at http://www.eclipse.org/legal/cpl-v10.html
  8. *
  9. * Software distributed under the License is distributed on an "AS
  10. * IS" basis, WITHOUT WARRANTY OF ANY KIND, either express or
  11. * implied. See the License for the specific language governing
  12. * rights and limitations under the License.
  13. *
  14. * Copyright (C) 2006, 2007 Ola Bini <ola@ologix.com>
  15. *
  16. * Alternatively, the contents of this file may be used under the terms of
  17. * either of the GNU General Public License Version 2 or later (the "GPL"),
  18. * or the GNU Lesser General Public License Version 2.1 or later (the "LGPL"),
  19. * in which case the provisions of the GPL or the LGPL are applicable instead
  20. * of those above. If you wish to allow use of your version of this file only
  21. * under the terms of either the GPL or the LGPL, and not to allow others to
  22. * use your version of this file under the terms of the EPL, indicate your
  23. * decision by deleting the provisions above and replace them with the notice
  24. * and other provisions required by the GPL or the LGPL. If you do not delete
  25. * the provisions above, a recipient may use your version of this file under
  26. * the terms of any one of the EPL, the GPL or the LGPL.
  27. ***** END LICENSE BLOCK *****/
  28. package org.jruby.ext.openssl;
  29. import java.security.GeneralSecurityException;
  30. import java.security.MessageDigest;
  31. import java.security.NoSuchAlgorithmException;
  32. import java.util.ArrayList;
  33. import java.util.HashSet;
  34. import java.util.List;
  35. import java.util.Set;
  36. import javax.crypto.spec.IvParameterSpec;
  37. import javax.crypto.spec.RC2ParameterSpec;
  38. import org.jruby.Ruby;
  39. import org.jruby.RubyClass;
  40. import org.jruby.RubyModule;
  41. import org.jruby.RubyNumeric;
  42. import org.jruby.RubyObject;
  43. import org.jruby.common.IRubyWarnings;
  44. import org.jruby.common.IRubyWarnings.ID;
  45. import org.jruby.anno.JRubyMethod;
  46. import org.jruby.anno.JRubyModule;
  47. import org.jruby.exceptions.RaiseException;
  48. import org.jruby.runtime.ObjectAllocator;
  49. import org.jruby.runtime.builtin.IRubyObject;
  50. import org.jruby.util.ByteList;
  51. /**
  52. * @author <a href="mailto:ola.bini@ki.se">Ola Bini</a>
  53. */
  54. public class Cipher extends RubyObject {
  55. private static final long serialVersionUID = 7727377435222646536L;
  56. // set to enable debug output
  57. private static final boolean DEBUG = false;
  58. private static ObjectAllocator CIPHER_ALLOCATOR = new ObjectAllocator() {
  59. public IRubyObject allocate(Ruby runtime, RubyClass klass) {
  60. return new Cipher(runtime, klass);
  61. }
  62. };
  63. public static void createCipher(Ruby runtime, RubyModule mOSSL) {
  64. RubyClass cCipher = mOSSL.defineClassUnder("Cipher", runtime.getObject(), CIPHER_ALLOCATOR);
  65. cCipher.defineAnnotatedMethods(Cipher.class);
  66. cCipher.defineAnnotatedMethods(CipherModule.class);
  67. RubyClass openSSLError = mOSSL.getClass("OpenSSLError");
  68. cCipher.defineClassUnder("CipherError", openSSLError, openSSLError.getAllocator());
  69. }
  70. @JRubyModule(name = "OpenSSL::Cipher")
  71. public static class CipherModule {
  72. @JRubyMethod(meta = true)
  73. public static IRubyObject ciphers(IRubyObject recv) {
  74. initializeCiphers();
  75. List<IRubyObject> result = new ArrayList<IRubyObject>();
  76. for (String cipher : CIPHERS) {
  77. result.add(recv.getRuntime().newString(cipher));
  78. result.add(recv.getRuntime().newString(cipher.toLowerCase()));
  79. }
  80. return recv.getRuntime().newArray(result);
  81. }
  82. public static boolean isSupportedCipher(String name) {
  83. initializeCiphers();
  84. return CIPHERS.indexOf(name.toUpperCase()) != -1;
  85. }
  86. private static boolean initialized = false;
  87. private static final List<String> CIPHERS = new ArrayList<String>();
  88. private static void initializeCiphers() {
  89. synchronized (CIPHERS) {
  90. if (initialized) {
  91. return;
  92. }
  93. String[] other = {"AES128", "AES192", "AES256", "BLOWFISH", "RC2-40-CBC", "RC2-64-CBC", "RC4", "RC4-40", "CAST", "CAST-CBC"};
  94. String[] bases = {"AES-128", "AES-192", "AES-256", "BF", "DES", "DES-EDE", "DES-EDE3", "RC2", "CAST5"};
  95. String[] suffixes = {"", "-CBC", "-CFB", "-CFB1", "-CFB8", "-ECB", "-OFB"};
  96. for (int i = 0, j = bases.length; i < j; i++) {
  97. for (int k = 0, l = suffixes.length; k < l; k++) {
  98. String val = bases[i] + suffixes[k];
  99. if (tryCipher(val)) {
  100. CIPHERS.add(val.toUpperCase());
  101. }
  102. }
  103. }
  104. for (int i = 0, j = other.length; i < j; i++) {
  105. if (tryCipher(other[i])) {
  106. CIPHERS.add(other[i].toUpperCase());
  107. }
  108. }
  109. initialized = true;
  110. }
  111. }
  112. }
  113. public static class Algorithm {
  114. private static final Set<String> BLOCK_MODES;
  115. static {
  116. BLOCK_MODES = new HashSet<String>();
  117. BLOCK_MODES.add("CBC");
  118. BLOCK_MODES.add("CFB");
  119. BLOCK_MODES.add("CFB1");
  120. BLOCK_MODES.add("CFB8");
  121. BLOCK_MODES.add("ECB");
  122. BLOCK_MODES.add("OFB");
  123. }
  124. public static String jsseToOssl(String inName, int keyLen) {
  125. String cryptoBase = null;
  126. String cryptoVersion = null;
  127. String cryptoMode = null;
  128. String[] parts = inName.split("/");
  129. if (parts.length != 1 && parts.length != 3) {
  130. return null;
  131. }
  132. cryptoBase = parts[0];
  133. if (parts.length > 2) {
  134. cryptoMode = parts[1];
  135. // padding: parts[2] is not used
  136. }
  137. if (!BLOCK_MODES.contains(cryptoMode)) {
  138. cryptoVersion = cryptoMode;
  139. cryptoMode = "CBC";
  140. }
  141. if (cryptoMode == null) {
  142. cryptoMode = "CBC";
  143. }
  144. if (cryptoBase.equals("DESede")) {
  145. cryptoBase = "DES";
  146. cryptoVersion = "EDE3";
  147. } else if (cryptoBase.equals("Blowfish")) {
  148. cryptoBase = "BF";
  149. }
  150. if (cryptoVersion == null) {
  151. cryptoVersion = String.valueOf(keyLen);
  152. }
  153. return cryptoBase + "-" + cryptoVersion + "-" + cryptoMode;
  154. }
  155. public static String getAlgorithmBase(javax.crypto.Cipher cipher) {
  156. String algoBase = cipher.getAlgorithm();
  157. if (algoBase.indexOf('/') != -1) {
  158. algoBase = algoBase.split("/")[0];
  159. }
  160. return algoBase;
  161. }
  162. public static String[] osslToJsse(String inName) {
  163. // assume PKCS5Padding
  164. return osslToJsse(inName, null);
  165. }
  166. public static String[] osslToJsse(String inName, String padding) {
  167. String[] split = inName.split("-");
  168. String cryptoBase = split[0];
  169. String cryptoVersion = null;
  170. String cryptoMode = null;
  171. String realName = null;
  172. String paddingType;
  173. if (padding == null || padding.equalsIgnoreCase("PKCS5Padding")) {
  174. paddingType = "PKCS5Padding";
  175. } else if (padding.equals("0") || padding.equalsIgnoreCase("NoPadding")) {
  176. paddingType = "NoPadding";
  177. } else if (padding.equalsIgnoreCase("ISO10126Padding")) {
  178. paddingType = "ISO10126Padding";
  179. } else {
  180. paddingType = "PKCS5Padding";
  181. }
  182. if ("bf".equalsIgnoreCase(cryptoBase)) {
  183. cryptoBase = "Blowfish";
  184. }
  185. if (split.length == 3) {
  186. cryptoVersion = split[1];
  187. cryptoMode = split[2];
  188. } else if (split.length == 2) {
  189. cryptoMode = split[1];
  190. } else {
  191. cryptoMode = "CBC";
  192. }
  193. if (cryptoBase.equalsIgnoreCase("CAST")) {
  194. realName = "CAST5";
  195. } else if (cryptoBase.equalsIgnoreCase("DES") && "EDE3".equalsIgnoreCase(cryptoVersion)) {
  196. realName = "DESede";
  197. } else {
  198. realName = cryptoBase;
  199. }
  200. if (!BLOCK_MODES.contains(cryptoMode.toUpperCase())) {
  201. cryptoVersion = cryptoMode;
  202. cryptoMode = "CBC";
  203. } else if (cryptoMode.equalsIgnoreCase("CFB1")) {
  204. // uglish SunJCE cryptoMode normalization.
  205. cryptoMode = "CFB";
  206. }
  207. if (realName.equalsIgnoreCase("RC4")) {
  208. realName = "RC4";
  209. cryptoMode = "NONE";
  210. paddingType = "NoPadding";
  211. } else {
  212. realName = realName + "/" + cryptoMode + "/" + paddingType;
  213. }
  214. return new String[]{cryptoBase, cryptoVersion, cryptoMode, realName, paddingType};
  215. }
  216. public static int[] osslKeyIvLength(String name) {
  217. String[] values = Algorithm.osslToJsse(name);
  218. String cryptoBase = values[0];
  219. String cryptoVersion = values[1];
  220. String cryptoMode = values[2];
  221. String realName = values[3];
  222. int keyLen = -1;
  223. int ivLen = -1;
  224. if (hasLen(cryptoBase) && null != cryptoVersion) {
  225. try {
  226. keyLen = Integer.parseInt(cryptoVersion) / 8;
  227. } catch (NumberFormatException e) {
  228. keyLen = -1;
  229. }
  230. }
  231. if (keyLen == -1) {
  232. if ("DES".equalsIgnoreCase(cryptoBase)) {
  233. ivLen = 8;
  234. if ("EDE3".equalsIgnoreCase(cryptoVersion)) {
  235. keyLen = 24;
  236. } else {
  237. keyLen = 8;
  238. }
  239. } else if ("RC4".equalsIgnoreCase(cryptoBase)) {
  240. ivLen = 0;
  241. keyLen = 16;
  242. } else {
  243. keyLen = 16;
  244. try {
  245. if ((javax.crypto.Cipher.getMaxAllowedKeyLength(name) / 8) < keyLen) {
  246. keyLen = javax.crypto.Cipher.getMaxAllowedKeyLength(name) / 8;
  247. }
  248. } catch (Exception e) {
  249. // I hate checked exceptions
  250. }
  251. }
  252. }
  253. if (ivLen == -1) {
  254. if ("AES".equalsIgnoreCase(cryptoBase)) {
  255. ivLen = 16;
  256. } else {
  257. ivLen = 8;
  258. }
  259. }
  260. return new int[] { keyLen, ivLen };
  261. }
  262. public static boolean hasLen(String cryptoBase) {
  263. return "AES".equalsIgnoreCase(cryptoBase) || "RC2".equalsIgnoreCase(cryptoBase) || "RC4".equalsIgnoreCase(cryptoBase);
  264. }
  265. }
  266. private static boolean tryCipher(final String rubyName) {
  267. String cryptoMode = Algorithm.osslToJsse(rubyName, null)[3];
  268. try {
  269. javax.crypto.Cipher.getInstance(cryptoMode);
  270. return true;
  271. } catch (NoSuchAlgorithmException nsae) {
  272. try {
  273. OpenSSLReal.getCipherBC(cryptoMode);
  274. return true;
  275. } catch (GeneralSecurityException gse) {
  276. return false;
  277. }
  278. } catch (Exception e) {
  279. return false;
  280. }
  281. }
  282. public Cipher(Ruby runtime, RubyClass type) {
  283. super(runtime, type);
  284. }
  285. private javax.crypto.Cipher ciph;
  286. private String name;
  287. private String cryptoBase;
  288. private String cryptoVersion;
  289. private String cryptoMode;
  290. private String padding_type;
  291. private String realName;
  292. private int keyLen = -1;
  293. private int generateKeyLen = -1;
  294. private int ivLen = -1;
  295. private boolean encryptMode = true;
  296. //private IRubyObject[] modeParams;
  297. private boolean ciphInited = false;
  298. private byte[] key;
  299. private byte[] realIV;
  300. private byte[] orgIV;
  301. private String padding;
  302. void dumpVars() {
  303. System.out.println("***** Cipher instance vars ****");
  304. System.out.println("name = " + name);
  305. System.out.println("cryptoBase = " + cryptoBase);
  306. System.out.println("cryptoVersion = " + cryptoVersion);
  307. System.out.println("cryptoMode = " + cryptoMode);
  308. System.out.println("padding_type = " + padding_type);
  309. System.out.println("realName = " + realName);
  310. System.out.println("keyLen = " + keyLen);
  311. System.out.println("ivLen = " + ivLen);
  312. System.out.println("ciph block size = " + ciph.getBlockSize());
  313. System.out.println("encryptMode = " + encryptMode);
  314. System.out.println("ciphInited = " + ciphInited);
  315. System.out.println("key.length = " + (key == null ? 0 : key.length));
  316. System.out.println("iv.length = " + (this.realIV == null ? 0 : this.realIV.length));
  317. System.out.println("padding = " + padding);
  318. System.out.println("ciphAlgo = " + ciph.getAlgorithm());
  319. System.out.println("*******************************");
  320. }
  321. @JRubyMethod(required = 1)
  322. public IRubyObject initialize(IRubyObject str) {
  323. name = str.toString();
  324. if (!CipherModule.isSupportedCipher(name)) {
  325. throw newCipherError(getRuntime(), String.format("unsupported cipher algorithm (%s)", name));
  326. }
  327. if (ciph != null) {
  328. throw getRuntime().newRuntimeError("Cipher already inititalized!");
  329. }
  330. updateCipher(name, padding);
  331. return this;
  332. }
  333. @Override
  334. @JRubyMethod(required = 1)
  335. public IRubyObject initialize_copy(IRubyObject obj) {
  336. if (this == obj) {
  337. return this;
  338. }
  339. checkFrozen();
  340. cryptoBase = ((Cipher) obj).cryptoBase;
  341. cryptoVersion = ((Cipher) obj).cryptoVersion;
  342. cryptoMode = ((Cipher) obj).cryptoMode;
  343. padding_type = ((Cipher) obj).padding_type;
  344. realName = ((Cipher) obj).realName;
  345. name = ((Cipher) obj).name;
  346. keyLen = ((Cipher) obj).keyLen;
  347. ivLen = ((Cipher) obj).ivLen;
  348. encryptMode = ((Cipher) obj).encryptMode;
  349. ciphInited = false;
  350. if (((Cipher) obj).key != null) {
  351. key = new byte[((Cipher) obj).key.length];
  352. System.arraycopy(((Cipher) obj).key, 0, key, 0, key.length);
  353. } else {
  354. key = null;
  355. }
  356. if (((Cipher) obj).realIV != null) {
  357. this.realIV = new byte[((Cipher) obj).realIV.length];
  358. System.arraycopy(((Cipher) obj).realIV, 0, this.realIV, 0, this.realIV.length);
  359. } else {
  360. this.realIV = null;
  361. }
  362. this.orgIV = this.realIV;
  363. padding = ((Cipher) obj).padding;
  364. ciph = getCipher();
  365. return this;
  366. }
  367. @JRubyMethod
  368. public IRubyObject name() {
  369. return getRuntime().newString(name);
  370. }
  371. @JRubyMethod
  372. public IRubyObject key_len() {
  373. return getRuntime().newFixnum(keyLen);
  374. }
  375. @JRubyMethod
  376. public IRubyObject iv_len() {
  377. return getRuntime().newFixnum(ivLen);
  378. }
  379. @JRubyMethod(name = "key_len=", required = 1)
  380. public IRubyObject set_key_len(IRubyObject len) {
  381. this.keyLen = RubyNumeric.fix2int(len);
  382. return len;
  383. }
  384. @JRubyMethod(name = "key=", required = 1)
  385. public IRubyObject set_key(IRubyObject key) {
  386. byte[] keyBytes;
  387. try {
  388. keyBytes = key.convertToString().getBytes();
  389. } catch (Exception e) {
  390. if (DEBUG) {
  391. e.printStackTrace();
  392. }
  393. throw newCipherError(getRuntime(), e.getMessage());
  394. }
  395. if (keyBytes.length < keyLen) {
  396. throw newCipherError(getRuntime(), "key length to short");
  397. }
  398. if (keyBytes.length > keyLen) {
  399. byte[] keys = new byte[keyLen];
  400. System.arraycopy(keyBytes, 0, keys, 0, keyLen);
  401. keyBytes = keys;
  402. }
  403. this.key = keyBytes;
  404. return key;
  405. }
  406. @JRubyMethod(name = "iv=", required = 1)
  407. public IRubyObject set_iv(IRubyObject iv) {
  408. byte[] ivBytes;
  409. try {
  410. ivBytes = iv.convertToString().getBytes();
  411. } catch (Exception e) {
  412. if (DEBUG) {
  413. e.printStackTrace();
  414. }
  415. throw newCipherError(getRuntime(), e.getMessage());
  416. }
  417. if (ivBytes.length < ivLen) {
  418. throw newCipherError(getRuntime(), "iv length to short");
  419. } else {
  420. // EVP_CipherInit_ex uses leading IV length of given sequence.
  421. byte[] iv2 = new byte[ivLen];
  422. System.arraycopy(ivBytes, 0, iv2, 0, ivLen);
  423. this.realIV = iv2;
  424. }
  425. this.orgIV = this.realIV;
  426. if (!isStreamCipher()) {
  427. ciphInited = false;
  428. }
  429. return iv;
  430. }
  431. @JRubyMethod
  432. public IRubyObject block_size() {
  433. checkInitialized();
  434. if (isStreamCipher()) {
  435. // getBlockSize() returns 0 for stream cipher in JCE. OpenSSL returns 1 for RC4.
  436. return getRuntime().newFixnum(1);
  437. }
  438. return getRuntime().newFixnum(ciph.getBlockSize());
  439. }
  440. protected void init(IRubyObject[] args, boolean encrypt) {
  441. org.jruby.runtime.Arity.checkArgumentCount(getRuntime(), args, 0, 2);
  442. encryptMode = encrypt;
  443. ciphInited = false;
  444. if (args.length > 0) {
  445. /*
  446. * oops. this code mistakes salt for IV.
  447. * We deprecated the arguments for this method, but we decided
  448. * keeping this behaviour for backward compatibility.
  449. */
  450. byte[] pass = args[0].convertToString().getBytes();
  451. byte[] iv = null;
  452. try {
  453. iv = "OpenSSL for Ruby rulez!".getBytes("ISO8859-1");
  454. byte[] iv2 = new byte[this.ivLen];
  455. System.arraycopy(iv, 0, iv2, 0, this.ivLen);
  456. iv = iv2;
  457. } catch (Exception e) {
  458. }
  459. if (args.length > 1 && !args[1].isNil()) {
  460. getRuntime().getWarnings().warning(ID.MISCELLANEOUS, "key derivation by " + getMetaClass().getRealClass().getName() + "#encrypt is deprecated; use " + getMetaClass().getRealClass().getName() + "::pkcs5_keyivgen instead");
  461. iv = args[1].convertToString().getBytes();
  462. if (iv.length > this.ivLen) {
  463. byte[] iv2 = new byte[this.ivLen];
  464. System.arraycopy(iv, 0, iv2, 0, this.ivLen);
  465. iv = iv2;
  466. }
  467. }
  468. MessageDigest digest = Digest.getDigest("MD5", getRuntime());
  469. OpenSSLImpl.KeyAndIv result = OpenSSLImpl.EVP_BytesToKey(keyLen, ivLen, digest, iv, pass, 2048);
  470. this.key = result.getKey();
  471. this.realIV = iv;
  472. this.orgIV = this.realIV;
  473. }
  474. }
  475. @JRubyMethod(optional = 2)
  476. public IRubyObject encrypt(IRubyObject[] args) {
  477. this.realIV = orgIV;
  478. init(args, true);
  479. return this;
  480. }
  481. @JRubyMethod(optional = 2)
  482. public IRubyObject decrypt(IRubyObject[] args) {
  483. this.realIV = orgIV;
  484. init(args, false);
  485. return this;
  486. }
  487. @JRubyMethod
  488. public IRubyObject reset() {
  489. checkInitialized();
  490. if (!isStreamCipher()) {
  491. this.realIV = orgIV;
  492. doInitialize();
  493. }
  494. return this;
  495. }
  496. private void updateCipher(String name, String padding) {
  497. // given 'rc4' must be 'RC4' here. OpenSSL checks it as a LN of object
  498. // ID and set SN. We don't check 'name' is allowed as a LN in ASN.1 for
  499. // the possibility of JCE specific algorithm so just do upperCase here
  500. // for OpenSSL compatibility.
  501. this.name = name.toUpperCase();
  502. this.padding = padding;
  503. String[] values = Algorithm.osslToJsse(name, padding);
  504. cryptoBase = values[0];
  505. cryptoVersion = values[1];
  506. cryptoMode = values[2];
  507. realName = values[3];
  508. padding_type = values[4];
  509. int[] lengths = Algorithm.osslKeyIvLength(name);
  510. keyLen = lengths[0];
  511. ivLen = lengths[1];
  512. if ("DES".equalsIgnoreCase(cryptoBase)) {
  513. generateKeyLen = keyLen / 8 * 7;
  514. }
  515. ciph = getCipher();
  516. }
  517. javax.crypto.Cipher getCipher() {
  518. try {
  519. return javax.crypto.Cipher.getInstance(realName);
  520. } catch (NoSuchAlgorithmException e) {
  521. try {
  522. return OpenSSLReal.getCipherBC(realName);
  523. } catch (GeneralSecurityException ignore) {
  524. }
  525. throw newCipherError(getRuntime(), "unsupported cipher algorithm (" + realName + ")");
  526. } catch (javax.crypto.NoSuchPaddingException e) {
  527. throw newCipherError(getRuntime(), "unsupported cipher padding (" + realName + ")");
  528. }
  529. }
  530. @JRubyMethod(required = 1, optional = 3)
  531. public IRubyObject pkcs5_keyivgen(IRubyObject[] args) {
  532. org.jruby.runtime.Arity.checkArgumentCount(getRuntime(), args, 1, 4);
  533. byte[] pass = args[0].convertToString().getBytes();
  534. byte[] salt = null;
  535. int iter = 2048;
  536. IRubyObject vdigest = getRuntime().getNil();
  537. if (args.length > 1) {
  538. if (!args[1].isNil()) {
  539. salt = args[1].convertToString().getBytes();
  540. }
  541. if (args.length > 2) {
  542. if (!args[2].isNil()) {
  543. iter = RubyNumeric.fix2int(args[2]);
  544. }
  545. if (args.length > 3) {
  546. vdigest = args[3];
  547. }
  548. }
  549. }
  550. if (null != salt) {
  551. if (salt.length != 8) {
  552. throw newCipherError(getRuntime(), "salt must be an 8-octet string");
  553. }
  554. }
  555. final String algorithm = vdigest.isNil() ? "MD5" : ((Digest) vdigest).getAlgorithm();
  556. MessageDigest digest = Digest.getDigest(algorithm, getRuntime());
  557. OpenSSLImpl.KeyAndIv result = OpenSSLImpl.EVP_BytesToKey(keyLen, ivLen, digest, salt, pass, iter);
  558. this.key = result.getKey();
  559. this.realIV = result.getIv();
  560. this.orgIV = this.realIV;
  561. doInitialize();
  562. return getRuntime().getNil();
  563. }
  564. private void doInitialize() {
  565. if (DEBUG) {
  566. System.out.println("*** doInitialize");
  567. dumpVars();
  568. }
  569. checkInitialized();
  570. if (key == null) {
  571. throw newCipherError(getRuntime(), "key not specified");
  572. }
  573. try {
  574. if (!"ECB".equalsIgnoreCase(cryptoMode)) {
  575. if (this.realIV == null) {
  576. // no IV yet, start out with all zeros
  577. this.realIV = new byte[ivLen];
  578. }
  579. if ("RC2".equalsIgnoreCase(cryptoBase)) {
  580. this.ciph.init(encryptMode ? javax.crypto.Cipher.ENCRYPT_MODE : javax.crypto.Cipher.DECRYPT_MODE, new SimpleSecretKey("RC2", this.key), new RC2ParameterSpec(this.key.length * 8, this.realIV));
  581. } else if ("RC4".equalsIgnoreCase(cryptoBase)) {
  582. this.ciph.init(encryptMode ? javax.crypto.Cipher.ENCRYPT_MODE : javax.crypto.Cipher.DECRYPT_MODE, new SimpleSecretKey("RC4", this.key));
  583. } else {
  584. this.ciph.init(encryptMode ? javax.crypto.Cipher.ENCRYPT_MODE : javax.crypto.Cipher.DECRYPT_MODE, new SimpleSecretKey(realName.split("/")[0], this.key), new IvParameterSpec(this.realIV));
  585. }
  586. } else {
  587. this.ciph.init(encryptMode ? javax.crypto.Cipher.ENCRYPT_MODE : javax.crypto.Cipher.DECRYPT_MODE, new SimpleSecretKey(realName.split("/")[0], this.key));
  588. }
  589. } catch (java.security.InvalidKeyException ike) {
  590. throw newCipherError(getRuntime(), ike.getMessage() + ": possibly you need to install Java Cryptography Extension (JCE) Unlimited Strength Jurisdiction Policy Files for your JRE");
  591. } catch (Exception e) {
  592. if (DEBUG) {
  593. e.printStackTrace();
  594. }
  595. throw newCipherError(getRuntime(), e.getMessage());
  596. }
  597. ciphInited = true;
  598. }
  599. private byte[] lastIv = null;
  600. @JRubyMethod
  601. public IRubyObject update(IRubyObject data) {
  602. if (DEBUG) {
  603. System.out.println("*** update [" + data + "]");
  604. }
  605. checkInitialized();
  606. byte[] val = data.convertToString().getBytes();
  607. if (val.length == 0) {
  608. throw getRuntime().newArgumentError("data must not be empty");
  609. }
  610. if (!ciphInited) {
  611. if (DEBUG) {
  612. System.out.println("BEFORE INITING");
  613. }
  614. doInitialize();
  615. if (DEBUG) {
  616. System.out.println("AFTER INITING");
  617. }
  618. }
  619. byte[] str = new byte[0];
  620. try {
  621. byte[] out = ciph.update(val);
  622. if (out != null) {
  623. str = out;
  624. if (this.realIV != null) {
  625. if (lastIv == null) {
  626. lastIv = new byte[ivLen];
  627. }
  628. byte[] tmpIv = encryptMode ? out : val;
  629. if (tmpIv.length >= ivLen) {
  630. System.arraycopy(tmpIv, tmpIv.length - ivLen, lastIv, 0, ivLen);
  631. }
  632. }
  633. }
  634. } catch (Exception e) {
  635. if (DEBUG) {
  636. e.printStackTrace();
  637. }
  638. throw newCipherError(getRuntime(), e.getMessage());
  639. }
  640. return getRuntime().newString(new ByteList(str, false));
  641. }
  642. @JRubyMethod(name = "<<")
  643. public IRubyObject update_deprecated(IRubyObject data) {
  644. getRuntime().getWarnings().warn(IRubyWarnings.ID.DEPRECATED_METHOD, "" + this.getMetaClass().getRealClass().getName() + "#<< is deprecated; use " + this.getMetaClass().getRealClass().getName() + "#update instead");
  645. return update(data);
  646. }
  647. @JRubyMethod(name = "final")
  648. public IRubyObject _final() {
  649. checkInitialized();
  650. if (!ciphInited) {
  651. doInitialize();
  652. }
  653. // trying to allow update after final like cruby-openssl. Bad idea.
  654. if ("RC4".equalsIgnoreCase(cryptoBase)) {
  655. return getRuntime().newString("");
  656. }
  657. ByteList str = new ByteList(ByteList.NULL_ARRAY);
  658. try {
  659. byte[] out = ciph.doFinal();
  660. if (out != null) {
  661. str = new ByteList(out, false);
  662. // TODO: Modifying this line appears to fix the issue, but I do
  663. // not have a good reason for why. Best I can tell, lastIv needs
  664. // to be set regardless of encryptMode, so we'll go with this
  665. // for now. JRUBY-3335.
  666. //if(this.realIV != null && encryptMode) {
  667. if (this.realIV != null) {
  668. if (lastIv == null) {
  669. lastIv = new byte[ivLen];
  670. }
  671. byte[] tmpIv = out;
  672. if (tmpIv.length >= ivLen) {
  673. System.arraycopy(tmpIv, tmpIv.length - ivLen, lastIv, 0, ivLen);
  674. }
  675. }
  676. }
  677. if (this.realIV != null) {
  678. this.realIV = lastIv;
  679. doInitialize();
  680. }
  681. } catch (Exception e) {
  682. throw newCipherError(getRuntime(), e.getMessage());
  683. }
  684. return getRuntime().newString(str);
  685. }
  686. @JRubyMethod(name = "padding=")
  687. public IRubyObject set_padding(IRubyObject padding) {
  688. updateCipher(name, padding.toString());
  689. return padding;
  690. }
  691. String getAlgorithm() {
  692. return this.ciph.getAlgorithm();
  693. }
  694. String getName() {
  695. return this.name;
  696. }
  697. String getCryptoBase() {
  698. return this.cryptoBase;
  699. }
  700. String getCryptoMode() {
  701. return this.cryptoMode;
  702. }
  703. int getKeyLen() {
  704. return keyLen;
  705. }
  706. int getGenerateKeyLen() {
  707. return (generateKeyLen == -1) ? keyLen : generateKeyLen;
  708. }
  709. private void checkInitialized() {
  710. if (ciph == null) {
  711. throw getRuntime().newRuntimeError("Cipher not inititalized!");
  712. }
  713. }
  714. private boolean isStreamCipher() {
  715. return ciph.getBlockSize() == 0;
  716. }
  717. private static RaiseException newCipherError(Ruby runtime, String message) {
  718. return Utils.newError(runtime, "OpenSSL::Cipher::CipherError", message);
  719. }
  720. }