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
This commit is contained in:
rhe 2016-06-14 12:40:55 +00:00
Родитель 8dd0a046a9
Коммит 40799e5ef9
3 изменённых файлов: 176 добавлений и 18 удалений

Просмотреть файл

@ -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):

Просмотреть файл

@ -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;
}
@ -855,6 +866,32 @@ ossl_ocspbres_verify(int argc, VALUE *argv, VALUE self)
return result;
}
/*
* 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
*/
@ -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));

Просмотреть файл

@ -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