summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorrhe <rhe@b2dd03c8-39d4-4d8f-98ff-823fe69b080e>2016-06-14 12:40:55 +0000
committerrhe <rhe@b2dd03c8-39d4-4d8f-98ff-823fe69b080e>2016-06-14 12:40:55 +0000
commit40799e5ef9dd1bcbb7a84564a9bfa45af21c4d02 (patch)
tree6409b7616380679256fcf24ff3d59436bb269cd8
parent8dd0a046a9adebdf5cbd53dd4f590981f06715ea (diff)
openssl: add missing #to_der to OCSP::{CertificateId,BasicResponse}
* ext/openssl/ossl_ocsp.c (ossl_ocspbres_to_der, ossl_ocspcid_to_der): Implement #to_der methods for OCSP::BasicResponse and OCSP::CertificateId. (ossl_ocspreq_initialize, ossl_ocspres_initialize): Use GetOCSP*() instead of raw DATA_PTR(). (ossl_ocspbres_initialize, ossl_ocspcid_initialize): Allow initializing from DER string. (Init_ossl_ocsp): Define new #to_der methods. * test/openssl/test_ocsp.rb: Test these changes. Also add missing tests for OCSP::{Response,Request}#to_der. git-svn-id: svn+ssh://ci.ruby-lang.org/ruby/trunk@55409 b2dd03c8-39d4-4d8f-98ff-823fe69b080e
-rw-r--r--ChangeLog17
-rw-r--r--ext/openssl/ossl_ocsp.c110
-rw-r--r--test/openssl/test_ocsp.rb67
3 files changed, 176 insertions, 18 deletions
diff --git a/ChangeLog b/ChangeLog
index 32e757c..05fb3d5 100644
--- a/ChangeLog
+++ b/ChangeLog
@@ -1,3 +1,20 @@
+Tue Jun 14 21:40:42 2016 Kazuki Yamaguchi <k@rhe.jp>
+
+ * ext/openssl/ossl_ocsp.c (ossl_ocspbres_to_der, ossl_ocspcid_to_der):
+ Implement #to_der methods for OCSP::BasicResponse and
+ OCSP::CertificateId.
+
+ (ossl_ocspreq_initialize, ossl_ocspres_initialize): Use GetOCSP*()
+ instead of raw DATA_PTR().
+
+ (ossl_ocspbres_initialize, ossl_ocspcid_initialize): Allow
+ initializing from DER string.
+
+ (Init_ossl_ocsp): Define new #to_der methods.
+
+ * test/openssl/test_ocsp.rb: Test these changes. Also add missing tests
+ for OCSP::{Response,Request}#to_der.
+
Tue Jun 14 21:35:00 2016 Kazuki Yamaguchi <k@rhe.jp>
* ext/openssl/openssl_missing.h (DH_set0_pqg, RSA_set0_key):
diff --git a/ext/openssl/ossl_ocsp.c b/ext/openssl/ossl_ocsp.c
index ae15d93..d4589da 100644
--- a/ext/openssl/ossl_ocsp.c
+++ b/ext/openssl/ossl_ocsp.c
@@ -180,15 +180,13 @@ ossl_ocspreq_initialize(int argc, VALUE *argv, VALUE self)
rb_scan_args(argc, argv, "01", &arg);
if(!NIL_P(arg)){
- OCSP_REQUEST *req = DATA_PTR(self), *x;
+ OCSP_REQUEST *req;
+ GetOCSPReq(self, req);
arg = ossl_to_der_if_possible(arg);
StringValue(arg);
- p = (unsigned char*)RSTRING_PTR(arg);
- x = d2i_OCSP_REQUEST(&req, &p, RSTRING_LEN(arg));
- DATA_PTR(self) = req;
- if(!x){
+ p = (unsigned char *)RSTRING_PTR(arg);
+ if (!d2i_OCSP_REQUEST(&req, &p, RSTRING_LEN(arg)))
ossl_raise(eOCSPError, "cannot load DER encoded request");
- }
}
return self;
@@ -463,15 +461,13 @@ ossl_ocspres_initialize(int argc, VALUE *argv, VALUE self)
rb_scan_args(argc, argv, "01", &arg);
if(!NIL_P(arg)){
- OCSP_RESPONSE *res = DATA_PTR(self), *x;
+ OCSP_RESPONSE *res;
+ GetOCSPRes(self, res);
arg = ossl_to_der_if_possible(arg);
StringValue(arg);
p = (unsigned char *)RSTRING_PTR(arg);
- x = d2i_OCSP_RESPONSE(&res, &p, RSTRING_LEN(arg));
- DATA_PTR(self) = res;
- if(!x){
+ if (!d2i_OCSP_RESPONSE(&res, &p, RSTRING_LEN(arg)))
ossl_raise(eOCSPError, "cannot load DER encoded response");
- }
}
return self;
@@ -584,14 +580,29 @@ ossl_ocspbres_alloc(VALUE klass)
/*
* call-seq:
- * OpenSSL::OCSP::BasicResponse.new(*) -> basic_response
+ * OpenSSL::OCSP::BasicResponse.new(der_string = nil) -> basic_response
*
- * Creates a new BasicResponse and ignores all arguments.
+ * Creates a new BasicResponse. If +der_string+ is given, decodes +der_string+
+ * as DER.
*/
static VALUE
ossl_ocspbres_initialize(int argc, VALUE *argv, VALUE self)
{
+ VALUE arg;
+ const unsigned char *p;
+
+ rb_scan_args(argc, argv, "01", &arg);
+ if (!NIL_P(arg)) {
+ OCSP_BASICRESP *res;
+ GetOCSPBasicRes(self, res);
+ arg = ossl_to_der_if_possible(arg);
+ StringValue(arg);
+ p = (unsigned char *)RSTRING_PTR(arg);
+ if (!d2i_OCSP_BASICRESP(&res, &p, RSTRING_LEN(arg)))
+ ossl_raise(eOCSPError, "d2i_OCSP_BASICRESP");
+ }
+
return self;
}
@@ -856,6 +867,32 @@ ossl_ocspbres_verify(int argc, VALUE *argv, VALUE self)
}
/*
+ * call-seq:
+ * basic_response.to_der -> String
+ *
+ * Encodes this basic response into a DER-encoded string.
+ */
+static VALUE
+ossl_ocspbres_to_der(VALUE self)
+{
+ OCSP_BASICRESP *res;
+ VALUE str;
+ long len;
+ unsigned char *p;
+
+ GetOCSPBasicRes(self, res);
+ if ((len = i2d_OCSP_BASICRESP(res, NULL)) <= 0)
+ ossl_raise(eOCSPError, NULL);
+ str = rb_str_new(0, len);
+ p = (unsigned char *)RSTRING_PTR(str);
+ if (i2d_OCSP_BASICRESP(res, &p) <= 0)
+ ossl_raise(eOCSPError, NULL);
+ ossl_str_adjust(str, p);
+
+ return str;
+}
+
+/*
* OCSP::CertificateId
*/
static VALUE
@@ -875,10 +912,14 @@ ossl_ocspcid_alloc(VALUE klass)
/*
* call-seq:
* OpenSSL::OCSP::CertificateId.new(subject, issuer, digest = nil) -> certificate_id
+ * OpenSSL::OCSP::CertificateId.new(der_string) -> certificate_id
*
* Creates a new OpenSSL::OCSP::CertificateId for the given +subject+ and
* +issuer+ X509 certificates. The +digest+ is used to compute the
* certificate ID and must be an OpenSSL::Digest instance.
+ *
+ * If only one argument is given, decodes it as DER representation of a
+ * certificate ID.
*/
static VALUE
@@ -889,7 +930,17 @@ ossl_ocspcid_initialize(int argc, VALUE *argv, VALUE self)
VALUE subject, issuer, digest;
const EVP_MD *md;
- if (rb_scan_args(argc, argv, "21", &subject, &issuer, &digest) == 0) {
+ GetOCSPCertId(self, id);
+ if (rb_scan_args(argc, argv, "12", &subject, &issuer, &digest) == 1) {
+ VALUE arg;
+ const unsigned char *p;
+
+ arg = ossl_to_der_if_possible(subject);
+ StringValue(arg);
+ p = (unsigned char *)RSTRING_PTR(arg);
+ if (!d2i_OCSP_CERTID(&id, &p, RSTRING_LEN(arg)))
+ ossl_raise(eOCSPError, "d2i_OCSP_CERTID");
+
return self;
}
@@ -904,9 +955,8 @@ ossl_ocspcid_initialize(int argc, VALUE *argv, VALUE self)
}
if(!newid)
ossl_raise(eOCSPError, NULL);
- GetOCSPCertId(self, id);
OCSP_CERTID_free(id);
- RDATA(self)->data = newid;
+ SetOCSPCertId(self, newid);
return self;
}
@@ -971,6 +1021,32 @@ ossl_ocspcid_get_serial(VALUE self)
return asn1integer_to_num(serial);
}
+/*
+ * call-seq:
+ * certificate_id.to_der -> String
+ *
+ * Encodes this certificate identifier into a DER-encoded string.
+ */
+static VALUE
+ossl_ocspcid_to_der(VALUE self)
+{
+ OCSP_CERTID *id;
+ VALUE str;
+ long len;
+ unsigned char *p;
+
+ GetOCSPCertId(self, id);
+ if ((len = i2d_OCSP_CERTID(id, NULL)) <= 0)
+ ossl_raise(eOCSPError, NULL);
+ str = rb_str_new(0, len);
+ p = (unsigned char *)RSTRING_PTR(str);
+ if (i2d_OCSP_CERTID(id, &p) <= 0)
+ ossl_raise(eOCSPError, NULL);
+ ossl_str_adjust(str, p);
+
+ return str;
+}
+
void
Init_ossl_ocsp(void)
{
@@ -1138,6 +1214,7 @@ Init_ossl_ocsp(void)
rb_define_method(cOCSPBasicRes, "status", ossl_ocspbres_get_status, 0);
rb_define_method(cOCSPBasicRes, "sign", ossl_ocspbres_sign, -1);
rb_define_method(cOCSPBasicRes, "verify", ossl_ocspbres_verify, -1);
+ rb_define_method(cOCSPBasicRes, "to_der", ossl_ocspbres_to_der, 0);
/*
* An OpenSSL::OCSP::CertificateId identifies a certificate to the CA so
@@ -1150,6 +1227,7 @@ Init_ossl_ocsp(void)
rb_define_method(cOCSPCertId, "cmp", ossl_ocspcid_cmp, 1);
rb_define_method(cOCSPCertId, "cmp_issuer", ossl_ocspcid_cmp_issuer, 1);
rb_define_method(cOCSPCertId, "serial", ossl_ocspcid_get_serial, 0);
+ rb_define_method(cOCSPCertId, "to_der", ossl_ocspcid_to_der, 0);
/* Internal error in issuer */
rb_define_const(mOCSP, "RESPONSE_STATUS_INTERNALERROR", INT2NUM(OCSP_RESPONSE_STATUS_INTERNALERROR));
diff --git a/test/openssl/test_ocsp.rb b/test/openssl/test_ocsp.rb
index d04b421..7d4b39a 100644
--- a/test/openssl/test_ocsp.rb
+++ b/test/openssl/test_ocsp.rb
@@ -8,6 +8,10 @@ class OpenSSL::TestOCSP < OpenSSL::TestCase
ca_subj = OpenSSL::X509::Name.parse("/DC=org/DC=ruby-lang/CN=TestCA")
ca_key = OpenSSL::TestUtils::TEST_KEY_RSA1024
ca_serial = 0xabcabcabcabc
+ ca_exts = [
+ ["basicConstraints", "CA:TRUE", true],
+ ["keyUsage", "cRLSign,keyCertSign", true],
+ ]
subj = OpenSSL::X509::Name.parse("/DC=org/DC=ruby-lang/CN=TestCert")
@key = OpenSSL::TestUtils::TEST_KEY_RSA1024
@@ -17,9 +21,17 @@ class OpenSSL::TestOCSP < OpenSSL::TestCase
dgst = OpenSSL::Digest::SHA1.new
@ca_cert = OpenSSL::TestUtils.issue_cert(
- ca_subj, ca_key, ca_serial, now, now+3600, [], nil, nil, dgst)
+ ca_subj, ca_key, ca_serial, now, now+3600, ca_exts, nil, nil, dgst)
@cert = OpenSSL::TestUtils.issue_cert(
- subj, @key, serial, now, now+3600, [], @ca_cert, nil, dgst)
+ subj, @key, serial, now, now+3600, [], @ca_cert, ca_key, dgst)
+
+ @key2 = OpenSSL::TestUtils::TEST_KEY_RSA2048
+ cert2_exts = [
+ ["extendedKeyUsage", "OCSPSigning", true],
+ ]
+ @cert2 = OpenSSL::TestUtils.issue_cert(
+ OpenSSL::X509::Name.parse("/DC=org/DC=ruby-lang/CN=TestCert2"),
+ @key2, serial+1, now, now+3600, cert2_exts, @ca_cert, ca_key, "SHA256")
end
def test_new_certificate_id
@@ -34,6 +46,30 @@ class OpenSSL::TestOCSP < OpenSSL::TestCase
assert_equal @cert.serial, cid.serial
end if defined?(OpenSSL::Digest::SHA256)
+ def test_certificate_id_der
+ cid = OpenSSL::OCSP::CertificateId.new(@cert, @ca_cert) # hash algorithm defaults to SHA-1
+ der = cid.to_der
+ asn1 = OpenSSL::ASN1.decode(der)
+ assert_equal OpenSSL::ASN1.ObjectId("SHA1").to_der, asn1.value[0].value[0].to_der
+ assert_equal OpenSSL::Digest::SHA1.digest(@cert.issuer.to_der), asn1.value[1].value
+ assert_equal OpenSSL::Digest::SHA1.digest(OpenSSL::ASN1.decode(@ca_cert.to_der).value[0].value[6].value[1].value), 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_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, @key, [@ca_cert], 0)
+ asn1 = OpenSSL::ASN1.decode(request.to_der)
+ 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_new_ocsp_request
request = OpenSSL::OCSP::Request.new
cid = OpenSSL::OCSP::CertificateId.new(@cert, @ca_cert, OpenSSL::Digest::SHA1.new)
@@ -43,6 +79,33 @@ class OpenSSL::TestOCSP < OpenSSL::TestCase
# in current implementation not same instance of certificate id, but should contain same data
assert_equal cid.serial, request.certid.first.serial
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(@cert2, @key2, [@ca_cert], 0)
+ der = bres.to_der
+ asn1 = OpenSSL::ASN1.decode(der)
+ assert_equal cid.to_der, asn1.value[0].value.find { |a| a.class == OpenSSL::ASN1::Sequence }.value[0].value[0].to_der
+ assert_equal OpenSSL::ASN1.Sequence([@cert2, @ca_cert]).to_der, asn1.value[3].value[0].to_der
+ assert_equal der, OpenSSL::OCSP::BasicResponse.new(der).to_der
+ 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(@cert2, @key2, [@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
end
end