Skip to content

Commit b209d97

Browse files
committed
handle X.509 authorityKeyIdentifier parsing somehow right
more stuff for dealing with #102
1 parent 418dad0 commit b209d97

File tree

2 files changed

+70
-26
lines changed

2 files changed

+70
-26
lines changed

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

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -414,6 +414,8 @@ private byte[] getSignature() {
414414
return cert.getSignature();
415415
}
416416

417+
BigInteger getSerial() { return serial; }
418+
417419
@JRubyMethod
418420
public IRubyObject serial() {
419421
return BN.newBN(getRuntime(), serial);
@@ -433,6 +435,8 @@ public IRubyObject set_serial(final IRubyObject serial) {
433435
this.serial = serialInt; return serial;
434436
}
435437

438+
X509Name getSubject() { return ((X509Name) subject); }
439+
436440
@JRubyMethod
437441
public IRubyObject subject() {
438442
return subject;
@@ -444,6 +448,8 @@ public IRubyObject set_subject(final IRubyObject subject) {
444448
return this.subject = subject;
445449
}
446450

451+
X509Name getIssuer() { return ((X509Name) issuer); }
452+
447453
@JRubyMethod
448454
public IRubyObject issuer() {
449455
return issuer;

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

Lines changed: 64 additions & 26 deletions
Original file line numberDiff line numberDiff line change
@@ -32,15 +32,8 @@
3232

3333
import java.security.GeneralSecurityException;
3434

35-
import org.bouncycastle.asn1.ASN1Boolean;
36-
import org.bouncycastle.asn1.ASN1Encodable;
37-
import org.bouncycastle.asn1.ASN1EncodableVector;
38-
import org.bouncycastle.asn1.ASN1Encoding;
39-
import org.bouncycastle.asn1.ASN1Integer;
40-
import org.bouncycastle.asn1.ASN1ObjectIdentifier;
41-
import org.bouncycastle.asn1.DERBitString;
42-
import org.bouncycastle.asn1.DEROctetString;
43-
import org.bouncycastle.asn1.DLSequence;
35+
import org.bouncycastle.asn1.*;
36+
import org.bouncycastle.asn1.x500.X500Name;
4437
import org.bouncycastle.asn1.x509.GeneralName;
4538
import org.bouncycastle.asn1.x509.GeneralNames;
4639

@@ -187,28 +180,28 @@ public IRubyObject create_ext(final ThreadContext context, final IRubyObject[] a
187180
final ASN1Encodable value;
188181
try {
189182
final String id = objectId.getId();
190-
if (id.equals("2.5.29.14")) { //subjectKeyIdentifier
183+
if (id.equals("2.5.29.14")) { // subjectKeyIdentifier
191184
value = new DEROctetString(parseSubjectKeyIdentifier(context, oid, valuex));
192185
}
193-
else if (id.equals("2.5.29.35")) { //authorityKeyIdentifier
186+
else if (id.equals("2.5.29.35")) { // authorityKeyIdentifier
194187
value = parseAuthorityKeyIdentifier(context, valuex);
195188
}
196-
else if (id.equals("2.5.29.17")) { //subjectAltName
189+
else if (id.equals("2.5.29.17")) { // subjectAltName
197190
value = parseSubjectAltName(valuex);
198191
}
199-
else if (id.equals("2.5.29.18")) { //issuerAltName
192+
else if (id.equals("2.5.29.18")) { // issuerAltName
200193
value = parseIssuerAltName(context, valuex);
201194
}
202-
else if (id.equals("2.5.29.19")) { //basicConstraints
195+
else if (id.equals("2.5.29.19")) { // basicConstraints
203196
value = parseBasicConstrains(valuex);
204197
}
205-
else if (id.equals("2.5.29.15")) { //keyUsage
198+
else if (id.equals("2.5.29.15")) { // keyUsage
206199
value = parseKeyUsage(oid, valuex);
207200
}
208-
else if (id.equals("2.16.840.1.113730.1.1")) { //nsCertType
201+
else if (id.equals("2.16.840.1.113730.1.1")) { // nsCertType
209202
value = parseNsCertType(oid, valuex);
210203
}
211-
else if (id.equals("2.5.29.37")) { //extendedKeyUsage
204+
else if (id.equals("2.5.29.37")) { // extendedKeyUsage
212205
value = parseExtendedKeyUsage(valuex);
213206
}
214207
else {
@@ -404,19 +397,31 @@ private static DLSequence parseBasicConstrains(final String valuex) {
404397
return new DLSequence(vec);
405398
}
406399

407-
private DLSequence parseAuthorityKeyIdentifier(final ThreadContext context, final String valuex) {
400+
private ASN1Sequence parseAuthorityKeyIdentifier(final ThreadContext context, final String valuex) {
408401
final ASN1EncodableVector vec = new ASN1EncodableVector();
409-
if ( valuex.startsWith("keyid:always") ) {
410-
vec.add(new DEROctetString(derDigest(context)));
411-
} else if ( valuex.startsWith("keyid") ) {
412-
vec.add(new DEROctetString(derDigest(context)));
402+
403+
for ( String value : valuex.split(",") ) { // e.g. "keyid:always,issuer:always"
404+
if ( value.startsWith("keyid:") ) { // keyid:always
405+
ASN1Encodable publicKeyIdentifier = new DEROctetString(publicKeyIdentifier(context));
406+
vec.add(new DERTaggedObject(false, 0, publicKeyIdentifier));
407+
}
408+
else if ( value.startsWith("issuer:") ) { // issuer:always
409+
GeneralName issuerName = new GeneralName(authorityCertIssuer(context));
410+
vec.add(new DERTaggedObject(false, 1, new GeneralNames(issuerName)));
411+
412+
BigInteger issuerSerial = getIssuerSerialNumber(context);
413+
if ( issuerSerial != null ) {
414+
vec.add(new DERTaggedObject(false, 2, new ASN1Integer(issuerSerial)));
415+
}
416+
}
413417
}
414-
return new DLSequence(vec);
418+
419+
return new DERSequence(vec);
415420
}
416421

417-
private byte[] derDigest(final ThreadContext context) {
422+
private byte[] publicKeyIdentifier(final ThreadContext context) {
418423
final Ruby runtime = context.runtime;
419-
IRubyObject pkey = getInstanceVariable("@issuer_certificate").callMethod(context, "public_key");
424+
IRubyObject pkey = getPublicKey(context);
420425
IRubyObject der;
421426
if (pkey instanceof PKeyRSA) {
422427
der = pkey.callMethod(context, "to_der");
@@ -427,6 +432,39 @@ private byte[] derDigest(final ThreadContext context) {
427432
return getSHA1Digest(runtime, der.asString().getBytes());
428433
}
429434

435+
private IRubyObject getPublicKey(final ThreadContext context) {
436+
IRubyObject issuer_cert = getInstanceVariable("@issuer_certificate");
437+
if ( issuer_cert instanceof X509Cert ) {
438+
return ((X509Cert) issuer_cert).public_key(context);
439+
}
440+
return issuer_cert.callMethod(context, "public_key");
441+
}
442+
443+
private X500Name authorityCertIssuer(final ThreadContext context) {
444+
IRubyObject issuer = getIssuer(context);
445+
if ( issuer instanceof X509Name ) {
446+
return ((X509Name) issuer).getX500Name();
447+
}
448+
throw new UnsupportedOperationException();
449+
}
450+
451+
private IRubyObject getIssuer(final ThreadContext context) {
452+
IRubyObject issuer_cert = getInstanceVariable("@issuer_certificate");
453+
if ( issuer_cert instanceof X509Cert ) {
454+
return ((X509Cert) issuer_cert).getIssuer();
455+
}
456+
return issuer_cert.callMethod(context, "issuer");
457+
}
458+
459+
private BigInteger getIssuerSerialNumber(final ThreadContext context) {
460+
IRubyObject issuer_cert = getInstanceVariable("@issuer_certificate");
461+
if ( issuer_cert instanceof X509Cert ) {
462+
return ((X509Cert) issuer_cert).getSerial();
463+
}
464+
IRubyObject serial = issuer_cert.callMethod(context, "serial");
465+
return serial.isNil() ? null : ((BN) serial).getValue();
466+
}
467+
430468
private static byte[] getSHA1Digest(Ruby runtime, byte[] bytes) {
431469
try {
432470
return SecurityHelper.getMessageDigest("SHA-1").digest(bytes);
@@ -507,7 +545,7 @@ private static ASN1Encodable parseSubjectAltName(final String valuex) throws IOE
507545

508546
private DEROctetString parseSubjectKeyIdentifier(final ThreadContext context, final String oid, final String valuex) {
509547
if ( "hash".equalsIgnoreCase(valuex) ) {
510-
return new DEROctetString(derDigest(context));
548+
return new DEROctetString(publicKeyIdentifier(context));
511549
}
512550
if ( valuex.length() == 20 || ! isHex(valuex) ) {
513551
return new DEROctetString(ByteList.plain(valuex));

0 commit comments

Comments
 (0)