PageRenderTime 3620ms CodeModel.GetById 19ms RepoModel.GetById 2ms app.codeStats 0ms

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

https://gitlab.com/essere.lab.public/qualitas.class-corpus
Java | 502 lines | 400 code | 44 blank | 58 comment | 99 complexity | 34303ccd54d61bc6e9b4eab3dada3732 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. * Copyright (C) 2007 Wiliam N Dortch <bill.dortch@gmail.com>
  16. *
  17. * Alternatively, the contents of this file may be used under the terms of
  18. * either of the GNU General Public License Version 2 or later (the "GPL"),
  19. * or the GNU Lesser General Public License Version 2.1 or later (the "LGPL"),
  20. * in which case the provisions of the GPL or the LGPL are applicable instead
  21. * of those above. If you wish to allow use of your version of this file only
  22. * under the terms of either the GPL or the LGPL, and not to allow others to
  23. * use your version of this file under the terms of the EPL, indicate your
  24. * decision by deleting the provisions above and replace them with the notice
  25. * and other provisions required by the GPL or the LGPL. If you do not delete
  26. * the provisions above, a recipient may use your version of this file under
  27. * the terms of any one of the EPL, the GPL or the LGPL.
  28. ***** END LICENSE BLOCK *****/
  29. package org.jruby.ext.openssl;
  30. import java.io.IOException;
  31. import java.io.StringReader;
  32. import java.io.StringWriter;
  33. import java.math.BigInteger;
  34. import java.security.KeyFactory;
  35. import java.security.KeyPair;
  36. import java.security.KeyPairGenerator;
  37. import java.security.NoSuchAlgorithmException;
  38. import java.security.PrivateKey;
  39. import java.security.PublicKey;
  40. import java.security.SecureRandom;
  41. import java.security.interfaces.DSAKey;
  42. import java.security.interfaces.DSAPrivateKey;
  43. import java.security.interfaces.DSAPublicKey;
  44. import java.security.spec.DSAPublicKeySpec;
  45. import java.security.spec.InvalidKeySpecException;
  46. import java.security.spec.PKCS8EncodedKeySpec;
  47. import java.security.spec.X509EncodedKeySpec;
  48. import org.jruby.Ruby;
  49. import org.jruby.RubyClass;
  50. import org.jruby.RubyFixnum;
  51. import org.jruby.RubyModule;
  52. import org.jruby.RubyNumeric;
  53. import org.jruby.RubyString;
  54. import org.jruby.anno.JRubyMethod;
  55. import org.jruby.exceptions.RaiseException;
  56. import org.jruby.ext.openssl.impl.CipherSpec;
  57. import org.jruby.ext.openssl.x509store.PEMInputOutput;
  58. import org.jruby.runtime.ObjectAllocator;
  59. import org.jruby.runtime.builtin.IRubyObject;
  60. /**
  61. * @author <a href="mailto:ola.bini@ki.se">Ola Bini</a>
  62. */
  63. public class PKeyDSA extends PKey {
  64. private static final long serialVersionUID = 2359742219218350277L;
  65. private static ObjectAllocator PKEYDSA_ALLOCATOR = new ObjectAllocator() {
  66. public IRubyObject allocate(Ruby runtime, RubyClass klass) {
  67. return new PKeyDSA(runtime, klass);
  68. }
  69. };
  70. public static void createPKeyDSA(Ruby runtime, RubyModule mPKey) {
  71. RubyClass cDSA = mPKey.defineClassUnder("DSA",mPKey.getClass("PKey"),PKEYDSA_ALLOCATOR);
  72. RubyClass pkeyError = mPKey.getClass("PKeyError");
  73. mPKey.defineClassUnder("DSAError",pkeyError,pkeyError.getAllocator());
  74. cDSA.defineAnnotatedMethods(PKeyDSA.class);
  75. }
  76. public static RaiseException newDSAError(Ruby runtime, String message) {
  77. return Utils.newError(runtime, "OpenSSL::PKey::DSAError", message);
  78. }
  79. public PKeyDSA(Ruby runtime, RubyClass type) {
  80. super(runtime, type);
  81. }
  82. public PKeyDSA(Ruby runtime, RubyClass type, DSAPrivateKey privKey, DSAPublicKey pubKey) {
  83. super(runtime, type);
  84. this.privKey = privKey;
  85. this.pubKey = pubKey;
  86. }
  87. public PKeyDSA(Ruby runtime, RubyClass type, DSAPublicKey pubKey) {
  88. this(runtime, type, null, pubKey);
  89. }
  90. private DSAPrivateKey privKey;
  91. private DSAPublicKey pubKey;
  92. // specValues holds individual DSAPublicKeySpec components. this allows
  93. // a public key to be constructed incrementally, as required by the
  94. // current implementation of Net::SSH.
  95. // (see net-ssh-1.1.2/lib/net/ssh/transport/ossl/buffer.rb #read_keyblob)
  96. private BigInteger[] specValues;
  97. private static final int SPEC_Y = 0;
  98. private static final int SPEC_P = 1;
  99. private static final int SPEC_Q = 2;
  100. private static final int SPEC_G = 3;
  101. @Override
  102. PublicKey getPublicKey() {
  103. return pubKey;
  104. }
  105. @Override
  106. PrivateKey getPrivateKey() {
  107. return privKey;
  108. }
  109. @Override
  110. String getAlgorithm() {
  111. return "DSA";
  112. }
  113. @JRubyMethod(name = "generate", meta = true)
  114. public static IRubyObject generate(IRubyObject recv, IRubyObject arg) {
  115. int keysize = RubyNumeric.fix2int(arg);
  116. PKeyDSA dsa = new PKeyDSA(recv.getRuntime(), (RubyClass) recv);
  117. dsaGenerate(dsa, keysize);
  118. return dsa;
  119. }
  120. /*
  121. * c: dsa_generate
  122. */
  123. private static void dsaGenerate(PKeyDSA dsa, int keysize) throws RaiseException {
  124. try {
  125. KeyPairGenerator gen = KeyPairGenerator.getInstance("DSA");
  126. gen.initialize(keysize, new SecureRandom());
  127. KeyPair pair = gen.generateKeyPair();
  128. dsa.privKey = (DSAPrivateKey) (pair.getPrivate());
  129. dsa.pubKey = (DSAPublicKey) (pair.getPublic());
  130. } catch (Exception e) {
  131. throw newDSAError(dsa.getRuntime(), e.getMessage());
  132. }
  133. }
  134. @JRubyMethod(rest = true)
  135. public IRubyObject initialize(IRubyObject[] args) {
  136. IRubyObject arg;
  137. IRubyObject pass = null;
  138. char[] passwd = null;
  139. if (org.jruby.runtime.Arity.checkArgumentCount(getRuntime(), args, 0, 2) == 0) {
  140. privKey = null;
  141. pubKey = null;
  142. } else {
  143. arg = args[0];
  144. if (args.length > 1) {
  145. pass = args[1];
  146. }
  147. if (arg instanceof RubyFixnum) {
  148. int keysize = RubyNumeric.fix2int(arg);
  149. dsaGenerate(this, keysize);
  150. } else {
  151. if (pass != null && !pass.isNil()) {
  152. passwd = pass.toString().toCharArray();
  153. }
  154. arg = OpenSSLImpl.to_der_if_possible(arg);
  155. RubyString str = arg.convertToString();
  156. Object val = null;
  157. KeyFactory fact = null;
  158. try {
  159. fact = KeyFactory.getInstance("DSA");
  160. } catch (NoSuchAlgorithmException e) {
  161. throw getRuntime().newLoadError("unsupported key algorithm (DSA)");
  162. }
  163. // TODO: ugly NoClassDefFoundError catching for no BC env. How can we remove this?
  164. if (null == val) {
  165. // PEM_read_bio_DSAPrivateKey
  166. try {
  167. val = PEMInputOutput.readDSAPrivateKey(new StringReader(str.toString()), passwd);
  168. } catch (NoClassDefFoundError e) {
  169. val = null;
  170. } catch (Exception e) {
  171. val = null;
  172. }
  173. }
  174. if (null == val) {
  175. // PEM_read_bio_DSAPublicKey
  176. try {
  177. val = PEMInputOutput.readDSAPublicKey(new StringReader(str.toString()), passwd);
  178. } catch (NoClassDefFoundError e) {
  179. val = null;
  180. } catch (Exception e) {
  181. val = null;
  182. }
  183. }
  184. if (null == val) {
  185. // PEM_read_bio_DSA_PUBKEY
  186. try {
  187. val = PEMInputOutput.readDSAPubKey(new StringReader(str.toString()));
  188. } catch (NoClassDefFoundError e) {
  189. val = null;
  190. } catch (Exception e) {
  191. val = null;
  192. }
  193. }
  194. if (null == val) {
  195. // d2i_DSAPrivateKey_bio
  196. try {
  197. val = org.jruby.ext.openssl.impl.PKey.readDSAPrivateKey(str.getBytes());
  198. } catch (NoClassDefFoundError e) {
  199. val = null;
  200. } catch (Exception e) {
  201. val = null;
  202. }
  203. }
  204. if (null == val) {
  205. // d2i_DSA_PUBKEY_bio
  206. try {
  207. val = org.jruby.ext.openssl.impl.PKey.readDSAPublicKey(str.getBytes());
  208. } catch (NoClassDefFoundError e) {
  209. val = null;
  210. } catch (Exception e) {
  211. val = null;
  212. }
  213. }
  214. if (null == val) {
  215. try {
  216. val = fact.generatePrivate(new PKCS8EncodedKeySpec(str.getBytes()));
  217. } catch (Exception e) {
  218. val = null;
  219. }
  220. }
  221. if (null == val) {
  222. try {
  223. val = fact.generatePublic(new X509EncodedKeySpec(str.getBytes()));
  224. } catch (Exception e) {
  225. val = null;
  226. }
  227. }
  228. if (null == val) {
  229. throw newDSAError(getRuntime(), "Neither PUB key nor PRIV key:");
  230. }
  231. if (val instanceof KeyPair) {
  232. PrivateKey privateKey = ((KeyPair) val).getPrivate();
  233. PublicKey publicKey = ((KeyPair) val).getPublic();
  234. if (privateKey instanceof DSAPrivateKey) {
  235. privKey = (DSAPrivateKey) privateKey;
  236. pubKey = (DSAPublicKey) publicKey;
  237. } else {
  238. throw newDSAError(getRuntime(), "Neither PUB key nor PRIV key:");
  239. }
  240. } else if (val instanceof DSAPrivateKey) {
  241. privKey = (DSAPrivateKey) val;
  242. } else if (val instanceof DSAPublicKey) {
  243. pubKey = (DSAPublicKey) val;
  244. privKey = null;
  245. } else {
  246. throw newDSAError(getRuntime(), "Neither PUB key nor PRIV key:");
  247. }
  248. }
  249. }
  250. return this;
  251. }
  252. @JRubyMethod(name="public?")
  253. public IRubyObject public_p() {
  254. return pubKey != null ? getRuntime().getTrue() : getRuntime().getFalse();
  255. }
  256. @JRubyMethod(name="private?")
  257. public IRubyObject private_p() {
  258. return privKey != null ? getRuntime().getTrue() : getRuntime().getFalse();
  259. }
  260. @JRubyMethod
  261. public IRubyObject to_der() {
  262. try {
  263. byte[] bytes = org.jruby.ext.openssl.impl.PKey.toDerDSAKey(pubKey, privKey);
  264. return RubyString.newString(getRuntime(), bytes);
  265. } catch (NoClassDefFoundError ncdfe) {
  266. throw newDSAError(getRuntime(), OpenSSLReal.bcExceptionMessage(ncdfe));
  267. } catch (IOException ioe) {
  268. throw newDSAError(getRuntime(), ioe.getMessage());
  269. }
  270. }
  271. @JRubyMethod
  272. public IRubyObject to_text() {
  273. StringBuilder result = new StringBuilder();
  274. if (privKey != null) {
  275. int len = privKey.getParams().getP().bitLength();
  276. result.append("Private-Key: (").append(len).append(" bit)").append("\n");
  277. result.append("priv:");
  278. addSplittedAndFormatted(result, privKey.getX());
  279. }
  280. result.append("pub:");
  281. addSplittedAndFormatted(result, pubKey.getY());
  282. result.append("P:");
  283. addSplittedAndFormatted(result, pubKey.getParams().getP());
  284. result.append("Q:");
  285. addSplittedAndFormatted(result, pubKey.getParams().getQ());
  286. result.append("G:");
  287. addSplittedAndFormatted(result, pubKey.getParams().getG());
  288. return getRuntime().newString(result.toString());
  289. }
  290. @JRubyMethod
  291. public IRubyObject public_key() {
  292. PKeyDSA val = new PKeyDSA(getRuntime(),getMetaClass().getRealClass());
  293. val.privKey = null;
  294. val.pubKey = this.pubKey;
  295. return val;
  296. }
  297. @JRubyMethod(name = { "export", "to_pem", "to_s" }, rest = true)
  298. public IRubyObject export(IRubyObject[] args) {
  299. StringWriter w = new StringWriter();
  300. org.jruby.runtime.Arity.checkArgumentCount(getRuntime(), args, 0, 2);
  301. CipherSpec ciph = null;
  302. char[] passwd = null;
  303. if (args.length > 0 && !args[0].isNil()) {
  304. org.jruby.ext.openssl.Cipher c = (org.jruby.ext.openssl.Cipher) args[0];
  305. ciph = new CipherSpec(c.getCipher(), c.getName(), c.getKeyLen() * 8);
  306. if (args.length > 1 && !args[1].isNil()) {
  307. passwd = args[1].toString().toCharArray();
  308. }
  309. }
  310. try {
  311. if (privKey != null) {
  312. PEMInputOutput.writeDSAPrivateKey(w, privKey, ciph, passwd);
  313. } else {
  314. PEMInputOutput.writeDSAPublicKey(w, pubKey);
  315. }
  316. w.close();
  317. return getRuntime().newString(w.toString());
  318. } catch (NoClassDefFoundError ncdfe) {
  319. throw newDSAError(getRuntime(), OpenSSLReal.bcExceptionMessage(ncdfe));
  320. } catch (IOException ioe) {
  321. throw newDSAError(getRuntime(), ioe.getMessage());
  322. }
  323. }
  324. @JRubyMethod
  325. public IRubyObject syssign(IRubyObject arg) {
  326. // TODO
  327. return getRuntime().getNil();
  328. }
  329. @JRubyMethod
  330. public IRubyObject sysverify(IRubyObject arg, IRubyObject arg2) {
  331. // TODO
  332. return getRuntime().getNil();
  333. }
  334. @JRubyMethod(name="p")
  335. public synchronized IRubyObject get_p() {
  336. // FIXME: return only for public?
  337. DSAKey key;
  338. BigInteger param;
  339. if ((key = this.pubKey) != null || (key = this.privKey) != null) {
  340. if ((param = key.getParams().getP()) != null) {
  341. return BN.newBN(getRuntime(), param);
  342. }
  343. } else if (specValues != null) {
  344. if ((param = specValues[SPEC_P]) != null) {
  345. return BN.newBN(getRuntime(), param);
  346. }
  347. }
  348. return getRuntime().getNil();
  349. }
  350. @JRubyMethod(name="p=")
  351. public synchronized IRubyObject set_p(IRubyObject p) {
  352. return setKeySpecComponent(SPEC_P, p);
  353. }
  354. @JRubyMethod(name="q")
  355. public synchronized IRubyObject get_q() {
  356. // FIXME: return only for public?
  357. DSAKey key;
  358. BigInteger param;
  359. if ((key = this.pubKey) != null || (key = this.privKey) != null) {
  360. if ((param = key.getParams().getQ()) != null) {
  361. return BN.newBN(getRuntime(), param);
  362. }
  363. } else if (specValues != null) {
  364. if ((param = specValues[SPEC_Q]) != null) {
  365. return BN.newBN(getRuntime(), param);
  366. }
  367. }
  368. return getRuntime().getNil();
  369. }
  370. @JRubyMethod(name="q=")
  371. public synchronized IRubyObject set_q(IRubyObject q) {
  372. return setKeySpecComponent(SPEC_Q, q);
  373. }
  374. @JRubyMethod(name="g")
  375. public synchronized IRubyObject get_g() {
  376. // FIXME: return only for public?
  377. DSAKey key;
  378. BigInteger param;
  379. if ((key = this.pubKey) != null || (key = this.privKey) != null) {
  380. if ((param = key.getParams().getG()) != null) {
  381. return BN.newBN(getRuntime(), param);
  382. }
  383. } else if (specValues != null) {
  384. if ((param = specValues[SPEC_G]) != null) {
  385. return BN.newBN(getRuntime(), param);
  386. }
  387. }
  388. return getRuntime().getNil();
  389. }
  390. @JRubyMethod(name="g=")
  391. public synchronized IRubyObject set_g(IRubyObject g) {
  392. return setKeySpecComponent(SPEC_G, g);
  393. }
  394. @JRubyMethod(name="pub_key")
  395. public synchronized IRubyObject get_pub_key() {
  396. DSAPublicKey key;
  397. BigInteger param;
  398. if ((key = this.pubKey) != null) {
  399. return BN.newBN(getRuntime(), key.getY());
  400. } else if (specValues != null) {
  401. if ((param = specValues[SPEC_Y]) != null) {
  402. return BN.newBN(getRuntime(), param);
  403. }
  404. }
  405. return getRuntime().getNil();
  406. }
  407. @JRubyMethod(name="priv_key")
  408. public synchronized IRubyObject get_priv_key() {
  409. DSAPrivateKey key;
  410. BigInteger param;
  411. if ((key = this.privKey) != null) {
  412. return BN.newBN(getRuntime(), key.getX());
  413. }
  414. return getRuntime().getNil();
  415. }
  416. @JRubyMethod(name="pub_key=")
  417. public synchronized IRubyObject set_pub_key(IRubyObject pub_key) {
  418. return setKeySpecComponent(SPEC_Y, pub_key);
  419. }
  420. private IRubyObject setKeySpecComponent(int index, IRubyObject value) {
  421. BigInteger[] vals;
  422. // illegal to set if we already have a key for this component
  423. // FIXME: allow changes after keys are created? MRI doesn't prevent it...
  424. if (this.pubKey != null || this.privKey != null ||
  425. (vals = this.specValues) != null && vals[index] != null) {
  426. throw newDSAError(getRuntime(), "illegal modification");
  427. }
  428. // get the BigInteger value
  429. BigInteger bival = BN.getBigInteger(value);
  430. if (vals != null) {
  431. // we already have some vals stored, store this one, too
  432. vals[index] = bival;
  433. // check to see if we have all values yet
  434. for (int i = vals.length; --i >= 0; ) {
  435. if (vals[i] == null) {
  436. // still missing components, return
  437. return value;
  438. }
  439. }
  440. // we now have all components. create the key.
  441. DSAPublicKeySpec spec = new DSAPublicKeySpec(vals[SPEC_Y], vals[SPEC_P], vals[SPEC_Q], vals[SPEC_G]);
  442. try {
  443. this.pubKey = (DSAPublicKey)KeyFactory.getInstance("DSA").generatePublic(spec);
  444. } catch (InvalidKeySpecException e) {
  445. throw newDSAError(getRuntime(), "invalid keyspec");
  446. } catch (NoSuchAlgorithmException e) {
  447. throw newDSAError(getRuntime(), "unsupported key algorithm (DSA)");
  448. }
  449. // clear out the specValues
  450. this.specValues = null;
  451. } else {
  452. // first value received, save
  453. this.specValues = new BigInteger[4];
  454. this.specValues[index] = bival;
  455. }
  456. return value;
  457. }
  458. }// PKeyDSA