-
Notifications
You must be signed in to change notification settings - Fork 81
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
* Basic structure of everything. Lots more to do. * Move toward more BC-centric processing TODO: - Replace manual construction of Request in #sign with OCSPReqBuilder - Fix verification * Verification of BasicResponses working * There seems to be an issue with decoding objects using ASN1.decode and then trying to reconstitute decoded objects by calling to_der on them. Some data seems to be left off. Not covering it here because of scope creep.
Showing
11 changed files
with
2,554 additions
and
6 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,215 @@ | ||
/* | ||
* 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 java.security.Security; | ||
import java.util.HashMap; | ||
import java.util.Map; | ||
|
||
import org.bouncycastle.jce.provider.BouncyCastleProvider; | ||
import org.jruby.Ruby; | ||
import org.jruby.RubyClass; | ||
import org.jruby.RubyFixnum; | ||
import org.jruby.RubyModule; | ||
import org.jruby.exceptions.RaiseException; | ||
import org.jruby.runtime.builtin.IRubyObject; | ||
|
||
/** | ||
* OCSP | ||
* | ||
* @author lampad | ||
*/ | ||
public class OCSP { | ||
|
||
//Response has valid confirmations | ||
private static final String _RESPONSE_STATUS_SUCCESSFUL_STR = "RESPONSE_STATUS_SUCCESSFUL"; | ||
private static final int _RESPONSE_STATUS_SUCCESSFUL = 0; | ||
//Illegal confirmation request | ||
private static final String _RESPONSE_STATUS_MALFORMEDREQUEST_STR = "RESPONSE_STATUS_MALFORMEDREQUEST"; | ||
private static final int _RESPONSE_STATUS_MALFORMEDREQUEST = 1; | ||
//Internal error in issuer | ||
private static final String _RESPONSE_STATUS_INTERNALERROR_STR = "RESPONSE_STATUS_INTERNALERROR"; | ||
private static final int _RESPONSE_STATUS_INTERNALERROR = 2; | ||
//Try again later | ||
private static final String _RESPONSE_STATUS_TRYLATER_STR = "RESPONSE_STATUS_TRYLATER"; | ||
private static final int _RESPONSE_STATUS_TRYLATER = 3; | ||
//You must sign the request and resubmit | ||
private static final String _RESPONSE_STATUS_SIGREQUIRED_STR = "RESPONSE_STATUS_SIGREQUIRED"; | ||
private static final int _RESPONSE_STATUS_SIGREQUIRED = 5; | ||
//Your request is unauthorized. | ||
private static final String _RESPONSE_STATUS_UNAUTHORIZED_STR = "RESPONSE_STATUS_UNAUTHORIZED"; | ||
private static final int _RESPONSE_STATUS_UNAUTHORIZED = 6; | ||
|
||
private static final Map<Integer, String> responseMap; | ||
|
||
//The certificate was revoked for an unknown reason | ||
private static final int _REVOKED_STATUS_NOSTATUS = -1; | ||
//The certificate was revoked for an unspecified reason | ||
private static final int _REVOKED_STATUS_UNSPECIFIED = 0; | ||
//The certificate was revoked due to a key compromise | ||
private static final int _REVOKED_STATUS_KEYCOMPROMISE = 1; | ||
//This CA certificate was revoked due to a key compromise | ||
private static final int _REVOKED_STATUS_CACOMPROMISE = 2; | ||
//The certificate subject's name or other information changed | ||
private static final int _REVOKED_STATUS_AFFILIATIONCHANGED = 3; | ||
//The certificate was superseded by a new certificate | ||
private static final int _REVOKED_STATUS_SUPERSEDED = 4; | ||
//The certificate is no longer needed | ||
private static final int _REVOKED_STATUS_CESSATIONOFOPERATION = 5; | ||
//The certificate is on hold | ||
private static final int _REVOKED_STATUS_CERTIFICATEHOLD = 6; | ||
//The certificate was previously on hold and should now be removed from the CRL | ||
private static final int _REVOKED_STATUS_REMOVEFROMCRL = 8; | ||
|
||
//Do not include certificates in the response | ||
private static final int _NOCERTS = 0x1; | ||
//Do not search certificates contained in the response for a signer | ||
private static final int _NOINTERN = 0x2; | ||
//Do not check the signature on the response | ||
private static final int _NOSIGS = 0x4; | ||
//Do not verify the certificate chain on the response | ||
private static final int _NOCHAIN = 0x8; | ||
//Do not verify the response at all | ||
private static final int _NOVERIFY = 0x10; | ||
//Do not check trust | ||
private static final int _NOEXPLICIT = 0x20; | ||
//(This flag is not used by OpenSSL 1.0.1g) | ||
private static final int _NOCASIGN = 0x40; | ||
//(This flag is not used by OpenSSL 1.0.1g) | ||
private static final int _NODELEGATED = 0x80; | ||
//Do not make additional signing certificate checks | ||
private static final int _NOCHECKS = 0x100; | ||
//Do not verify additional certificates | ||
private static final int _TRUSTOTHER = 0x200; | ||
//Identify the response by signing the certificate key ID | ||
private static final int _RESPID_KEY = 0x400; | ||
//Do not include producedAt time in response | ||
private static final int _NOTIME = 0x800; | ||
|
||
/* | ||
* Indicates the certificate is not revoked but does not necessarily mean | ||
* the certificate was issued or that this response is within the | ||
* certificate's validity interval | ||
*/ | ||
private static final int _V_CERTSTATUS_GOOD = 0; | ||
/* Indicates the certificate has been revoked either permanently or | ||
* temporarily (on hold). | ||
*/ | ||
private static final int _V_CERTSTATUS_REVOKED = 1; | ||
/* Indicates the responder does not know about the certificate being | ||
* requested. | ||
*/ | ||
private static final int _V_CERTSTATUS_UNKNOWN = 2; | ||
|
||
//The responder ID is based on the key name. | ||
private static final int _V_RESPID_NAME = 0; | ||
//The responder ID is based on the public key. | ||
private static final int _V_RESPID_KEY =1; | ||
|
||
static { | ||
Map<Integer, String> resMap = new HashMap<Integer, String>(); | ||
resMap.put(_RESPONSE_STATUS_SUCCESSFUL, _RESPONSE_STATUS_SUCCESSFUL_STR); | ||
resMap.put(_RESPONSE_STATUS_MALFORMEDREQUEST, _RESPONSE_STATUS_MALFORMEDREQUEST_STR); | ||
resMap.put(_RESPONSE_STATUS_INTERNALERROR, _RESPONSE_STATUS_INTERNALERROR_STR); | ||
resMap.put(_RESPONSE_STATUS_TRYLATER, _RESPONSE_STATUS_TRYLATER_STR); | ||
resMap.put(_RESPONSE_STATUS_SIGREQUIRED, _RESPONSE_STATUS_SIGREQUIRED_STR); | ||
resMap.put(_RESPONSE_STATUS_UNAUTHORIZED, _RESPONSE_STATUS_UNAUTHORIZED_STR); | ||
responseMap = resMap; | ||
} | ||
|
||
public static void createOCSP(final Ruby runtime, final RubyModule OpenSSL) { | ||
final RubyModule OCSP = OpenSSL.defineModuleUnder("OCSP"); | ||
final RubyClass OpenSSLError = OpenSSL.getClass("OpenSSLError"); | ||
Security.addProvider(new BouncyCastleProvider()); | ||
OCSP.defineClassUnder("OCSPError", OpenSSLError, OpenSSLError.getAllocator()); | ||
|
||
OCSPBasicResponse.createBasicResponse(runtime, OCSP); | ||
OCSPCertificateId.createCertificateId(runtime, OCSP); | ||
OCSPRequest.createRequest(runtime, OCSP); | ||
OCSPResponse.createResponse(runtime, OCSP); | ||
OCSPSingleResponse.createSingleResponse(runtime, OCSP); | ||
|
||
//ResponseStatuses | ||
OCSP.setConstant(_RESPONSE_STATUS_SUCCESSFUL_STR, runtime.newFixnum(_RESPONSE_STATUS_SUCCESSFUL)); | ||
OCSP.setConstant(_RESPONSE_STATUS_MALFORMEDREQUEST_STR, runtime.newFixnum(_RESPONSE_STATUS_MALFORMEDREQUEST)); | ||
OCSP.setConstant(_RESPONSE_STATUS_INTERNALERROR_STR, runtime.newFixnum(_RESPONSE_STATUS_INTERNALERROR)); | ||
OCSP.setConstant(_RESPONSE_STATUS_TRYLATER_STR, runtime.newFixnum(_RESPONSE_STATUS_TRYLATER)); | ||
OCSP.setConstant(_RESPONSE_STATUS_SIGREQUIRED_STR, runtime.newFixnum(_RESPONSE_STATUS_SIGREQUIRED)); | ||
OCSP.setConstant(_RESPONSE_STATUS_UNAUTHORIZED_STR, runtime.newFixnum(_RESPONSE_STATUS_UNAUTHORIZED)); | ||
|
||
//RevocationReasons | ||
OCSP.setConstant("REVOKED_STATUS_NOSTATUS", runtime.newFixnum(_REVOKED_STATUS_NOSTATUS)); | ||
OCSP.setConstant("REVOKED_STATUS_UNSPECIFIED", runtime.newFixnum(_REVOKED_STATUS_UNSPECIFIED)); | ||
OCSP.setConstant("REVOKED_STATUS_KEYCOMPROMISE", runtime.newFixnum(_REVOKED_STATUS_KEYCOMPROMISE)); | ||
OCSP.setConstant("REVOKED_STATUS_CACOMPROMISE", runtime.newFixnum(_REVOKED_STATUS_CACOMPROMISE)); | ||
OCSP.setConstant("REVOKED_STATUS_AFFILIATIONCHANGED", runtime.newFixnum(_REVOKED_STATUS_AFFILIATIONCHANGED)); | ||
OCSP.setConstant("REVOKED_STATUS_SUPERSEDED", runtime.newFixnum(_REVOKED_STATUS_SUPERSEDED)); | ||
OCSP.setConstant("REVOKED_STATUS_CESSATIONOFOPERATION", runtime.newFixnum(_REVOKED_STATUS_CESSATIONOFOPERATION)); | ||
OCSP.setConstant("REVOKED_STATUS_CERTIFICATEHOLD", runtime.newFixnum(_REVOKED_STATUS_CERTIFICATEHOLD)); | ||
OCSP.setConstant("REVOKED_STATUS_REMOVEFROMCRL", runtime.newFixnum(_REVOKED_STATUS_REMOVEFROMCRL)); | ||
|
||
OCSP.setConstant("NOCERTS", runtime.newFixnum(_NOCERTS)); | ||
OCSP.setConstant("NOINTERN", runtime.newFixnum(_NOINTERN)); | ||
OCSP.setConstant("NOSIGS", runtime.newFixnum(_NOSIGS)); | ||
OCSP.setConstant("NOCHAIN", runtime.newFixnum(_NOCHAIN)); | ||
OCSP.setConstant("NOVERIFY", runtime.newFixnum(_NOVERIFY)); | ||
OCSP.setConstant("NOEXPLICIT", runtime.newFixnum(_NOEXPLICIT)); | ||
OCSP.setConstant("NOCASIGN", runtime.newFixnum(_NOCASIGN)); | ||
OCSP.setConstant("NODELEGATED", runtime.newFixnum(_NODELEGATED)); | ||
OCSP.setConstant("NOCHECKS", runtime.newFixnum(_NOCHECKS)); | ||
OCSP.setConstant("TRUSTOTHER", runtime.newFixnum(_TRUSTOTHER)); | ||
OCSP.setConstant("RESPID_KEY", runtime.newFixnum(_RESPID_KEY)); | ||
OCSP.setConstant("NOTIME", runtime.newFixnum(_NOTIME)); | ||
|
||
OCSP.setConstant("V_CERTSTATUS_GOOD", runtime.newFixnum(_V_CERTSTATUS_GOOD)); | ||
OCSP.setConstant("V_CERTSTATUS_REVOKED", runtime.newFixnum(_V_CERTSTATUS_REVOKED)); | ||
OCSP.setConstant("V_CERTSTATUS_UNKNOWN", runtime.newFixnum(_V_CERTSTATUS_UNKNOWN)); | ||
|
||
OCSP.setConstant("V_RESPID_NAME", runtime.newFixnum(_V_RESPID_NAME)); | ||
OCSP.setConstant("V_RESPID_KEY", runtime.newFixnum(_V_RESPID_KEY)); | ||
} | ||
|
||
public static String getResponseStringForValue(IRubyObject fixnum) { | ||
RubyFixnum rubyFixnum = (RubyFixnum) fixnum; | ||
return responseMap.get((int)rubyFixnum.getLongValue()); | ||
} | ||
|
||
public static RaiseException newOCSPError(Ruby runtime, Exception ex) { | ||
return Utils.newError(runtime, _OCSP(runtime).getClass("OCSPError"), ex); | ||
} | ||
|
||
static RubyModule _OCSP(final Ruby runtime) { | ||
return (RubyModule) runtime.getModule("OpenSSL").getConstant("OCSP"); | ||
} | ||
|
||
} |
710 changes: 710 additions & 0 deletions
710
src/main/java/org/jruby/ext/openssl/OCSPBasicResponse.java
Large diffs are not rendered by default.
Oops, something went wrong.
321 changes: 321 additions & 0 deletions
321
src/main/java/org/jruby/ext/openssl/OCSPCertificateId.java
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,321 @@ | ||
/* | ||
* 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 java.io.IOException; | ||
import java.math.BigInteger; | ||
|
||
import org.bouncycastle.asn1.ASN1Encoding; | ||
import org.bouncycastle.asn1.ASN1ObjectIdentifier; | ||
import org.bouncycastle.asn1.ocsp.CertID; | ||
import org.bouncycastle.asn1.x509.AlgorithmIdentifier; | ||
import org.bouncycastle.cert.X509CertificateHolder; | ||
import org.bouncycastle.cert.ocsp.CertificateID; | ||
import org.bouncycastle.operator.DigestCalculator; | ||
import org.bouncycastle.operator.OperatorCreationException; | ||
import org.bouncycastle.operator.bc.BcDigestCalculatorProvider; | ||
import org.jruby.Ruby; | ||
import org.jruby.RubyBignum; | ||
import org.jruby.RubyClass; | ||
import org.jruby.RubyFixnum; | ||
import org.jruby.RubyModule; | ||
import org.jruby.RubyObject; | ||
import org.jruby.RubyString; | ||
import org.jruby.anno.JRubyMethod; | ||
import org.jruby.exceptions.RaiseException; | ||
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.OCSP._OCSP; | ||
import static org.jruby.ext.openssl.Digest._Digest; | ||
|
||
/** | ||
* An OpenSSL::OCSP::CertificateId identifies a certificate to the | ||
* CA so that a status check can be performed. | ||
* | ||
* @author lampad | ||
*/ | ||
public class OCSPCertificateId extends RubyObject { | ||
private static final long serialVersionUID = 6324454052172773918L; | ||
|
||
private static ObjectAllocator CERTIFICATEID_ALLOCATOR = new ObjectAllocator() { | ||
public IRubyObject allocate(Ruby runtime, RubyClass klass) { | ||
return new OCSPCertificateId(runtime, klass); | ||
} | ||
}; | ||
|
||
public static void createCertificateId(final Ruby runtime, final RubyModule _OCSP) { | ||
RubyClass _certificateId = _OCSP.defineClassUnder("CertificateId", runtime.getObject(), CERTIFICATEID_ALLOCATOR); | ||
_certificateId.defineAnnotatedMethods(OCSPCertificateId.class); | ||
} | ||
|
||
private CertID bcCertId; | ||
private X509Cert originalIssuer; | ||
|
||
public OCSPCertificateId(Ruby runtime, RubyClass metaClass) { | ||
super(runtime, metaClass); | ||
} | ||
|
||
public OCSPCertificateId(Ruby runtime) { | ||
this(runtime, (RubyClass) _OCSP(runtime).getConstantAt("CertificateId")); | ||
} | ||
|
||
@JRubyMethod(name = "initialize", visibility = Visibility.PRIVATE) | ||
public IRubyObject initialize(final ThreadContext context, IRubyObject subject, IRubyObject issuer, IRubyObject digest) { | ||
if (digest == null || digest.isNil()) { | ||
return initialize(context, subject, issuer); | ||
} | ||
|
||
X509Cert subjectCert = (X509Cert) subject; | ||
originalIssuer = (X509Cert) issuer; | ||
BigInteger serial = subjectCert.getSerial(); | ||
|
||
return initializeImpl(context, serial, originalIssuer, digest); | ||
} | ||
|
||
@JRubyMethod(name = "initialize", visibility = Visibility.PRIVATE) | ||
public IRubyObject initialize(final ThreadContext context, IRubyObject subject, IRubyObject issuer) { | ||
Ruby runtime = context.getRuntime(); | ||
|
||
X509Cert subjectCert = (X509Cert) subject; | ||
originalIssuer = (X509Cert) issuer; | ||
BigInteger serial = subjectCert.getSerial(); | ||
|
||
Digest digestInstance = new Digest(runtime, _Digest(runtime)); | ||
IRubyObject digest = digestInstance.initialize(context, new IRubyObject[] { RubyString.newString(runtime, "SHA1") }); | ||
|
||
return initializeImpl(context, serial, originalIssuer, digest); | ||
} | ||
|
||
@JRubyMethod(name = "initialize", visibility = Visibility.PRIVATE) | ||
public IRubyObject initialize(final ThreadContext context, IRubyObject der) { | ||
Ruby runtime = context.getRuntime(); | ||
|
||
RubyString derStr = StringHelper.readPossibleDERInput(context, der); | ||
try { | ||
return initializeImpl(derStr.getBytes()); | ||
} | ||
catch (IOException e) { | ||
throw newOCSPError(runtime, e); | ||
} | ||
} | ||
|
||
private IRubyObject initializeImpl(final ThreadContext context, BigInteger serial, | ||
IRubyObject issuerCert, IRubyObject digest) { | ||
Ruby runtime = context.getRuntime(); | ||
|
||
Digest rubyDigest = (Digest) digest; | ||
ASN1ObjectIdentifier oid = ASN1.sym2Oid(runtime, rubyDigest.getName().toLowerCase()); | ||
AlgorithmIdentifier bcAlgId = new AlgorithmIdentifier(oid); | ||
BcDigestCalculatorProvider calculatorProvider = new BcDigestCalculatorProvider(); | ||
DigestCalculator calc; | ||
try { | ||
calc = calculatorProvider.get(bcAlgId); | ||
} | ||
catch (OperatorCreationException e) { | ||
throw newOCSPError(runtime, e); | ||
} | ||
|
||
X509Cert rubyCert = (X509Cert) issuerCert; | ||
|
||
try { | ||
this.bcCertId = new CertificateID(calc, new X509CertificateHolder(rubyCert.getAuxCert().getEncoded()), serial).toASN1Primitive(); | ||
} | ||
catch (Exception e) { | ||
throw newOCSPError(runtime, e); | ||
} | ||
|
||
return this; | ||
} | ||
|
||
private IRubyObject initializeImpl(byte[] derByteStream) throws IOException { | ||
this.bcCertId = CertID.getInstance(derByteStream); | ||
|
||
return this; | ||
} | ||
|
||
@JRubyMethod(name = "serial") | ||
public IRubyObject serial() { | ||
return RubyBignum.newBignum(getRuntime(), bcCertId.getSerialNumber().getValue()); | ||
} | ||
|
||
@JRubyMethod(name = "issuer_name_hash") | ||
public IRubyObject issuer_name_hash() { | ||
Ruby runtime = getRuntime(); | ||
String oidSym = ASN1.oid2Sym(runtime, getBCCertificateID().getHashAlgOID()); | ||
RubyString digestName = RubyString.newString(runtime, oidSym); | ||
|
||
// For whatever reason, the MRI Ruby tests appear to suggest that they compute the hexdigest hash | ||
// of the issuer name over the original name instead of the hash computed in the created CertID. | ||
// I'm not sure how it's supposed to work with a passed in DER string since presumably the hash | ||
// is already computed and can't be reversed to get to the original name and thus we just compute | ||
// a hash of a hash if we don't have the original issuer around. | ||
if (originalIssuer == null) { | ||
try { | ||
return Digest.hexdigest(runtime.getCurrentContext(), this, digestName, | ||
RubyString.newString(runtime, bcCertId.getIssuerNameHash().getEncoded("DER"))); | ||
} | ||
catch (IOException e) { | ||
throw newOCSPError(runtime, e); | ||
} | ||
} | ||
else { | ||
return Digest.hexdigest(runtime.getCurrentContext(), this, digestName, | ||
originalIssuer.getSubject().to_der(runtime.getCurrentContext())); | ||
} | ||
} | ||
|
||
// For whatever reason, the MRI Ruby tests appear to suggest that they compute the hexdigest hash | ||
// of the issuer key over the original key instead of the hash computed in the created CertID. | ||
// I'm not sure how it's supposed to work with a passed in DER string since presumably the hash | ||
// is already computed and can't be reversed to get to the original key, so we just compute | ||
// a hash of a hash if we don't have the original issuer around. | ||
@JRubyMethod(name = "issuer_key_hash") | ||
public IRubyObject issuer_key_hash() { | ||
Ruby runtime = getRuntime(); | ||
String oidSym = ASN1.oid2Sym(runtime, getBCCertificateID().getHashAlgOID()); | ||
RubyString digestName = RubyString.newString(runtime, oidSym); | ||
|
||
if (originalIssuer == null) { | ||
try { | ||
return Digest.hexdigest(runtime.getCurrentContext(), this, RubyString.newString(runtime, oidSym), | ||
RubyString.newString(runtime, bcCertId.getIssuerKeyHash().getEncoded("DER"))); | ||
} | ||
catch (IOException e) { | ||
throw newOCSPError(runtime, e); | ||
} | ||
} | ||
else { | ||
PKey key = (PKey)originalIssuer.public_key(runtime.getCurrentContext()); | ||
return Digest.hexdigest(runtime.getCurrentContext(), this, digestName, key.to_der()); | ||
} | ||
} | ||
|
||
@JRubyMethod(name = "hash_algorithm") | ||
public IRubyObject hash_algorithm() { | ||
Ruby runtime = getRuntime(); | ||
ASN1ObjectIdentifier oid = bcCertId.getHashAlgorithm().getAlgorithm(); | ||
Integer nid = ASN1.oid2nid(runtime, oid); | ||
String ln = ASN1.nid2ln(runtime, nid); | ||
|
||
return RubyString.newString(runtime, ln); | ||
} | ||
|
||
@JRubyMethod(name = "cmp") | ||
public IRubyObject cmp(IRubyObject other) { | ||
Ruby runtime = getRuntime(); | ||
RubyFixnum ret = (RubyFixnum) this.cmp_issuer(other); | ||
if (!ret.eql(RubyFixnum.zero(runtime))) return ret; | ||
OCSPCertificateId that = (OCSPCertificateId) other; | ||
return RubyFixnum.newFixnum( | ||
runtime, | ||
this.getCertID().getSerialNumber().getValue().compareTo( | ||
that.getCertID().getSerialNumber().getValue() | ||
) | ||
); | ||
} | ||
|
||
@JRubyMethod(name = "cmp_issuer") | ||
public IRubyObject cmp_issuer(IRubyObject other) { | ||
Ruby runtime = getRuntime(); | ||
if ( equals(other) ) { | ||
return RubyFixnum.zero(runtime); | ||
} | ||
if (other instanceof OCSPCertificateId) { | ||
OCSPCertificateId that = (OCSPCertificateId) other; | ||
CertID thisCert = this.getCertID(); | ||
CertID thatCert = that.getCertID(); | ||
int ret = thisCert.getHashAlgorithm().getAlgorithm().toString().compareTo( | ||
thatCert.getHashAlgorithm().getAlgorithm().toString()); | ||
if (ret != 0) return RubyFixnum.newFixnum(runtime, ret); | ||
ret = thisCert.getIssuerNameHash().toString().compareTo( | ||
thatCert.getIssuerNameHash().toString()); | ||
if (ret != 0) return RubyFixnum.newFixnum(runtime, ret); | ||
return RubyFixnum.newFixnum(runtime, | ||
thisCert.getIssuerKeyHash().toString().compareTo( | ||
thatCert.getIssuerKeyHash().toString())); | ||
} | ||
else { | ||
return runtime.getCurrentContext().nil; | ||
} | ||
} | ||
|
||
@JRubyMethod(name = "to_der") | ||
public IRubyObject to_der() { | ||
Ruby runtime = getRuntime(); | ||
try { | ||
return StringHelper.newString(runtime, bcCertId.getEncoded(ASN1Encoding.DER)); | ||
} | ||
catch (IOException e) { | ||
throw newOCSPError(runtime, e); | ||
} | ||
} | ||
|
||
@Override | ||
@JRubyMethod(visibility = Visibility.PRIVATE) | ||
public IRubyObject initialize_copy(IRubyObject obj) { | ||
if ( this == obj ) return this; | ||
|
||
checkFrozen(); | ||
this.bcCertId = ((OCSPCertificateId)obj).getCertID(); | ||
return this; | ||
} | ||
|
||
@Override | ||
public boolean equals(Object other) { | ||
if ( this == other ) return true; | ||
if ( other instanceof OCSPCertificateId ) { | ||
OCSPCertificateId that = (OCSPCertificateId) other; | ||
return this.getCertID().equals(that.getCertID()); | ||
} | ||
else { | ||
return false; | ||
} | ||
} | ||
|
||
public CertID getCertID() { | ||
return bcCertId; | ||
} | ||
|
||
public CertificateID getBCCertificateID() { | ||
if (bcCertId == null) return null; | ||
return new CertificateID(bcCertId); | ||
} | ||
|
||
private static RaiseException newOCSPError(Ruby runtime, Exception e) { | ||
return Utils.newError(runtime, _OCSP(runtime).getClass("OCSPError"), e); | ||
} | ||
|
||
} |
Large diffs are not rendered by default.
Oops, something went wrong.
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,196 @@ | ||
/* | ||
* 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 static org.jruby.ext.openssl.OCSP._OCSP; | ||
|
||
import java.io.IOException; | ||
|
||
import org.bouncycastle.asn1.ASN1TaggedObject; | ||
import org.bouncycastle.cert.ocsp.BasicOCSPResp; | ||
import org.bouncycastle.cert.ocsp.OCSPResp; | ||
import org.bouncycastle.cert.ocsp.OCSPRespBuilder; | ||
import org.jruby.Ruby; | ||
import org.jruby.RubyClass; | ||
import org.jruby.RubyFixnum; | ||
import org.jruby.RubyModule; | ||
import org.jruby.RubyObject; | ||
import org.jruby.RubyString; | ||
import org.jruby.anno.JRubyMethod; | ||
import org.jruby.exceptions.RaiseException; | ||
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; | ||
|
||
/* | ||
* An OpenSSL::OCSP::Response contains the status of a certificate check which | ||
* is created from an OpenSSL::OCSP::Request. | ||
* | ||
* @author lampad | ||
*/ | ||
public class OCSPResponse extends RubyObject { | ||
private static final long serialVersionUID = 5763247988029815198L; | ||
|
||
private static ObjectAllocator RESPONSE_ALLOCATOR = new ObjectAllocator() { | ||
public IRubyObject allocate(Ruby runtime, RubyClass klass) { | ||
return new OCSPResponse(runtime, klass); | ||
} | ||
}; | ||
|
||
public OCSPResponse(Ruby runtime, RubyClass metaClass) { | ||
super(runtime, metaClass); | ||
} | ||
|
||
public OCSPResponse(Ruby runtime) { | ||
this(runtime, (RubyClass) _OCSP(runtime).getConstantAt("Response")); | ||
} | ||
|
||
public static void createResponse(final Ruby runtime, final RubyModule _OCSP) { | ||
RubyClass _request = _OCSP.defineClassUnder("Response", runtime.getObject(), RESPONSE_ALLOCATOR); | ||
_request.defineAnnotatedMethods(OCSPResponse.class); | ||
} | ||
|
||
private org.bouncycastle.asn1.ocsp.OCSPResponse bcResp; | ||
|
||
@JRubyMethod(name = "initialize", rest = true, visibility = Visibility.PRIVATE) | ||
public IRubyObject initialize(final ThreadContext context, IRubyObject args[]) { | ||
Ruby runtime = context.getRuntime(); | ||
|
||
if ( Arity.checkArgumentCount(runtime, args, 0, 1) == 0 ) return this; | ||
|
||
RubyString derString = (RubyString) args[0]; | ||
try { | ||
bcResp = org.bouncycastle.asn1.ocsp.OCSPResponse.getInstance(ASN1TaggedObject.fromByteArray(derString.getBytes())); | ||
} | ||
catch (IOException e) { | ||
throw newOCSPError(runtime, e); | ||
} | ||
|
||
return this; | ||
} | ||
|
||
@JRubyMethod(name = "create", meta = true) | ||
public static IRubyObject create(final ThreadContext context, final IRubyObject self, IRubyObject status) { | ||
Ruby runtime = context.runtime; | ||
OCSPRespBuilder builder = new OCSPRespBuilder(); | ||
OCSPResp tmpResp; | ||
OCSPResponse ret = new OCSPResponse(runtime); | ||
try { | ||
tmpResp = builder.build(RubyFixnum.fix2int((RubyFixnum)status), null); | ||
ret.initialize(context, new IRubyObject[] { RubyString.newString(runtime, tmpResp.getEncoded())}); | ||
} | ||
catch (Exception e) { | ||
throw newOCSPError(runtime, e); | ||
} | ||
|
||
return ret; | ||
} | ||
|
||
@JRubyMethod(name = "create", meta = true) | ||
public static IRubyObject create(final ThreadContext context, final IRubyObject self, IRubyObject status, IRubyObject basicResponse) { | ||
Ruby runtime = context.runtime; | ||
if (basicResponse == null || basicResponse.isNil()) { | ||
return create(context, self, status); | ||
} | ||
else { | ||
OCSPResponse ret = new OCSPResponse(runtime); | ||
OCSPBasicResponse rubyBasicResp = (OCSPBasicResponse) basicResponse; | ||
OCSPRespBuilder builder = new OCSPRespBuilder(); | ||
try { | ||
OCSPResp tmpResp = builder.build(RubyFixnum.fix2int((RubyFixnum)status), new BasicOCSPResp(rubyBasicResp.getASN1BCOCSPResp())); | ||
ret.initialize(context, new IRubyObject[] { RubyString.newString(runtime, tmpResp.getEncoded())}); | ||
} | ||
catch (Exception e) { | ||
throw newOCSPError(runtime, e); | ||
} | ||
|
||
return ret; | ||
} | ||
} | ||
|
||
@Override | ||
@JRubyMethod(name = "initialize_copy", visibility = Visibility.PRIVATE) | ||
public IRubyObject initialize_copy(IRubyObject obj) { | ||
if ( this == obj ) return this; | ||
|
||
checkFrozen(); | ||
this.bcResp = ((OCSPResponse)obj).getBCResp(); | ||
return this; | ||
} | ||
|
||
@JRubyMethod(name = "basic") | ||
public IRubyObject basic() { | ||
Ruby runtime = getRuntime(); | ||
ThreadContext context = runtime.getCurrentContext(); | ||
if (bcResp == null || bcResp.getResponseBytes() == null || bcResp.getResponseBytes().getResponse() == null) { | ||
return getRuntime().getCurrentContext().nil; | ||
} | ||
else { | ||
OCSPBasicResponse ret = new OCSPBasicResponse(runtime); | ||
return ret.initialize(context, RubyString.newString(runtime, bcResp.getResponseBytes().getResponse().getOctets())); | ||
} | ||
} | ||
|
||
@JRubyMethod(name = "status") | ||
public IRubyObject status() { | ||
return RubyFixnum.newFixnum(getRuntime(), bcResp.getResponseStatus().getValue().longValue()); | ||
} | ||
|
||
@JRubyMethod(name = "status_string") | ||
public IRubyObject status_string() { | ||
String statusStr = OCSP.getResponseStringForValue(status()); | ||
return RubyString.newString(getRuntime(), statusStr); | ||
} | ||
|
||
@JRubyMethod(name = "to_der") | ||
public IRubyObject to_der() { | ||
Ruby runtime = getRuntime(); | ||
try { | ||
return RubyString.newString(runtime, bcResp.getEncoded()); | ||
} | ||
catch (IOException e) { | ||
throw newOCSPError(runtime, e); | ||
} | ||
} | ||
|
||
public org.bouncycastle.asn1.ocsp.OCSPResponse getBCResp() { | ||
return bcResp; | ||
} | ||
|
||
private static RaiseException newOCSPError(Ruby runtime, Exception e) { | ||
return Utils.newError(runtime, _OCSP(runtime).getClass("OCSPError"), e); | ||
} | ||
|
||
} |
319 changes: 319 additions & 0 deletions
319
src/main/java/org/jruby/ext/openssl/OCSPSingleResponse.java
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,319 @@ | ||
/* | ||
* 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 static org.jruby.ext.openssl.OCSP._OCSP; | ||
|
||
import java.io.IOException; | ||
import java.text.ParseException; | ||
import java.util.ArrayList; | ||
import java.util.Arrays; | ||
import java.util.Date; | ||
import java.util.List; | ||
|
||
import org.bouncycastle.asn1.ASN1Encodable; | ||
import org.bouncycastle.asn1.ASN1GeneralizedTime; | ||
import org.bouncycastle.asn1.ASN1ObjectIdentifier; | ||
import org.bouncycastle.asn1.DERTaggedObject; | ||
import org.bouncycastle.asn1.ocsp.CertID; | ||
import org.bouncycastle.asn1.ocsp.RevokedInfo; | ||
import org.bouncycastle.asn1.ocsp.SingleResponse; | ||
import org.bouncycastle.asn1.x509.Extension; | ||
import org.bouncycastle.asn1.x509.Extensions; | ||
import org.jruby.Ruby; | ||
import org.jruby.RubyArray; | ||
import org.jruby.RubyBoolean; | ||
import org.jruby.RubyClass; | ||
import org.jruby.RubyFixnum; | ||
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.runtime.Arity; | ||
import org.jruby.runtime.ObjectAllocator; | ||
import org.jruby.runtime.ThreadContext; | ||
import org.jruby.runtime.Visibility; | ||
import org.jruby.runtime.builtin.IRubyObject; | ||
|
||
/* | ||
* An OpenSSL::OCSP::SingleResponse represents an OCSP SingleResponse structure, | ||
* which contains the basic information of the status of the certificate. | ||
* | ||
* @author lampad | ||
*/ | ||
public class OCSPSingleResponse extends RubyObject { | ||
private static final long serialVersionUID = 7947277768033100227L; | ||
|
||
private static ObjectAllocator SINGLERESPONSE_ALLOCATOR = new ObjectAllocator() { | ||
public IRubyObject allocate(Ruby runtime, RubyClass klass) { | ||
return new OCSPSingleResponse(runtime, klass); | ||
} | ||
}; | ||
|
||
public static void createSingleResponse(final Ruby runtime, final RubyModule _OCSP) { | ||
RubyClass _request = _OCSP.defineClassUnder("SingleResponse", runtime.getObject(), SINGLERESPONSE_ALLOCATOR); | ||
_request.defineAnnotatedMethods(OCSPSingleResponse.class); | ||
} | ||
|
||
public OCSPSingleResponse(Ruby runtime, RubyClass metaClass) { | ||
super(runtime, metaClass); | ||
} | ||
|
||
public OCSPSingleResponse(Ruby runtime) { | ||
this(runtime, (RubyClass) _OCSP(runtime).getConstantAt("SingleResponse")); | ||
} | ||
|
||
private SingleResponse bcSingleResponse; | ||
|
||
@JRubyMethod(name = "initialize", visibility = Visibility.PRIVATE) | ||
public IRubyObject initialize(final ThreadContext context, IRubyObject derStr) { | ||
Ruby runtime = context.getRuntime(); | ||
RubyString rubyDerStr = (RubyString) derStr; | ||
try { | ||
bcSingleResponse = SingleResponse.getInstance(DERTaggedObject.fromByteArray(rubyDerStr.getBytes())); | ||
} | ||
catch (IOException e) { | ||
throw newOCSPError(runtime, e); | ||
} | ||
|
||
return this; | ||
} | ||
|
||
@JRubyMethod(name = "cert_status") | ||
public IRubyObject cert_status() { | ||
return RubyFixnum.newFixnum(getRuntime(), bcSingleResponse.getCertStatus().getTagNo()); | ||
} | ||
|
||
@JRubyMethod(name = "certid") | ||
public IRubyObject certid() { | ||
Ruby runtime = getRuntime(); | ||
ThreadContext context = runtime.getCurrentContext(); | ||
CertID bcCertId = bcSingleResponse.getCertID(); | ||
OCSPCertificateId rubyCertId = new OCSPCertificateId(runtime); | ||
try { | ||
rubyCertId.initialize(context, RubyString.newString(runtime, bcCertId.getEncoded())); | ||
} | ||
catch (IOException e) { | ||
throw newOCSPError(runtime, e); | ||
} | ||
|
||
return rubyCertId; | ||
} | ||
|
||
@JRubyMethod(name = "check_validity", rest = true) | ||
public IRubyObject check_validity(IRubyObject[] args) { | ||
Ruby runtime = getRuntime(); | ||
int nsec, maxsec; | ||
Date thisUpdate, nextUpdate; | ||
|
||
if ( Arity.checkArgumentCount(runtime, args, 0, 2) == 0 ) { | ||
nsec = 0; | ||
maxsec = -1; | ||
} | ||
else if ( Arity.checkArgumentCount(runtime, args, 0, 2) == 1 ) { | ||
RubyFixnum rNsec = (RubyFixnum) args[0]; | ||
nsec = (int)rNsec.getLongValue(); | ||
maxsec = -1; | ||
} | ||
else { | ||
RubyFixnum rNsec = (RubyFixnum) args[0]; | ||
RubyFixnum rMaxsec = (RubyFixnum) args[1]; | ||
nsec = (int)rNsec.getLongValue(); | ||
maxsec = (int)rMaxsec.getLongValue(); | ||
} | ||
|
||
try { | ||
ASN1GeneralizedTime bcThisUpdate = bcSingleResponse.getThisUpdate(); | ||
if (bcThisUpdate == null) { | ||
thisUpdate = null; | ||
} | ||
else { | ||
thisUpdate = bcThisUpdate.getDate(); | ||
} | ||
ASN1GeneralizedTime bcNextUpdate = bcSingleResponse.getNextUpdate(); | ||
if (bcNextUpdate == null) { | ||
nextUpdate = null; | ||
} | ||
else { | ||
nextUpdate = bcNextUpdate.getDate(); | ||
} | ||
} | ||
catch (ParseException e) { | ||
throw newOCSPError(runtime, e); | ||
} | ||
|
||
return RubyBoolean.newBoolean(runtime, checkValidityImpl(thisUpdate, nextUpdate, nsec, maxsec)); | ||
} | ||
|
||
@JRubyMethod(name = "extensions") | ||
public IRubyObject extensions() { | ||
Ruby runtime = getRuntime(); | ||
Extensions exts = bcSingleResponse.getSingleExtensions(); | ||
if (exts == null) return RubyArray.newEmptyArray(runtime); | ||
List<X509Extension> retExts = new ArrayList<X509Extension>(); | ||
List<ASN1ObjectIdentifier> extOids = Arrays.asList(exts.getExtensionOIDs()); | ||
for (ASN1ObjectIdentifier extOid : extOids) { | ||
Extension ext = exts.getExtension(extOid); | ||
ASN1Encodable extAsn1 = ext.getParsedValue(); | ||
X509Extension retExt = X509Extension.newExtension(runtime, extOid, extAsn1, ext.isCritical()); | ||
retExts.add(retExt); | ||
} | ||
|
||
return RubyArray.newArray(runtime, retExts); | ||
} | ||
|
||
@JRubyMethod(name = "next_update") | ||
public IRubyObject next_update() { | ||
Ruby runtime = getRuntime(); | ||
if (bcSingleResponse.getNextUpdate() == null) return runtime.getCurrentContext().nil; | ||
Date nextUpdate; | ||
try { | ||
nextUpdate = bcSingleResponse.getNextUpdate().getDate(); | ||
} | ||
catch (ParseException e) { | ||
throw newOCSPError(runtime, e); | ||
} | ||
|
||
if (nextUpdate == null) { | ||
return runtime.getCurrentContext().nil; | ||
} | ||
|
||
return RubyTime.newTime(runtime, nextUpdate.getTime()); | ||
} | ||
|
||
@JRubyMethod(name = "this_update") | ||
public IRubyObject this_update() { | ||
Ruby runtime = getRuntime(); | ||
if (bcSingleResponse.getThisUpdate() == null) return runtime.getCurrentContext().nil; | ||
Date thisUpdate; | ||
try { | ||
thisUpdate = bcSingleResponse.getThisUpdate().getDate(); | ||
} | ||
catch (ParseException e) { | ||
throw newOCSPError(runtime, e); | ||
} | ||
|
||
return RubyTime.newTime(runtime, thisUpdate.getTime()); | ||
} | ||
|
||
@JRubyMethod(name = "revocation_reason") | ||
public IRubyObject revocation_reason() { | ||
Ruby runtime = getRuntime(); | ||
RubyFixnum revoked = (RubyFixnum) _OCSP(runtime).getConstant("V_CERTSTATUS_REVOKED"); | ||
if (bcSingleResponse.getCertStatus().getTagNo() == (int)revoked.getLongValue()) { | ||
try { | ||
RevokedInfo revokedInfo = RevokedInfo.getInstance( | ||
DERTaggedObject.fromByteArray(bcSingleResponse.getCertStatus().getStatus().toASN1Primitive().getEncoded()) | ||
); | ||
return RubyFixnum.newFixnum(runtime, revokedInfo.getRevocationReason().getValue().intValue()); | ||
} | ||
catch (IOException e) { | ||
throw newOCSPError(runtime, e); | ||
} | ||
} | ||
return runtime.getCurrentContext().nil; | ||
} | ||
|
||
@JRubyMethod(name = "revocation_time") | ||
public IRubyObject revocation_time() { | ||
Ruby runtime = getRuntime(); | ||
RubyFixnum revoked = (RubyFixnum) _OCSP(runtime).getConstant("V_CERTSTATUS_REVOKED"); | ||
if (bcSingleResponse.getCertStatus().getTagNo() == (int)revoked.getLongValue()) { | ||
try { | ||
RevokedInfo revokedInfo = RevokedInfo.getInstance( | ||
DERTaggedObject.fromByteArray(bcSingleResponse.getCertStatus().getStatus().toASN1Primitive().getEncoded()) | ||
); | ||
return RubyTime.newTime(runtime, revokedInfo.getRevocationTime().getDate().getTime()); | ||
} | ||
catch (Exception e) { | ||
throw newOCSPError(runtime, e); | ||
} | ||
} | ||
return runtime.getCurrentContext().nil; | ||
} | ||
|
||
@JRubyMethod(name = "to_der") | ||
public IRubyObject to_der() { | ||
Ruby runtime = getRuntime(); | ||
try { | ||
return RubyString.newString(runtime, bcSingleResponse.getEncoded()); | ||
} | ||
catch (IOException e) { | ||
throw newOCSPError(runtime, e); | ||
} | ||
} | ||
|
||
public SingleResponse getBCSingleResp() { | ||
return bcSingleResponse; | ||
} | ||
|
||
// see OCSP_check_validity in ocsp_cl.c | ||
private boolean checkValidityImpl(Date thisUpdate, Date nextUpdate, int nsec, int maxsec) { | ||
boolean ret = true; | ||
Date currentTime = new Date(); | ||
Date tempTime = new Date(); | ||
|
||
tempTime.setTime(currentTime.getTime() + (nsec*1000)); | ||
if (thisUpdate.compareTo(tempTime) > 0) { | ||
ret = false; | ||
} | ||
|
||
if (maxsec >= 0) { | ||
tempTime.setTime(currentTime.getTime() - (maxsec*1000)); | ||
if (thisUpdate.compareTo(tempTime) < 0) { | ||
ret = false; | ||
} | ||
} | ||
|
||
if (nextUpdate == null) { | ||
return ret; | ||
} | ||
|
||
tempTime.setTime(currentTime.getTime() - (nsec*1000)); | ||
if (nextUpdate.compareTo(tempTime) < 0) { | ||
ret = false; | ||
} | ||
|
||
if (nextUpdate.compareTo(thisUpdate) < 0) { | ||
ret = false; | ||
} | ||
|
||
return ret; | ||
} | ||
|
||
private static RaiseException newOCSPError(Ruby runtime, Exception e) { | ||
return Utils.newError(runtime, _OCSP(runtime).getClass("OCSPError"), e); | ||
} | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,294 @@ | ||
# coding: US-ASCII | ||
require File.expand_path('test_helper', File.dirname(__FILE__)) | ||
|
||
class TestOCSP < TestCase | ||
include SSLTestHelper | ||
|
||
def setup | ||
super | ||
# @ca_cert | ||
# | | ||
# @cert | ||
# |----------| | ||
# @cert2 @ocsp_cert | ||
now = Time.now | ||
|
||
ca_subj = OpenSSL::X509::Name.parse("/DC=org/DC=ruby-lang/CN=TestCA") | ||
@ca_key = OpenSSL::PKey::RSA.new TEST_KEY_RSA1024 | ||
ca_exts = [ | ||
["basicConstraints", "CA:TRUE", true], | ||
["keyUsage", "cRLSign,keyCertSign", true], | ||
] | ||
@ca_cert = issue_cert(ca_subj, @ca_key, 1, now, now+1800, ca_exts, nil, nil, OpenSSL::Digest::SHA1.new) | ||
|
||
cert_subj = OpenSSL::X509::Name.parse("/DC=org/DC=ruby-lang/CN=TestCA2") | ||
@cert_key = OpenSSL::PKey::RSA.new TEST_KEY_RSA1024 | ||
cert_exts = [ | ||
["basicConstraints", "CA:TRUE", true], | ||
["keyUsage", "cRLSign,keyCertSign", true], | ||
] | ||
@cert = issue_cert(cert_subj, @cert_key, 5, now, now+1800, cert_exts, @ca_cert, @ca_key, OpenSSL::Digest::SHA1.new) | ||
|
||
cert2_subj = OpenSSL::X509::Name.parse("/DC=org/DC=ruby-lang/CN=TestCert") | ||
@cert2_key = OpenSSL::PKey::RSA.new TEST_KEY_RSA1024 | ||
cert2_exts = [] | ||
@cert2 = issue_cert(cert2_subj, @cert2_key, 10, now, now+1800, cert2_exts, @cert, @cert_key, OpenSSL::Digest::SHA1.new) | ||
|
||
ocsp_subj = OpenSSL::X509::Name.parse("/DC=org/DC=ruby-lang/CN=TestCAOCSP") | ||
@ocsp_key = OpenSSL::PKey::RSA.new TEST_KEY_RSA2048 | ||
ocsp_exts = [ | ||
["extendedKeyUsage", "OCSPSigning", true], | ||
] | ||
@ocsp_cert = issue_cert(ocsp_subj, @ocsp_key, 100, now, now+1800, ocsp_exts, @cert, @cert_key, OpenSSL::Digest::SHA1.new) | ||
end | ||
|
||
def test_new_certificate_id | ||
cid = OpenSSL::OCSP::CertificateId.new(@cert, @ca_cert) | ||
assert_kind_of OpenSSL::OCSP::CertificateId, cid | ||
assert_equal @cert.serial, cid.serial | ||
cid = OpenSSL::OCSP::CertificateId.new(@cert, @ca_cert, OpenSSL::Digest::SHA256.new) | ||
assert_kind_of OpenSSL::OCSP::CertificateId, cid | ||
assert_equal @cert.serial, cid.serial | ||
end | ||
|
||
def test_certificate_id_issuer_name_hash | ||
cid = OpenSSL::OCSP::CertificateId.new(@cert, @ca_cert) | ||
assert_equal OpenSSL::Digest::SHA1.hexdigest(@cert.issuer.to_der), cid.issuer_name_hash | ||
assert_equal "d91f736ac4dc3242f0fb9b77a3149bd83c5c43d0", cid.issuer_name_hash | ||
end | ||
|
||
def test_certificate_id_issuer_key_hash | ||
cid = OpenSSL::OCSP::CertificateId.new(@cert, @ca_cert) | ||
assert_equal OpenSSL::Digest::SHA1.hexdigest(OpenSSL::ASN1.decode(@ca_cert.to_der).value[0].value[6].value[1].value), cid.issuer_key_hash | ||
assert_equal "d1fef9fbf8ae1bc160cbfa03e2596dd873089213", cid.issuer_key_hash | ||
end | ||
|
||
def test_certificate_id_hash_algorithm | ||
cid_sha1 = OpenSSL::OCSP::CertificateId.new(@cert, @ca_cert, OpenSSL::Digest::SHA1.new) | ||
cid_sha256 = OpenSSL::OCSP::CertificateId.new(@cert, @ca_cert, OpenSSL::Digest::SHA256.new) | ||
assert_equal "sha1", cid_sha1.hash_algorithm | ||
assert_equal "sha256", cid_sha256.hash_algorithm | ||
end | ||
|
||
def test_certificate_id_der | ||
cid = OpenSSL::OCSP::CertificateId.new(@cert, @ca_cert) | ||
der = cid.to_der | ||
asn1 = OpenSSL::ASN1.decode(der) | ||
# hash algorithm defaults to SHA-1 | ||
assert_equal OpenSSL::ASN1.ObjectId("SHA1").to_der, asn1.value[0].value[0].to_der | ||
assert_equal [cid.issuer_name_hash].pack("H*"), asn1.value[1].value | ||
assert_equal [cid.issuer_key_hash].pack("H*"), asn1.value[2].value | ||
assert_equal @cert.serial, asn1.value[3].value | ||
assert_equal der, OpenSSL::OCSP::CertificateId.new(der).to_der | ||
end | ||
|
||
def test_certificate_id_dup | ||
cid = OpenSSL::OCSP::CertificateId.new(@cert, @ca_cert) | ||
other = cid.dup.to_der | ||
assert_equal cid.to_der, other | ||
end | ||
|
||
def test_request_der | ||
request = OpenSSL::OCSP::Request.new | ||
cid = OpenSSL::OCSP::CertificateId.new(@cert, @ca_cert, OpenSSL::Digest::SHA1.new) | ||
request.add_certid(cid) | ||
request.sign(@cert, @cert_key, [@ca_cert], 0) | ||
asn1 = OpenSSL::ASN1.decode(request.to_der) | ||
# TODO: ASN1#to_der seems to be missing some data... | ||
# assert_equal cid.to_der, asn1.value[0].value.find { |a| a.tag_class == :UNIVERSAL }.value[0].value[0].to_der | ||
assert_equal OpenSSL::ASN1.ObjectId("sha1WithRSAEncryption").to_der, asn1.value[1].value[0].value[0].value[0].to_der | ||
# assert_equal @cert.to_der, asn1.value[1].value[0].value[2].value[0].value[0].to_der | ||
# assert_equal @ca_cert.to_der, asn1.value[1].value[0].value[2].value[0].value[1].to_der | ||
# assert_equal asn1.to_der, OpenSSL::OCSP::Request.new(asn1.to_der).to_der | ||
end | ||
|
||
def test_request_sign_verify | ||
cid = OpenSSL::OCSP::CertificateId.new(@cert, @ca_cert) | ||
store = OpenSSL::X509::Store.new.add_cert(@ca_cert) | ||
|
||
# with signer cert | ||
req = OpenSSL::OCSP::Request.new.add_certid(cid) | ||
req.sign(@cert, @cert_key, []) | ||
assert_equal true, req.verify([], store) | ||
|
||
# without signer cert | ||
req = OpenSSL::OCSP::Request.new.add_certid(cid) | ||
req.sign(@cert, @cert_key, nil) | ||
assert_equal false, req.verify([@cert2], store) | ||
assert_equal false, req.verify([], store) # no signer | ||
assert_equal false, req.verify([], store, OpenSSL::OCSP::NOVERIFY) | ||
|
||
assert_equal true, req.verify([@cert], store, OpenSSL::OCSP::NOINTERN) | ||
ret = req.verify([@cert], store) | ||
if ret || OpenSSL::OPENSSL_VERSION =~ /OpenSSL/ && OpenSSL::OPENSSL_VERSION_NUMBER >= 0x10002000 | ||
assert_equal true, ret | ||
else | ||
# RT2560; OCSP_request_verify() does not find signer cert from 'certs' when | ||
# OCSP_NOINTERN is not specified. | ||
# fixed by OpenSSL 1.0.1j, 1.0.2 and LibreSSL 2.4.2 | ||
pend "RT2560: ocsp_req_find_signer" | ||
end | ||
end | ||
|
||
def test_request_nonce | ||
req0 = OpenSSL::OCSP::Request.new | ||
req1 = OpenSSL::OCSP::Request.new.add_nonce("NONCE") | ||
req2 = OpenSSL::OCSP::Request.new.add_nonce("ABCDE") | ||
bres = OpenSSL::OCSP::BasicResponse.new | ||
assert_equal 2, req0.check_nonce(bres) | ||
bres.copy_nonce(req1) | ||
assert_equal 3, req0.check_nonce(bres) | ||
assert_equal 1, req1.check_nonce(bres) | ||
bres.add_nonce("NONCE") | ||
assert_equal 1, req1.check_nonce(bres) | ||
assert_equal 0, req2.check_nonce(bres) | ||
end | ||
|
||
def test_request_dup | ||
request = OpenSSL::OCSP::Request.new | ||
cid = OpenSSL::OCSP::CertificateId.new(@cert, @ca_cert, OpenSSL::Digest::SHA1.new) | ||
request.add_certid(cid) | ||
assert_equal request.to_der, request.dup.to_der | ||
end | ||
|
||
def test_basic_response_der | ||
bres = OpenSSL::OCSP::BasicResponse.new | ||
cid = OpenSSL::OCSP::CertificateId.new(@cert, @ca_cert, OpenSSL::Digest::SHA1.new) | ||
bres.add_status(cid, OpenSSL::OCSP::V_CERTSTATUS_GOOD, 0, nil, -300, 500, []) | ||
bres.add_nonce("NONCE") | ||
bres.sign(@ocsp_cert, @ocsp_key, [@ca_cert], 0) | ||
der = bres.to_der | ||
asn1 = OpenSSL::ASN1.decode(der) | ||
assert_equal OpenSSL::ASN1.Sequence([@ocsp_cert, @ca_cert]).to_der, asn1.value[3].value[0].to_der | ||
assert_equal der, OpenSSL::OCSP::BasicResponse.new(der).to_der | ||
rescue TypeError | ||
if /GENERALIZEDTIME/ =~ $!.message | ||
pend "OCSP_basic_sign() is broken" | ||
else | ||
raise | ||
end | ||
end | ||
|
||
def test_basic_response_sign_verify | ||
store = OpenSSL::X509::Store.new.add_cert(@ca_cert) | ||
|
||
# signed by CA | ||
bres = OpenSSL::OCSP::BasicResponse.new | ||
cid = OpenSSL::OCSP::CertificateId.new(@cert, @ca_cert, OpenSSL::Digest::SHA256.new) | ||
bres.add_status(cid, OpenSSL::OCSP::V_CERTSTATUS_GOOD, nil, -400, -300, 500, []) | ||
bres.sign(@ca_cert, @ca_key, nil, 0, OpenSSL::Digest::SHA256.new) | ||
assert_equal false, bres.verify([], store) # signer not found | ||
assert_equal true, bres.verify([@ca_cert], store) | ||
bres.sign(@ca_cert, @ca_key, [], 0, OpenSSL::Digest::SHA256.new) | ||
assert_equal true, bres.verify([], store) | ||
|
||
# signed by OCSP signer | ||
bres = OpenSSL::OCSP::BasicResponse.new | ||
cid = OpenSSL::OCSP::CertificateId.new(@cert2, @cert) | ||
bres.add_status(cid, OpenSSL::OCSP::V_CERTSTATUS_GOOD, nil, -400, -300, 500, []) | ||
bres.sign(@ocsp_cert, @ocsp_key, [@cert]) | ||
assert_equal true, bres.verify([], store) | ||
assert_equal false, bres.verify([], store, OpenSSL::OCSP::NOCHAIN) | ||
# OpenSSL had a bug on this; test that our workaround works | ||
bres.sign(@ocsp_cert, @ocsp_key, []) | ||
assert_equal true, bres.verify([@cert], store) | ||
end | ||
|
||
def test_basic_response_dup | ||
bres = OpenSSL::OCSP::BasicResponse.new | ||
cid = OpenSSL::OCSP::CertificateId.new(@cert, @ca_cert, OpenSSL::Digest::SHA1.new) | ||
bres.add_status(cid, OpenSSL::OCSP::V_CERTSTATUS_GOOD, 0, nil, -300, 500, []) | ||
bres.sign(@ocsp_cert, @ocsp_key, [@ca_cert], 0) | ||
assert_equal bres.to_der, bres.dup.to_der | ||
end | ||
|
||
def test_basic_response_response_operations | ||
bres = OpenSSL::OCSP::BasicResponse.new | ||
now = Time.at(Time.now.to_i) | ||
cid1 = OpenSSL::OCSP::CertificateId.new(@cert, @ca_cert, OpenSSL::Digest::SHA1.new) | ||
cid2 = OpenSSL::OCSP::CertificateId.new(@ocsp_cert, @ca_cert, OpenSSL::Digest::SHA1.new) | ||
cid3 = OpenSSL::OCSP::CertificateId.new(@ca_cert, @ca_cert, OpenSSL::Digest::SHA1.new) | ||
bres.add_status(cid1, OpenSSL::OCSP::V_CERTSTATUS_REVOKED, OpenSSL::OCSP::REVOKED_STATUS_UNSPECIFIED, now - 400, -300, nil, nil) | ||
bres.add_status(cid2, OpenSSL::OCSP::V_CERTSTATUS_GOOD, nil, nil, -300, 500, []) | ||
|
||
assert_equal 2, bres.responses.size | ||
single = bres.responses.first | ||
assert_equal cid1.to_der, single.certid.to_der | ||
assert_equal OpenSSL::OCSP::V_CERTSTATUS_REVOKED, single.cert_status | ||
assert_equal OpenSSL::OCSP::REVOKED_STATUS_UNSPECIFIED, single.revocation_reason | ||
assert_equal now - 400, single.revocation_time | ||
assert_in_delta (now - 301), single.this_update, 1 | ||
assert_equal nil, single.next_update | ||
assert_equal [], single.extensions | ||
|
||
assert_equal cid2.to_der, bres.find_response(cid2).certid.to_der | ||
assert_equal nil, bres.find_response(cid3) | ||
end | ||
|
||
def test_single_response_der | ||
bres = OpenSSL::OCSP::BasicResponse.new | ||
cid = OpenSSL::OCSP::CertificateId.new(@cert, @ca_cert) | ||
bres.add_status(cid, OpenSSL::OCSP::V_CERTSTATUS_GOOD, nil, nil, -300, 500, nil) | ||
single = bres.responses[0] | ||
der = single.to_der | ||
asn1 = OpenSSL::ASN1.decode(der) | ||
assert_equal :CONTEXT_SPECIFIC, asn1.value[1].tag_class | ||
assert_equal 0, asn1.value[1].tag # good | ||
assert_equal der, OpenSSL::OCSP::SingleResponse.new(der).to_der | ||
end | ||
|
||
def test_single_response_check_validity | ||
bres = OpenSSL::OCSP::BasicResponse.new | ||
cid1 = OpenSSL::OCSP::CertificateId.new(@cert, @ca_cert, OpenSSL::Digest::SHA1.new) | ||
cid2 = OpenSSL::OCSP::CertificateId.new(@ocsp_cert, @ca_cert, OpenSSL::Digest::SHA1.new) | ||
bres.add_status(cid1, OpenSSL::OCSP::V_CERTSTATUS_REVOKED, OpenSSL::OCSP::REVOKED_STATUS_UNSPECIFIED, -400, -300, -50, []) | ||
bres.add_status(cid2, OpenSSL::OCSP::V_CERTSTATUS_REVOKED, OpenSSL::OCSP::REVOKED_STATUS_UNSPECIFIED, -400, -300, nil, []) | ||
bres.add_status(cid2, OpenSSL::OCSP::V_CERTSTATUS_GOOD, nil, nil, Time.now + 100, nil, nil) | ||
|
||
if bres.responses[2].check_validity # thisUpdate is in future; must fail | ||
# LibreSSL bug; skip for now | ||
pend "OCSP_check_validity() is broken" | ||
end | ||
|
||
single1 = bres.responses[0] | ||
assert_equal false, single1.check_validity | ||
assert_equal false, single1.check_validity(30) | ||
assert_equal true, single1.check_validity(60) | ||
single2 = bres.responses[1] | ||
assert_equal true, single2.check_validity | ||
assert_equal true, single2.check_validity(0, 500) | ||
assert_equal false, single2.check_validity(0, 200) | ||
end | ||
|
||
def test_response | ||
bres = OpenSSL::OCSP::BasicResponse.new | ||
cid = OpenSSL::OCSP::CertificateId.new(@cert, @ca_cert, OpenSSL::Digest::SHA1.new) | ||
bres.add_status(cid, OpenSSL::OCSP::V_CERTSTATUS_GOOD, 0, nil, -300, 500, []) | ||
bres.sign(@ocsp_cert, @ocsp_key, []) | ||
res = OpenSSL::OCSP::Response.create(OpenSSL::OCSP::RESPONSE_STATUS_SUCCESSFUL, bres) | ||
|
||
assert_equal bres.to_der, res.basic.to_der | ||
assert_equal OpenSSL::OCSP::RESPONSE_STATUS_SUCCESSFUL, res.status | ||
end | ||
|
||
def test_response_der | ||
bres = OpenSSL::OCSP::BasicResponse.new | ||
cid = OpenSSL::OCSP::CertificateId.new(@cert, @ca_cert, OpenSSL::Digest::SHA1.new) | ||
bres.add_status(cid, OpenSSL::OCSP::V_CERTSTATUS_GOOD, 0, nil, -300, 500, []) | ||
bres.sign(@ocsp_cert, @ocsp_key, [@ca_cert], 0) | ||
res = OpenSSL::OCSP::Response.create(OpenSSL::OCSP::RESPONSE_STATUS_SUCCESSFUL, bres) | ||
der = res.to_der | ||
asn1 = OpenSSL::ASN1.decode(der) | ||
assert_equal OpenSSL::OCSP::RESPONSE_STATUS_SUCCESSFUL, asn1.value[0].value | ||
assert_equal OpenSSL::ASN1.ObjectId("basicOCSPResponse").to_der, asn1.value[1].value[0].value[0].to_der | ||
assert_equal bres.to_der, asn1.value[1].value[0].value[1].value | ||
assert_equal der, OpenSSL::OCSP::Response.new(der).to_der | ||
end | ||
|
||
def test_response_dup | ||
bres = OpenSSL::OCSP::BasicResponse.new | ||
bres.sign(@ocsp_cert, @ocsp_key, [@ca_cert], 0) | ||
res = OpenSSL::OCSP::Response.create(OpenSSL::OCSP::RESPONSE_STATUS_SUCCESSFUL, bres) | ||
assert_equal res.to_der, res.dup.to_der | ||
end | ||
end |