Skip to content

Commit a014686

Browse files
committed
[fix] RSA private key should generate after set_key
far from complete but should improve RSA key behavior
1 parent 6a0bc28 commit a014686

File tree

3 files changed

+111
-60
lines changed

3 files changed

+111
-60
lines changed

lib/jopenssl/_compat23.rb

Lines changed: 0 additions & 24 deletions
Original file line numberDiff line numberDiff line change
@@ -42,30 +42,6 @@ def set_pqg(p, q, g)
4242

4343
end
4444

45-
class RSA
46-
47-
def set_key(n, e, d)
48-
self.n = n
49-
self.e = e
50-
self.d = d
51-
self
52-
end
53-
54-
def set_factors(p, q)
55-
self.p = p
56-
self.q = q
57-
self
58-
end
59-
60-
def set_crt_params(dmp1, dmq1, iqmp)
61-
self.dmp1 = dmp1
62-
self.dmq1 = dmq1
63-
self.iqmp = iqmp
64-
self
65-
end
66-
67-
end
68-
6945
end
7046

7147
end

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

Lines changed: 73 additions & 34 deletions
Original file line numberDiff line numberDiff line change
@@ -40,12 +40,13 @@
4040
import java.security.NoSuchAlgorithmException;
4141
import java.security.PrivateKey;
4242
import java.security.PublicKey;
43-
import java.security.SecureRandom;
4443
import java.security.interfaces.RSAPrivateCrtKey;
44+
import java.security.interfaces.RSAPrivateKey;
4545
import java.security.interfaces.RSAPublicKey;
4646
import java.security.spec.InvalidKeySpecException;
4747
import java.security.spec.RSAKeyGenParameterSpec;
4848
import java.security.spec.RSAPrivateCrtKeySpec;
49+
import java.security.spec.RSAPrivateKeySpec;
4950
import java.security.spec.RSAPublicKeySpec;
5051

5152
import static javax.crypto.Cipher.*;
@@ -108,7 +109,11 @@ public static RaiseException newRSAError(Ruby runtime, String message) {
108109
}
109110

110111
static RaiseException newRSAError(Ruby runtime, Throwable cause) {
111-
return Utils.newError(runtime, _PKey(runtime).getClass("RSAError"), cause.getMessage(), cause);
112+
return newRSAError(runtime, cause.getMessage(), cause);
113+
}
114+
115+
static RaiseException newRSAError(Ruby runtime, String message, Throwable cause) {
116+
return Utils.newError(runtime, _PKey(runtime).getClass("RSAError"), message, cause);
112117
}
113118

