Skip to content

Commit 3b853eb

Browse files
committed
[fix] cert subjectKeyId ext value generation
now that PKey#to_der is aligned we need to handle the payload unpacking from ASN.1 for the public-key part (this is the part we need to feed into the hash fn)
1 parent 1e00c00 commit 3b853eb

File tree

6 files changed

+119
-17
lines changed

6 files changed

+119
-17
lines changed

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

Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -41,6 +41,9 @@
4141
import java.security.spec.PKCS8EncodedKeySpec;
4242
import java.security.spec.X509EncodedKeySpec;
4343

44+
import org.bouncycastle.asn1.ASN1InputStream;
45+
import org.bouncycastle.asn1.ASN1Primitive;
46+
import org.bouncycastle.asn1.ASN1Sequence;
4447
import org.jruby.Ruby;
4548
import org.jruby.RubyClass;
4649
import org.jruby.RubyModule;
@@ -230,6 +233,16 @@ public IRubyObject sign(IRubyObject digest, IRubyObject data) {
230233
}
231234
}
232235

236+
public ASN1Primitive toASN1PublicInfo() throws IOException {
237+
ASN1InputStream input = new ASN1InputStream(to_der().getBytes());
238+
239+
ASN1Primitive data = input.readObject();
240+
if (data instanceof ASN1Sequence) {
241+
return ((ASN1Sequence) data).getObjectAt(1).toASN1Primitive();
242+
}
243+
return data;
244+
}
245+
233246
static ByteList sign(final String signAlg, final PrivateKey privateKey, final ByteList data)
234247
throws NoSuchAlgorithmException, InvalidKeyException, SignatureException {
235248
Signature signature = SecurityHelper.getSignature(signAlg);

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

Lines changed: 7 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -40,6 +40,7 @@
4040
import java.security.spec.DSAPublicKeySpec;
4141
import java.security.spec.InvalidKeySpecException;
4242

43+
import org.bouncycastle.asn1.ASN1Primitive;
4344
import org.jruby.Ruby;
4445
import org.jruby.RubyBoolean;
4546
import org.jruby.RubyClass;
@@ -62,8 +63,8 @@
6263
import static org.jruby.ext.openssl.OpenSSL.*;
6364
import static org.jruby.ext.openssl.impl.PKey.readDSAPrivateKey;
6465
import static org.jruby.ext.openssl.impl.PKey.readDSAPublicKey;
66+
import static org.jruby.ext.openssl.impl.PKey.toASN1Primitive;
6567
import static org.jruby.ext.openssl.impl.PKey.toDerDSAKey;
66-
import static org.jruby.ext.openssl.PKey._PKey;
6768

6869
/**
6970
* @author <a href="mailto:[email protected]">Ola Bini</a>
@@ -317,6 +318,11 @@ public RubyString to_der() {
317318
return StringHelper.newString(getRuntime(), bytes);
318319
}
319320

321+
@Override
322+
public ASN1Primitive toASN1PublicInfo() {
323+
return toASN1Primitive(publicKey);
324+
}
325+
320326
@JRubyMethod
321327
public RubyString to_text() {
322328
StringBuilder result = new StringBuilder();

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

Lines changed: 7 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -50,6 +50,7 @@
5050

5151
import static javax.crypto.Cipher.*;
5252

53+
import org.bouncycastle.asn1.ASN1Primitive;
5354
import org.jruby.Ruby;
5455
import org.jruby.RubyClass;
5556
import org.jruby.RubyBignum;
@@ -71,9 +72,9 @@
7172
import org.jruby.ext.openssl.impl.CipherSpec;
7273
import org.jruby.ext.openssl.x509store.PEMInputOutput;
7374
import static org.jruby.ext.openssl.OpenSSL.*;
74-
import static org.jruby.ext.openssl.PKey._PKey;
7575
import static org.jruby.ext.openssl.impl.PKey.readRSAPrivateKey;
7676
import static org.jruby.ext.openssl.impl.PKey.readRSAPublicKey;
77+
import static org.jruby.ext.openssl.impl.PKey.toASN1Primitive;
7778
import static org.jruby.ext.openssl.impl.PKey.toDerRSAKey;
7879

7980
/**
@@ -371,6 +372,11 @@ public RubyString to_der() {
371372
return StringHelper.newString(getRuntime(), bytes);
372373
}
373374

375+
@Override
376+
public ASN1Primitive toASN1PublicInfo() {
377+
return toASN1Primitive(publicKey);
378+
}
379+
374380
@JRubyMethod
375381
public PKeyRSA public_key() {
376382
return new PKeyRSA(getRuntime(), this.publicKey);

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

Lines changed: 15 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -47,6 +47,7 @@
4747
import org.jruby.RubyObject;
4848
import org.jruby.RubyString;
4949
import org.jruby.anno.JRubyMethod;
50+
import org.jruby.exceptions.RaiseException;
5051
import org.jruby.runtime.Arity;
5152
import org.jruby.runtime.Block;
5253
import org.jruby.runtime.ObjectAllocator;
@@ -431,14 +432,23 @@ private byte[] issuerPublicKeyIdentifier(final ThreadContext context) {
431432

432433
private byte[] publicKeyIdentifier(final ThreadContext context, final IRubyObject pkey) {
433434
final Ruby runtime = context.runtime;
434-
IRubyObject der;
435-
if (pkey instanceof PKeyRSA) {
436-
der = pkey.callMethod(context, "to_der");
435+
ByteList encoded;
436+
if (pkey instanceof PKeyRSA || pkey instanceof PKeyDSA) {
437+
try {
438+
ASN1Primitive info = ((PKey) pkey).toASN1PublicInfo();
439+
encoded = new ByteList(info.getEncoded(), false);
440+
} catch (IOException ex) {
441+
RaiseException error = context.runtime.newRuntimeError(ex.toString());
442+
error.initCause(ex);
443+
throw error;
444+
}
437445
} else {
438-
der = ASN1.decode(context, ASN1._ASN1(runtime), pkey.callMethod(context, "to_der"));
446+
IRubyObject der = ASN1.decode(context, ASN1._ASN1(runtime), pkey.callMethod(context, "to_der"));
439447
der = der.callMethod(context, "value").callMethod(context, "[]", runtime.newFixnum(1)).callMethod(context, "value");
448+
encoded = der.asString().getByteList();
440449
}
441-
return getSHA1Digest(runtime, der.asString().getByteList());
450+
451+
return getSHA1Digest(runtime, encoded);
442452
}
443453

444454
private IRubyObject getSubjectPublicKey(final ThreadContext context) {

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

Lines changed: 26 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -54,6 +54,7 @@
5454
import java.security.spec.X509EncodedKeySpec;
5555
import javax.crypto.spec.DHParameterSpec;
5656

57+
import org.bouncycastle.asn1.ASN1Encodable;
5758
import org.bouncycastle.asn1.ASN1EncodableVector;
5859
import org.bouncycastle.asn1.ASN1Encoding;
5960
import org.bouncycastle.asn1.ASN1InputStream;
@@ -311,12 +312,9 @@ public static KeyPair readECPrivateKey(final KeyFactory ecFactory, final byte[]
311312

312313
public static byte[] toDerRSAKey(RSAPublicKey pubKey, RSAPrivateCrtKey privKey) throws IOException {
313314
if ( pubKey != null && privKey == null ) {
314-
ASN1EncodableVector vec = new ASN1EncodableVector();
315-
vec.add(new ASN1Integer(pubKey.getModulus()));
316-
vec.add(new ASN1Integer(pubKey.getPublicExponent()));
317315
// pubKey.getEncoded() :
318316
return KeyUtil.getEncodedSubjectPublicKeyInfo(
319-
new AlgorithmIdentifier(PKCSObjectIdentifiers.rsaEncryption, DERNull.INSTANCE), new DERSequence(vec)
317+
new AlgorithmIdentifier(PKCSObjectIdentifiers.rsaEncryption, DERNull.INSTANCE), toASN1Primitive(pubKey)
320318
);
321319
}
322320
ASN1EncodableVector vec = new ASN1EncodableVector();
@@ -332,16 +330,28 @@ public static byte[] toDerRSAKey(RSAPublicKey pubKey, RSAPrivateCrtKey privKey)
332330
return new DERSequence(vec).toASN1Primitive().getEncoded(ASN1Encoding.DER);
333331
}
334332

333+
public static ASN1Sequence toASN1Primitive(RSAPublicKey pubKey) {
334+
ASN1EncodableVector vec = new ASN1EncodableVector();
335+
vec.add(new ASN1Integer(pubKey.getModulus()));
336+
vec.add(new ASN1Integer(pubKey.getPublicExponent()));
337+
return new DERSequence(vec);
338+
}
339+
335340
public static byte[] toDerDSAKey(DSAPublicKey pubKey, DSAPrivateKey privKey) throws IOException {
336341
if ( pubKey != null && privKey == null ) {
337342
// pubKey.getEncoded() :
338343
final DSAParams params = pubKey.getParams();
339344
if (params == null) {
340-
return new SubjectPublicKeyInfo(new AlgorithmIdentifier(X9ObjectIdentifiers.id_dsa),
341-
new ASN1Integer(pubKey.getY())).getEncoded(ASN1Encoding.DER);
345+
return new SubjectPublicKeyInfo(
346+
new AlgorithmIdentifier(X9ObjectIdentifiers.id_dsa),
347+
toASN1Primitive(pubKey)
348+
).getEncoded(ASN1Encoding.DER);
342349
}
343-
return new SubjectPublicKeyInfo(new AlgorithmIdentifier(X9ObjectIdentifiers.id_dsa,
344-
new DSAParameter(params.getP(), params.getQ(), params.getG())), new ASN1Integer(pubKey.getY())
350+
return new SubjectPublicKeyInfo(
351+
new AlgorithmIdentifier(X9ObjectIdentifiers.id_dsa,
352+
new DSAParameter(params.getP(), params.getQ(), params.getG())
353+
),
354+
toASN1Primitive(pubKey)
345355
).getEncoded(ASN1Encoding.DER);
346356
}
347357
if ( privKey != null && pubKey != null ) {
@@ -359,11 +369,17 @@ public static byte[] toDerDSAKey(DSAPublicKey pubKey, DSAPrivateKey privKey) thr
359369
throw new IllegalArgumentException("private key as well as public key are null");
360370
}
361371
final DSAParams params = privKey.getParams();
362-
return new PrivateKeyInfo(new AlgorithmIdentifier(X9ObjectIdentifiers.id_dsa,
363-
new DSAParameter(params.getP(), params.getQ(), params.getG())), new ASN1Integer(privKey.getX())
372+
return new PrivateKeyInfo(
373+
new AlgorithmIdentifier(X9ObjectIdentifiers.id_dsa,
374+
new DSAParameter(params.getP(), params.getQ(), params.getG())),
375+
new ASN1Integer(privKey.getX())
364376
).getEncoded(ASN1Encoding.DER);
365377
}
366378

379+
public static ASN1Primitive toASN1Primitive(DSAPublicKey pubKey) {
380+
return new ASN1Integer(pubKey.getY());
381+
}
382+
367383
public static byte[] toDerDHKey(BigInteger p, BigInteger g) throws IOException {
368384
ASN1EncodableVector vec = new ASN1EncodableVector();
369385
if ( p != null ) vec.add( new ASN1Integer(p) );

src/test/ruby/x509/test_x509ext.rb

Lines changed: 51 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -126,6 +126,57 @@ def test_to_der_is_the_same_for_non_critical
126126
assert ext1.to_der != ext2.to_der
127127
end
128128

129+
def test_subject_key_identifier_hash
130+
#key = Fixtures.pkey("rsa1024")
131+
key = OpenSSL::PKey::RSA.new(1024)
132+
subject = "/C=FR/ST=IDF/L=PARIS/O=Company/CN=myhost.example"
133+
cert = OpenSSL::X509::Certificate.new
134+
cert.subject = cert.issuer = OpenSSL::X509::Name.parse(subject)
135+
136+
cert.not_before = now = Time.new(2020)
137+
cert.not_after = now + 5 * 365 * 24 * 60 * 60
138+
cert.public_key = key.public_key
139+
cert.serial = 0x0
140+
cert.version = 2
141+
142+
ef = OpenSSL::X509::ExtensionFactory.new
143+
ef.subject_certificate = ef.issuer_certificate = cert
144+
145+
cert.add_extension ef.create_extension('basicConstraints', 'CA:FALSE', true)
146+
cert.add_extension ef.create_extension('keyUsage', 'keyEncipherment,dataEncipherment,digitalSignature')
147+
148+
sequence = rsa_key_seq(cert.public_key)
149+
pp sequence if $DEBUG
150+
151+
cert.add_extension ef.create_extension('subjectKeyIdentifier', 'hash')
152+
cert.add_extension ef.create_extension('authorityKeyIdentifier', 'keyid:always,issuer:always')
153+
154+
cert.sign key, OpenSSL::Digest::SHA256.new
155+
156+
assert_equal 4, cert.extensions.size
157+
assert_equal 'subjectKeyIdentifier', cert.extensions[2].oid
158+
159+
assert_equal rsa_key_id(cert.public_key), cert.extensions[2].value
160+
#assert_equal "D1:FE:F9:FB:F8:AE:1B:C1:60:CB:FA:03:E2:59:6D:D8:73:08:92:13", rsa_key_id(cert.public_key)
161+
#assert_equal "D1:FE:F9:FB:F8:AE:1B:C1:60:CB:FA:03:E2:59:6D:D8:73:08:92:13", cert.extensions[2].value
162+
163+
# keyid:...\nDirName:/C=FR/ST=IDF/L=PARIS/O=Company/CN=myhost.example\nserial:00\n
164+
auth_key_id_value = cert.extensions[3].value
165+
key_id, dir_name, serial = auth_key_id_value.split("\n")
166+
# assert_equal 'keyid:' + rsa_key_id(key), key_id
167+
# assert_equal "DirName:#{subject}", dir_name
168+
# assert_equal 'serial:00', serial
169+
end
170+
171+
def rsa_key_seq(public_key)
172+
OpenSSL::ASN1::Sequence [ OpenSSL::ASN1::Integer.new(public_key.n), OpenSSL::ASN1::Integer.new(public_key.e) ]
173+
end
174+
175+
def rsa_key_id(public_key)
176+
key_id = OpenSSL::Digest::SHA1.hexdigest(rsa_key_seq(public_key).to_der)
177+
key_id.scan(/../).join(':').upcase
178+
end
179+
129180
def test_subject_alt_name_sign_to_pem
130181
domain_list = 'test.example.com,test2.example.com,example.com,www.example.com'
131182

0 commit comments

Comments
 (0)