Skip to content

Commit dacede1

Browse files
committed
[compat] improve DSA key parsing and errors
1 parent 411aefb commit dacede1

File tree

3 files changed

+52
-12
lines changed

3 files changed

+52
-12
lines changed

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

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -302,6 +302,9 @@ public RubyBoolean private_p() {
302302

303303
@JRubyMethod(name = "public_to_der")
304304
public RubyString public_to_der(ThreadContext context) {
305+
if (publicKey == null) {
306+
throw newPKeyError(context.runtime, "incompletely initialized DSA key");
307+
}
305308
final byte[] bytes;
306309
try {
307310
bytes = toDerDSAPublicKey(publicKey);
@@ -325,6 +328,9 @@ public RubyString to_der() {
325328
catch (NoClassDefFoundError e) {
326329
throw newDSAError(getRuntime(), bcExceptionMessage(e));
327330
}
331+
catch (IllegalArgumentException e) {
332+
throw newPKeyError(getRuntime(), e.getMessage());
333+
}
328334
catch (IOException e) {
329335
throw newDSAError(getRuntime(), e.getMessage(), e);
330336
}

src/main/java/org/jruby/ext/openssl/impl/PKey.java

Lines changed: 26 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -119,14 +119,28 @@ public static KeyPair readPrivateKey(final Type type, final PrivateKeyInfo keyIn
119119
exp1.getValue(), exp2.getValue(), crtCoef.getValue());
120120
break;
121121
case DSA:
122-
seq = (ASN1Sequence) keyInfo.parsePrivateKey();
123-
ASN1Integer p = (ASN1Integer) seq.getObjectAt(1);
124-
ASN1Integer q = (ASN1Integer) seq.getObjectAt(2);
125-
ASN1Integer g = (ASN1Integer) seq.getObjectAt(3);
126-
ASN1Integer y = (ASN1Integer) seq.getObjectAt(4);
127-
ASN1Integer x = (ASN1Integer) seq.getObjectAt(5);
128-
privSpec = new DSAPrivateKeySpec(x.getValue(), p.getValue(), q.getValue(), g.getValue());
129-
pubSpec = new DSAPublicKeySpec(y.getValue(), p.getValue(), q.getValue(), g.getValue());
122+
final ASN1Encodable parsedDSAKey = keyInfo.parsePrivateKey();
123+
if (parsedDSAKey instanceof ASN1Integer) {
124+
// PKCS#8 format: private key is just x (INTEGER), params in AlgorithmIdentifier
125+
final BigInteger xVal = ((ASN1Integer) parsedDSAKey).getValue();
126+
final DSAParameter dsaParam = DSAParameter.getInstance(keyInfo.getPrivateKeyAlgorithm().getParameters());
127+
final BigInteger pVal = dsaParam.getP();
128+
final BigInteger qVal = dsaParam.getQ();
129+
final BigInteger gVal = dsaParam.getG();
130+
final BigInteger yVal = gVal.modPow(xVal, pVal);
131+
privSpec = new DSAPrivateKeySpec(xVal, pVal, qVal, gVal);
132+
pubSpec = new DSAPublicKeySpec(yVal, pVal, qVal, gVal);
133+
} else {
134+
// Traditional "DSA PRIVATE KEY" format: SEQUENCE { version, p, q, g, y, x }
135+
seq = (ASN1Sequence) parsedDSAKey;
136+
ASN1Integer p = (ASN1Integer) seq.getObjectAt(1);
137+
ASN1Integer q = (ASN1Integer) seq.getObjectAt(2);
138+
ASN1Integer g = (ASN1Integer) seq.getObjectAt(3);
139+
ASN1Integer y = (ASN1Integer) seq.getObjectAt(4);
140+
ASN1Integer x = (ASN1Integer) seq.getObjectAt(5);
141+
privSpec = new DSAPrivateKeySpec(x.getValue(), p.getValue(), q.getValue(), g.getValue());
142+
pubSpec = new DSAPublicKeySpec(y.getValue(), p.getValue(), q.getValue(), g.getValue());
143+
}
130144
break;
131145
case EC:
132146
return readECPrivateKey(SecurityHelper.getKeyFactory("EC"), keyInfo);
@@ -142,9 +156,8 @@ public static PublicKey readPublicKey(final byte[] input) throws IOException {
142156
// Try PEM first
143157
try (Reader in = new InputStreamReader(new ByteArrayInputStream(input))) {
144158
Object pemObject = new PEMParser(in).readObject();
145-
if (pemObject != null) {
146-
SubjectPublicKeyInfo publicKeyInfo = SubjectPublicKeyInfo.getInstance(pemObject);
147-
return new JcaPEMKeyConverter().getPublicKey(publicKeyInfo);
159+
if (pemObject instanceof SubjectPublicKeyInfo) {
160+
return new JcaPEMKeyConverter().getPublicKey((SubjectPublicKeyInfo) pemObject);
148161
}
149162
}
150163
// Fall back to DER-encoded SubjectPublicKeyInfo
@@ -335,7 +348,8 @@ public static ASN1Sequence toASN1Primitive(final RSAPublicKey publicKey) {
335348
return new DERSequence(vec);
336349
}
337350

338-
public static byte[] toDerDSAKey(DSAPublicKey pubKey, DSAPrivateKey privKey) throws IOException {
351+
public static byte[] toDerDSAKey(DSAPublicKey pubKey, DSAPrivateKey privKey)
352+
throws IOException, IllegalArgumentException {
339353
if ( pubKey != null && privKey == null ) {
340354
return toDerDSAPublicKey(pubKey);
341355
}

src/test/ruby/dsa/test_dsa.rb

Lines changed: 20 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -8,6 +8,7 @@ def setup
88
end
99

1010
def test_private
11+
# Traditional "DSA PRIVATE KEY" (OpenSSL legacy format)
1112
key = Fixtures.pkey("dsa1024")
1213
assert_equal true, key.private?
1314
key2 = OpenSSL::PKey::DSA.new(key.to_der)
@@ -18,12 +19,31 @@ def test_private
1819
assert_equal false, key4.private?
1920
end
2021

22+
def test_private_pkcs8
23+
# PKCS#8 "PRIVATE KEY" format: private key is bare INTEGER (x),
24+
# params (p, q, g) come from AlgorithmIdentifier; y must be derived.
25+
key = Fixtures.pkey("dsa2048")
26+
assert_equal true, key.private?
27+
key2 = OpenSSL::PKey::DSA.new(key.to_der)
28+
assert_equal true, key2.private?
29+
key3 = key.public_key
30+
assert_equal false, key3.private?
31+
key4 = OpenSSL::PKey::DSA.new(key3.to_der)
32+
assert_equal false, key4.private?
33+
end
34+
2135
def test_new
2236
key = OpenSSL::PKey::DSA.new(2048)
2337
pem = key.public_key.to_pem
2438
OpenSSL::PKey::DSA.new pem
2539
end
2640

41+
def test_new_empty
42+
key = OpenSSL::PKey::DSA.new
43+
assert_nil(key.p)
44+
assert_raise(OpenSSL::PKey::PKeyError) { key.to_der }
45+
end
46+
2747
def test_dup
2848
key = Fixtures.pkey("dsa1024")
2949
key2 = key.dup

0 commit comments

Comments
 (0)