114119
public PKeyRSA(Ruby runtime, RubyClass type) {
@@ -126,7 +131,7 @@ public PKeyRSA(Ruby runtime, RubyClass type, RSAPrivateCrtKey privKey, RSAPublic
126131
}
127132

128133
private volatile RSAPublicKey publicKey;
129-
private volatile transient RSAPrivateCrtKey privateKey;
134+
private volatile transient RSAPrivateKey privateKey;
130135

131136
// fields to hold individual RSAPublicKeySpec components. this allows
132137
// a public key to be constructed incrementally, as required by the
@@ -317,8 +322,9 @@ public IRubyObject initialize(final ThreadContext context, final IRubyObject[] a
317322
}
318323
else if ( key instanceof RSAPrivateCrtKey ) {
319324
this.privateKey = (RSAPrivateCrtKey) key;
325+
BigInteger exponent = ((RSAPrivateCrtKey) key).getPublicExponent();
320326
try {
321-
this.publicKey = (RSAPublicKey) rsaFactory.generatePublic(new RSAPublicKeySpec(privateKey.getModulus(), privateKey.getPublicExponent()));
327+
this.publicKey = (RSAPublicKey) rsaFactory.generatePublic(new RSAPublicKeySpec(privateKey.getModulus(), exponent));
322328
} catch (GeneralSecurityException e) {
323329
throw newRSAError(runtime, e.getMessage());
324330
} catch (RuntimeException e) {
@@ -355,7 +361,7 @@ public RubyBoolean private_p() {
355361
public RubyString to_der() {
356362
final byte[] bytes;
357363
try {
358-
bytes = toDerRSAKey(publicKey, privateKey);
364+
bytes = toDerRSAKey(publicKey, privateKey instanceof RSAPrivateCrtKey ? (RSAPrivateCrtKey) privateKey : null);
359365
}
360366
catch (NoClassDefFoundError e) {
361367
throw newRSAError(getRuntime(), bcExceptionMessage(e));
@@ -380,7 +386,8 @@ public PKeyRSA public_key() {
380386
public IRubyObject params(final ThreadContext context) {
381387
final Ruby runtime = context.runtime;
382388
RubyHash hash = RubyHash.newHash(runtime);
383-
if ( privateKey != null ) {
389+
if (privateKey instanceof RSAPrivateCrtKey) {
390+
RSAPrivateCrtKey privateKey = (RSAPrivateCrtKey) this.privateKey;
384391
hash.op_aset(context, runtime.newString("iqmp"), BN.newBN(runtime, privateKey.getCrtCoefficient()));
385392
hash.op_aset(context, runtime.newString("n"), BN.newBN(runtime, privateKey.getModulus()));
386393
hash.op_aset(context, runtime.newString("d"), BN.newBN(runtime, privateKey.getPrivateExponent()));
@@ -406,7 +413,8 @@ public IRubyObject params(final ThreadContext context) {
406413
@JRubyMethod
407414
public RubyString to_text() {
408415
StringBuilder result = new StringBuilder();
409-
if (privateKey != null) {
416+
if (privateKey instanceof RSAPrivateCrtKey) {
417+
RSAPrivateCrtKey privateKey = (RSAPrivateCrtKey) this.privateKey;
410418
int len = privateKey.getModulus().bitLength();
411419
result.append("Private-Key: (").append(len).append(" bit)").append('\n');
412420
result.append("modulus:");
@@ -446,8 +454,8 @@ public RubyString to_pem(ThreadContext context, final IRubyObject[] args) {
446454

447455
try {
448456
final StringWriter writer = new StringWriter();
449-
if ( privateKey != null ) {
450-
PEMInputOutput.writeRSAPrivateKey(writer, privateKey, spec, passwd);
457+
if (privateKey instanceof RSAPrivateCrtKey) {
458+
PEMInputOutput.writeRSAPrivateKey(writer, (RSAPrivateCrtKey) privateKey, spec, passwd);
451459
}
452460
else {
453461
PEMInputOutput.writeRSAPublicKey(writer, publicKey);
@@ -603,8 +611,8 @@ public synchronized IRubyObject set_iqmp(final ThreadContext context, IRubyObjec
603611
@JRubyMethod(name="iqmp")
604612
public synchronized IRubyObject get_iqmp() {
605613
BigInteger iqmp;
606-
if (privateKey != null) {
607-
iqmp = privateKey.getCrtCoefficient();
614+
if (privateKey instanceof RSAPrivateCrtKey) {
615+
iqmp = ((RSAPrivateCrtKey) privateKey).getCrtCoefficient();
608616
} else {
609617
iqmp = rsa_iqmp;
610618
}
@@ -617,8 +625,8 @@ public synchronized IRubyObject get_iqmp() {
617625
@JRubyMethod(name="dmp1")
618626
public synchronized IRubyObject get_dmp1() {
619627
BigInteger dmp1;
620-
if (privateKey != null) {
621-
dmp1 = privateKey.getPrimeExponentP();
628+
if (privateKey instanceof RSAPrivateCrtKey) {
629+
dmp1 = ((RSAPrivateCrtKey) privateKey).getPrimeExponentP();
622630
} else {
623631
dmp1 = rsa_dmp1;
624632
}
@@ -631,8 +639,8 @@ public synchronized IRubyObject get_dmp1() {
631639
@JRubyMethod(name="dmq1")
632640
public synchronized IRubyObject get_dmq1() {
633641
BigInteger dmq1;
634-
if (privateKey != null) {
635-
dmq1 = privateKey.getPrimeExponentQ();
642+
if (privateKey instanceof RSAPrivateCrtKey) {
643+
dmq1 = ((RSAPrivateCrtKey) privateKey).getPrimeExponentQ();
636644
} else {
637645
dmq1 = rsa_dmq1;
638646
}
@@ -659,8 +667,8 @@ public synchronized IRubyObject get_d() {
659667
@JRubyMethod(name="p")
660668
public synchronized IRubyObject get_p() {
661669
BigInteger p;
662-
if (privateKey != null) {
663-
p = privateKey.getPrimeP();
670+
if (privateKey instanceof RSAPrivateCrtKey) {
671+
p = ((RSAPrivateCrtKey) privateKey).getPrimeP();
664672
} else {
665673
p = rsa_p;
666674
}
@@ -673,8 +681,8 @@ public synchronized IRubyObject get_p() {
673681
@JRubyMethod(name="q")
674682
public synchronized IRubyObject get_q() {
675683
BigInteger q;
676-
if (privateKey != null) {
677-
q = privateKey.getPrimeQ();
684+
if (privateKey instanceof RSAPrivateCrtKey) {
685+
q = ((RSAPrivateCrtKey) privateKey).getPrimeQ();
678686
} else {
679687
q = rsa_q;
680688
}
@@ -687,8 +695,8 @@ public synchronized IRubyObject get_q() {
687695
private BigInteger getPublicExponent() {
688696
if (publicKey != null) {
689697
return publicKey.getPublicExponent();
690-
} else if (privateKey != null) {
691-
return privateKey.getPublicExponent();
698+
} else if (privateKey instanceof RSAPrivateCrtKey) {
699+
return ((RSAPrivateCrtKey) privateKey).getPublicExponent();
692700
} else {
693701
return rsa_e;
694702
}
@@ -750,6 +758,32 @@ public synchronized IRubyObject set_n(final ThreadContext context, IRubyObject v
750758
return value;
751759
}
752760

761+
@JRubyMethod
762+
public IRubyObject set_key(final ThreadContext context, IRubyObject n, IRubyObject e, IRubyObject d) {
763+
this.rsa_n = BN.getBigInteger(n);
764+
this.rsa_e = BN.getBigInteger(e);
765+
this.rsa_d = BN.getBigInteger(d);
766+
generatePrivateKeyIfParams(context);
767+
return this;
768+
}
769+
770+
@JRubyMethod
771+
public IRubyObject set_factors(final ThreadContext context, IRubyObject p, IRubyObject q) {
772+
this.rsa_p = BN.getBigInteger(p);
773+
this.rsa_q = BN.getBigInteger(q);
774+
generatePrivateKeyIfParams(context);
775+
return this;
776+
}
777+
778+
@JRubyMethod
779+
public IRubyObject set_crt_params(final ThreadContext context, IRubyObject dmp1, IRubyObject dmq1, IRubyObject iqmp) {
780+
this.rsa_dmp1 = BN.asBigInteger(dmp1);
781+
this.rsa_dmq1 = BN.asBigInteger(dmq1);
782+
this.rsa_iqmp = BN.asBigInteger(iqmp);
783+
generatePrivateKeyIfParams(context);
784+
return this;
785+
}
786+
753787
private void generatePublicKeyIfParams(final ThreadContext context) {
754788
final Ruby runtime = context.runtime;
755789

@@ -783,14 +817,12 @@ private void generatePublicKeyIfParams(final ThreadContext context) {
783817
private void generatePrivateKeyIfParams(final ThreadContext context) {
784818
final Ruby runtime = context.runtime;
785819

786-
if ( privateKey != null ) throw newRSAError(runtime, "illegal modification");
787-
788820
// Don't access the rsa_n and rsa_e fields directly. They may have
789821
// already been consumed and cleared by generatePublicKeyIfParams.
790822
BigInteger _rsa_n = getModulus();
791823
BigInteger _rsa_e = getPublicExponent();
792824

793-
if (_rsa_n != null && _rsa_e != null && rsa_p != null && rsa_q != null && rsa_d != null && rsa_dmp1 != null && rsa_dmq1 != null && rsa_iqmp != null) {
825+
if (_rsa_n != null && _rsa_e != null && rsa_d != null) {
794826
final KeyFactory rsaFactory;
795827
try {
796828
rsaFactory = SecurityHelper.getKeyFactory("RSA");
@@ -799,17 +831,24 @@ private void generatePrivateKeyIfParams(final ThreadContext context) {
799831
throw runtime.newLoadError("unsupported key algorithm (RSA)");
800832
}
801833

802-
try {
803-
privateKey = (RSAPrivateCrtKey) rsaFactory.generatePrivate(
804-
new RSAPrivateCrtKeySpec(_rsa_n, _rsa_e, rsa_d, rsa_p, rsa_q, rsa_dmp1, rsa_dmq1, rsa_iqmp)
805-
);
806-
}
807-
catch (InvalidKeySpecException e) {
808-
throw newRSAError(runtime, "invalid parameters");
834+
if (rsa_p != null && rsa_q != null && rsa_dmp1 != null && rsa_dmq1 != null && rsa_iqmp != null) {
835+
try {
836+
privateKey = (RSAPrivateCrtKey) rsaFactory.generatePrivate(
837+
new RSAPrivateCrtKeySpec(_rsa_n, _rsa_e, rsa_d, rsa_p, rsa_q, rsa_dmp1, rsa_dmq1, rsa_iqmp)
838+
);
839+
} catch (InvalidKeySpecException e) {
840+
throw newRSAError(runtime, "invalid parameters", e);
841+
}
842+
rsa_n = null; rsa_e = null; rsa_d = null;
843+
rsa_p = null; rsa_q = null;
844+
rsa_dmp1 = null; rsa_dmq1 = null; rsa_iqmp = null;
845+
} else {
846+
try {
847+
privateKey = (RSAPrivateKey) rsaFactory.generatePrivate(new RSAPrivateKeySpec(_rsa_n, rsa_d));
848+
} catch (InvalidKeySpecException e) {
849+
throw newRSAError(runtime, "invalid parameters", e);
850+
}
809851
}
810-
rsa_n = null; rsa_e = null;
811-
rsa_d = null; rsa_p = null; rsa_q = null;
812-
rsa_dmp1 = null; rsa_dmq1 = null; rsa_iqmp = null;
813852
}
814853
}
815854

src/test/ruby/rsa/test_rsa.rb

Lines changed: 38 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -9,6 +9,37 @@ def setup
99
require 'base64'
1010
end
1111

12+
def test_private
13+
# Generated by key size and public exponent
14+
key = OpenSSL::PKey::RSA.new(512, 3)
15+
assert(key.private?)
16+
17+
# Generated by DER
18+
key2 = OpenSSL::PKey::RSA.new(key.to_der)
19+
assert(key2.private?)
20+
21+
# public key
22+
key3 = key.public_key
23+
assert(!key3.private?)
24+
25+
# Generated by public key DER
26+
key4 = OpenSSL::PKey::RSA.new(key3.to_der)
27+
assert(!key4.private?)
28+
rsa1024 = Fixtures.pkey("rsa1024")
29+
30+
#if !openssl?(3, 0, 0)
31+
# Generated by RSA#set_key
32+
key5 = OpenSSL::PKey::RSA.new
33+
key5.set_key(rsa1024.n, rsa1024.e, rsa1024.d)
34+
assert(key5.private?)
35+
36+
# Generated by RSA#set_key, without d
37+
key6 = OpenSSL::PKey::RSA.new
38+
key6.set_key(rsa1024.n, rsa1024.e, nil)
39+
assert(!key6.private?)
40+
#end
41+
end
42+
1243
def test_oid
1344
key = OpenSSL::PKey::RSA.new
1445
assert_equal 'rsaEncryption', key.oid
@@ -72,9 +103,14 @@ def test_rsa_from_params_public_first
72103
rsa = OpenSSL::PKey::RSA.new
73104
rsa.e, rsa.n = key.e, key.n
74105
assert_nothing_raised { rsa.public_encrypt('Test string') }
75-
[:e, :n].each {|param| assert_equal(key.send(param), rsa.send(param)) }
106+
[:e, :n].each { |param| assert_equal(key.send(param), rsa.send(param)) }
107+
108+
rsa = OpenSSL::PKey::RSA.new
109+
rsa.set_key(key.n, key.e, key.d)
110+
# rsa.d, rsa.p, rsa.q, rsa.iqmp, rsa.dmp1, rsa.dmq1 = key.d, key.p, key.q, key.iqmp, key.dmp1, key.dmq1
111+
rsa.set_factors(key.p, key.q)
112+
rsa.set_crt_params(key.dmp1, key.dmq1, key.iqmp)
76113

77-
rsa.d, rsa.p, rsa.q, rsa.iqmp, rsa.dmp1, rsa.dmq1 = key.d, key.p, key.q, key.iqmp, key.dmp1, key.dmq1
78114
assert_nothing_raised { rsa.private_encrypt('Test string') }
79115
[:e, :n, :d, :p, :q, :iqmp, :dmp1, :dmq1].each do |param|
80116
assert_equal(key.send(param), rsa.send(param), param)

0 commit comments

Comments
 (0)