/*
* The contents of this file are subject to the Common Public License Version 1.0
* (the "License"); you may not use this file except in compliance with the License.
* You may obtain a copy of the License at http://www.eclipse.org/legal/cpl-v10.html
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS
* FOR APARTICULAR PURPOSE AND NONINFRINGEMENT.
* IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM,
* DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE,
* ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
* DEALINGS IN THE SOFTWARE.
*
* Copyright (C) 2017 Donovan Lampa <donovan.lampa@gmail.com>
* Copyright (C) 2009-2017 The JRuby Team
*
* Alternatively, the contents of this file may be used under the terms of
* either of the GNU General Public License Version 2 or later (the "GPL"),
* or the GNU Lesser General Public License Version 2.1 or later (the "LGPL"),
* in which case the provisions of the GPL or the LGPL are applicable instead
* of those above. If you wish to allow use of your version of this file only
* under the terms of either the GPL or the LGPL, and not to allow others to
* use your version of this file under the terms of the EPL, indicate your
* decision by deleting the provisions above and replace them with the notice
* and other provisions required by the GPL or the LGPL. If you do not delete
* the provisions above, a recipient may use your version of this file under
* the terms of any one of the EPL, the GPL or the LGPL.
*
*
* JRuby-OpenSSL includes software by The Legion of the Bouncy Castle Inc.
* Please, visit (http://bouncycastle.org/license.html) for licensing details.
*/
package org.jruby.ext.openssl;
import org.bouncycastle.asn1.ASN1GeneralizedTime;
import org.bouncycastle.asn1.ASN1ObjectIdentifier;
import org.bouncycastle.asn1.DERNull;
import org.bouncycastle.asn1.ocsp.BasicOCSPResponse;
import org.bouncycastle.asn1.ocsp.CertID;
import org.bouncycastle.asn1.ocsp.CertStatus;
import org.bouncycastle.asn1.ocsp.OCSPObjectIdentifiers;
import org.bouncycastle.asn1.ocsp.ResponderID;
import org.bouncycastle.asn1.ocsp.RevokedInfo;
import org.bouncycastle.asn1.ocsp.SingleResponse;
import org.bouncycastle.asn1.x509.CRLReason;
import org.bouncycastle.asn1.x509.Extension;
import org.bouncycastle.asn1.x509.Extensions;
import org.bouncycastle.asn1.x509.SubjectPublicKeyInfo;
import org.bouncycastle.cert.X509CertificateHolder;
import org.bouncycastle.cert.ocsp.BasicOCSPResp;
import org.bouncycastle.cert.ocsp.BasicOCSPRespBuilder;
import org.bouncycastle.cert.ocsp.CertificateID;
import org.bouncycastle.cert.ocsp.RespID;
import org.bouncycastle.cert.ocsp.SingleResp;
import org.bouncycastle.operator.ContentSigner;
import org.bouncycastle.operator.ContentVerifierProvider;
import org.bouncycastle.operator.DigestCalculator;
import org.bouncycastle.operator.DigestCalculatorProvider;
import org.bouncycastle.operator.OperatorCreationException;
import org.bouncycastle.operator.jcajce.JcaContentSignerBuilder;
import org.bouncycastle.operator.jcajce.JcaContentVerifierProviderBuilder;
import org.bouncycastle.operator.jcajce.JcaDigestCalculatorProviderBuilder;
import org.jruby.Ruby;
import org.jruby.RubyArray;
import org.jruby.RubyBoolean;
import org.jruby.RubyClass;
import org.jruby.RubyFixnum;
import org.jruby.RubyInteger;
import org.jruby.RubyModule;
import org.jruby.RubyObject;
import org.jruby.RubyString;
import org.jruby.RubyTime;
import org.jruby.anno.JRubyMethod;
import org.jruby.exceptions.RaiseException;
import org.jruby.ext.openssl.impl.ASN1Registry;
import org.jruby.ext.openssl.x509store.X509AuxCertificate;
import org.jruby.ext.openssl.x509store.X509Utils;
import org.jruby.runtime.Arity;
import org.jruby.runtime.ObjectAllocator;
import org.jruby.runtime.ThreadContext;
import org.jruby.runtime.Visibility;
import org.jruby.runtime.builtin.IRubyObject;
import static org.jruby.ext.openssl.Digest._Digest;
import static org.jruby.ext.openssl.OCSP._OCSP;
import static org.jruby.ext.openssl.OCSP.newOCSPError;
import static org.jruby.ext.openssl.X509._X509;
import java.io.IOException;
import java.security.MessageDigest;
import java.security.PublicKey;
import java.security.cert.CertificateEncodingException;
import java.security.cert.CertificateParsingException;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Date;
import java.util.Iterator;
import java.util.List;
/*
* An OpenSSL::OCSP::BasicResponse contains the status of a certificate
* check which is created from an OpenSSL::OCSP::Request.
* A BasicResponse is more detailed than a Response.
*
* @author lampad
*/
public class OCSPBasicResponse extends RubyObject {
private static final long serialVersionUID = 8755480816625884227L;
private static final String OCSP_NOCERTS = "NOCERTS";
private static final String OCSP_NOCHAIN = "NOCHAIN";
private static final String OCSP_NOCHECKS = "NOCHECKS";
private static final String OCSP_NOTIME = "NOTIME";
private static final String OCSP_NOSIGS = "NOSIGS";
private static final String OCSP_NOVERIFY = "NOVERIFY";
private static final String OCSP_NOINTERN = "NOINTERN";
private static final String OCSP_RESPID_KEY = "RESPID_KEY";
private static final String OCSP_TRUSTOTHER = "TRUSTOTHER";
private static ObjectAllocator BASICRESPONSE_ALLOCATOR = new ObjectAllocator() {
public IRubyObject allocate(Ruby runtime, RubyClass klass) {
return new OCSPBasicResponse(runtime, klass);
}
};
public static void createBasicResponse(final Ruby runtime, final RubyModule OCSP) {
RubyClass BasicResponse = OCSP.defineClassUnder("BasicResponse", runtime.getObject(), BASICRESPONSE_ALLOCATOR);
BasicResponse.defineAnnotatedMethods(OCSPBasicResponse.class);
}
private byte[] nonce;
private List<OCSPSingleResponse> singleResponses = new ArrayList<OCSPSingleResponse>();
private BasicOCSPResponse asn1BCBasicOCSPResp;
private List<Extension> extensions = new ArrayList<Extension>();
public OCSPBasicResponse(Ruby runtime, RubyClass metaClass) {
super(runtime, metaClass);
}
public OCSPBasicResponse(Ruby runtime) {
this(runtime, (RubyClass) _OCSP(runtime).getConstantAt("BasicResponse"));
}
@JRubyMethod(name = "initialize", visibility = Visibility.PRIVATE)
public IRubyObject initialize(final ThreadContext context, IRubyObject der) {
if (der == null || der.isNil()) return this;
asn1BCBasicOCSPResp = BasicOCSPResponse.getInstance(StringHelper.readPossibleDERInput(context, der).getBytes());
return this;
}
@JRubyMethod(name = "initialize", visibility = Visibility.PRIVATE)
public IRubyObject initialize(final ThreadContext context) {
return this;
}
@Override
@JRubyMethod(name = "initialize_copy", visibility = Visibility.PRIVATE)
public IRubyObject initialize_copy(IRubyObject obj) {
if ( this == obj ) return this;
checkFrozen();
this.asn1BCBasicOCSPResp = ((OCSPBasicResponse)obj).getASN1BCOCSPResp();
return this;
}
@JRubyMethod(name = "add_nonce", rest = true)
public OCSPBasicResponse add_nonce(IRubyObject[] args) {
Ruby runtime = getRuntime();
byte[] tmpNonce;
if ( Arity.checkArgumentCount(runtime, args, 0, 1) == 0 ) {
tmpNonce = generateNonce();
}
else {
RubyString input = (RubyString)args[0];
tmpNonce = input.getBytes();
}
extensions.add(new Extension(OCSPObjectIdentifiers.id_pkix_ocsp_nonce, false, tmpNonce));
nonce = tmpNonce;
return this;
}
@JRubyMethod(name = "add_status", rest = true)
public OCSPBasicResponse add_status(final ThreadContext context, IRubyObject[] args) {
Ruby runtime = context.getRuntime();
Arity.checkArgumentCount(runtime, args, 7, 7);
IRubyObject certificateId = args[0];
IRubyObject status = args[1];
IRubyObject reason = args[2];
IRubyObject revocation_time = args[3];
IRubyObject this_update = args[4];
IRubyObject next_update = args[5];
IRubyObject extensions = args[6];
CertStatus certStatus = null;
switch (RubyFixnum.fix2int((RubyFixnum)status)) {
case 0 :
certStatus = new CertStatus();
break;
case 1 :
ASN1GeneralizedTime revTime = rubyIntOrTimeToGenTime(revocation_time);
RevokedInfo revokedInfo = new RevokedInfo(revTime,
CRLReason.lookup(RubyFixnum.fix2int((RubyFixnum)reason)));
certStatus = new CertStatus(revokedInfo);
break;
case 2 :
certStatus = new CertStatus(2, DERNull.INSTANCE);
break;
default :
break;
}
ASN1GeneralizedTime thisUpdate = rubyIntOrTimeToGenTime(this_update);
ASN1GeneralizedTime nextUpdate = rubyIntOrTimeToGenTime(next_update);
Extensions singleExtensions = convertRubyExtensions(extensions);
CertID certID = ((OCSPCertificateId)certificateId).getCertID();
SingleResponse ocspSingleResp = new SingleResponse(certID, certStatus, thisUpdate, nextUpdate, singleExtensions);
OCSPSingleResponse rubySingleResp = new OCSPSingleResponse(runtime);
try {
rubySingleResp.initialize(context, RubyString.newString(runtime, ocspSingleResp.getEncoded()));
singleResponses.add(rubySingleResp);
}
catch (IOException e) {
throw newOCSPError(runtime, e);
}
return this;
}
@JRubyMethod(name = "copy_nonce")
public IRubyObject copy_nonce(final ThreadContext context, IRubyObject request) {
add_nonce(new IRubyObject[] {RubyString.newString(getRuntime(), ((OCSPRequest)request).getNonce())});
return RubyFixnum.one(context.getRuntime());
}
@JRubyMethod(name = "find_response")
public IRubyObject find_response(final ThreadContext context, IRubyObject certId) {
if (certId.isNil()) return context.nil;
OCSPCertificateId rubyCertId = (OCSPCertificateId)certId;
IRubyObject retResp = context.nil;
for (OCSPSingleResponse singleResp : singleResponses) {
CertID thisId = rubyCertId.getCertID();
CertID thatId = singleResp.getBCSingleResp().getCertID();
if (thisId.equals(thatId)) {
retResp = singleResp;
break;
}
}
return retResp;
}
@JRubyMethod(name = "responses")
public IRubyObject responses() {
return RubyArray.newArray(getRuntime(), singleResponses);
}
@JRubyMethod(name = "sign", rest = true)
public IRubyObject sign(final ThreadContext context, IRubyObject[] args) {
Ruby runtime = context.getRuntime();
int flag = 0;
IRubyObject additionalCerts = context.nil;
IRubyObject flags = context.nil;
IRubyObject digest = context.nil;
Digest digestInstance = new Digest(runtime, _Digest(runtime));
List<X509CertificateHolder> addlCerts = new ArrayList<X509CertificateHolder>();
switch (Arity.checkArgumentCount(runtime, args, 2, 5)) {
case 3 :
additionalCerts = args[2];
break;
case 4 :
additionalCerts = args[2];
flags = args[3];
break;
case 5 :
additionalCerts = args[2];
flags = args[3];
digest = args[4];
break;
default :
break;
}
if (digest.isNil()) digest = digestInstance.initialize(context, new IRubyObject[] { RubyString.newString(runtime, "SHA1") });
if (!flags.isNil()) flag = RubyFixnum.fix2int(flags);
if (additionalCerts.isNil()) flag |= RubyFixnum.fix2int((RubyFixnum)_OCSP(runtime).getConstant(OCSP_NOCERTS));
X509Cert signer = (X509Cert) args[0];
PKey signerKey = (PKey) args[1];
String keyAlg = signerKey.getAlgorithm();
String digAlg = ((Digest) digest).getShortAlgorithm();
JcaContentSignerBuilder signerBuilder = new JcaContentSignerBuilder(digAlg + "with" + keyAlg);
signerBuilder.setProvider("BC");
ContentSigner contentSigner = null;
try {
contentSigner = signerBuilder.build(signerKey.getPrivateKey());
}
catch (OperatorCreationException e) {
throw newOCSPError(runtime, e);
}
BasicOCSPRespBuilder respBuilder = null;
try {
if ((flag & RubyFixnum.fix2int((RubyFixnum)_OCSP(runtime).getConstant(OCSP_RESPID_KEY))) != 0) {
JcaDigestCalculatorProviderBuilder dcpb = new JcaDigestCalculatorProviderBuilder();
dcpb.setProvider("BC");
DigestCalculatorProvider dcp = dcpb.build();
DigestCalculator calculator = dcp.get(contentSigner.getAlgorithmIdentifier());
respBuilder = new BasicOCSPRespBuilder(SubjectPublicKeyInfo.getInstance(signerKey.getPublicKey().getEncoded()), calculator);
}
else {
respBuilder = new BasicOCSPRespBuilder(new RespID(signer.getSubject().getX500Name()));
}
}
catch (Exception e) {
throw newOCSPError(runtime, e);
}
X509CertificateHolder[] chain = null;
try {
if ((flag & RubyFixnum.fix2int((RubyFixnum)_OCSP(runtime).getConstant(OCSP_NOCERTS))) == 0) {
addlCerts.add(new X509CertificateHolder(signer.getAuxCert().getEncoded()));
if (!additionalCerts.isNil()) {
Iterator<java.security.cert.Certificate> rubyAddlCerts = ((RubyArray)additionalCerts).iterator();
while (rubyAddlCerts.hasNext()) {
java.security.cert.Certificate cert = rubyAddlCerts.next();
addlCerts.add(new X509CertificateHolder(cert.getEncoded()));
}
}
chain = addlCerts.toArray(new X509CertificateHolder[addlCerts.size()]);
}
}
catch (Exception e) {
throw newOCSPError(runtime, e);
}
Date producedAt = null;
if ((flag & RubyFixnum.fix2int((RubyFixnum)_OCSP(runtime).getConstant(OCSP_NOTIME))) == 0) {
producedAt = new Date();
}
for (OCSPSingleResponse resp : singleResponses) {
SingleResp singleResp = new SingleResp(resp.getBCSingleResp());
respBuilder.addResponse(singleResp.getCertID(),
singleResp.getCertStatus(),
singleResp.getThisUpdate(),
singleResp.getNextUpdate(),
resp.getBCSingleResp().getSingleExtensions());
}
try {
Extension[] respExtAry = new Extension[extensions.size()];
Extensions respExtensions = new Extensions(extensions.toArray(respExtAry));
BasicOCSPResp bcBasicOCSPResp = respBuilder.setResponseExtensions(respExtensions).build(contentSigner, chain, producedAt);
asn1BCBasicOCSPResp = BasicOCSPResponse.getInstance(bcBasicOCSPResp.getEncoded());
}
catch (Exception e) {
throw newOCSPError(runtime, e);
}
return this;
}
@JRubyMethod(name = "verify", rest = true)
public IRubyObject verify(final ThreadContext context, IRubyObject[] args) {
Ruby runtime = context.runtime;
int flags = 0;
IRubyObject certificates = args[0];
IRubyObject store = args[1];
boolean ret = false;
if (Arity.checkArgumentCount(runtime, args, 2, 3) == 3) {
flags = RubyFixnum.fix2int(args[2]);
}
JcaContentVerifierProviderBuilder jcacvpb = new JcaContentVerifierProviderBuilder();
jcacvpb.setProvider("BC");
BasicOCSPResp basicOCSPResp = getBasicOCSPResp();
java.security.cert.Certificate signer = findSignerCert(context, asn1BCBasicOCSPResp, convertRubyCerts(certificates), flags);
if ( signer == null ) return RubyBoolean.newBoolean(runtime, false);
if ( (flags & RubyFixnum.fix2int((RubyFixnum)_OCSP(runtime).getConstant(OCSP_NOINTERN))) == 0 &&
(flags & RubyFixnum.fix2int((RubyFixnum)_OCSP(runtime).getConstant(OCSP_TRUSTOTHER))) != 0 ) {
flags |= RubyFixnum.fix2int((RubyFixnum)_OCSP(runtime).getConstant(OCSP_NOVERIFY));
}
if ( (flags & RubyFixnum.fix2int((RubyFixnum)_OCSP(runtime).getConstant(OCSP_NOSIGS))) == 0 ) {
PublicKey sPKey = signer.getPublicKey();
if ( sPKey == null ) return RubyBoolean.newBoolean(runtime, false);
try {
ContentVerifierProvider cvp = jcacvpb.build(sPKey);
ret = basicOCSPResp.isSignatureValid(cvp);
}
catch (Exception e) {
throw newOCSPError(runtime, e);
}
}
if ((flags & RubyFixnum.fix2int((RubyFixnum)_OCSP(runtime).getConstant(OCSP_NOVERIFY))) == 0) {
List<X509Cert> untrustedCerts = null;
if ((flags & RubyFixnum.fix2int((RubyFixnum)_OCSP(runtime).getConstant(OCSP_NOCHAIN))) != 0) {
}
else if (basicOCSPResp.getCerts() != null && (certificates != null && !((RubyArray)certificates).isEmpty())) {
untrustedCerts = getCertsFromResp();
Iterator<java.security.cert.Certificate> certIt = ((RubyArray)certificates).iterator();
while (certIt.hasNext()) {
try {
untrustedCerts.add(X509Cert.wrap(context, certIt.next().getEncoded()));
}
catch (CertificateEncodingException e) {
throw newOCSPError(runtime, e);
}
}
}
else {
untrustedCerts = getCertsFromResp();
}
RubyArray rUntrustedCerts = RubyArray.newEmptyArray(runtime);
if (untrustedCerts != null) {
X509Cert[] rubyCerts = new X509Cert[untrustedCerts.size()];
rUntrustedCerts = RubyArray.newArray(runtime, untrustedCerts.toArray(rubyCerts));
}
X509StoreContext ctx;
try {
ctx = X509StoreContext.newStoreContext(context, (X509Store)store, X509Cert.wrap(runtime, signer), rUntrustedCerts);
}
catch (CertificateEncodingException e) {
throw newOCSPError(runtime, e);
}
ctx.set_purpose(context, _X509(runtime).getConstant("PURPOSE_OCSP_HELPER"));
ret = ctx.verify(context).isTrue();
IRubyObject chain = ctx.chain(context);
if ((flags & RubyFixnum.fix2int((RubyFixnum)_OCSP(runtime).getConstant(OCSP_NOCHECKS))) > 0) {
ret = true;
}
try {
if (checkIssuer(getBasicOCSPResp(), chain)) return RubyBoolean.newBoolean(runtime, true);
}
catch (IOException e) {
throw newOCSPError(runtime, e);
}
if ((flags & RubyFixnum.fix2int((RubyFixnum)_OCSP(runtime).getConstant(OCSP_NOCHAIN))) != 0) {
return RubyBoolean.newBoolean(runtime, ret);
}
else {
X509Cert rootCA = (X509Cert)((RubyArray)chain).last();
PublicKey rootKey = rootCA.getAuxCert().getPublicKey();
try {
// check if self-signed and valid (trusts itself)
rootCA.getAuxCert().verify(rootKey);
ret = true;
}
catch (Exception e) {
ret = false;
}
}
}
return RubyBoolean.newBoolean(runtime, ret);
}
@JRubyMethod(name = "status")
public IRubyObject status(ThreadContext context) {
final Ruby runtime = context.runtime;
RubyArray ret = RubyArray.newArray(runtime, singleResponses.size());
for (OCSPSingleResponse resp : singleResponses) {
RubyArray respAry = RubyArray.newArray(runtime, 7);
respAry.append(resp.certid(context));
respAry.append(resp.cert_status());
respAry.append(resp.revocation_reason());
respAry.append(resp.revocation_time());
respAry.append(resp.this_update());
respAry.append(resp.next_update());
respAry.append(resp.extensions());
ret.add(respAry);
}
return ret;
}
@JRubyMethod(name = "to_der")
public IRubyObject to_der() {
Ruby runtime = getRuntime();
IRubyObject ret;
try {
ret = RubyString.newString(runtime, asn1BCBasicOCSPResp.getEncoded());
}
catch (IOException e) {
throw newOCSPError(runtime, e);
}
return ret;
}
private boolean checkIssuer(BasicOCSPResp basicOCSPResp, IRubyObject chain) throws IOException {
boolean ret = false;
if ( ((RubyArray)chain).size() <= 0 ) return false;
List<SingleResp> singleResponses = Arrays.asList(basicOCSPResp.getResponses());
CertificateID certId = checkCertIds(singleResponses);
X509Cert signer = (X509Cert)((RubyArray)chain).first();
if (((RubyArray)chain).size() > 1) {
X509Cert signerCA = (X509Cert)((RubyArray)chain).entry(1);
if(matchIssuerId(signerCA, certId, singleResponses)) {
return checkDelegated(signerCA);
}
}
else {
ret = matchIssuerId(signer, certId, singleResponses);
}
return ret;
}
private boolean checkDelegated(X509Cert signerCA) {
try {
return (signerCA.getAuxCert().getExFlags() & X509Utils.EXFLAG_XKUSAGE) != 0 &&
(signerCA.getAuxCert().getExtendedKeyUsage().contains(ASN1Registry.OBJ_OCSP_sign));
}
catch (CertificateParsingException e) {
throw newOCSPError(getRuntime(), e);
}
}
private boolean matchIssuerId(X509Cert signerCA, CertificateID certId, List<SingleResp> singleResponses) throws IOException {
Ruby runtime = getRuntime();
if (certId == null) {
//gotta check em all
for(SingleResp resp : singleResponses) {
CertificateID tempId = resp.getCertID();
if(!matchIssuerId(signerCA, tempId, null)) return false;
}
return true;
}
else {
// we have a matching cid
ASN1ObjectIdentifier alg = certId.getHashAlgOID();
String sym = ASN1.oid2Sym(runtime, alg);
MessageDigest md = Digest.getDigest(runtime, sym);
byte[] issuerNameDigest = md.digest(signerCA.getIssuer().getX500Name().getEncoded());
byte[] issuerKeyDigest = md.digest(signerCA.getAuxCert().getPublicKey().getEncoded());
if(!issuerNameDigest.equals(certId.getIssuerNameHash())) return false;
if(!issuerKeyDigest.equals(certId.getIssuerKeyHash())) return false;
return true;
}
}
private CertificateID checkCertIds(List<SingleResp> singleResponses) {
ArrayList<SingleResp> ary = new ArrayList<SingleResp>(singleResponses);
CertificateID cid = ary.remove(0).getCertID();
for (SingleResp singleResp : ary) {
if (!cid.equals(singleResp.getCertID())) return null;
}
return cid;
}
public BasicOCSPResponse getASN1BCOCSPResp() {
return this.asn1BCBasicOCSPResp;
}
public byte[] getNonce() {
return this.nonce;
}
private byte[] generateNonce() {
// OSSL currently generates 16 byte nonce by default
return generateNonce(new byte[16]);
}
private byte[] generateNonce(byte[] bytes) {
OpenSSL.getSecureRandom(getRuntime()).nextBytes(bytes);
return bytes;
}
private ASN1GeneralizedTime rubyIntOrTimeToGenTime(IRubyObject intOrTime) {
if (intOrTime.isNil()) return null;
Date retTime = new Date();
if (intOrTime instanceof RubyInteger) {
retTime.setTime(retTime.getTime() + RubyFixnum.fix2int((RubyFixnum)intOrTime)*1000);
}
else if (intOrTime instanceof RubyTime) {
retTime = ((RubyTime)intOrTime).getJavaDate();
}
else {
throw Utils.newArgumentError(
getRuntime(), new IllegalArgumentException("Unknown Revocation Time class: " + intOrTime.getClass().getName())
);
}
return new ASN1GeneralizedTime(retTime);
}
private Extensions convertRubyExtensions(IRubyObject extensions) {
if (extensions.isNil()) return null;
List<Extension> retExtensions = new ArrayList<Extension>();
Iterator<IRubyObject> rubyExtensions = ((RubyArray)extensions).iterator();
while (rubyExtensions.hasNext()) {
X509Extension rubyExt = (X509Extension)rubyExtensions.next();
Extension ext = Extension.getInstance(((RubyString)rubyExt.to_der()).getBytes());
retExtensions.add(ext);
}
Extension[] exts = new Extension[retExtensions.size()];
retExtensions.toArray(exts);
return new Extensions(exts);
}
private List<java.security.cert.Certificate> convertRubyCerts(IRubyObject certificates) {
Iterator<java.security.cert.Certificate> it = ((RubyArray)certificates).iterator();
List<java.security.cert.Certificate> ret = new ArrayList<java.security.cert.Certificate>();
while (it.hasNext()) {
ret.add(it.next());
}
return ret;
}
private java.security.cert.Certificate findSignerCert(final ThreadContext context,
BasicOCSPResponse basicResp, List<java.security.cert.Certificate> certificates, int flags) {
final Ruby runtime = context.runtime;
ResponderID respID = basicResp.getTbsResponseData().getResponderID();
java.security.cert.Certificate ret;
ret = findSignerByRespId(context, certificates, respID);
if (ret == null && (flags & RubyFixnum.fix2int((RubyFixnum)_OCSP(runtime).getConstant(OCSP_NOINTERN))) == 0) {
List<X509AuxCertificate> javaCerts = new ArrayList<X509AuxCertificate>();
for (X509CertificateHolder cert : getBasicOCSPResp().getCerts()) {
try {
javaCerts.add(X509Cert.wrap(context, cert.getEncoded()).getAuxCert());
}
catch (IOException e) {
throw newOCSPError(runtime, e);
}
}
ret = findSignerByRespId(context, javaCerts, respID);
}
return ret;
}
private java.security.cert.Certificate findSignerByRespId(final ThreadContext context, List<? extends java.security.cert.Certificate> certificates, ResponderID respID) {
if (respID.getName() != null) {
for (java.security.cert.Certificate cert : certificates) {
try {
X509Cert rubyCert = X509Cert.wrap(context, cert);
if (rubyCert.getSubject().getX500Name().equals(respID.getName())) return cert;
}
catch (CertificateEncodingException e) {
throw newOCSPError(context.runtime, e);
}
}
}
else {
// Ignore anything that's not SHA1 (weirdly) SHA_DIGEST_LENGTH == 20
if (respID.getKeyHash().length != 20) return null;
for (java.security.cert.Certificate cert : certificates) {
byte[] pubKeyDigest = Digest.digest(
context,
this,
RubyString.newString(context.runtime, "SHA1"),
RubyString.newString(context.runtime, cert.getPublicKey().getEncoded())
).getBytes();
if (respID.getKeyHash().equals(pubKeyDigest)) return cert;
}
}
return null;
}
private List<X509Cert> getCertsFromResp() {
Ruby runtime = getRuntime();
ThreadContext context = runtime.getCurrentContext();
List<X509Cert> retCerts = new ArrayList<X509Cert>();
List<X509CertificateHolder> respCerts = Arrays.asList(getBasicOCSPResp().getCerts());
for (X509CertificateHolder cert : respCerts) {
try {
retCerts.add(X509Cert.wrap(context, cert.getEncoded()));
}
catch (IOException e) {
throw newOCSPError(runtime, e);
}
}
return retCerts;
}
private BasicOCSPResp getBasicOCSPResp() {
return new BasicOCSPResp(asn1BCBasicOCSPResp);
}
}