Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
44 changes: 26 additions & 18 deletions src/main/java/org/jruby/ext/openssl/OCSPBasicResponse.java
Original file line number Diff line number Diff line change
Expand Up @@ -122,7 +122,6 @@ public static void createBasicResponse(final Ruby runtime, final RubyModule OCSP
BasicResponse.defineAnnotatedMethods(OCSPBasicResponse.class);
}

private byte[] nonce;
private List<OCSPSingleResponse> singleResponses = new ArrayList<>();
private BasicOCSPResponse asn1BCBasicOCSPResp;
private List<Extension> extensions = new ArrayList<>();
Expand Down Expand Up @@ -155,29 +154,27 @@ public IRubyObject initialize_copy(IRubyObject obj) {
if ( this == obj ) return this;

checkFrozen();
this.asn1BCBasicOCSPResp = ((OCSPBasicResponse)obj).getASN1BCOCSPResp();
OCSPBasicResponse other = (OCSPBasicResponse) obj;
this.asn1BCBasicOCSPResp = other.getASN1BCOCSPResp();
this.extensions = new ArrayList<>(other.extensions);
this.singleResponses = new ArrayList<>(other.singleResponses);
return this;
}

@JRubyMethod(name = "add_nonce", rest = true)
public OCSPBasicResponse add_nonce(ThreadContext context, IRubyObject... args) {
byte[] tmpNonce;
if ( Arity.checkArgumentCount(context.runtime, args, 0, 1) == 0 ) {
try {
tmpNonce = generateNonce(context);
}
catch (NoSuchAlgorithmException e) {
throw newOCSPError(context.runtime, e);
}
final boolean random = Arity.checkArgumentCount(context.runtime, args, 0, 1) == 0 || args[0].isNil();
final Extension nonceExt;
try {
byte[] nonce = random ? generateNonce(context) : args[0].convertToString().getBytes();
nonceExt = OCSPRequest.createNonceExtension(nonce);
}
else {
RubyString input = (RubyString) args[0];
tmpNonce = input.getBytes();
catch (NoSuchAlgorithmException|IOException e) {
throw newOCSPError(context.runtime, e);
}

extensions.add(new Extension(OCSPObjectIdentifiers.id_pkix_ocsp_nonce, false, tmpNonce));
nonce = tmpNonce;

extensions.removeIf(ext -> OCSPObjectIdentifiers.id_pkix_ocsp_nonce.equals(ext.getExtnId()));
extensions.add(nonceExt);
return this;
}

Expand Down Expand Up @@ -232,7 +229,14 @@ public OCSPBasicResponse add_status(final ThreadContext context, IRubyObject[] a

@JRubyMethod(name = "copy_nonce")
public IRubyObject copy_nonce(final ThreadContext context, IRubyObject request) {
add_nonce(context, RubyString.newString(context.runtime, ((OCSPRequest) request).getNonce()));
if (!(request instanceof OCSPRequest)) {
throw context.runtime.newTypeError(request, (RubyClass) _OCSP(context.runtime).getConstantAt("Request"));
}
byte[] reqNonce = ((OCSPRequest) request).getNonce();
if (reqNonce == null) {
return RubyFixnum.two(context.runtime);
}
add_nonce(context, RubyString.newString(context.runtime, reqNonce));
return RubyFixnum.one(context.runtime);
}

Expand Down Expand Up @@ -593,7 +597,11 @@ public BasicOCSPResponse getASN1BCOCSPResp() {
}

public byte[] getNonce() {
return this.nonce;
for (Extension ext : extensions) {
if (OCSPObjectIdentifiers.id_pkix_ocsp_nonce.equals(ext.getExtnId())) return OCSPRequest.extractNonce(ext);
}
if (asn1BCBasicOCSPResp == null) return null;
return OCSPRequest.extractNonce(asn1BCBasicOCSPResp.getTbsResponseData().getResponseExtensions());
}

private ASN1GeneralizedTime rubyIntOrTimeToGenTime(IRubyObject intOrTime) {
Expand Down
5 changes: 5 additions & 0 deletions src/main/java/org/jruby/ext/openssl/OCSPCertificateId.java
Original file line number Diff line number Diff line change
Expand Up @@ -87,6 +87,11 @@ public OCSPCertificateId(Ruby runtime, RubyClass metaClass) {
public OCSPCertificateId(Ruby runtime) {
this(runtime, (RubyClass) _OCSP(runtime).getConstantAt("CertificateId"));
}

OCSPCertificateId(Ruby runtime, CertID bcCertId) {
this(runtime);
this.bcCertId = bcCertId;
}

@JRubyMethod(name = "initialize", visibility = Visibility.PRIVATE)
public IRubyObject initialize(final ThreadContext context, IRubyObject subject, IRubyObject issuer, IRubyObject digest) {
Expand Down
165 changes: 105 additions & 60 deletions src/main/java/org/jruby/ext/openssl/OCSPRequest.java
Original file line number Diff line number Diff line change
Expand Up @@ -46,11 +46,16 @@
import org.jruby.ext.openssl.log.Logger;

import org.bouncycastle.asn1.ASN1Encodable;
import org.bouncycastle.asn1.ASN1EncodableVector;
import org.bouncycastle.asn1.ASN1Encoding;
import org.bouncycastle.asn1.ASN1ObjectIdentifier;
import org.bouncycastle.asn1.ASN1OctetString;
import org.bouncycastle.asn1.ASN1Primitive;
import org.bouncycastle.asn1.ASN1Sequence;
import org.bouncycastle.asn1.DEROctetString;
import org.bouncycastle.asn1.DERSequence;
import org.bouncycastle.asn1.ocsp.OCSPObjectIdentifiers;
import org.bouncycastle.asn1.ocsp.Request;
import org.bouncycastle.asn1.ocsp.Signature;
import org.bouncycastle.asn1.ocsp.TBSRequest;
import org.bouncycastle.asn1.x500.X500Name;
Expand Down Expand Up @@ -82,6 +87,7 @@
import org.jruby.runtime.Visibility;
import org.jruby.runtime.builtin.IRubyObject;

import static org.jruby.ext.openssl.OCSP.generateNonce;
import static org.jruby.ext.openssl.OpenSSL.throwOperationErrorOrOriginal;
import static org.jruby.ext.openssl.OCSP._OCSP;
import static org.jruby.ext.openssl.OCSP.newOCSPError;
Expand Down Expand Up @@ -114,8 +120,6 @@ public static void createRequest(final Ruby runtime, final RubyModule _OCSP) {
private final static String OCSP_TRUSTOTHER = "TRUSTOTHER";
private final static String OCSP_NOCHAIN = "NOCHAIN";
private org.bouncycastle.asn1.ocsp.OCSPRequest asn1bcReq;
private List<OCSPCertificateId> certificateIds = new ArrayList<OCSPCertificateId>();
private byte[] nonce;

@JRubyMethod(name = "initialize", rest = true, visibility = Visibility.PRIVATE)
public IRubyObject initialize(final ThreadContext context, IRubyObject[] args) {
Expand All @@ -125,83 +129,98 @@ public IRubyObject initialize(final ThreadContext context, IRubyObject[] args) {

RubyString derString = StringHelper.readPossibleDERInput(context, args[0]);
asn1bcReq = org.bouncycastle.asn1.ocsp.OCSPRequest.getInstance(derString.getBytes());

return this;
}

@JRubyMethod(name = "add_certid")
public IRubyObject add_certid(IRubyObject certId) {
Ruby runtime = getRuntime();
OCSPCertificateId rubyCertId = (OCSPCertificateId) certId;
certificateIds.add(rubyCertId);

OCSPReqBuilder builder = new OCSPReqBuilder();
for (OCSPCertificateId certificateId : certificateIds) {
builder.addRequest(new CertificateID(certificateId.getCertID()));
}

try {
asn1bcReq = org.bouncycastle.asn1.ocsp.OCSPRequest.getInstance(builder.build().getEncoded());
}
catch (Exception e) {
throw newOCSPError(runtime, e);
}

if (nonce != null) {
addNonceImpl();

GeneralName requestorName = null;
Extensions requestExts = null; // optional
Signature signature = null; // optional
ASN1EncodableVector requests = new ASN1EncodableVector();

if (asn1bcReq != null) {
TBSRequest currentTbsReq = asn1bcReq.getTbsRequest();
requestorName = currentTbsReq.getRequestorName();
requestExts = currentTbsReq.getRequestExtensions();
ASN1Sequence reqList = currentTbsReq.getRequestList();
for (int i = 0; i < reqList.size(); i++) {
requests.add(reqList.getObjectAt(i));
}
signature = asn1bcReq.getOptionalSignature();
}

requests.add(new Request(rubyCertId.getCertID(), null));
TBSRequest newTbsReq = new TBSRequest(requestorName, new DERSequence(requests), requestExts);
asn1bcReq = new org.bouncycastle.asn1.ocsp.OCSPRequest(newTbsReq, signature);

return this;
}

@JRubyMethod(name = "add_nonce", rest = true)
public IRubyObject add_nonce(ThreadContext context, IRubyObject[] args) {
if ( Arity.checkArgumentCount(context.runtime, args, 0, 1) == 0 ) {
try {
nonce = OCSP.generateNonce(context);
}
catch (NoSuchAlgorithmException e) {
throw newOCSPError(context.runtime, e);
}
final boolean random = Arity.checkArgumentCount(context.runtime, args, 0, 1) == 0 || args[0].isNil();
final Extension nonceExt;
try {
byte[] nonce = random ? OCSP.generateNonce(context) : args[0].convertToString().getBytes();
nonceExt = createNonceExtension(nonce);
}
else {
RubyString input = (RubyString) args[0];
nonce = input.getBytes();
catch (NoSuchAlgorithmException|IOException e) {
throw newOCSPError(context.runtime, e);
}

addNonceImpl();
addNonceImpl(nonceExt);
return this;
}

// BC doesn't have support for nonces... gotta do things manually
private void addNonceImpl() {

private void addNonceImpl(final Extension nonceExt) {
GeneralName requestorName = null;
ASN1Sequence requestList = new DERSequence();
Extensions extensions;
Signature sig = null;
List<Extension> tmpExtensions = new ArrayList<Extension>();
Signature signature = null;
List<Extension> tmpExtensions = new ArrayList<>();

if (asn1bcReq != null) {
TBSRequest currentTbsReq = asn1bcReq.getTbsRequest();
extensions = currentTbsReq.getRequestExtensions();
sig = asn1bcReq.getOptionalSignature();
Enumeration<ASN1ObjectIdentifier> oids = extensions.oids();
while (oids.hasMoreElements()) {
tmpExtensions.add(extensions.getExtension(oids.nextElement()));
requestorName = currentTbsReq.getRequestorName();
requestList = currentTbsReq.getRequestList();
Extensions extensions = currentTbsReq.getRequestExtensions(); // optional
if (extensions != null) {
Enumeration<ASN1ObjectIdentifier> oids = extensions.oids();
while (oids.hasMoreElements()) {
ASN1ObjectIdentifier oid = oids.nextElement();
if (!OCSPObjectIdentifiers.id_pkix_ocsp_nonce.equals(oid)) {
tmpExtensions.add(extensions.getExtension(oid));
}
}
}
signature = asn1bcReq.getOptionalSignature();
}
tmpExtensions.add(new Extension(OCSPObjectIdentifiers.id_pkix_ocsp_nonce, false, nonce));
Extension[] exts = new Extension[tmpExtensions.size()];
Extensions newExtensions = new Extensions(tmpExtensions.toArray(exts));

tmpExtensions.add(nonceExt);

Extensions newExtensions = new Extensions(tmpExtensions.toArray(new Extension[tmpExtensions.size()]));
TBSRequest newTbsReq = new TBSRequest(requestorName, requestList, newExtensions);

asn1bcReq = new org.bouncycastle.asn1.ocsp.OCSPRequest(newTbsReq, sig);
asn1bcReq = new org.bouncycastle.asn1.ocsp.OCSPRequest(newTbsReq, signature);
}

static Extension createNonceExtension(final byte[] nonce) throws IOException {
final byte[] nonceOctet = new DEROctetString(nonce).getEncoded();
return new Extension(OCSPObjectIdentifiers.id_pkix_ocsp_nonce, false, nonceOctet);
}

@JRubyMethod(name = "certid")
public IRubyObject certid() {
Ruby runtime = getRuntime();
return RubyArray.newArray(runtime, certificateIds);
public IRubyObject certid(ThreadContext context) {
if (asn1bcReq == null) return context.nil;
ASN1Sequence reqList = asn1bcReq.getTbsRequest().getRequestList();
if (reqList.size() == 0) return context.nil;

RubyArray certIds = RubyArray.newArray(context.runtime, reqList.size());
for (int i = 0; i < reqList.size(); i++) {
certIds.append(new OCSPCertificateId(context.runtime, Request.getInstance(reqList.getObjectAt(i)).getReqCert()));
}
return certIds;
}

@JRubyMethod(name = "signed?")
Expand All @@ -212,16 +231,17 @@ public RubyBoolean signed_p() {
@JRubyMethod(name = "check_nonce")
public IRubyObject check_nonce(ThreadContext context, IRubyObject response) {
final Ruby runtime = context.runtime;
final byte[] reqNonce = getNonce();
if (response instanceof OCSPBasicResponse) {
OCSPBasicResponse rubyBasicRes = (OCSPBasicResponse) response;
return checkNonceImpl(runtime, this.nonce, rubyBasicRes.getNonce());
return checkNonceImpl(runtime, reqNonce, rubyBasicRes.getNonce());
}
else if (response instanceof OCSPResponse) {
OCSPResponse rubyResp = (OCSPResponse) response;
return checkNonceImpl(runtime, this.nonce, ((OCSPBasicResponse)rubyResp.basic(context)).getNonce());
return checkNonceImpl(runtime, reqNonce, ((OCSPBasicResponse)rubyResp.basic(context)).getNonce());
}
else {
return checkNonceImpl(runtime, this.nonce, null);
return checkNonceImpl(runtime, reqNonce, null);
}
}

Expand Down Expand Up @@ -271,8 +291,16 @@ public IRubyObject sign(final ThreadContext context, IRubyObject[] args) {

OCSPReqBuilder builder = new OCSPReqBuilder();
builder.setRequestorName(signer.getSubject().getX500Name());
for (OCSPCertificateId certId : certificateIds) {
builder.addRequest(new CertificateID(certId.getCertID()));
if (asn1bcReq != null) {
ASN1Sequence reqList = asn1bcReq.getTbsRequest().getRequestList();
for (int i = 0; i < reqList.size(); i++) {
Request req = Request.getInstance(reqList.getObjectAt(i));
builder.addRequest(new CertificateID(req.getReqCert()), req.getSingleRequestExtensions());
}
Extensions exts = asn1bcReq.getTbsRequest().getRequestExtensions();
if (exts != null) {
builder.setRequestExtensions(exts);
}
}

List<X509CertificateHolder> certChain = new ArrayList<>();
Expand Down Expand Up @@ -304,8 +332,6 @@ public IRubyObject sign(final ThreadContext context, IRubyObject[] args) {
// handlePotentialOperationError(ex, e -> newSecurityError(runtime, e));
//}

if (nonce != null) addNonceImpl();

return this;
}
catch (Throwable ex) {
Expand Down Expand Up @@ -410,7 +436,8 @@ public IRubyObject initialize_copy(IRubyObject obj) {
if ( this == obj ) return this;

checkFrozen();
this.asn1bcReq = ((OCSPRequest)obj).asn1bcReq;
OCSPRequest other = (OCSPRequest) obj;
this.asn1bcReq = other.asn1bcReq;
return this;
}

Expand All @@ -437,7 +464,25 @@ private java.security.cert.Certificate findCertByName(ASN1Encodable genX500Name,
}

public byte[] getNonce() {
return this.nonce;
if (asn1bcReq == null) return null;
return extractNonce(asn1bcReq.getTbsRequest().getRequestExtensions());
}

static byte[] extractNonce(final Extensions exts) {
return exts == null ? null : extractNonce(exts.getExtension(OCSPObjectIdentifiers.id_pkix_ocsp_nonce));
}

static byte[] extractNonce(final Extension ext) {
if (ext == null) return null;

final byte[] value = ext.getExtnValue().getOctets();
try {
ASN1Primitive primitive = ASN1Primitive.fromByteArray(value);
if (primitive instanceof ASN1OctetString) {
return ((ASN1OctetString) primitive).getOctets();
}
} catch (IOException ignored) {}
return value;
}

private IRubyObject checkNonceImpl(Ruby runtime, byte[] reqNonce, byte[] respNonce) {
Expand Down
Loading
Loading