Skip to content

Commit e6dcdcf

Browse files
committed
Merge pull request #83 from philr/set_dsa_private_key_from_params
Allow DSA private keys to be initialized from parameters
2 parents b0cffd6 + d219115 commit e6dcdcf

File tree

3 files changed

+167
-54
lines changed

3 files changed

+167
-54
lines changed

src/main/java/org/jruby/ext/openssl/PKeyDSA.java

Lines changed: 91 additions & 54 deletions
Original file line numberDiff line numberDiff line change
@@ -42,6 +42,7 @@
4242
import java.security.interfaces.DSAKey;
4343
import java.security.interfaces.DSAPrivateKey;
4444
import java.security.interfaces.DSAPublicKey;
45+
import java.security.spec.DSAPrivateKeySpec;
4546
import java.security.spec.DSAPublicKeySpec;
4647
import java.security.spec.InvalidKeySpecException;
4748

@@ -111,6 +112,7 @@ public PKeyDSA(Ruby runtime, RubyClass type, DSAPrivateKey privKey, DSAPublicKey
111112
// a public key to be constructed incrementally, as required by the
112113
// current implementation of Net::SSH.
113114
// (see net-ssh-1.1.2/lib/net/ssh/transport/ossl/buffer.rb #read_keyblob)
115+
private transient volatile BigInteger dsa_x;
114116
private transient volatile BigInteger dsa_y;
115117
private transient volatile BigInteger dsa_p;
116118
private transient volatile BigInteger dsa_q;
@@ -368,85 +370,96 @@ public IRubyObject sysverify(IRubyObject arg, IRubyObject arg2) {
368370
return getRuntime().getNil();
369371
}
370372

371-
@JRubyMethod(name = "p")
372-
public synchronized IRubyObject get_p() {
373-
// FIXME: return only for public?
374-
DSAKey key; BigInteger param;
375-
if ((key = this.publicKey) != null || (key = this.privateKey) != null) {
376-
if ((param = key.getParams().getP()) != null) {
377-
return BN.newBN(getRuntime(), param);
378-
}
373+
private DSAKey getDsaKey() {
374+
DSAKey result;
375+
return (result = publicKey) != null ? result : privateKey;
376+
}
377+
378+
private IRubyObject toBN(BigInteger value) {
379+
return value == null ? getRuntime().getNil() : BN.newBN(getRuntime(), value);
380+
}
381+
382+
private synchronized BigInteger getP() {
383+
DSAKey key = getDsaKey();
384+
if (key != null) {
385+
return key.getParams().getP();
379386
}
380-
else if (dsa_p != null) {
381-
return BN.newBN(getRuntime(), dsa_p);
387+
else {
388+
return dsa_p;
382389
}
383-
return getRuntime().getNil();
390+
}
391+
392+
@JRubyMethod(name = "p")
393+
public IRubyObject get_p() {
394+
return toBN(getP());
384395
}
385396

386397
@JRubyMethod(name = "p=")
387398
public synchronized IRubyObject set_p(IRubyObject p) {
388399
return setKeySpecComponent(SPEC_P, p);
389400
}
390401

391-
@JRubyMethod(name = "q")
392-
public synchronized IRubyObject get_q() {
393-
// FIXME: return only for public?
394-
DSAKey key; BigInteger param;
395-
if ((key = this.publicKey) != null || (key = this.privateKey) != null) {
396-
if ((param = key.getParams().getQ()) != null) {
397-
return BN.newBN(getRuntime(), param);
398-
}
402+
private synchronized BigInteger getQ() {
403+
DSAKey key = getDsaKey();
404+
if (key != null) {
405+
return key.getParams().getQ();
399406
}
400-
else if (dsa_q != null) {
401-
return BN.newBN(getRuntime(), dsa_q);
407+
else {
408+
return dsa_q;
402409
}
403-
return getRuntime().getNil();
410+
}
411+
412+
@JRubyMethod(name = "q")
413+
public IRubyObject get_q() {
414+
return toBN(getQ());
404415
}
405416

406417
@JRubyMethod(name = "q=")
407418
public synchronized IRubyObject set_q(IRubyObject q) {
408419
return setKeySpecComponent(SPEC_Q, q);
409420
}
410421

411-
@JRubyMethod(name = "g")
412-
public synchronized IRubyObject get_g() {
413-
// FIXME: return only for public?
414-
DSAKey key; BigInteger param;
415-
if ((key = this.publicKey) != null || (key = this.privateKey) != null) {
416-
if ((param = key.getParams().getG()) != null) {
417-
return BN.newBN(getRuntime(), param);
418-
}
422+
private synchronized BigInteger getG() {
423+
DSAKey key = getDsaKey();
424+
if (key != null) {
425+
return key.getParams().getG();
419426
}
420-
else if (dsa_g != null) {
421-
return BN.newBN(getRuntime(), dsa_g);
427+
else {
428+
return dsa_g;
422429
}
423-
return getRuntime().getNil();
430+
}
431+
432+
@JRubyMethod(name = "g")
433+
public IRubyObject get_g() {
434+
return toBN(getG());
424435
}
425436

426437
@JRubyMethod(name = "g=")
427438
public synchronized IRubyObject set_g(IRubyObject g) {
428439
return setKeySpecComponent(SPEC_G, g);
429440
}
430441

431-
@JRubyMethod(name = "pub_key")
432-
public synchronized IRubyObject get_pub_key() {
433-
DSAPublicKey key;
434-
if ( ( key = this.publicKey ) != null ) {
435-
return BN.newBN(getRuntime(), key.getY());
436-
}
437-
else if (dsa_y != null) {
438-
return BN.newBN(getRuntime(), dsa_y);
439-
}
440-
return getRuntime().getNil();
441-
}
442-
443442
@JRubyMethod(name = "priv_key")
444443
public synchronized IRubyObject get_priv_key() {
445444
DSAPrivateKey key;
446445
if ((key = this.privateKey) != null) {
447-
return BN.newBN(getRuntime(), key.getX());
446+
return toBN(key.getX());
448447
}
449-
return getRuntime().getNil();
448+
return toBN(dsa_x);
449+
}
450+
451+
@JRubyMethod(name = "priv_key=")
452+
public synchronized IRubyObject set_priv_key(IRubyObject priv_key) {
453+
return setKeySpecComponent(SPEC_X, priv_key);
454+
}
455+
456+
@JRubyMethod(name = "pub_key")
457+
public synchronized IRubyObject get_pub_key() {
458+
DSAPublicKey key;
459+
if ( ( key = this.publicKey ) != null ) {
460+
return toBN(key.getY());
461+
}
462+
return toBN(dsa_y);
450463
}
451464

452465
@JRubyMethod(name = "pub_key=")
@@ -458,15 +471,38 @@ private IRubyObject setKeySpecComponent(final int index, final IRubyObject value
458471
final BigInteger val = BN.getBigInteger(value);
459472

460473
switch (index) {
474+
case SPEC_X: this.dsa_x = val; break;
461475
case SPEC_Y: this.dsa_y = val; break;
462476
case SPEC_P: this.dsa_p = val; break;
463477
case SPEC_Q: this.dsa_q = val; break;
464478
case SPEC_G: this.dsa_g = val; break;
465479
}
466480

467-
if ( dsa_y != null && dsa_p != null && dsa_q != null && dsa_g != null ) {
468-
// we now have all components. create the key :
469-
DSAPublicKeySpec spec = new DSAPublicKeySpec(dsa_y, dsa_p, dsa_q, dsa_g);
481+
// Don't access the dsa_p, dsa_q and dsa_g fields directly. They may
482+
// have already been consumed and cleared.
483+
BigInteger _dsa_p = getP();
484+
BigInteger _dsa_q = getQ();
485+
BigInteger _dsa_g = getG();
486+
487+
if ( dsa_x != null && _dsa_p != null && _dsa_q != null && _dsa_g != null ) {
488+
// we now have all private key components. create the key :
489+
DSAPrivateKeySpec spec = new DSAPrivateKeySpec(dsa_x, _dsa_p, _dsa_q, _dsa_g);
490+
try {
491+
this.privateKey = (DSAPrivateKey) SecurityHelper.getKeyFactory("DSA").generatePrivate(spec);
492+
}
493+
catch (InvalidKeySpecException e) {
494+
throw newDSAError(getRuntime(), "invalid keyspec", e);
495+
}
496+
catch (NoSuchAlgorithmException e) {
497+
throw newDSAError(getRuntime(), "unsupported key algorithm (DSA)", e);
498+
}
499+
// clear out the specValues
500+
this.dsa_x = this.dsa_p = this.dsa_q = this.dsa_g = null;
501+
}
502+
503+
if ( dsa_y != null && _dsa_p != null && _dsa_q != null && _dsa_g != null ) {
504+
// we now have all public key components. create the key :
505+
DSAPublicKeySpec spec = new DSAPublicKeySpec(dsa_y, _dsa_p, _dsa_q, _dsa_g);
470506
try {
471507
this.publicKey = (DSAPublicKey) SecurityHelper.getKeyFactory("DSA").generatePublic(spec);
472508
}
@@ -483,10 +519,11 @@ private IRubyObject setKeySpecComponent(final int index, final IRubyObject value
483519
return value;
484520
}
485521

486-
private static final int SPEC_Y = 0;
487-
private static final int SPEC_P = 1;
488-
private static final int SPEC_Q = 2;
489-
private static final int SPEC_G = 3;
522+
private static final int SPEC_X = 0;
523+
private static final int SPEC_Y = 1;
524+
private static final int SPEC_P = 2;
525+
private static final int SPEC_Q = 3;
526+
private static final int SPEC_G = 4;
490527

491528
public static RaiseException newDSAError(Ruby runtime, String message) {
492529
return Utils.newError(runtime, _PKey(runtime).getClass("DSAError"), message);

src/test/ruby/dsa/private_key.pem

Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,12 @@
1+
-----BEGIN DSA PRIVATE KEY-----
2+
MIIBugIBAAKBgQDpdW60slBJrsrXrsputlqXFlT70CA0czpJZWbppiv4fed941TN
3+
/v/ICLjrNcsWXMbU5hb4faPrMZpAbUuIK+tMtJzz7sWMiINtso1FlQE/sUYBFqCv
4+
Tmkj52N0dPGsE7qmZ6ZknaJn6DbrAL569+5NIe9CR6cTtwL4IPWVXT3HQQIVAJqv
5+
o3Yrj85paNmGTIZfVt/oymAFAoGAb+S//7DQc6S/AK6r26BpQ/C4swSx1MTSl490
6+
hBJw0Czns5djqz9QB6ELufshGES1gcDGrYIncxQTGw1tPoJVrA+kefPVbRaYs2qM
7+
HasEfM1GfILfu4XDBB4xAoFryjKizOu8MwEXTsPLiTe9MdiT90NfcgSyIty1FgFP
8+
ZSz0JLMCgYBmAYli6D4DGB05NCVXBWPiu42c78gGCgibrbvCXpozB22TdMWA41ho
9+
7Oy7diBJLuJPUdmSsK++RE0bxlDl6QfmxTqfdb0ZUZ4u2bC9VeSM8ZtkbtxRpJGU
10+
p6znJdL83f05H2bhkKWI6a+vj894wRbtj+ube2UZPgKHFvkgdv732QIUKPp2Kkq+
11+
UQDoq2xu7v84G0sFIhc=
12+
-----END DSA PRIVATE KEY-----

src/test/ruby/dsa/test_dsa.rb

Lines changed: 64 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,64 @@
1+
# coding: US-ASCII
2+
require File.expand_path('../test_helper', File.dirname(__FILE__))
3+
4+
class TestDsa < TestCase
5+
6+
def setup
7+
super
8+
self.class.disable_security_restrictions!
9+
require 'base64'
10+
end
11+
12+
def test_dsa_param_accessors
13+
key_file = File.join(File.dirname(__FILE__), 'private_key.pem')
14+
key = OpenSSL::PKey::DSA.new(File.read(key_file))
15+
16+
[:priv_key, :pub_key, :p, :q, :g].each do |param|
17+
dsa = OpenSSL::PKey::DSA.new
18+
assert_nil(dsa.send(param))
19+
value = key.send(param)
20+
dsa.send("#{param}=", value)
21+
assert_equal(value, dsa.send(param), param)
22+
end
23+
end
24+
25+
def test_dsa_from_params_private_first
26+
key_file = File.join(File.dirname(__FILE__), 'private_key.pem')
27+
key = OpenSSL::PKey::DSA.new(File.read(key_file))
28+
29+
dsa = OpenSSL::PKey::DSA.new
30+
dsa.priv_key, dsa.p, dsa.q, dsa.g = key.priv_key, key.p, key.q, key.g
31+
assert(dsa.private?)
32+
assert(!dsa.public?)
33+
[:priv_key, :p, :q, :g].each do |param|
34+
assert_equal(key.send(param), dsa.send(param), param)
35+
end
36+
37+
dsa.pub_key = key.pub_key
38+
assert(dsa.private?)
39+
assert(dsa.public?)
40+
[:priv_key, :pub_key, :p, :q, :g].each do |param|
41+
assert_equal(key.send(param), dsa.send(param), param)
42+
end
43+
end
44+
45+
def test_dsa_from_params_public_first
46+
key_file = File.join(File.dirname(__FILE__), 'private_key.pem')
47+
key = OpenSSL::PKey::DSA.new(File.read(key_file))
48+
49+
dsa = OpenSSL::PKey::DSA.new
50+
dsa.pub_key, dsa.p, dsa.q, dsa.g = key.pub_key, key.p, key.q, key.g
51+
assert(!dsa.private?)
52+
assert(dsa.public?)
53+
[:pub_key, :p, :q, :g].each do |param|
54+
assert_equal(key.send(param), dsa.send(param), param)
55+
end
56+
57+
dsa.priv_key = key.priv_key
58+
assert(dsa.private?)
59+
assert(dsa.public?)
60+
[:priv_key, :pub_key, :p, :q, :g].each do |param|
61+
assert_equal(key.send(param), dsa.send(param), param)
62+
end
63+
end
64+
end

0 commit comments

Comments
 (0)