From fcbf63e62c627deae76c1b8cb8c0876c536ed811 Mon Sep 17 00:00:00 2001
From: Jari Vetoniemi <jari.vetoniemi@indooratlas.com>
Date: Mon, 16 Mar 2020 18:49:26 +0900
Subject: Fresh start

---
 jni/ruby/test/openssl/test_asn1.rb                | 609 ++++++++++++++
 jni/ruby/test/openssl/test_bn.rb                  |  52 ++
 jni/ruby/test/openssl/test_buffering.rb           |  87 ++
 jni/ruby/test/openssl/test_cipher.rb              | 260 ++++++
 jni/ruby/test/openssl/test_config.rb              | 297 +++++++
 jni/ruby/test/openssl/test_digest.rb              | 126 +++
 jni/ruby/test/openssl/test_engine.rb              |  75 ++
 jni/ruby/test/openssl/test_fips.rb                |  14 +
 jni/ruby/test/openssl/test_hmac.rb                |  41 +
 jni/ruby/test/openssl/test_ns_spki.rb             |  51 ++
 jni/ruby/test/openssl/test_ocsp.rb                |  47 ++
 jni/ruby/test/openssl/test_pair.rb                | 372 +++++++++
 jni/ruby/test/openssl/test_partial_record_read.rb |  34 +
 jni/ruby/test/openssl/test_pkcs12.rb              | 209 +++++
 jni/ruby/test/openssl/test_pkcs5.rb               |  97 +++
 jni/ruby/test/openssl/test_pkcs7.rb               | 297 +++++++
 jni/ruby/test/openssl/test_pkey_dh.rb             |  82 ++
 jni/ruby/test/openssl/test_pkey_dsa.rb            | 240 ++++++
 jni/ruby/test/openssl/test_pkey_ec.rb             | 211 +++++
 jni/ruby/test/openssl/test_pkey_rsa.rb            | 313 ++++++++
 jni/ruby/test/openssl/test_ssl.rb                 | 933 ++++++++++++++++++++++
 jni/ruby/test/openssl/test_ssl_session.rb         | 380 +++++++++
 jni/ruby/test/openssl/test_x509cert.rb            | 226 ++++++
 jni/ruby/test/openssl/test_x509crl.rb             | 220 +++++
 jni/ruby/test/openssl/test_x509ext.rb             |  69 ++
 jni/ruby/test/openssl/test_x509name.rb            | 367 +++++++++
 jni/ruby/test/openssl/test_x509req.rb             | 158 ++++
 jni/ruby/test/openssl/test_x509store.rb           | 231 ++++++
 jni/ruby/test/openssl/utils.rb                    | 333 ++++++++
 29 files changed, 6431 insertions(+)
 create mode 100644 jni/ruby/test/openssl/test_asn1.rb
 create mode 100644 jni/ruby/test/openssl/test_bn.rb
 create mode 100644 jni/ruby/test/openssl/test_buffering.rb
 create mode 100644 jni/ruby/test/openssl/test_cipher.rb
 create mode 100644 jni/ruby/test/openssl/test_config.rb
 create mode 100644 jni/ruby/test/openssl/test_digest.rb
 create mode 100644 jni/ruby/test/openssl/test_engine.rb
 create mode 100644 jni/ruby/test/openssl/test_fips.rb
 create mode 100644 jni/ruby/test/openssl/test_hmac.rb
 create mode 100644 jni/ruby/test/openssl/test_ns_spki.rb
 create mode 100644 jni/ruby/test/openssl/test_ocsp.rb
 create mode 100644 jni/ruby/test/openssl/test_pair.rb
 create mode 100644 jni/ruby/test/openssl/test_partial_record_read.rb
 create mode 100644 jni/ruby/test/openssl/test_pkcs12.rb
 create mode 100644 jni/ruby/test/openssl/test_pkcs5.rb
 create mode 100644 jni/ruby/test/openssl/test_pkcs7.rb
 create mode 100644 jni/ruby/test/openssl/test_pkey_dh.rb
 create mode 100644 jni/ruby/test/openssl/test_pkey_dsa.rb
 create mode 100644 jni/ruby/test/openssl/test_pkey_ec.rb
 create mode 100644 jni/ruby/test/openssl/test_pkey_rsa.rb
 create mode 100644 jni/ruby/test/openssl/test_ssl.rb
 create mode 100644 jni/ruby/test/openssl/test_ssl_session.rb
 create mode 100644 jni/ruby/test/openssl/test_x509cert.rb
 create mode 100644 jni/ruby/test/openssl/test_x509crl.rb
 create mode 100644 jni/ruby/test/openssl/test_x509ext.rb
 create mode 100644 jni/ruby/test/openssl/test_x509name.rb
 create mode 100644 jni/ruby/test/openssl/test_x509req.rb
 create mode 100644 jni/ruby/test/openssl/test_x509store.rb
 create mode 100644 jni/ruby/test/openssl/utils.rb

(limited to 'jni/ruby/test/openssl')

diff --git a/jni/ruby/test/openssl/test_asn1.rb b/jni/ruby/test/openssl/test_asn1.rb
new file mode 100644
index 0000000..9fb5a55
--- /dev/null
+++ b/jni/ruby/test/openssl/test_asn1.rb
@@ -0,0 +1,609 @@
+require_relative 'utils'
+
+class  OpenSSL::TestASN1 < Test::Unit::TestCase
+  def test_decode
+    subj = OpenSSL::X509::Name.parse("/DC=org/DC=ruby-lang/CN=TestCA")
+    key = OpenSSL::TestUtils::TEST_KEY_RSA1024
+    now = Time.at(Time.now.to_i) # suppress usec
+    s = 0xdeadbeafdeadbeafdeadbeafdeadbeaf
+    exts = [
+      ["basicConstraints","CA:TRUE,pathlen:1",true],
+      ["keyUsage","keyCertSign, cRLSign",true],
+      ["subjectKeyIdentifier","hash",false],
+    ]
+    dgst = OpenSSL::Digest::SHA1.new
+    cert = OpenSSL::TestUtils.issue_cert(
+      subj, key, s, now, now+3600, exts, nil, nil, dgst)
+
+
+    asn1 = OpenSSL::ASN1.decode(cert)
+    assert_equal(OpenSSL::ASN1::Sequence, asn1.class)
+    assert_equal(3, asn1.value.size)
+    tbs_cert, sig_alg, sig_val = *asn1.value
+
+    assert_equal(OpenSSL::ASN1::Sequence, tbs_cert.class)
+    assert_equal(8, tbs_cert.value.size)
+
+    version = tbs_cert.value[0]
+    assert_equal(:CONTEXT_SPECIFIC, version.tag_class)
+    assert_equal(0, version.tag)
+    assert_equal(1, version.value.size)
+    assert_equal(OpenSSL::ASN1::Integer, version.value[0].class)
+    assert_equal(2, version.value[0].value)
+
+    serial = tbs_cert.value[1]
+    assert_equal(OpenSSL::ASN1::Integer, serial.class)
+    assert_equal(0xdeadbeafdeadbeafdeadbeafdeadbeaf, serial.value)
+
+    sig = tbs_cert.value[2]
+    assert_equal(OpenSSL::ASN1::Sequence, sig.class)
+    assert_equal(2, sig.value.size)
+    assert_equal(OpenSSL::ASN1::ObjectId, sig.value[0].class)
+    assert_equal("1.2.840.113549.1.1.5", sig.value[0].oid)
+    assert_equal(OpenSSL::ASN1::Null, sig.value[1].class)
+
+    dn = tbs_cert.value[3] # issuer
+    assert_equal(subj.hash, OpenSSL::X509::Name.new(dn).hash)
+    assert_equal(OpenSSL::ASN1::Sequence, dn.class)
+    assert_equal(3, dn.value.size)
+    assert_equal(OpenSSL::ASN1::Set, dn.value[0].class)
+    assert_equal(OpenSSL::ASN1::Set, dn.value[1].class)
+    assert_equal(OpenSSL::ASN1::Set, dn.value[2].class)
+    assert_equal(1, dn.value[0].value.size)
+    assert_equal(1, dn.value[1].value.size)
+    assert_equal(1, dn.value[2].value.size)
+    assert_equal(OpenSSL::ASN1::Sequence, dn.value[0].value[0].class)
+    assert_equal(OpenSSL::ASN1::Sequence, dn.value[1].value[0].class)
+    assert_equal(OpenSSL::ASN1::Sequence, dn.value[2].value[0].class)
+    assert_equal(2, dn.value[0].value[0].value.size)
+    assert_equal(2, dn.value[1].value[0].value.size)
+    assert_equal(2, dn.value[2].value[0].value.size)
+    oid, value = *dn.value[0].value[0].value
+    assert_equal(OpenSSL::ASN1::ObjectId, oid.class)
+    assert_equal("0.9.2342.19200300.100.1.25", oid.oid)
+    assert_equal(OpenSSL::ASN1::IA5String, value.class)
+    assert_equal("org", value.value)
+    oid, value = *dn.value[1].value[0].value
+    assert_equal(OpenSSL::ASN1::ObjectId, oid.class)
+    assert_equal("0.9.2342.19200300.100.1.25", oid.oid)
+    assert_equal(OpenSSL::ASN1::IA5String, value.class)
+    assert_equal("ruby-lang", value.value)
+    oid, value = *dn.value[2].value[0].value
+    assert_equal(OpenSSL::ASN1::ObjectId, oid.class)
+    assert_equal("2.5.4.3", oid.oid)
+    assert_equal(OpenSSL::ASN1::UTF8String, value.class)
+    assert_equal("TestCA", value.value)
+
+    validity = tbs_cert.value[4]
+    assert_equal(OpenSSL::ASN1::Sequence, validity.class)
+    assert_equal(2, validity.value.size)
+    assert_equal(OpenSSL::ASN1::UTCTime, validity.value[0].class)
+    assert_equal(now, validity.value[0].value)
+    assert_equal(OpenSSL::ASN1::UTCTime, validity.value[1].class)
+    assert_equal(now+3600, validity.value[1].value)
+
+    dn = tbs_cert.value[5] # subject
+    assert_equal(subj.hash, OpenSSL::X509::Name.new(dn).hash)
+    assert_equal(OpenSSL::ASN1::Sequence, dn.class)
+    assert_equal(3, dn.value.size)
+    assert_equal(OpenSSL::ASN1::Set, dn.value[0].class)
+    assert_equal(OpenSSL::ASN1::Set, dn.value[1].class)
+    assert_equal(OpenSSL::ASN1::Set, dn.value[2].class)
+    assert_equal(1, dn.value[0].value.size)
+    assert_equal(1, dn.value[1].value.size)
+    assert_equal(1, dn.value[2].value.size)
+    assert_equal(OpenSSL::ASN1::Sequence, dn.value[0].value[0].class)
+    assert_equal(OpenSSL::ASN1::Sequence, dn.value[1].value[0].class)
+    assert_equal(OpenSSL::ASN1::Sequence, dn.value[2].value[0].class)
+    assert_equal(2, dn.value[0].value[0].value.size)
+    assert_equal(2, dn.value[1].value[0].value.size)
+    assert_equal(2, dn.value[2].value[0].value.size)
+    oid, value = *dn.value[0].value[0].value
+    assert_equal(OpenSSL::ASN1::ObjectId, oid.class)
+    assert_equal("0.9.2342.19200300.100.1.25", oid.oid)
+    assert_equal(OpenSSL::ASN1::IA5String, value.class)
+    assert_equal("org", value.value)
+    oid, value = *dn.value[1].value[0].value
+    assert_equal(OpenSSL::ASN1::ObjectId, oid.class)
+    assert_equal("0.9.2342.19200300.100.1.25", oid.oid)
+    assert_equal(OpenSSL::ASN1::IA5String, value.class)
+    assert_equal("ruby-lang", value.value)
+    oid, value = *dn.value[2].value[0].value
+    assert_equal(OpenSSL::ASN1::ObjectId, oid.class)
+    assert_equal("2.5.4.3", oid.oid)
+    assert_equal(OpenSSL::ASN1::UTF8String, value.class)
+    assert_equal("TestCA", value.value)
+
+    pkey = tbs_cert.value[6]
+    assert_equal(OpenSSL::ASN1::Sequence, pkey.class)
+    assert_equal(2, pkey.value.size)
+    assert_equal(OpenSSL::ASN1::Sequence, pkey.value[0].class)
+    assert_equal(2, pkey.value[0].value.size)
+    assert_equal(OpenSSL::ASN1::ObjectId, pkey.value[0].value[0].class)
+    assert_equal("1.2.840.113549.1.1.1", pkey.value[0].value[0].oid)
+    assert_equal(OpenSSL::ASN1::BitString, pkey.value[1].class)
+    assert_equal(0, pkey.value[1].unused_bits)
+    spkey = OpenSSL::ASN1.decode(pkey.value[1].value)
+    assert_equal(OpenSSL::ASN1::Sequence, spkey.class)
+    assert_equal(2, spkey.value.size)
+    assert_equal(OpenSSL::ASN1::Integer, spkey.value[0].class)
+    assert_equal(143085709396403084580358323862163416700436550432664688288860593156058579474547937626086626045206357324274536445865308750491138538454154232826011964045825759324933943290377903384882276841880081931690695505836279972214003660451338124170055999155993192881685495391496854691199517389593073052473319331505702779271, spkey.value[0].value)
+    assert_equal(OpenSSL::ASN1::Integer, spkey.value[1].class)
+    assert_equal(65537, spkey.value[1].value)
+
+    extensions = tbs_cert.value[7]
+    assert_equal(:CONTEXT_SPECIFIC, extensions.tag_class)
+    assert_equal(3, extensions.tag)
+    assert_equal(1, extensions.value.size)
+    assert_equal(OpenSSL::ASN1::Sequence, extensions.value[0].class)
+    assert_equal(3, extensions.value[0].value.size)
+
+    ext = extensions.value[0].value[0]  # basicConstraints
+    assert_equal(OpenSSL::ASN1::Sequence, ext.class)
+    assert_equal(3, ext.value.size)
+    assert_equal(OpenSSL::ASN1::ObjectId, ext.value[0].class)
+    assert_equal("2.5.29.19",  ext.value[0].oid)
+    assert_equal(OpenSSL::ASN1::Boolean, ext.value[1].class)
+    assert_equal(true, ext.value[1].value)
+    assert_equal(OpenSSL::ASN1::OctetString, ext.value[2].class)
+    extv = OpenSSL::ASN1.decode(ext.value[2].value)
+    assert_equal(OpenSSL::ASN1::Sequence, extv.class)
+    assert_equal(2, extv.value.size)
+    assert_equal(OpenSSL::ASN1::Boolean, extv.value[0].class)
+    assert_equal(true, extv.value[0].value)
+    assert_equal(OpenSSL::ASN1::Integer, extv.value[1].class)
+    assert_equal(1, extv.value[1].value)
+
+    ext = extensions.value[0].value[1]  # keyUsage
+    assert_equal(OpenSSL::ASN1::Sequence, ext.class)
+    assert_equal(3, ext.value.size)
+    assert_equal(OpenSSL::ASN1::ObjectId, ext.value[0].class)
+    assert_equal("2.5.29.15",  ext.value[0].oid)
+    assert_equal(OpenSSL::ASN1::Boolean, ext.value[1].class)
+    assert_equal(true, ext.value[1].value)
+    assert_equal(OpenSSL::ASN1::OctetString, ext.value[2].class)
+    extv = OpenSSL::ASN1.decode(ext.value[2].value)
+    assert_equal(OpenSSL::ASN1::BitString, extv.class)
+    str = "\000"; str[0] = 0b00000110.chr
+    assert_equal(str, extv.value)
+
+    ext = extensions.value[0].value[2]  # subjetKeyIdentifier
+    assert_equal(OpenSSL::ASN1::Sequence, ext.class)
+    assert_equal(2, ext.value.size)
+    assert_equal(OpenSSL::ASN1::ObjectId, ext.value[0].class)
+    assert_equal("2.5.29.14",  ext.value[0].oid)
+    assert_equal(OpenSSL::ASN1::OctetString, ext.value[1].class)
+    extv = OpenSSL::ASN1.decode(ext.value[1].value)
+    assert_equal(OpenSSL::ASN1::OctetString, extv.class)
+    sha1 = OpenSSL::Digest::SHA1.new
+    sha1.update(pkey.value[1].value)
+    assert_equal(sha1.digest, extv.value)
+
+    assert_equal(OpenSSL::ASN1::Sequence, sig_alg.class)
+    assert_equal(2, sig_alg.value.size)
+    assert_equal(OpenSSL::ASN1::ObjectId, pkey.value[0].value[0].class)
+    assert_equal("1.2.840.113549.1.1.1", pkey.value[0].value[0].oid)
+    assert_equal(OpenSSL::ASN1::Null, pkey.value[0].value[1].class)
+
+    assert_equal(OpenSSL::ASN1::BitString, sig_val.class)
+    cululated_sig = key.sign(OpenSSL::Digest::SHA1.new, tbs_cert.to_der)
+    assert_equal(cululated_sig, sig_val.value)
+  end
+
+  def test_encode_boolean
+    encode_decode_test(OpenSSL::ASN1::Boolean, [true, false])
+  end
+
+  def test_encode_integer
+    encode_decode_test(OpenSSL::ASN1::Integer, [72, -127, -128, 128, -1, 0, 1, -(2**12345), 2**12345])
+  end
+
+  def test_encode_nil
+    m = OpenSSL::ASN1
+    [
+      m::Boolean, m::Integer, m::BitString, m::OctetString,
+      m::ObjectId, m::Enumerated, m::UTF8String, m::UTCTime,
+      m::GeneralizedTime, m::Sequence, m::Set
+    ].each do |klass|
+      #Primitives raise TypeError, Constructives NoMethodError
+      assert_raise(TypeError, NoMethodError) { klass.send(:new, nil).to_der }
+    end
+  end
+
+  def encode_decode_test(type, values)
+    values.each do |v|
+      assert_equal(v, OpenSSL::ASN1.decode(type.new(v).to_der).value)
+    end
+  end
+
+  def test_decode_pem #should fail gracefully (cf. [ruby-dev:44542])
+    pem = <<-_EOS_
+-----BEGIN CERTIFICATE-----
+MIIC8zCCAdugAwIBAgIBATANBgkqhkiG9w0BAQUFADA9MRMwEQYKCZImiZPyLGQB
+GRYDb3JnMRkwFwYKCZImiZPyLGQBGRYJcnVieS1sYW5nMQswCQYDVQQDDAJDQTAe
+Fw0xMTA5MjUxMzQ4MjZaFw0xMTA5MjUxNDQ4MjZaMD0xEzARBgoJkiaJk/IsZAEZ
+FgNvcmcxGTAXBgoJkiaJk/IsZAEZFglydWJ5LWxhbmcxCzAJBgNVBAMMAkNBMIIB
+IjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEAuV9ht9J7k4NBs38jOXvvTKY9
+gW8nLICSno5EETR1cuF7i4pNs9I1QJGAFAX0BEO4KbzXmuOvfCpD3CU+Slp1enen
+fzq/t/e/1IRW0wkJUJUFQign4CtrkJL+P07yx18UjyPlBXb81ApEmAB5mrJVSrWm
+qbjs07JbuS4QQGGXLc+Su96DkYKmSNVjBiLxVVSpyZfAY3hD37d60uG+X8xdW5v6
+8JkRFIhdGlb6JL8fllf/A/blNwdJOhVr9mESHhwGjwfSeTDPfd8ZLE027E5lyAVX
+9KZYcU00mOX+fdxOSnGqS/8JDRh0EPHDL15RcJjV2J6vZjPb0rOYGDoMcH+94wID
+AQABMA0GCSqGSIb3DQEBBQUAA4IBAQAiAtrIr1pLX4GYN5klviWKb8HC9ICYuAFI
+NfE3FwqzErEVXotuMe3yPVyB3Bv6rjYY/x5EtS5+WPTbHlvHZTkfcsnTpizcn4mW
+dJ6dDRaFCHt1YKKjUxqBt9lvvrc3nReYZN/P+s1mrDhWzGf8iPZgf8sFUHgnaK7W
+CXRVXmPFgCDRNpDDVQ0MQkr509yYfTH+dujNzqTCwSvkyZFyQ7Oe8Yj0VR6kquG3
+rEzBQ0F9dUyqQ9gyRg8KHhDfv9HzT1d/rnUZMkoombwYBRIUChGCYV0GnJcan2Zm
+/93PnPG1IvPjYNd5VlV+sXSnaxQn974HRCsMv7jA8BD6IgSaX6WK
+-----END CERTIFICATE-----
+    _EOS_
+    assert_raise(OpenSSL::ASN1::ASN1Error) { OpenSSL::ASN1.decode(pem) }
+    assert_raise(OpenSSL::ASN1::ASN1Error) { OpenSSL::ASN1.decode_all(pem) }
+  end
+
+  def test_primitive_cannot_set_infinite_length
+    begin
+      prim = OpenSSL::ASN1::Integer.new(50)
+      assert_equal(false, prim.infinite_length)
+      prim.infinite_length = true
+      flunk('Could set infinite length on primitive value')
+    rescue NoMethodError
+      #ok
+    end
+  end
+
+  def test_decode_all
+    expected = %w{ 02 01 01 02 01 02 02 01 03 }
+    raw = [expected.join('')].pack('H*')
+    ary = OpenSSL::ASN1.decode_all(raw)
+    assert_equal(3, ary.size)
+    ary.each_with_index do |asn1, i|
+      assert_universal(OpenSSL::ASN1::INTEGER, asn1)
+      assert_equal(i + 1, asn1.value)
+    end
+  end
+
+  def test_decode_utctime
+    expected = Time.at 1374535380
+    assert_equal expected, OpenSSL::ASN1.decode("\x17\v1307222323Z").value
+
+    expected += 17
+    assert_equal expected, OpenSSL::ASN1.decode("\x17\r130722232317Z").value
+  end
+
+  def test_create_inf_length_primitive
+    expected = %w{ 24 80 04 01 61 00 00 }
+    raw = [expected.join('')].pack('H*')
+    val = OpenSSL::ASN1::OctetString.new('a')
+    cons = OpenSSL::ASN1::Constructive.new([val,
+                                            OpenSSL::ASN1::EndOfContent.new],
+                                            OpenSSL::ASN1::OCTET_STRING,
+                                            nil,
+                                            :UNIVERSAL)
+    cons.infinite_length = true
+    assert_equal(nil, cons.tagging)
+    assert_equal(raw, cons.to_der)
+    asn1 = OpenSSL::ASN1.decode(raw)
+    assert(asn1.infinite_length)
+    assert_equal(raw, asn1.to_der)
+  end
+
+  def test_cons_without_inf_length_forbidden
+    assert_raise(OpenSSL::ASN1::ASN1Error) do
+      val = OpenSSL::ASN1::OctetString.new('a')
+      cons = OpenSSL::ASN1::Constructive.new([val],
+                                            OpenSSL::ASN1::OCTET_STRING,
+                                            nil,
+                                            :UNIVERSAL)
+      cons.to_der
+    end
+  end
+
+  def test_cons_without_array_forbidden
+    assert_raise(OpenSSL::ASN1::ASN1Error) do
+      val = OpenSSL::ASN1::OctetString.new('a')
+      cons = OpenSSL::ASN1::Constructive.new(val,
+                                            OpenSSL::ASN1::OCTET_STRING,
+                                            nil,
+                                            :UNIVERSAL)
+      cons.infinite_length = true
+      cons.to_der
+    end
+  end
+
+  def test_parse_empty_sequence
+    expected = %w{ A0 07 30 02 30 00 02 01 00 }
+    raw = [expected.join('')].pack('H*')
+    asn1 = OpenSSL::ASN1.decode(raw)
+    assert_equal(raw, asn1.to_der)
+    assert_equal(2, asn1.value.size)
+    seq = asn1.value[0]
+    assert_equal(1, seq.value.size)
+    inner_seq = seq.value[0]
+    assert_equal(0, inner_seq.value.size)
+  end
+
+  def test_parse_tagged_0_infinite
+    expected = %w{ 30 80 02 01 01 80 01 02 00 00 }
+    raw = [expected.join('')].pack('H*')
+    asn1 = OpenSSL::ASN1.decode(raw)
+    assert_equal(3, asn1.value.size)
+    int = asn1.value[0]
+    assert_universal(OpenSSL::ASN1::INTEGER, int)
+    tagged = asn1.value[1]
+    assert_equal(0, tagged.tag)
+    assert_universal(OpenSSL::ASN1::EOC, asn1.value[2])
+    assert_equal(raw, asn1.to_der)
+  end
+
+  def test_seq_infinite_length
+    begin
+      content = [ OpenSSL::ASN1::Null.new(nil),
+                  OpenSSL::ASN1::EndOfContent.new ]
+      cons = OpenSSL::ASN1::Sequence.new(content)
+      cons.infinite_length = true
+      expected = %w{ 30 80 05 00 00 00 }
+      raw = [expected.join('')].pack('H*')
+      assert_equal(raw, cons.to_der)
+      assert_equal(raw, OpenSSL::ASN1.decode(raw).to_der)
+    end
+  end
+
+  def test_set_infinite_length
+    begin
+      content = [ OpenSSL::ASN1::Null.new(nil),
+                  OpenSSL::ASN1::EndOfContent.new() ]
+      cons = OpenSSL::ASN1::Set.new(content)
+      cons.infinite_length = true
+      expected = %w{ 31 80 05 00 00 00 }
+      raw = [expected.join('')].pack('H*')
+      assert_equal(raw, cons.to_der)
+      assert_equal(raw, OpenSSL::ASN1.decode(raw).to_der)
+    end
+  end
+
+  def test_octet_string_infinite_length
+    begin
+      octets = [ OpenSSL::ASN1::OctetString.new('aaa'),
+                 OpenSSL::ASN1::EndOfContent.new() ]
+      cons = OpenSSL::ASN1::Constructive.new(
+        octets,
+        OpenSSL::ASN1::OCTET_STRING,
+        nil,
+        :UNIVERSAL)
+      cons.infinite_length = true
+      expected = %w{ 24 80 04 03 61 61 61 00 00 }
+      raw = [expected.join('')].pack('H*')
+      assert_equal(raw, cons.to_der)
+      assert_equal(raw, OpenSSL::ASN1.decode(raw).to_der)
+    end
+  end
+
+  def test_prim_explicit_tagging
+    begin
+      oct_str = OpenSSL::ASN1::OctetString.new("a", 0, :EXPLICIT)
+      expected = %w{ A0 03 04 01 61 }
+      raw = [expected.join('')].pack('H*')
+      assert_equal(raw, oct_str.to_der)
+      assert_equal(raw, OpenSSL::ASN1.decode(raw).to_der)
+    end
+  end
+
+  def test_prim_explicit_tagging_tag_class
+    begin
+      oct_str = OpenSSL::ASN1::OctetString.new("a", 0, :EXPLICIT)
+      oct_str2 = OpenSSL::ASN1::OctetString.new(
+        "a",
+        0,
+        :EXPLICIT,
+        :CONTEXT_SPECIFIC)
+      assert_equal(oct_str.to_der, oct_str2.to_der)
+    end
+  end
+
+  def test_prim_implicit_tagging
+    begin
+      int = OpenSSL::ASN1::Integer.new(1, 0, :IMPLICIT)
+      expected = %w{ 80 01 01 }
+      raw = [expected.join('')].pack('H*')
+      assert_equal(raw, int.to_der)
+      assert_equal(raw, OpenSSL::ASN1.decode(raw).to_der)
+    end
+  end
+
+  def test_prim_implicit_tagging_tag_class
+    begin
+      int = OpenSSL::ASN1::Integer.new(1, 0, :IMPLICIT)
+      int2 = OpenSSL::ASN1::Integer.new(1, 0, :IMPLICIT, :CONTEXT_SPECIFIC);
+      assert_equal(int.to_der, int2.to_der)
+    end
+  end
+
+  def test_cons_explicit_tagging
+    begin
+      content = [ OpenSSL::ASN1::PrintableString.new('abc') ]
+      seq = OpenSSL::ASN1::Sequence.new(content, 2, :EXPLICIT)
+      expected = %w{ A2 07 30 05 13 03 61 62 63 }
+      raw = [expected.join('')].pack('H*')
+      assert_equal(raw, seq.to_der)
+      assert_equal(raw, OpenSSL::ASN1.decode(raw).to_der)
+    end
+  end
+
+  def test_cons_explicit_tagging_inf_length
+    begin
+      content = [ OpenSSL::ASN1::PrintableString.new('abc') ,
+                  OpenSSL::ASN1::EndOfContent.new() ]
+      seq = OpenSSL::ASN1::Sequence.new(content, 2, :EXPLICIT)
+      seq.infinite_length = true
+      expected = %w{ A2 80 30 80 13 03 61 62 63 00 00 00 00 }
+      raw = [expected.join('')].pack('H*')
+      assert_equal(raw, seq.to_der)
+      assert_equal(raw, OpenSSL::ASN1.decode(raw).to_der)
+    end
+  end
+
+  def test_cons_implicit_tagging
+    begin
+      content = [ OpenSSL::ASN1::Null.new(nil) ]
+      seq = OpenSSL::ASN1::Sequence.new(content, 1, :IMPLICIT)
+      expected = %w{ A1 02 05 00 }
+      raw = [expected.join('')].pack('H*')
+      assert_equal(raw, seq.to_der)
+      assert_equal(raw, OpenSSL::ASN1.decode(raw).to_der)
+    end
+  end
+
+  def test_cons_implicit_tagging_inf_length
+    begin
+      content = [ OpenSSL::ASN1::Null.new(nil),
+                  OpenSSL::ASN1::EndOfContent.new() ]
+      seq = OpenSSL::ASN1::Sequence.new(content, 1, :IMPLICIT)
+      seq.infinite_length = true
+      expected = %w{ A1 80 05 00 00 00 }
+      raw = [expected.join('')].pack('H*')
+      assert_equal(raw, seq.to_der)
+      assert_equal(raw, OpenSSL::ASN1.decode(raw).to_der)
+    end
+  end
+
+  def test_octet_string_infinite_length_explicit_tagging
+    begin
+      octets = [ OpenSSL::ASN1::OctetString.new('aaa'),
+                 OpenSSL::ASN1::EndOfContent.new() ]
+      cons = OpenSSL::ASN1::Constructive.new(
+        octets,
+        1,
+        :EXPLICIT)
+      cons.infinite_length = true
+      expected = %w{ A1 80 24 80 04 03 61 61 61 00 00 00 00 }
+      raw = [expected.join('')].pack('H*')
+      assert_equal(raw, cons.to_der)
+      assert_equal(raw, OpenSSL::ASN1.decode(raw).to_der)
+    end
+  end
+
+  def test_octet_string_infinite_length_implicit_tagging
+    begin
+      octets = [ OpenSSL::ASN1::OctetString.new('aaa'),
+                 OpenSSL::ASN1::EndOfContent.new() ]
+      cons = OpenSSL::ASN1::Constructive.new(
+        octets,
+        0,
+        :IMPLICIT)
+      cons.infinite_length = true
+      expected = %w{ A0 80 04 03 61 61 61 00 00 }
+      raw = [expected.join('')].pack('H*')
+      assert_equal(raw, cons.to_der)
+      assert_equal(raw, OpenSSL::ASN1.decode(raw).to_der)
+    end
+  end
+
+  def test_recursive_octet_string_infinite_length
+    begin
+      octets_sub1 = [ OpenSSL::ASN1::OctetString.new("\x01"),
+                      OpenSSL::ASN1::EndOfContent.new() ]
+      octets_sub2 = [ OpenSSL::ASN1::OctetString.new("\x02"),
+                      OpenSSL::ASN1::EndOfContent.new() ]
+      container1 = OpenSSL::ASN1::Constructive.new(
+        octets_sub1,
+        OpenSSL::ASN1::OCTET_STRING,
+        nil,
+        :UNIVERSAL)
+      container1.infinite_length = true
+      container2 = OpenSSL::ASN1::Constructive.new(
+        octets_sub2,
+        OpenSSL::ASN1::OCTET_STRING,
+        nil,
+        :UNIVERSAL)
+      container2.infinite_length = true
+      octets3 = OpenSSL::ASN1::OctetString.new("\x03")
+
+      octets = [ container1, container2, octets3,
+                 OpenSSL::ASN1::EndOfContent.new() ]
+      cons = OpenSSL::ASN1::Constructive.new(
+        octets,
+        OpenSSL::ASN1::OCTET_STRING,
+        nil,
+        :UNIVERSAL)
+      cons.infinite_length = true
+      expected = %w{ 24 80 24 80 04 01 01 00 00 24 80 04 01 02 00 00 04 01 03 00 00 }
+      raw = [expected.join('')].pack('H*')
+      assert_equal(raw, cons.to_der)
+      assert_equal(raw, OpenSSL::ASN1.decode(raw).to_der)
+    end
+  end
+
+  def test_bit_string_infinite_length
+    begin
+      content = [ OpenSSL::ASN1::BitString.new("\x01"),
+                  OpenSSL::ASN1::EndOfContent.new() ]
+      cons = OpenSSL::ASN1::Constructive.new(
+        content,
+        OpenSSL::ASN1::BIT_STRING,
+        nil,
+        :UNIVERSAL)
+      cons.infinite_length = true
+      expected = %w{ 23 80 03 02 00 01 00 00 }
+      raw = [expected.join('')].pack('H*')
+      assert_equal(raw, cons.to_der)
+      assert_equal(raw, OpenSSL::ASN1.decode(raw).to_der)
+    end
+  end
+
+  def test_primitive_inf_length
+    assert_raises(OpenSSL::ASN1::ASN1Error) do
+      spec = %w{ 02 80 02 01 01 00 00 }
+      raw = [spec.join('')].pack('H*')
+      OpenSSL::ASN1.decode(raw)
+      OpenSSL::ASN1.decode_all(raw)
+    end
+  end
+
+  def test_recursive_octet_string_parse
+    test = %w{ 24 80 24 80 04 01 01 00 00 24 80 04 01 02 00 00 04 01 03 00 00 }
+    raw = [test.join('')].pack('H*')
+    asn1 = OpenSSL::ASN1.decode(raw)
+    assert_equal(OpenSSL::ASN1::Constructive, asn1.class)
+    assert_universal(OpenSSL::ASN1::OCTET_STRING, asn1)
+    assert_equal(true, asn1.infinite_length)
+    assert_equal(4, asn1.value.size)
+    nested1 = asn1.value[0]
+    assert_equal(OpenSSL::ASN1::Constructive, nested1.class)
+    assert_universal(OpenSSL::ASN1::OCTET_STRING, nested1)
+    assert_equal(true, nested1.infinite_length)
+    assert_equal(2, nested1.value.size)
+    oct1 = nested1.value[0]
+    assert_universal(OpenSSL::ASN1::OCTET_STRING, oct1)
+    assert_equal(false, oct1.infinite_length)
+    assert_universal(OpenSSL::ASN1::EOC, nested1.value[1])
+    assert_equal(false, nested1.value[1].infinite_length)
+    nested2 = asn1.value[1]
+    assert_equal(OpenSSL::ASN1::Constructive, nested2.class)
+    assert_universal(OpenSSL::ASN1::OCTET_STRING, nested2)
+    assert_equal(true, nested2.infinite_length)
+    assert_equal(2, nested2.value.size)
+    oct2 = nested2.value[0]
+    assert_universal(OpenSSL::ASN1::OCTET_STRING, oct2)
+    assert_equal(false, oct2.infinite_length)
+    assert_universal(OpenSSL::ASN1::EOC, nested2.value[1])
+    assert_equal(false, nested2.value[1].infinite_length)
+    oct3 = asn1.value[2]
+    assert_universal(OpenSSL::ASN1::OCTET_STRING, oct3)
+    assert_equal(false, oct3.infinite_length)
+    assert_universal(OpenSSL::ASN1::EOC, asn1.value[3])
+    assert_equal(false, asn1.value[3].infinite_length)
+  end
+
+  private
+
+  def assert_universal(tag, asn1)
+    assert_equal(tag, asn1.tag)
+    if asn1.respond_to?(:tagging)
+      assert_nil(asn1.tagging)
+    end
+    assert_equal(:UNIVERSAL, asn1.tag_class)
+  end
+
+end if defined?(OpenSSL::TestUtils)
+
diff --git a/jni/ruby/test/openssl/test_bn.rb b/jni/ruby/test/openssl/test_bn.rb
new file mode 100644
index 0000000..667cb29
--- /dev/null
+++ b/jni/ruby/test/openssl/test_bn.rb
@@ -0,0 +1,52 @@
+require_relative 'utils'
+
+if defined?(OpenSSL::TestUtils)
+
+class OpenSSL::TestBN < Test::Unit::TestCase
+  def test_new_str
+    e1 = OpenSSL::BN.new(999.to_s(16), 16) # OpenSSL::BN.new(str, 16) must be most stable
+    e2 = OpenSSL::BN.new((2**107-1).to_s(16), 16)
+    assert_equal(e1, OpenSSL::BN.new("999"))
+    assert_equal(e2, OpenSSL::BN.new((2**107-1).to_s))
+    assert_equal(e1, OpenSSL::BN.new("999", 10))
+    assert_equal(e2, OpenSSL::BN.new((2**107-1).to_s, 10))
+    assert_equal(e1, OpenSSL::BN.new("\x03\xE7", 2))
+    assert_equal(e2, OpenSSL::BN.new("\a\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF", 2))
+    assert_equal(e1, OpenSSL::BN.new("\x00\x00\x00\x02\x03\xE7", 0))
+    assert_equal(e2, OpenSSL::BN.new("\x00\x00\x00\x0E\a\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF", 0))
+  end
+
+  def test_new_bn
+    e1 = OpenSSL::BN.new(999.to_s(16), 16)
+    e2 = OpenSSL::BN.new((2**107-1).to_s(16), 16)
+    assert_equal(e1, OpenSSL::BN.new(e1))
+    assert_equal(e2, OpenSSL::BN.new(e2))
+  end
+
+  def test_new_integer
+    assert_equal(999.to_bn, OpenSSL::BN.new(999))
+    assert_equal((2 ** 107 - 1).to_bn, OpenSSL::BN.new(2 ** 107 - 1))
+    assert_equal(-999.to_bn, OpenSSL::BN.new(-999))
+    assert_equal((-(2 ** 107 - 1)).to_bn, OpenSSL::BN.new(-(2 ** 107 - 1)))
+  end
+
+  def test_to_bn
+    e1 = OpenSSL::BN.new(999.to_s(16), 16)
+    e2 = OpenSSL::BN.new((2**107-1).to_s(16), 16)
+    assert_equal(e1, 999.to_bn)
+    assert_equal(e2, (2**107-1).to_bn)
+  end
+
+  def test_prime_p
+    assert_equal(true, OpenSSL::BN.new((2 ** 107 - 1).to_s(16), 16).prime?)
+    assert_equal(true, OpenSSL::BN.new((2 ** 127 - 1).to_s(16), 16).prime?(1))
+  end
+
+  def test_cmp_nil
+    bn = OpenSSL::BN.new('1')
+    assert_equal(false, bn == nil)
+    assert_equal(true,  bn != nil)
+  end
+end
+
+end
diff --git a/jni/ruby/test/openssl/test_buffering.rb b/jni/ruby/test/openssl/test_buffering.rb
new file mode 100644
index 0000000..c62dd4d
--- /dev/null
+++ b/jni/ruby/test/openssl/test_buffering.rb
@@ -0,0 +1,87 @@
+require_relative 'utils'
+require 'stringio'
+
+class OpenSSL::TestBuffering < Test::Unit::TestCase
+
+  class IO
+    include OpenSSL::Buffering
+
+    attr_accessor :sync
+
+    def initialize
+      @io = ""
+      def @io.sync
+        true
+      end
+
+      super
+
+      @sync = false
+    end
+
+    def string
+      @io
+    end
+
+    def sysread(size)
+      str = @io.slice!(0, size)
+      raise EOFError if str.empty?
+      str
+    end
+
+    def syswrite(str)
+      @io << str
+      str.size
+    end
+  end
+
+  def setup
+    @io = IO.new
+  end
+
+  def test_flush
+    @io.write 'a'
+
+    refute @io.sync
+    assert_empty @io.string
+
+    assert_equal @io, @io.flush
+
+    refute @io.sync
+    assert_equal 'a', @io.string
+  end
+
+  def test_flush_error
+    @io.write 'a'
+
+    refute @io.sync
+    assert_empty @io.string
+
+    def @io.syswrite *a
+      raise SystemCallError, 'fail'
+    end
+
+    assert_raises SystemCallError do
+      @io.flush
+    end
+
+    refute @io.sync, 'sync must not change'
+  end
+
+  def test_getc
+    @io.syswrite('abc')
+    assert_equal(?a, @io.getc)
+    assert_equal(?b, @io.getc)
+    assert_equal(?c, @io.getc)
+  end
+
+  def test_each_byte
+    @io.syswrite('abc')
+    res = []
+    @io.each_byte do |c|
+      res << c
+    end
+    assert_equal([97, 98, 99], res)
+  end
+
+end if defined?(OpenSSL::TestUtils)
diff --git a/jni/ruby/test/openssl/test_cipher.rb b/jni/ruby/test/openssl/test_cipher.rb
new file mode 100644
index 0000000..6f92c38
--- /dev/null
+++ b/jni/ruby/test/openssl/test_cipher.rb
@@ -0,0 +1,260 @@
+require_relative 'utils'
+
+if defined?(OpenSSL::TestUtils)
+
+class OpenSSL::TestCipher < Test::Unit::TestCase
+
+  class << self
+
+    def has_cipher?(name)
+      ciphers = OpenSSL::Cipher.ciphers
+      # redefine method so we can use the cached ciphers value from the closure
+      # and need not recompute the list each time
+      define_singleton_method :has_cipher? do |name|
+        ciphers.include?(name)
+      end
+      has_cipher?(name)
+    end
+
+    def has_ciphers?(list)
+      list.all? { |name| has_cipher?(name) }
+    end
+
+  end
+
+  def setup
+    @c1 = OpenSSL::Cipher::Cipher.new("DES-EDE3-CBC")
+    @c2 = OpenSSL::Cipher::DES.new(:EDE3, "CBC")
+    @key = "\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0"
+    @iv = "\0\0\0\0\0\0\0\0"
+    @hexkey = "0000000000000000000000000000000000000000000000"
+    @hexiv = "0000000000000000"
+    @data = "DATA"
+  end
+
+  def teardown
+    @c1 = @c2 = nil
+  end
+
+  def test_crypt
+    @c1.encrypt.pkcs5_keyivgen(@key, @iv)
+    @c2.encrypt.pkcs5_keyivgen(@key, @iv)
+    s1 = @c1.update(@data) + @c1.final
+    s2 = @c2.update(@data) + @c2.final
+    assert_equal(s1, s2, "encrypt")
+
+    @c1.decrypt.pkcs5_keyivgen(@key, @iv)
+    @c2.decrypt.pkcs5_keyivgen(@key, @iv)
+    assert_equal(@data, @c1.update(s1)+@c1.final, "decrypt")
+    assert_equal(@data, @c2.update(s2)+@c2.final, "decrypt")
+  end
+
+  def test_info
+    assert_equal("DES-EDE3-CBC", @c1.name, "name")
+    assert_equal("DES-EDE3-CBC", @c2.name, "name")
+    assert_kind_of(Fixnum, @c1.key_len, "key_len")
+    assert_kind_of(Fixnum, @c1.iv_len, "iv_len")
+  end
+
+  def test_dup
+    assert_equal(@c1.name, @c1.dup.name, "dup")
+    assert_equal(@c1.name, @c1.clone.name, "clone")
+    @c1.encrypt
+    @c1.key = @key
+    @c1.iv = @iv
+    tmpc = @c1.dup
+    s1 = @c1.update(@data) + @c1.final
+    s2 = tmpc.update(@data) + tmpc.final
+    assert_equal(s1, s2, "encrypt dup")
+  end
+
+  def test_reset
+    @c1.encrypt
+    @c1.key = @key
+    @c1.iv = @iv
+    s1 = @c1.update(@data) + @c1.final
+    @c1.reset
+    s2 = @c1.update(@data) + @c1.final
+    assert_equal(s1, s2, "encrypt reset")
+  end
+
+  def test_empty_data
+    @c1.encrypt
+    assert_raise(ArgumentError){ @c1.update("") }
+  end
+
+  def test_initialize
+    assert_raise(RuntimeError) {@c1.__send__(:initialize, "DES-EDE3-CBC")}
+    assert_raise(RuntimeError) {OpenSSL::Cipher.allocate.final}
+  end
+
+  def test_ctr_if_exists
+    begin
+      cipher = OpenSSL::Cipher.new('aes-128-ctr')
+      cipher.encrypt
+      cipher.pkcs5_keyivgen('password')
+      c = cipher.update('hello,world') + cipher.final
+      cipher.decrypt
+      cipher.pkcs5_keyivgen('password')
+      assert_equal('hello,world', cipher.update(c) + cipher.final)
+    end
+  end if has_cipher?('aes-128-ctr')
+
+  if OpenSSL::OPENSSL_VERSION_NUMBER > 0x00907000
+    def test_ciphers
+      OpenSSL::Cipher.ciphers.each{|name|
+        next if /netbsd/ =~ RUBY_PLATFORM && /idea|rc5/i =~ name
+        begin
+          assert_kind_of(OpenSSL::Cipher::Cipher, OpenSSL::Cipher::Cipher.new(name))
+        rescue OpenSSL::Cipher::CipherError => e
+          next if /wrap/ =~ name and e.message == 'wrap mode not allowed'
+          raise
+        end
+      }
+    end
+
+    def test_AES
+      pt = File.read(__FILE__)
+      %w(ECB CBC CFB OFB).each{|mode|
+        c1 = OpenSSL::Cipher::AES256.new(mode)
+        c1.encrypt
+        c1.pkcs5_keyivgen("passwd")
+        ct = c1.update(pt) + c1.final
+
+        c2 = OpenSSL::Cipher::AES256.new(mode)
+        c2.decrypt
+        c2.pkcs5_keyivgen("passwd")
+        assert_equal(pt, c2.update(ct) + c2.final)
+      }
+    end
+
+    def test_AES_crush
+      500.times do
+        assert_nothing_raised("[Bug #2768]") do
+          # it caused OpenSSL SEGV by uninitialized key
+          OpenSSL::Cipher::AES128.new("ECB").update "." * 17
+        end
+      end
+    end
+  end
+
+  if has_ciphers?(['aes-128-gcm', 'aes-192-gcm', 'aes-256-gcm'])
+
+    def test_authenticated
+      cipher = OpenSSL::Cipher.new('aes-128-gcm')
+      assert(cipher.authenticated?)
+      cipher = OpenSSL::Cipher.new('aes-128-cbc')
+      refute(cipher.authenticated?)
+    end
+
+    def test_aes_gcm
+      ['aes-128-gcm', 'aes-192-gcm', 'aes-256-gcm'].each do |algo|
+        pt = "You should all use Authenticated Encryption!"
+        cipher, key, iv = new_encryptor(algo)
+
+        cipher.auth_data = "aad"
+        ct  = cipher.update(pt) + cipher.final
+        tag = cipher.auth_tag
+        assert_equal(16, tag.size)
+
+        decipher = new_decryptor(algo, key, iv)
+        decipher.auth_tag = tag
+        decipher.auth_data = "aad"
+
+        assert_equal(pt, decipher.update(ct) + decipher.final)
+      end
+    end
+
+    def test_aes_gcm_short_tag
+      ['aes-128-gcm', 'aes-192-gcm', 'aes-256-gcm'].each do |algo|
+        pt = "You should all use Authenticated Encryption!"
+        cipher, key, iv = new_encryptor(algo)
+
+        cipher.auth_data = "aad"
+        ct  = cipher.update(pt) + cipher.final
+        tag = cipher.auth_tag(8)
+        assert_equal(8, tag.size)
+
+        decipher = new_decryptor(algo, key, iv)
+        decipher.auth_tag = tag
+        decipher.auth_data = "aad"
+
+        assert_equal(pt, decipher.update(ct) + decipher.final)
+      end
+    end
+
+    def test_aes_gcm_wrong_tag
+      pt = "You should all use Authenticated Encryption!"
+      cipher, key, iv = new_encryptor('aes-128-gcm')
+
+      cipher.auth_data = "aad"
+      ct  = cipher.update(pt) + cipher.final
+      tag = cipher.auth_tag
+
+      decipher = new_decryptor('aes-128-gcm', key, iv)
+      tag.setbyte(-1, (tag.getbyte(-1) + 1) & 0xff)
+      decipher.auth_tag = tag
+      decipher.auth_data = "aad"
+
+      assert_raise OpenSSL::Cipher::CipherError do
+        decipher.update(ct) + decipher.final
+      end
+    end
+
+    def test_aes_gcm_wrong_auth_data
+      pt = "You should all use Authenticated Encryption!"
+      cipher, key, iv = new_encryptor('aes-128-gcm')
+
+      cipher.auth_data = "aad"
+      ct  = cipher.update(pt) + cipher.final
+      tag = cipher.auth_tag
+
+      decipher = new_decryptor('aes-128-gcm', key, iv)
+      decipher.auth_tag = tag
+      decipher.auth_data = "daa"
+
+      assert_raise OpenSSL::Cipher::CipherError do
+        decipher.update(ct) + decipher.final
+      end
+    end
+
+    def test_aes_gcm_wrong_ciphertext
+      pt = "You should all use Authenticated Encryption!"
+      cipher, key, iv = new_encryptor('aes-128-gcm')
+
+      cipher.auth_data = "aad"
+      ct  = cipher.update(pt) + cipher.final
+      tag = cipher.auth_tag
+
+      decipher = new_decryptor('aes-128-gcm', key, iv)
+      decipher.auth_tag = tag
+      decipher.auth_data = "aad"
+
+      assert_raise OpenSSL::Cipher::CipherError do
+        decipher.update(ct[0..-2] << ct[-1].succ) + decipher.final
+      end
+    end
+
+  end
+
+  private
+
+  def new_encryptor(algo)
+    cipher = OpenSSL::Cipher.new(algo)
+    cipher.encrypt
+    key = cipher.random_key
+    iv = cipher.random_iv
+    [cipher, key, iv]
+  end
+
+  def new_decryptor(algo, key, iv)
+    OpenSSL::Cipher.new(algo).tap do |cipher|
+      cipher.decrypt
+      cipher.key = key
+      cipher.iv = iv
+    end
+  end
+
+end
+
+end
diff --git a/jni/ruby/test/openssl/test_config.rb b/jni/ruby/test/openssl/test_config.rb
new file mode 100644
index 0000000..62f9fab
--- /dev/null
+++ b/jni/ruby/test/openssl/test_config.rb
@@ -0,0 +1,297 @@
+require_relative 'utils'
+
+class OpenSSL::TestConfig < Test::Unit::TestCase
+  def setup
+    file = Tempfile.open("openssl.cnf")
+    file << <<__EOD__
+HOME = .
+[ ca ]
+default_ca = CA_default
+[ CA_default ]
+dir = ./demoCA
+certs                =                  ./certs
+__EOD__
+    file.close
+    @tmpfile = file
+    @it = OpenSSL::Config.new(file.path)
+  end
+
+  def teardown
+    @tmpfile.close!
+  end
+
+  def test_constants
+    assert(defined?(OpenSSL::Config::DEFAULT_CONFIG_FILE))
+    config_file = OpenSSL::Config::DEFAULT_CONFIG_FILE
+    skip "DEFAULT_CONFIG_FILE may return a wrong path on your platforms. [Bug #6830]" unless File.readable?(config_file)
+    assert_nothing_raised do
+      OpenSSL::Config.load(config_file)
+    end
+  end
+
+  def test_s_parse
+    c = OpenSSL::Config.parse('')
+    assert_equal("[ default ]\n\n", c.to_s)
+    c = OpenSSL::Config.parse(@it.to_s)
+    assert_equal(['CA_default', 'ca', 'default'], c.sections.sort)
+  end
+
+  def test_s_parse_format
+    c = OpenSSL::Config.parse(<<__EOC__)
+ baz =qx\t                # "baz = qx"
+
+foo::bar = baz            # shortcut section::key format
+  default::bar = baz      # ditto
+a=\t \t                   # "a = ": trailing spaces are ignored
+ =b                       # " = b": empty key
+ =c                       # " = c": empty key (override the above line)
+    d=                    # "c = ": trailing comment is ignored
+
+sq = 'foo''b\\'ar'
+    dq ="foo""''\\""
+    dq2 = foo""bar
+esc=a\\r\\n\\b\\tb
+foo\\bar = foo\\b\\\\ar
+foo\\bar::foo\\bar = baz
+[default1  default2]\t\t  # space is allowed in section name
+          fo =b  ar       # space allowed in value
+[emptysection]
+ [doller ]
+foo=bar
+bar = $(foo)
+baz = 123$(default::bar)456${foo}798
+qux = ${baz}
+quxx = $qux.$qux
+__EOC__
+    assert_equal(['default', 'default1  default2', 'doller', 'emptysection', 'foo', 'foo\\bar'], c.sections.sort)
+    assert_equal(['', 'a', 'bar', 'baz', 'd', 'dq', 'dq2', 'esc', 'foo\\bar', 'sq'], c['default'].keys.sort)
+    assert_equal('c', c['default'][''])
+    assert_equal('', c['default']['a'])
+    assert_equal('qx', c['default']['baz'])
+    assert_equal('', c['default']['d'])
+    assert_equal('baz', c['default']['bar'])
+    assert_equal("foob'ar", c['default']['sq'])
+    assert_equal("foo''\"", c['default']['dq'])
+    assert_equal("foobar", c['default']['dq2'])
+    assert_equal("a\r\n\b\tb", c['default']['esc'])
+    assert_equal("foo\b\\ar", c['default']['foo\\bar'])
+    assert_equal('baz', c['foo']['bar'])
+    assert_equal('baz', c['foo\\bar']['foo\\bar'])
+    assert_equal('b  ar', c['default1  default2']['fo'])
+
+    # dolloer
+    assert_equal('bar', c['doller']['foo'])
+    assert_equal('bar', c['doller']['bar'])
+    assert_equal('123baz456bar798', c['doller']['baz'])
+    assert_equal('123baz456bar798', c['doller']['qux'])
+    assert_equal('123baz456bar798.123baz456bar798', c['doller']['quxx'])
+
+    excn = assert_raise(OpenSSL::ConfigError) do
+      OpenSSL::Config.parse("foo = $bar")
+    end
+    assert_equal("error in line 1: variable has no value", excn.message)
+
+    excn = assert_raise(OpenSSL::ConfigError) do
+      OpenSSL::Config.parse("foo = $(bar")
+    end
+    assert_equal("error in line 1: no close brace", excn.message)
+
+    excn = assert_raise(OpenSSL::ConfigError) do
+      OpenSSL::Config.parse("f o =b  ar      # no space in key")
+    end
+    assert_equal("error in line 1: missing equal sign", excn.message)
+
+    excn = assert_raise(OpenSSL::ConfigError) do
+      OpenSSL::Config.parse(<<__EOC__)
+# comment 1               # comments
+
+#
+ # comment 2
+\t#comment 3
+  [second    ]\t
+[third                    # section not terminated
+__EOC__
+    end
+    assert_equal("error in line 7: missing close square bracket", excn.message)
+  end
+
+  def test_s_load
+    # alias of new
+    c = OpenSSL::Config.load
+    assert_equal("", c.to_s)
+    assert_equal([], c.sections)
+    #
+    Tempfile.create("openssl.cnf") {|file|
+      file.close
+      c = OpenSSL::Config.load(file.path)
+      assert_equal("[ default ]\n\n", c.to_s)
+      assert_equal(['default'], c.sections)
+    }
+  end
+
+  def test_initialize
+    c = OpenSSL::Config.new
+    assert_equal("", c.to_s)
+    assert_equal([], c.sections)
+  end
+
+  def test_initialize_with_empty_file
+    Tempfile.create("openssl.cnf") {|file|
+      file.close
+      c = OpenSSL::Config.new(file.path)
+      assert_equal("[ default ]\n\n", c.to_s)
+      assert_equal(['default'], c.sections)
+    }
+  end
+
+  def test_initialize_with_example_file
+    assert_equal(['CA_default', 'ca', 'default'], @it.sections.sort)
+  end
+
+  def test_get_value
+    assert_equal('CA_default', @it.get_value('ca', 'default_ca'))
+    assert_equal(nil, @it.get_value('ca', 'no such key'))
+    assert_equal(nil, @it.get_value('no such section', 'no such key'))
+    assert_equal('.', @it.get_value('', 'HOME'))
+    assert_raise(TypeError) do
+      @it.get_value(nil, 'HOME') # not allowed unlike Config#value
+    end
+    # fallback to 'default' ugly...
+    assert_equal('.', @it.get_value('unknown', 'HOME'))
+  end
+
+  def test_get_value_ENV
+    key = ENV.keys.first
+    assert_not_nil(key) # make sure we have at least one ENV var.
+    assert_equal(ENV[key], @it.get_value('ENV', key))
+  end
+
+  def test_value
+    # supress deprecation warnings
+    OpenSSL::TestUtils.silent do
+      assert_equal('CA_default', @it.value('ca', 'default_ca'))
+      assert_equal(nil, @it.value('ca', 'no such key'))
+      assert_equal(nil, @it.value('no such section', 'no such key'))
+      assert_equal('.', @it.value('', 'HOME'))
+      assert_equal('.', @it.value(nil, 'HOME'))
+      assert_equal('.', @it.value('HOME'))
+      # fallback to 'default' ugly...
+      assert_equal('.', @it.value('unknown', 'HOME'))
+    end
+  end
+
+  def test_value_ENV
+    OpenSSL::TestUtils.silent do
+      key = ENV.keys.first
+      assert_not_nil(key) # make sure we have at least one ENV var.
+      assert_equal(ENV[key], @it.value('ENV', key))
+    end
+  end
+
+  def test_aref
+    assert_equal({'HOME' => '.'}, @it['default'])
+    assert_equal({'dir' => './demoCA', 'certs' => './certs'}, @it['CA_default'])
+    assert_equal({}, @it['no_such_section'])
+    assert_equal({}, @it[''])
+  end
+
+  def test_section
+    OpenSSL::TestUtils.silent do
+      assert_equal({'HOME' => '.'}, @it.section('default'))
+      assert_equal({'dir' => './demoCA', 'certs' => './certs'}, @it.section('CA_default'))
+      assert_equal({}, @it.section('no_such_section'))
+      assert_equal({}, @it.section(''))
+    end
+  end
+
+  def test_sections
+    assert_equal(['CA_default', 'ca', 'default'], @it.sections.sort)
+    @it['new_section'] = {'foo' => 'bar'}
+    assert_equal(['CA_default', 'ca', 'default', 'new_section'], @it.sections.sort)
+    @it['new_section'] = {}
+    assert_equal(['CA_default', 'ca', 'default', 'new_section'], @it.sections.sort)
+  end
+
+  def test_add_value
+    c = OpenSSL::Config.new
+    assert_equal("", c.to_s)
+    # add key
+    c.add_value('default', 'foo', 'bar')
+    assert_equal("[ default ]\nfoo=bar\n\n", c.to_s)
+    # add another key
+    c.add_value('default', 'baz', 'qux')
+    assert_equal('bar', c['default']['foo'])
+    assert_equal('qux', c['default']['baz'])
+    # update the value
+    c.add_value('default', 'baz', 'quxxx')
+    assert_equal('bar', c['default']['foo'])
+    assert_equal('quxxx', c['default']['baz'])
+    # add section and key
+    c.add_value('section', 'foo', 'bar')
+    assert_equal('bar', c['default']['foo'])
+    assert_equal('quxxx', c['default']['baz'])
+    assert_equal('bar', c['section']['foo'])
+  end
+
+  def test_aset
+    @it['foo'] = {'bar' => 'baz'}
+    assert_equal({'bar' => 'baz'}, @it['foo'])
+    @it['foo'] = {'bar' => 'qux', 'baz' => 'quxx'}
+    assert_equal({'bar' => 'qux', 'baz' => 'quxx'}, @it['foo'])
+
+    # OpenSSL::Config is add only for now.
+    @it['foo'] = {'foo' => 'foo'}
+    assert_equal({'foo' => 'foo', 'bar' => 'qux', 'baz' => 'quxx'}, @it['foo'])
+    # you cannot override or remove any section and key.
+    @it['foo'] = {}
+    assert_equal({'foo' => 'foo', 'bar' => 'qux', 'baz' => 'quxx'}, @it['foo'])
+  end
+
+  def test_each
+    # each returns [section, key, value] array.
+    ary = @it.map { |e| e }.sort { |a, b| a[0] <=> b[0] }
+    assert_equal(4, ary.size)
+    assert_equal('CA_default', ary[0][0])
+    assert_equal('CA_default', ary[1][0])
+    assert_equal(["ca", "default_ca", "CA_default"], ary[2])
+    assert_equal(["default", "HOME", "."], ary[3])
+  end
+
+  def test_to_s
+    c = OpenSSL::Config.parse("[empty]\n")
+    assert_equal("[ default ]\n\n[ empty ]\n\n", c.to_s)
+  end
+
+  def test_inspect
+    assert_match(/#<OpenSSL::Config sections=\[.*\]>/, @it.inspect)
+  end
+
+  def test_freeze
+    c = OpenSSL::Config.new
+    c['foo'] = [['key', 'value']]
+    c.freeze
+
+    bug = '[ruby-core:18377]'
+    # RuntimeError for 1.9, TypeError for 1.8
+    e = assert_raise(TypeError, bug) do
+      c['foo'] = [['key', 'wrong']]
+    end
+    assert_match(/can't modify/, e.message, bug)
+  end
+
+  def test_dup
+    assert(!@it.sections.empty?)
+    c = @it.dup
+    assert_equal(@it.sections.sort, c.sections.sort)
+    @it['newsection'] = {'a' => 'b'}
+    assert_not_equal(@it.sections.sort, c.sections.sort)
+  end
+
+  def test_clone
+    assert(!@it.sections.empty?)
+    c = @it.clone
+    assert_equal(@it.sections.sort, c.sections.sort)
+    @it['newsection'] = {'a' => 'b'}
+    assert_not_equal(@it.sections.sort, c.sections.sort)
+  end
+end if defined?(OpenSSL::TestUtils)
diff --git a/jni/ruby/test/openssl/test_digest.rb b/jni/ruby/test/openssl/test_digest.rb
new file mode 100644
index 0000000..a23b2ef
--- /dev/null
+++ b/jni/ruby/test/openssl/test_digest.rb
@@ -0,0 +1,126 @@
+require_relative 'utils'
+
+if defined?(OpenSSL::TestUtils)
+
+class OpenSSL::TestDigest < Test::Unit::TestCase
+  def setup
+    @d1 = OpenSSL::Digest.new("MD5")
+    @d2 = OpenSSL::Digest::MD5.new
+    @md = Digest::MD5.new
+    @data = "DATA"
+  end
+
+  def teardown
+    @d1 = @d2 = @md = nil
+  end
+
+  def test_digest
+    assert_equal(@md.digest, @d1.digest)
+    assert_equal(@md.hexdigest, @d1.hexdigest)
+    @d1 << @data
+    @d2 << @data
+    @md << @data
+    assert_equal(@md.digest, @d1.digest)
+    assert_equal(@md.hexdigest, @d1.hexdigest)
+    assert_equal(@d1.digest, @d2.digest)
+    assert_equal(@d1.hexdigest, @d2.hexdigest)
+    assert_equal(@md.digest, OpenSSL::Digest::MD5.digest(@data))
+    assert_equal(@md.hexdigest, OpenSSL::Digest::MD5.hexdigest(@data))
+  end
+
+  def test_eql
+    assert(@d1 == @d2, "==")
+    d = @d1.clone
+    assert(d == @d1, "clone")
+  end
+
+  def test_info
+    assert_equal("MD5", @d1.name, "name")
+    assert_equal("MD5", @d2.name, "name")
+    assert_equal(16, @d1.size, "size")
+  end
+
+  def test_dup
+    @d1.update(@data)
+    assert_equal(@d1.name, @d1.dup.name, "dup")
+    assert_equal(@d1.name, @d1.clone.name, "clone")
+    assert_equal(@d1.digest, @d1.clone.digest, "clone .digest")
+  end
+
+  def test_reset
+    @d1.update(@data)
+    dig1 = @d1.digest
+    @d1.reset
+    @d1.update(@data)
+    dig2 = @d1.digest
+    assert_equal(dig1, dig2, "reset")
+  end
+
+  def test_digest_constants
+    algs = %w(DSS1 MD4 MD5 RIPEMD160 SHA SHA1)
+    if OpenSSL::OPENSSL_VERSION_NUMBER > 0x00908000
+      algs += %w(SHA224 SHA256 SHA384 SHA512)
+    end
+    algs.each do |alg|
+      assert_not_nil(OpenSSL::Digest.new(alg))
+      klass = OpenSSL::Digest.const_get(alg)
+      assert_not_nil(klass.new)
+    end
+  end
+
+  def test_digest_by_oid_and_name
+    check_digest(OpenSSL::ASN1::ObjectId.new("MD5"))
+    check_digest(OpenSSL::ASN1::ObjectId.new("SHA1"))
+  end
+
+  if OpenSSL::OPENSSL_VERSION_NUMBER > 0x00908000
+    def encode16(str)
+      str.unpack("H*").first
+    end
+
+    def test_098_features
+      sha224_a = "abd37534c7d9a2efb9465de931cd7055ffdb8879563ae98078d6d6d5"
+      sha256_a = "ca978112ca1bbdcafac231b39a23dc4da786eff8147c4e72b9807785afee48bb"
+      sha384_a = "54a59b9f22b0b80880d8427e548b7c23abd873486e1f035dce9cd697e85175033caa88e6d57bc35efae0b5afd3145f31"
+      sha512_a = "1f40fc92da241694750979ee6cf582f2d5d7d28e18335de05abc54d0560e0f5302860c652bf08d560252aa5e74210546f369fbbbce8c12cfc7957b2652fe9a75"
+
+      assert_equal(sha224_a, OpenSSL::Digest::SHA224.hexdigest("a"))
+      assert_equal(sha256_a, OpenSSL::Digest::SHA256.hexdigest("a"))
+      assert_equal(sha384_a, OpenSSL::Digest::SHA384.hexdigest("a"))
+      assert_equal(sha512_a, OpenSSL::Digest::SHA512.hexdigest("a"))
+
+      assert_equal(sha224_a, encode16(OpenSSL::Digest::SHA224.digest("a")))
+      assert_equal(sha256_a, encode16(OpenSSL::Digest::SHA256.digest("a")))
+      assert_equal(sha384_a, encode16(OpenSSL::Digest::SHA384.digest("a")))
+      assert_equal(sha512_a, encode16(OpenSSL::Digest::SHA512.digest("a")))
+    end
+
+    def test_digest_by_oid_and_name_sha2
+      check_digest(OpenSSL::ASN1::ObjectId.new("SHA224"))
+      check_digest(OpenSSL::ASN1::ObjectId.new("SHA256"))
+      check_digest(OpenSSL::ASN1::ObjectId.new("SHA384"))
+      check_digest(OpenSSL::ASN1::ObjectId.new("SHA512"))
+    end
+  end
+
+  def test_openssl_digest
+    assert_equal OpenSSL::Digest::MD5, OpenSSL::Digest("MD5")
+
+    assert_raises NameError do
+      OpenSSL::Digest("no such digest")
+    end
+  end
+
+  private
+
+  def check_digest(oid)
+    d = OpenSSL::Digest.new(oid.sn)
+    assert_not_nil(d)
+    d = OpenSSL::Digest.new(oid.ln)
+    assert_not_nil(d)
+    d = OpenSSL::Digest.new(oid.oid)
+    assert_not_nil(d)
+  end
+end
+
+end
diff --git a/jni/ruby/test/openssl/test_engine.rb b/jni/ruby/test/openssl/test_engine.rb
new file mode 100644
index 0000000..a7264d0
--- /dev/null
+++ b/jni/ruby/test/openssl/test_engine.rb
@@ -0,0 +1,75 @@
+require_relative 'utils'
+
+class OpenSSL::TestEngine < Test::Unit::TestCase
+
+  def teardown
+    OpenSSL::Engine.cleanup # [ruby-core:40669]
+    assert_equal(0, OpenSSL::Engine.engines.size)
+  end
+
+  def test_engines_free # [ruby-dev:44173]
+    OpenSSL::Engine.load("openssl")
+    OpenSSL::Engine.engines
+    OpenSSL::Engine.engines
+  end
+
+  def test_openssl_engine_builtin
+    engine = OpenSSL::Engine.load("openssl")
+    assert_equal(true, engine)
+    assert_equal(1, OpenSSL::Engine.engines.size)
+  end
+
+  def test_openssl_engine_by_id_string
+    engine = get_engine
+    assert_not_nil(engine)
+    assert_equal(1, OpenSSL::Engine.engines.size)
+  end
+
+  def test_openssl_engine_id_name_inspect
+    engine = get_engine
+    assert_equal("openssl", engine.id)
+    assert_not_nil(engine.name)
+    assert_not_nil(engine.inspect)
+  end
+
+  def test_openssl_engine_digest_sha1
+    engine = get_engine
+    digest = engine.digest("SHA1")
+    assert_not_nil(digest)
+    data = "test"
+    assert_equal(OpenSSL::Digest::SHA1.digest(data), digest.digest(data))
+  end
+
+  def test_openssl_engine_cipher_rc4
+    engine = get_engine
+    algo = "RC4" #AES is not supported by openssl Engine (<=1.0.0e)
+    data = "a" * 1000
+    key = OpenSSL::Random.random_bytes(16)
+    # suppress message from openssl Engine's RC4 cipher [ruby-core:41026]
+    err_back = $stderr.dup
+    $stderr.reopen(IO::NULL)
+    encrypted = crypt_data(data, key, :encrypt) { engine.cipher(algo) }
+    decrypted = crypt_data(encrypted, key, :decrypt) { OpenSSL::Cipher.new(algo) }
+    assert_equal(data, decrypted)
+  ensure
+    if err_back
+      $stderr.reopen(err_back)
+      err_back.close
+    end
+  end
+
+  private
+
+  def get_engine
+    OpenSSL::Engine.by_id("openssl")
+  end
+
+  def crypt_data(data, key, mode)
+    cipher = yield
+    cipher.send mode
+    cipher.key = key
+    cipher.update(data) + cipher.final
+  end
+
+end if defined?(OpenSSL::TestUtils)
+
diff --git a/jni/ruby/test/openssl/test_fips.rb b/jni/ruby/test/openssl/test_fips.rb
new file mode 100644
index 0000000..6e4ac6d
--- /dev/null
+++ b/jni/ruby/test/openssl/test_fips.rb
@@ -0,0 +1,14 @@
+require_relative 'utils'
+
+if defined?(OpenSSL::TestUtils)
+
+class OpenSSL::TestFIPS < Test::Unit::TestCase
+
+  def test_fips_mode_is_reentrant
+    OpenSSL.fips_mode = false
+    OpenSSL.fips_mode = false
+  end
+
+end
+
+end
diff --git a/jni/ruby/test/openssl/test_hmac.rb b/jni/ruby/test/openssl/test_hmac.rb
new file mode 100644
index 0000000..f709ebd
--- /dev/null
+++ b/jni/ruby/test/openssl/test_hmac.rb
@@ -0,0 +1,41 @@
+# coding: UTF-8
+
+require_relative 'utils'
+
+class OpenSSL::TestHMAC < Test::Unit::TestCase
+  def setup
+    @digest = OpenSSL::Digest::MD5
+    @key = "KEY"
+    @data = "DATA"
+    @h1 = OpenSSL::HMAC.new(@key, @digest.new)
+    @h2 = OpenSSL::HMAC.new(@key, "MD5")
+  end
+
+  def teardown
+  end
+
+  def test_hmac
+    @h1.update(@data)
+    @h2.update(@data)
+    assert_equal(@h1.digest, @h2.digest)
+
+    assert_equal(OpenSSL::HMAC.digest(@digest.new, @key, @data), @h1.digest, "digest")
+    assert_equal(OpenSSL::HMAC.hexdigest(@digest.new, @key, @data), @h1.hexdigest, "hexdigest")
+
+    assert_equal(OpenSSL::HMAC.digest("MD5", @key, @data), @h2.digest, "digest")
+    assert_equal(OpenSSL::HMAC.hexdigest("MD5", @key, @data), @h2.hexdigest, "hexdigest")
+  end
+
+  def test_dup
+    @h1.update(@data)
+    h = @h1.dup
+    assert_equal(@h1.digest, h.digest, "dup digest")
+  end
+
+  def test_binary_update
+    data = "Lücíllé: Bût... yøü sáîd hé wås âlrîght.\nDr. Físhmån: Yés. Hé's løst hîs léft hånd, sø hé's gøîng tø bé åll rîght"
+    hmac = OpenSSL::HMAC.new("qShkcwN92rsM9nHfdnP4ugcVU2iI7iM/trovs01ZWok", "SHA256")
+    result = hmac.update(data).hexdigest
+    assert_equal "a13984b929a07912e4e21c5720876a8e150d6f67f854437206e7f86547248396", result
+  end
+end if defined?(OpenSSL::TestUtils)
diff --git a/jni/ruby/test/openssl/test_ns_spki.rb b/jni/ruby/test/openssl/test_ns_spki.rb
new file mode 100644
index 0000000..ab07bfb
--- /dev/null
+++ b/jni/ruby/test/openssl/test_ns_spki.rb
@@ -0,0 +1,51 @@
+require_relative 'utils'
+
+if defined?(OpenSSL::TestUtils)
+
+class OpenSSL::TestNSSPI < Test::Unit::TestCase
+  def setup
+    # This request data is adopt from the specification of
+    # "Netscape Extensions for User Key Generation".
+    # -- http://wp.netscape.com/eng/security/comm4-keygen.html
+    @b64  = "MIHFMHEwXDANBgkqhkiG9w0BAQEFAANLADBIAkEAnX0TILJrOMUue+PtwBRE6XfV"
+    @b64 << "WtKQbsshxk5ZhcUwcwyvcnIq9b82QhJdoACdD34rqfCAIND46fXKQUnb0mvKzQID"
+    @b64 << "AQABFhFNb3ppbGxhSXNNeUZyaWVuZDANBgkqhkiG9w0BAQQFAANBAAKv2Eex2n/S"
+    @b64 << "r/7iJNroWlSzSMtTiQTEB+ADWHGj9u1xrUrOilq/o2cuQxIfZcNZkYAkWP4DubqW"
+    @b64 << "i0//rgBvmco="
+  end
+
+  def test_build_data
+    key1 = OpenSSL::TestUtils::TEST_KEY_RSA1024
+    key2 = OpenSSL::TestUtils::TEST_KEY_RSA2048
+    spki = OpenSSL::Netscape::SPKI.new
+    spki.challenge = "RandomString"
+    spki.public_key = key1.public_key
+    spki.sign(key1, OpenSSL::Digest::SHA1.new)
+    assert(spki.verify(spki.public_key))
+    assert(spki.verify(key1.public_key))
+    assert(!spki.verify(key2.public_key))
+
+    der = spki.to_der
+    spki = OpenSSL::Netscape::SPKI.new(der)
+    assert_equal("RandomString", spki.challenge)
+    assert_equal(key1.public_key.to_der, spki.public_key.to_der)
+    assert(spki.verify(spki.public_key))
+    assert_not_nil(spki.to_text)
+  end
+
+  def test_decode_data
+    spki = OpenSSL::Netscape::SPKI.new(@b64)
+    assert_equal(@b64, spki.to_pem)
+    assert_equal(@b64.unpack("m").first, spki.to_der)
+    assert_equal("MozillaIsMyFriend", spki.challenge)
+    assert_equal(OpenSSL::PKey::RSA, spki.public_key.class)
+
+    spki = OpenSSL::Netscape::SPKI.new(@b64.unpack("m").first)
+    assert_equal(@b64, spki.to_pem)
+    assert_equal(@b64.unpack("m").first, spki.to_der)
+    assert_equal("MozillaIsMyFriend", spki.challenge)
+    assert_equal(OpenSSL::PKey::RSA, spki.public_key.class)
+  end
+end
+
+end
diff --git a/jni/ruby/test/openssl/test_ocsp.rb b/jni/ruby/test/openssl/test_ocsp.rb
new file mode 100644
index 0000000..af727d8
--- /dev/null
+++ b/jni/ruby/test/openssl/test_ocsp.rb
@@ -0,0 +1,47 @@
+require_relative "utils"
+
+if defined?(OpenSSL::TestUtils)
+
+class OpenSSL::TestOCSP < Test::Unit::TestCase
+  def setup
+    ca_subj = OpenSSL::X509::Name.parse("/DC=org/DC=ruby-lang/CN=TestCA")
+    ca_key = OpenSSL::TestUtils::TEST_KEY_RSA1024
+    ca_serial = 0xabcabcabcabc
+
+    subj = OpenSSL::X509::Name.parse("/DC=org/DC=ruby-lang/CN=TestCert")
+    @key = OpenSSL::TestUtils::TEST_KEY_RSA1024
+    serial = 0xabcabcabcabd
+
+    now = Time.at(Time.now.to_i) # suppress usec
+    dgst = OpenSSL::Digest::SHA1.new
+
+    @ca_cert = OpenSSL::TestUtils.issue_cert(
+       ca_subj, ca_key, ca_serial, now, now+3600, [], nil, nil, dgst)
+    @cert = OpenSSL::TestUtils.issue_cert(
+       subj, @key, serial, now, now+3600, [], @ca_cert, nil, dgst)
+  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
+  end
+
+  def test_new_certificate_id_with_digest
+    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 if defined?(OpenSSL::Digest::SHA256)
+
+  def test_new_ocsp_request
+    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, [@cert])
+    assert_kind_of OpenSSL::OCSP::Request, request
+    # in current implementation not same instance of certificate id, but should contain same data
+    assert_equal cid.serial, request.certid.first.serial
+  end
+end
+
+end
diff --git a/jni/ruby/test/openssl/test_pair.rb b/jni/ruby/test/openssl/test_pair.rb
new file mode 100644
index 0000000..3aca5f4
--- /dev/null
+++ b/jni/ruby/test/openssl/test_pair.rb
@@ -0,0 +1,372 @@
+require_relative 'utils'
+
+if defined?(OpenSSL::TestUtils)
+
+require 'socket'
+require_relative '../ruby/ut_eof'
+
+module OpenSSL::SSLPairM
+  def server
+    host = "127.0.0.1"
+    port = 0
+    ctx = OpenSSL::SSL::SSLContext.new()
+    ctx.ciphers = "ADH"
+    ctx.tmp_dh_callback = proc { OpenSSL::TestUtils::TEST_KEY_DH1024 }
+    tcps = create_tcp_server(host, port)
+    ssls = OpenSSL::SSL::SSLServer.new(tcps, ctx)
+    return ssls
+  end
+
+  def client(port)
+    host = "127.0.0.1"
+    ctx = OpenSSL::SSL::SSLContext.new()
+    ctx.ciphers = "ADH"
+    s = create_tcp_client(host, port)
+    ssl = OpenSSL::SSL::SSLSocket.new(s, ctx)
+    ssl.connect
+    ssl.sync_close = true
+    ssl
+  end
+
+  def ssl_pair
+    ssls = server
+    th = Thread.new {
+      ns = ssls.accept
+      ssls.close
+      ns
+    }
+    port = ssls.to_io.local_address.ip_port
+    c = client(port)
+    s = th.value
+    if block_given?
+      begin
+        yield c, s
+      ensure
+        c.close unless c.closed?
+        s.close unless s.closed?
+      end
+    else
+      return c, s
+    end
+  ensure
+    if th && th.alive?
+      th.kill
+      th.join
+    end
+  end
+end
+
+module OpenSSL::SSLPair
+  include OpenSSL::SSLPairM
+
+  def create_tcp_server(host, port)
+    TCPServer.new(host, port)
+  end
+
+  def create_tcp_client(host, port)
+    TCPSocket.new(host, port)
+  end
+end
+
+module OpenSSL::SSLPairLowlevelSocket
+  include OpenSSL::SSLPairM
+
+  def create_tcp_server(host, port)
+    Addrinfo.tcp(host, port).listen
+  end
+
+  def create_tcp_client(host, port)
+    Addrinfo.tcp(host, port).connect
+  end
+end
+
+module OpenSSL::TestEOF1M
+  def open_file(content)
+    s1, s2 = ssl_pair
+    th = Thread.new { s2 << content; s2.close }
+    yield s1
+  ensure
+    th.join
+    s1.close
+  end
+end
+
+module OpenSSL::TestEOF2M
+  def open_file(content)
+    s1, s2 = ssl_pair
+    th = Thread.new { s1 << content; s1.close }
+    yield s2
+  ensure
+    th.join
+    s2.close
+  end
+end
+
+module OpenSSL::TestPairM
+  def test_getc
+    ssl_pair {|s1, s2|
+      s1 << "a"
+      assert_equal(?a, s2.getc)
+    }
+  end
+
+  def test_readpartial
+    ssl_pair {|s1, s2|
+      s2.write "a\nbcd"
+      assert_equal("a\n", s1.gets)
+      result = ""
+      result << s1.readpartial(10) until result.length == 3
+      assert_equal("bcd", result)
+      s2.write "efg"
+      result = ""
+      result << s1.readpartial(10) until result.length == 3
+      assert_equal("efg", result)
+      s2.close
+      assert_raise(EOFError) { s1.readpartial(10) }
+      assert_raise(EOFError) { s1.readpartial(10) }
+      assert_equal("", s1.readpartial(0))
+    }
+  end
+
+  def test_readall
+    ssl_pair {|s1, s2|
+      s2.close
+      assert_equal("", s1.read)
+    }
+  end
+
+  def test_readline
+    ssl_pair {|s1, s2|
+      s2.close
+      assert_raise(EOFError) { s1.readline }
+    }
+  end
+
+  def test_puts_meta
+    ssl_pair {|s1, s2|
+      begin
+        old = $/
+        $/ = '*'
+        s1.puts 'a'
+      ensure
+        $/ = old
+      end
+      s1.close
+      assert_equal("a\n", s2.read)
+    }
+  end
+
+  def test_puts_empty
+    ssl_pair {|s1, s2|
+      s1.puts
+      s1.close
+      assert_equal("\n", s2.read)
+    }
+  end
+
+  def test_read_nonblock
+    ssl_pair {|s1, s2|
+      err = nil
+      assert_raise(OpenSSL::SSL::SSLErrorWaitReadable) {
+        begin
+          s2.read_nonblock(10)
+        ensure
+          err = $!
+        end
+      }
+      assert_kind_of(IO::WaitReadable, err)
+      s1.write "abc\ndef\n"
+      IO.select([s2])
+      assert_equal("ab", s2.read_nonblock(2))
+      assert_equal("c\n", s2.gets)
+      ret = nil
+      assert_nothing_raised("[ruby-core:20298]") { ret = s2.read_nonblock(10) }
+      assert_equal("def\n", ret)
+      s1.close
+      sleep 0.1
+      assert_raise(EOFError) { s2.read_nonblock(10) }
+    }
+  end
+
+  def test_read_nonblock_no_exception
+    ssl_pair {|s1, s2|
+      assert_equal :wait_readable, s2.read_nonblock(10, exception: false)
+      s1.write "abc\ndef\n"
+      IO.select([s2])
+      assert_equal("ab", s2.read_nonblock(2, exception: false))
+      assert_equal("c\n", s2.gets)
+      ret = nil
+      assert_nothing_raised("[ruby-core:20298]") { ret = s2.read_nonblock(10, exception: false) }
+      assert_equal("def\n", ret)
+      s1.close
+      sleep 0.1
+      assert_equal(nil, s2.read_nonblock(10, exception: false))
+    }
+  end
+
+  def write_nonblock(socket, meth, str)
+    ret = socket.send(meth, str)
+    ret.is_a?(Symbol) ? 0 : ret
+  end
+
+  def write_nonblock_no_ex(socket, str)
+    ret = socket.write_nonblock str, exception: false
+    ret.is_a?(Symbol) ? 0 : ret
+  end
+
+  def test_write_nonblock
+    ssl_pair {|s1, s2|
+      n = 0
+      begin
+        n += write_nonblock s1, :write_nonblock, "a" * 100000
+        n += write_nonblock s1, :write_nonblock, "b" * 100000
+        n += write_nonblock s1, :write_nonblock, "c" * 100000
+        n += write_nonblock s1, :write_nonblock, "d" * 100000
+        n += write_nonblock s1, :write_nonblock, "e" * 100000
+        n += write_nonblock s1, :write_nonblock, "f" * 100000
+      rescue IO::WaitWritable
+      end
+      s1.close
+      assert_equal(n, s2.read.length)
+    }
+  end
+
+  def test_write_nonblock_no_exceptions
+    ssl_pair {|s1, s2|
+      n = 0
+      begin
+        n += write_nonblock_no_ex s1, "a" * 100000
+        n += write_nonblock_no_ex s1, "b" * 100000
+        n += write_nonblock_no_ex s1, "c" * 100000
+        n += write_nonblock_no_ex s1, "d" * 100000
+        n += write_nonblock_no_ex s1, "e" * 100000
+        n += write_nonblock_no_ex s1, "f" * 100000
+      rescue OpenSSL::SSL::SSLError => e
+        # on some platforms (maybe depend on OpenSSL version), writing to
+        # SSLSocket after SSL_ERROR_WANT_WRITE causes this error.
+        raise e if n == 0
+      end
+      s1.close
+      assert_equal(n, s2.read.length)
+    }
+  end
+
+  def test_write_nonblock_with_buffered_data
+    ssl_pair {|s1, s2|
+      s1.write "foo"
+      s1.write_nonblock("bar")
+      s1.write "baz"
+      s1.close
+      assert_equal("foobarbaz", s2.read)
+    }
+  end
+
+  def test_write_nonblock_with_buffered_data_no_exceptions
+    ssl_pair {|s1, s2|
+      s1.write "foo"
+      s1.write_nonblock("bar", exception: false)
+      s1.write "baz"
+      s1.close
+      assert_equal("foobarbaz", s2.read)
+    }
+  end
+
+  def tcp_pair
+    host = "127.0.0.1"
+    serv = TCPServer.new(host, 0)
+    port = serv.connect_address.ip_port
+    sock1 = TCPSocket.new(host, port)
+    sock2 = serv.accept
+    serv.close
+    [sock1, sock2]
+  ensure
+    serv.close if serv && !serv.closed?
+  end
+
+  def test_connect_accept_nonblock
+    ctx = OpenSSL::SSL::SSLContext.new()
+    ctx.ciphers = "ADH"
+    ctx.tmp_dh_callback = proc { OpenSSL::TestUtils::TEST_KEY_DH1024 }
+
+    sock1, sock2 = tcp_pair
+
+    th = Thread.new {
+      s2 = OpenSSL::SSL::SSLSocket.new(sock2, ctx)
+      s2.sync_close = true
+      begin
+        sleep 0.2
+        s2.accept_nonblock
+      rescue IO::WaitReadable
+        IO.select([s2])
+        retry
+      rescue IO::WaitWritable
+        IO.select(nil, [s2])
+        retry
+      end
+      s2
+    }
+
+    sleep 0.1
+    ctx = OpenSSL::SSL::SSLContext.new()
+    ctx.ciphers = "ADH"
+    s1 = OpenSSL::SSL::SSLSocket.new(sock1, ctx)
+    begin
+      sleep 0.2
+      s1.connect_nonblock
+    rescue IO::WaitReadable
+      IO.select([s1])
+      retry
+    rescue IO::WaitWritable
+      IO.select(nil, [s1])
+      retry
+    end
+    s1.sync_close = true
+
+    s2 = th.value
+
+    s1.print "a\ndef"
+    assert_equal("a\n", s2.gets)
+  ensure
+    th.join
+    s1.close if s1 && !s1.closed?
+    s2.close if s2 && !s2.closed?
+    sock1.close if sock1 && !sock1.closed?
+    sock2.close if sock2 && !sock2.closed?
+  end
+end
+
+class OpenSSL::TestEOF1 < Test::Unit::TestCase
+  include TestEOF
+  include OpenSSL::SSLPair
+  include OpenSSL::TestEOF1M
+end
+
+class OpenSSL::TestEOF1LowlevelSocket < Test::Unit::TestCase
+  include TestEOF
+  include OpenSSL::SSLPairLowlevelSocket
+  include OpenSSL::TestEOF1M
+end
+
+class OpenSSL::TestEOF2 < Test::Unit::TestCase
+  include TestEOF
+  include OpenSSL::SSLPair
+  include OpenSSL::TestEOF2M
+end
+
+class OpenSSL::TestEOF2LowlevelSocket < Test::Unit::TestCase
+  include TestEOF
+  include OpenSSL::SSLPairLowlevelSocket
+  include OpenSSL::TestEOF2M
+end
+
+class OpenSSL::TestPair < Test::Unit::TestCase
+  include OpenSSL::SSLPair
+  include OpenSSL::TestPairM
+end
+
+class OpenSSL::TestPairLowlevelSocket < Test::Unit::TestCase
+  include OpenSSL::SSLPairLowlevelSocket
+  include OpenSSL::TestPairM
+end
+
+end
diff --git a/jni/ruby/test/openssl/test_partial_record_read.rb b/jni/ruby/test/openssl/test_partial_record_read.rb
new file mode 100644
index 0000000..1899a30
--- /dev/null
+++ b/jni/ruby/test/openssl/test_partial_record_read.rb
@@ -0,0 +1,34 @@
+require_relative "utils"
+
+if defined?(OpenSSL::TestUtils)
+
+  class OpenSSL::TestPartialRecordRead < OpenSSL::SSLTestCase
+    def test_partial_tls_record_read_nonblock
+      start_server(OpenSSL::SSL::VERIFY_NONE, true, :server_proc =>
+          Proc.new do |server_ctx, server_ssl|
+            begin
+              server_ssl.io.write("\x01") # the beginning of a TLS record
+              sleep 6                     # do not finish prematurely before the read by the client is attempted
+            ensure
+              server_ssl.close
+            end
+          end
+      ) do |server, port|
+        sock = TCPSocket.new("127.0.0.1", port)
+        ssl = OpenSSL::SSL::SSLSocket.new(sock)
+        ssl.sync_close = true
+        begin
+          ssl.connect
+          sleep 3  # wait is required for the (incomplete) TLS record to arrive at the client socket
+
+          # Should raise a IO::WaitReadable since a full TLS record is not available for reading.
+          assert_raise(IO::WaitReadable) { ssl.read_nonblock(1) }
+        ensure
+          ssl.close
+        end
+      end
+    end
+
+  end
+
+end
diff --git a/jni/ruby/test/openssl/test_pkcs12.rb b/jni/ruby/test/openssl/test_pkcs12.rb
new file mode 100644
index 0000000..4e37904
--- /dev/null
+++ b/jni/ruby/test/openssl/test_pkcs12.rb
@@ -0,0 +1,209 @@
+require_relative "utils"
+
+if defined?(OpenSSL::TestUtils)
+
+module OpenSSL
+  class TestPKCS12 < Test::Unit::TestCase
+    include OpenSSL::TestUtils
+
+    def setup
+      ca = OpenSSL::X509::Name.parse("/DC=org/DC=ruby-lang/CN=CA")
+
+      now = Time.now
+      ca_exts = [
+        ["basicConstraints","CA:TRUE",true],
+        ["keyUsage","keyCertSign, cRLSign",true],
+        ["subjectKeyIdentifier","hash",false],
+        ["authorityKeyIdentifier","keyid:always",false],
+      ]
+
+      @cacert = issue_cert(ca, TEST_KEY_RSA2048, 1, now, now+3600, ca_exts,
+                            nil, nil, OpenSSL::Digest::SHA1.new)
+
+      inter_ca = OpenSSL::X509::Name.parse("/DC=org/DC=ruby-lang/CN=Intermediate CA")
+      inter_ca_key = OpenSSL::PKey.read <<-_EOS_
+-----BEGIN RSA PRIVATE KEY-----
+MIICXAIBAAKBgQDp7hIG0SFMG/VWv1dBUWziAPrNmkMXJgTCAoB7jffzRtyyN04K
+oq/89HAszTMStZoMigQURfokzKsjpUp8OYCAEsBtt9d5zPndWMz/gHN73GrXk3LT
+ZsxEn7Xv5Da+Y9F/Hx2QZUHarV5cdZixq2NbzWGwrToogOQMh2pxN3Z/0wIDAQAB
+AoGBAJysUyx3olpsGzv3OMRJeahASbmsSKTXVLZvoIefxOINosBFpCIhZccAG6UV
+5c/xCvS89xBw8aD15uUfziw3AuT8QPEtHCgfSjeT7aWzBfYswEgOW4XPuWr7EeI9
+iNHGD6z+hCN/IQr7FiEBgTp6A+i/hffcSdR83fHWKyb4M7TRAkEA+y4BNd668HmC
+G5MPRx25n6LixuBxrNp1umfjEI6UZgEFVpYOg4agNuimN6NqM253kcTR94QNTUs5
+Kj3EhG1YWwJBAO5rUjiOyCNVX2WUQrOMYK/c1lU7fvrkdygXkvIGkhsPoNRzLPeA
+HGJszKtrKD8bNihWpWNIyqKRHfKVD7yXT+kCQGCAhVCIGTRoypcDghwljHqLnysf
+ci0h5ZdPcIqc7ODfxYhFsJ/Rql5ONgYsT5Ig/+lOQAkjf+TRYM4c2xKx2/8CQBvG
+jv6dy70qDgIUgqzONtlmHeYyFzn9cdBO5sShdVYHvRHjFSMEXsosqK9zvW2UqvuK
+FJx7d3f29gkzynCLJDkCQGQZlEZJC4vWmWJGRKJ24P6MyQn3VsPfErSKOg4lvyM3
+Li8JsX5yIiuVYaBg/6ha3tOg4TCa5K/3r3tVliRZ2Es=
+-----END RSA PRIVATE KEY-----
+      _EOS_
+
+      @inter_cacert = issue_cert(inter_ca, inter_ca_key, 2, now, now+3600, ca_exts,
+                                 @cacert, TEST_KEY_RSA2048, OpenSSL::Digest::SHA1.new)
+
+      exts = [
+        ["keyUsage","digitalSignature",true],
+        ["subjectKeyIdentifier","hash",false],
+      ]
+      ee = OpenSSL::X509::Name.parse("/DC=org/DC=ruby-lang/CN=Ruby PKCS12 Test Certificate")
+      @mycert = issue_cert(ee, TEST_KEY_RSA1024, 3, now, now+3600, exts,
+                           @inter_cacert, inter_ca_key, OpenSSL::Digest::SHA1.new)
+    end
+
+    def test_create
+      pkcs12 = OpenSSL::PKCS12.create(
+        "omg",
+        "hello",
+        TEST_KEY_RSA1024,
+        @mycert
+      )
+      assert_equal @mycert, pkcs12.certificate
+      assert_equal TEST_KEY_RSA1024, pkcs12.key
+      assert_nil pkcs12.ca_certs
+    end
+
+    def test_create_no_pass
+      pkcs12 = OpenSSL::PKCS12.create(
+        nil,
+        "hello",
+        TEST_KEY_RSA1024,
+        @mycert
+      )
+      assert_equal @mycert, pkcs12.certificate
+      assert_equal TEST_KEY_RSA1024, pkcs12.key
+      assert_nil pkcs12.ca_certs
+
+      decoded = OpenSSL::PKCS12.new(pkcs12.to_der)
+      assert_cert @mycert, decoded.certificate
+    end
+
+    def test_create_with_chain
+      chain = [@inter_cacert, @cacert]
+
+      pkcs12 = OpenSSL::PKCS12.create(
+        "omg",
+        "hello",
+        TEST_KEY_RSA1024,
+        @mycert,
+        chain
+      )
+      assert_equal chain, pkcs12.ca_certs
+    end
+
+    def test_create_with_chain_decode
+      chain = [@cacert, @inter_cacert]
+
+      passwd = "omg"
+
+      pkcs12 = OpenSSL::PKCS12.create(
+        passwd,
+        "hello",
+        TEST_KEY_RSA1024,
+        @mycert,
+        chain
+      )
+
+      decoded = OpenSSL::PKCS12.new(pkcs12.to_der, passwd)
+      assert_equal chain.size, decoded.ca_certs.size
+      assert_include_cert @cacert, decoded.ca_certs
+      assert_include_cert @inter_cacert, decoded.ca_certs
+      assert_cert @mycert, decoded.certificate
+      assert_equal TEST_KEY_RSA1024.to_der, decoded.key.to_der
+    end
+
+    def test_create_with_bad_nid
+      assert_raises(ArgumentError) do
+        OpenSSL::PKCS12.create(
+          "omg",
+          "hello",
+          TEST_KEY_RSA1024,
+          @mycert,
+          [],
+          "foo"
+        )
+      end
+    end
+
+    def test_create_with_itr
+      OpenSSL::PKCS12.create(
+        "omg",
+        "hello",
+        TEST_KEY_RSA1024,
+        @mycert,
+        [],
+        nil,
+        nil,
+        2048
+      )
+
+      assert_raises(TypeError) do
+        OpenSSL::PKCS12.create(
+          "omg",
+          "hello",
+          TEST_KEY_RSA1024,
+          @mycert,
+          [],
+          nil,
+          nil,
+          "omg"
+        )
+      end
+    end
+
+    def test_create_with_mac_itr
+      OpenSSL::PKCS12.create(
+        "omg",
+        "hello",
+        TEST_KEY_RSA1024,
+        @mycert,
+        [],
+        nil,
+        nil,
+        nil,
+        2048
+      )
+
+      assert_raises(TypeError) do
+        OpenSSL::PKCS12.create(
+          "omg",
+          "hello",
+          TEST_KEY_RSA1024,
+          @mycert,
+          [],
+          nil,
+          nil,
+          nil,
+          "omg"
+        )
+      end
+    end
+
+    private
+    def assert_cert expected, actual
+      [
+        :subject,
+        :issuer,
+        :serial,
+        :not_before,
+        :not_after,
+      ].each do |attribute|
+        assert_equal expected.send(attribute), actual.send(attribute)
+      end
+      assert_equal expected.to_der, actual.to_der
+    end
+
+    def assert_include_cert cert, ary
+      der = cert.to_der
+      ary.each do |candidate|
+        if candidate.to_der == der
+          return true
+        end
+      end
+      false
+    end
+
+  end
+end
+
+end
diff --git a/jni/ruby/test/openssl/test_pkcs5.rb b/jni/ruby/test/openssl/test_pkcs5.rb
new file mode 100644
index 0000000..5e85dde
--- /dev/null
+++ b/jni/ruby/test/openssl/test_pkcs5.rb
@@ -0,0 +1,97 @@
+require_relative 'utils'
+
+class OpenSSL::TestPKCS5 < Test::Unit::TestCase
+
+  def test_pbkdf2_hmac_sha1_rfc6070_c_1_len_20
+    p ="password"
+    s = "salt"
+    c = 1
+    dk_len = 20
+    raw = %w{ 0c 60 c8 0f 96 1f 0e 71
+              f3 a9 b5 24 af 60 12 06
+              2f e0 37 a6 }
+    expected = [raw.join('')].pack('H*')
+    value = OpenSSL::PKCS5.pbkdf2_hmac_sha1(p, s, c, dk_len)
+    assert_equal(expected, value)
+  end
+
+  def test_pbkdf2_hmac_sha1_rfc6070_c_2_len_20
+    p ="password"
+    s = "salt"
+    c = 2
+    dk_len = 20
+    raw = %w{ ea 6c 01 4d c7 2d 6f 8c
+              cd 1e d9 2a ce 1d 41 f0
+              d8 de 89 57 }
+    expected = [raw.join('')].pack('H*')
+    value = OpenSSL::PKCS5.pbkdf2_hmac_sha1(p, s, c, dk_len)
+    assert_equal(expected, value)
+  end
+
+  def test_pbkdf2_hmac_sha1_rfc6070_c_4096_len_20
+    p ="password"
+    s = "salt"
+    c = 4096
+    dk_len = 20
+    raw = %w{ 4b 00 79 01 b7 65 48 9a
+              be ad 49 d9 26 f7 21 d0
+              65 a4 29 c1 }
+    expected = [raw.join('')].pack('H*')
+    value = OpenSSL::PKCS5.pbkdf2_hmac_sha1(p, s, c, dk_len)
+    assert_equal(expected, value)
+  end
+
+# takes too long!
+#  def test_pbkdf2_hmac_sha1_rfc6070_c_16777216_len_20
+#    p ="password"
+#    s = "salt"
+#    c = 16777216
+#    dk_len = 20
+#    raw = %w{ ee fe 3d 61 cd 4d a4 e4
+#              e9 94 5b 3d 6b a2 15 8c
+#              26 34 e9 84 }
+#    expected = [raw.join('')].pack('H*')
+#    value = OpenSSL::PKCS5.pbkdf2_hmac_sha1(p, s, c, dk_len)
+#    assert_equal(expected, value)
+#  end
+
+  def test_pbkdf2_hmac_sha1_rfc6070_c_4096_len_25
+    p ="passwordPASSWORDpassword"
+    s = "saltSALTsaltSALTsaltSALTsaltSALTsalt"
+    c = 4096
+    dk_len = 25
+
+    raw = %w{ 3d 2e ec 4f e4 1c 84 9b
+              80 c8 d8 36 62 c0 e4 4a
+              8b 29 1a 96 4c f2 f0 70
+              38 }
+    expected = [raw.join('')].pack('H*')
+    value = OpenSSL::PKCS5.pbkdf2_hmac_sha1(p, s, c, dk_len)
+    assert_equal(expected, value)
+  end
+
+  def test_pbkdf2_hmac_sha1_rfc6070_c_4096_len_16
+    p ="pass\0word"
+    s = "sa\0lt"
+    c = 4096
+    dk_len = 16
+    raw = %w{ 56 fa 6a a7 55 48 09 9d
+              cc 37 d7 f0 34 25 e0 c3 }
+    expected = [raw.join('')].pack('H*')
+    value = OpenSSL::PKCS5.pbkdf2_hmac_sha1(p, s, c, dk_len)
+    assert_equal(expected, value)
+  end
+
+  def test_pbkdf2_hmac_sha256_c_20000_len_32
+    #unfortunately no official test vectors available yet for SHA-2
+    p ="password"
+    s = OpenSSL::Random.random_bytes(16)
+    c = 20000
+    dk_len = 32
+    digest = OpenSSL::Digest::SHA256.new
+    value1 = OpenSSL::PKCS5.pbkdf2_hmac(p, s, c, dk_len, digest)
+    value2 = OpenSSL::PKCS5.pbkdf2_hmac(p, s, c, dk_len, digest)
+    assert_equal(value1, value2)
+  end if OpenSSL::PKCS5.respond_to?(:pbkdf2_hmac)
+
+end if defined?(OpenSSL::TestUtils)
diff --git a/jni/ruby/test/openssl/test_pkcs7.rb b/jni/ruby/test/openssl/test_pkcs7.rb
new file mode 100644
index 0000000..47bd4f3
--- /dev/null
+++ b/jni/ruby/test/openssl/test_pkcs7.rb
@@ -0,0 +1,297 @@
+require_relative 'utils'
+
+if defined?(OpenSSL::TestUtils)
+
+class OpenSSL::TestPKCS7 < Test::Unit::TestCase
+  def setup
+    @rsa1024 = OpenSSL::TestUtils::TEST_KEY_RSA1024
+    @rsa2048 = OpenSSL::TestUtils::TEST_KEY_RSA2048
+    ca = OpenSSL::X509::Name.parse("/DC=org/DC=ruby-lang/CN=CA")
+    ee1 = OpenSSL::X509::Name.parse("/DC=org/DC=ruby-lang/CN=EE1")
+    ee2 = OpenSSL::X509::Name.parse("/DC=org/DC=ruby-lang/CN=EE2")
+
+    now = Time.now
+    ca_exts = [
+      ["basicConstraints","CA:TRUE",true],
+      ["keyUsage","keyCertSign, cRLSign",true],
+      ["subjectKeyIdentifier","hash",false],
+      ["authorityKeyIdentifier","keyid:always",false],
+    ]
+    @ca_cert = issue_cert(ca, @rsa2048, 1, now, now+3600, ca_exts,
+                           nil, nil, OpenSSL::Digest::SHA1.new)
+    ee_exts = [
+      ["keyUsage","Non Repudiation, Digital Signature, Key Encipherment",true],
+      ["authorityKeyIdentifier","keyid:always",false],
+      ["extendedKeyUsage","clientAuth, emailProtection, codeSigning",false],
+    ]
+    @ee1_cert = issue_cert(ee1, @rsa1024, 2, now, now+1800, ee_exts,
+                           @ca_cert, @rsa2048, OpenSSL::Digest::SHA1.new)
+    @ee2_cert = issue_cert(ee2, @rsa1024, 3, now, now+1800, ee_exts,
+                           @ca_cert, @rsa2048, OpenSSL::Digest::SHA1.new)
+  end
+
+  def issue_cert(*args)
+    OpenSSL::TestUtils.issue_cert(*args)
+  end
+
+  def test_signed
+    store = OpenSSL::X509::Store.new
+    store.add_cert(@ca_cert)
+    ca_certs = [@ca_cert]
+
+    data = "aaaaa\r\nbbbbb\r\nccccc\r\n"
+    tmp = OpenSSL::PKCS7.sign(@ee1_cert, @rsa1024, data, ca_certs)
+    p7 = OpenSSL::PKCS7.new(tmp.to_der)
+    certs = p7.certificates
+    signers = p7.signers
+    assert(p7.verify([], store))
+    assert_equal(data, p7.data)
+    assert_equal(2, certs.size)
+    assert_equal(@ee1_cert.subject.to_s, certs[0].subject.to_s)
+    assert_equal(@ca_cert.subject.to_s, certs[1].subject.to_s)
+    assert_equal(1, signers.size)
+    assert_equal(@ee1_cert.serial, signers[0].serial)
+    assert_equal(@ee1_cert.issuer.to_s, signers[0].issuer.to_s)
+
+    # Normaly OpenSSL tries to translate the supplied content into canonical
+    # MIME format (e.g. a newline character is converted into CR+LF).
+    # If the content is a binary, PKCS7::BINARY flag should be used.
+
+    data = "aaaaa\nbbbbb\nccccc\n"
+    flag = OpenSSL::PKCS7::BINARY
+    tmp = OpenSSL::PKCS7.sign(@ee1_cert, @rsa1024, data, ca_certs, flag)
+    p7 = OpenSSL::PKCS7.new(tmp.to_der)
+    certs = p7.certificates
+    signers = p7.signers
+    assert(p7.verify([], store))
+    assert_equal(data, p7.data)
+    assert_equal(2, certs.size)
+    assert_equal(@ee1_cert.subject.to_s, certs[0].subject.to_s)
+    assert_equal(@ca_cert.subject.to_s, certs[1].subject.to_s)
+    assert_equal(1, signers.size)
+    assert_equal(@ee1_cert.serial, signers[0].serial)
+    assert_equal(@ee1_cert.issuer.to_s, signers[0].issuer.to_s)
+
+    # A signed-data which have multiple signatures can be created
+    # through the following steps.
+    #   1. create two signed-data
+    #   2. copy signerInfo and certificate from one to another
+
+    tmp1 = OpenSSL::PKCS7.sign(@ee1_cert, @rsa1024, data, [], flag)
+    tmp2 = OpenSSL::PKCS7.sign(@ee2_cert, @rsa1024, data, [], flag)
+    tmp1.add_signer(tmp2.signers[0])
+    tmp1.add_certificate(@ee2_cert)
+
+    p7 = OpenSSL::PKCS7.new(tmp1.to_der)
+    certs = p7.certificates
+    signers = p7.signers
+    assert(p7.verify([], store))
+    assert_equal(data, p7.data)
+    assert_equal(2, certs.size)
+    assert_equal(2, signers.size)
+    assert_equal(@ee1_cert.serial, signers[0].serial)
+    assert_equal(@ee1_cert.issuer.to_s, signers[0].issuer.to_s)
+    assert_equal(@ee2_cert.serial, signers[1].serial)
+    assert_equal(@ee2_cert.issuer.to_s, signers[1].issuer.to_s)
+  end
+
+  def test_detached_sign
+    store = OpenSSL::X509::Store.new
+    store.add_cert(@ca_cert)
+    ca_certs = [@ca_cert]
+
+    data = "aaaaa\nbbbbb\nccccc\n"
+    flag = OpenSSL::PKCS7::BINARY|OpenSSL::PKCS7::DETACHED
+    tmp = OpenSSL::PKCS7.sign(@ee1_cert, @rsa1024, data, ca_certs, flag)
+    p7 = OpenSSL::PKCS7.new(tmp.to_der)
+    assert_nothing_raised do
+      OpenSSL::ASN1.decode(p7)
+    end
+
+    certs = p7.certificates
+    signers = p7.signers
+    assert(!p7.verify([], store))
+    assert(p7.verify([], store, data))
+    assert_equal(data, p7.data)
+    assert_equal(2, certs.size)
+    assert_equal(@ee1_cert.subject.to_s, certs[0].subject.to_s)
+    assert_equal(@ca_cert.subject.to_s, certs[1].subject.to_s)
+    assert_equal(1, signers.size)
+    assert_equal(@ee1_cert.serial, signers[0].serial)
+    assert_equal(@ee1_cert.issuer.to_s, signers[0].issuer.to_s)
+  end
+
+  def test_enveloped
+    if OpenSSL::OPENSSL_VERSION_NUMBER <= 0x0090704f
+      # PKCS7_encrypt() of OpenSSL-0.9.7d goes to SEGV.
+      # http://www.mail-archive.com/openssl-dev@openssl.org/msg17376.html
+      return
+    end
+
+    certs = [@ee1_cert, @ee2_cert]
+    cipher = OpenSSL::Cipher::AES.new("128-CBC")
+    data = "aaaaa\nbbbbb\nccccc\n"
+
+    tmp = OpenSSL::PKCS7.encrypt(certs, data, cipher, OpenSSL::PKCS7::BINARY)
+    p7 = OpenSSL::PKCS7.new(tmp.to_der)
+    recip = p7.recipients
+    assert_equal(:enveloped, p7.type)
+    assert_equal(2, recip.size)
+
+    assert_equal(@ca_cert.subject.to_s, recip[0].issuer.to_s)
+    assert_equal(2, recip[0].serial)
+    assert_equal(data, p7.decrypt(@rsa1024, @ee1_cert))
+
+    assert_equal(@ca_cert.subject.to_s, recip[1].issuer.to_s)
+    assert_equal(3, recip[1].serial)
+    assert_equal(data, p7.decrypt(@rsa1024, @ee2_cert))
+  end
+
+  def test_graceful_parsing_failure #[ruby-core:43250]
+    contents = File.read(__FILE__)
+    assert_raise(ArgumentError) { OpenSSL::PKCS7.new(contents) }
+  end
+
+  def test_set_type_signed
+    p7 = OpenSSL::PKCS7.new
+    p7.type = "signed"
+    assert_equal(:signed, p7.type)
+  end
+
+  def test_set_type_data
+    p7 = OpenSSL::PKCS7.new
+    p7.type = "data"
+    assert_equal(:data, p7.type)
+  end
+
+  def test_set_type_signed_and_enveloped
+    p7 = OpenSSL::PKCS7.new
+    p7.type = "signedAndEnveloped"
+    assert_equal(:signedAndEnveloped, p7.type)
+  end
+
+  def test_set_type_enveloped
+    p7 = OpenSSL::PKCS7.new
+    p7.type = "enveloped"
+    assert_equal(:enveloped, p7.type)
+  end
+
+  def test_set_type_encrypted
+    p7 = OpenSSL::PKCS7.new
+    p7.type = "encrypted"
+    assert_equal(:encrypted, p7.type)
+  end
+
+  def test_degenerate_pkcs7
+    ca_cert_pem = <<END
+-----BEGIN CERTIFICATE-----
+MIID4DCCAsigAwIBAgIJAL1oVI72wmQwMA0GCSqGSIb3DQEBBQUAMFMxCzAJBgNV
+BAYTAkFVMQ4wDAYDVQQIEwVTdGF0ZTENMAsGA1UEBxMEQ2l0eTEQMA4GA1UEChMH
+RXhhbXBsZTETMBEGA1UEAxMKRXhhbXBsZSBDQTAeFw0xMjEwMTgwOTE2NTBaFw0y
+MjEwMTYwOTE2NTBaMFMxCzAJBgNVBAYTAkFVMQ4wDAYDVQQIEwVTdGF0ZTENMAsG
+A1UEBxMEQ2l0eTEQMA4GA1UEChMHRXhhbXBsZTETMBEGA1UEAxMKRXhhbXBsZSBD
+QTCCASIwDQYJKoZIhvcNAQEBBQADggEPADCCAQoCggEBAMTSPNxOkd5NN19XO0fJ
+tGVlWN4DWuvVL9WbWnXJXX9rU6X8sSOL9RrRA64eEZf2UBFjz9fMHZj/OGcxZpus
+4YtzfSrMU6xfvsIHeqX+mT60ms2RfX4UXab50MQArBin3JVKHGnOi25uyAOylVFU
+TuzzQJvKyB67vjuRPMlVAgVAZAP07ru9gW0ajt/ODxvUfvXxp5SFF68mVP2ipMBr
+4fujUwQC6cVHmnuL6p87VFoo9uk87TSQVDOQGL8MK4moMFtEW9oUTU22CgnxnCsS
+sCCELYhy9BdaTWQH26LzMfhnwSuIRHZyprW4WZtU0akrYXNiCj8o92rZmQWXJDbl
+qNECAwEAAaOBtjCBszAdBgNVHQ4EFgQUNtVw4jvkZZbkdQbkYi2/F4QN79owgYMG
+A1UdIwR8MHqAFDbVcOI75GWW5HUG5GItvxeEDe/aoVekVTBTMQswCQYDVQQGEwJB
+VTEOMAwGA1UECBMFU3RhdGUxDTALBgNVBAcTBENpdHkxEDAOBgNVBAoTB0V4YW1w
+bGUxEzARBgNVBAMTCkV4YW1wbGUgQ0GCCQC9aFSO9sJkMDAMBgNVHRMEBTADAQH/
+MA0GCSqGSIb3DQEBBQUAA4IBAQBvJIsY9bIqliZ3WD1KoN4cvAQeRAPsoLXQkkHg
+P6Nrcw9rJ5JvoHfYbo5aNlwbnkbt/B2xlVEXUYpJoBZFXafgxG2gJleioIgnaDS4
+FPPwZf1C5ZrOgUBfxTGjHex4ghSAoNGOd35jQzin5NGKOvZclPjZ2vQ++LP3aA2l
+9Fn2qASS46IzMGJlC75mlTOTQwDM16UunMAK26lNG9J6q02o4d/oU2a7x0fD80yF
+64kNA1wDAwaVCYiUH541qKp+b4iDqer8nf8HqzYDFlpje18xYZMEd1hj8dVOharM
+pISJ+D52hV/BGEYF8r5k3hpC5d76gSP2oCcaY0XvLBf97qik
+-----END CERTIFICATE-----
+END
+    p7 = OpenSSL::PKCS7.new
+    p7.type = "signed"
+    ca_cert = OpenSSL::X509::Certificate.new(ca_cert_pem)
+    p7.add_certificate ca_cert
+    p7.add_data ""
+
+    assert_nothing_raised do
+      p7.to_pem
+    end
+  end
+
+  def test_split_content
+     pki_message_pem = <<END
+-----BEGIN PKCS7-----
+MIIHSwYJKoZIhvcNAQcCoIIHPDCCBzgCAQExCzAJBgUrDgMCGgUAMIIDiAYJKoZI
+hvcNAQcBoIIDeQSCA3UwgAYJKoZIhvcNAQcDoIAwgAIBADGCARAwggEMAgEAMHUw
+cDEQMA4GA1UECgwHZXhhbXBsZTEXMBUGA1UEAwwOVEFSTUFDIFJPT1QgQ0ExIjAg
+BgkqhkiG9w0BCQEWE3NvbWVvbmVAZXhhbXBsZS5vcmcxCzAJBgNVBAYTAlVTMRIw
+EAYDVQQHDAlUb3duIEhhbGwCAWYwDQYJKoZIhvcNAQEBBQAEgYBspXXse8ZhG1FE
+E3PVAulbvrdR52FWPkpeLvSjgEkYzTiUi0CC3poUL1Ku5mOlavWAJgoJpFICDbvc
+N4ZNDCwOhnzoI9fMGmm1gvPQy15BdhhZRo9lP7Ga/Hg2APKT0/0yhPsmJ+w+u1e7
+OoJEVeEZ27x3+u745bGEcu8of5th6TCABgkqhkiG9w0BBwEwFAYIKoZIhvcNAwcE
+CBNs2U5mMsd/oIAEggIQU6cur8QBz02/4eMpHdlU9IkyrRMiaMZ/ky9zecOAjnvY
+d2jZqS7RhczpaNJaSli3GmDsKrF+XqE9J58s9ScGqUigzapusTsxIoRUPr7Ztb0a
+pg8VWDipAsuw7GfEkgx868sV93uC4v6Isfjbhd+JRTFp/wR1kTi7YgSXhES+RLUW
+gQbDIDgEQYxJ5U951AJtnSpjs9za2ZkTdd8RSEizJK0bQ1vqLoApwAVgZqluATqQ
+AHSDCxhweVYw6+y90B9xOrqPC0eU7Wzryq2+Raq5ND2Wlf5/N11RQ3EQdKq/l5Te
+ijp9PdWPlkUhWVoDlOFkysjk+BE+7AkzgYvz9UvBjmZsMsWqf+KsZ4S8/30ndLzu
+iucsu6eOnFLLX8DKZxV6nYffZOPzZZL8hFBcE7PPgSdBEkazMrEBXq1j5mN7exbJ
+NOA5uGWyJNBMOCe+1JbxG9UeoqvCCTHESxEeDu7xR3NnSOD47n7cXwHr81YzK2zQ
+5oWpP3C8jzI7tUjLd1S0Z3Psd17oaCn+JOfUtuB0nc3wfPF/WPo0xZQodWxp2/Cl
+EltR6qr1zf5C7GwmLzBZ6bHFAIT60/JzV0/56Pn8ztsRFtI4cwaBfTfvnwi8/sD9
+/LYOMY+/b6UDCUSR7RTN7XfrtAqDEzSdzdJkOWm1jvM8gkLmxpZdvxG3ZvDYnEQE
+5Nq+un5nAny1wf3rWierBAjE5ntiAmgs5AAAAAAAAAAAAACgggHqMIIB5jCCAU+g
+AwIBAgIBATANBgkqhkiG9w0BAQUFADAvMS0wKwYDVQQDEyQwQUM5RjAyNi1EQ0VB
+LTRDMTItOTEyNy1DMEZEN0QyQThCNUEwHhcNMTIxMDE5MDk0NTQ3WhcNMTMxMDE5
+MDk0NTQ3WjAvMS0wKwYDVQQDEyQwQUM5RjAyNi1EQ0VBLTRDMTItOTEyNy1DMEZE
+N0QyQThCNUEwgZ8wDQYJKoZIhvcNAQEBBQADgY0AMIGJAoGBALTsTNyGIsKvyw56
+WI3Gll/RmjsupkrdEtPbx7OjS9MEgyhOAf9+u6CV0LJGHpy7HUeROykF6xpbSdCm
+Mr6kNObl5N0ljOb8OmV4atKjmGg1rWawDLyDQ9Dtuby+dzfHtzAzP+J/3ZoOtSqq
+AHVTnCclU1pm/uHN0HZ5nL5iLJTvAgMBAAGjEjAQMA4GA1UdDwEB/wQEAwIFoDAN
+BgkqhkiG9w0BAQUFAAOBgQA8K+BouEV04HRTdMZd3akjTQOm6aEGW4nIRnYIf8ZV
+mvUpLirVlX/unKtJinhGisFGpuYLMpemx17cnGkBeLCQRvHQjC+ho7l8/LOGheMS
+nvu0XHhvmJtRbm8MKHhogwZqHFDnXonvjyqhnhEtK5F2Fimcce3MoF2QtEe0UWv/
+8DGCAaowggGmAgEBMDQwLzEtMCsGA1UEAxMkMEFDOUYwMjYtRENFQS00QzEyLTkx
+MjctQzBGRDdEMkE4QjVBAgEBMAkGBSsOAwIaBQCggc0wEgYKYIZIAYb4RQEJAjEE
+EwIxOTAYBgkqhkiG9w0BCQMxCwYJKoZIhvcNAQcBMBwGCSqGSIb3DQEJBTEPFw0x
+MjEwMTkwOTQ1NDdaMCAGCmCGSAGG+EUBCQUxEgQQ2EFUJdQNwQDxclIQ8qNyYzAj
+BgkqhkiG9w0BCQQxFgQUy8GFXPpAwRJUT3rdvNC9Pn+4eoswOAYKYIZIAYb4RQEJ
+BzEqEygwRkU3QzJEQTVEMDc2NzFFOTcxNDlCNUE3MDRCMERDNkM4MDYwRDJBMA0G
+CSqGSIb3DQEBAQUABIGAWUNdzvU2iiQOtihBwF0h48Nnw/2qX8uRjg6CVTOMcGji
+BxjUMifEbT//KJwljshl4y3yBLqeVYLOd04k6aKSdjgdZnrnUPI6p5tL5PfJkTAE
+L6qflZ9YCU5erE4T5U98hCQBMh4nOYxgaTjnZzhpkKQuEiKq/755cjzTzlI/eok=
+-----END PKCS7-----
+END
+    pki_message_content_pem = <<END
+-----BEGIN PKCS7-----
+MIIDawYJKoZIhvcNAQcDoIIDXDCCA1gCAQAxggEQMIIBDAIBADB1MHAxEDAOBgNV
+BAoMB2V4YW1wbGUxFzAVBgNVBAMMDlRBUk1BQyBST09UIENBMSIwIAYJKoZIhvcN
+AQkBFhNzb21lb25lQGV4YW1wbGUub3JnMQswCQYDVQQGEwJVUzESMBAGA1UEBwwJ
+VG93biBIYWxsAgFmMA0GCSqGSIb3DQEBAQUABIGAbKV17HvGYRtRRBNz1QLpW763
+UedhVj5KXi70o4BJGM04lItAgt6aFC9SruZjpWr1gCYKCaRSAg273DeGTQwsDoZ8
+6CPXzBpptYLz0MteQXYYWUaPZT+xmvx4NgDyk9P9MoT7JifsPrtXuzqCRFXhGdu8
+d/ru+OWxhHLvKH+bYekwggI9BgkqhkiG9w0BBwEwFAYIKoZIhvcNAwcECBNs2U5m
+Msd/gIICGFOnLq/EAc9Nv+HjKR3ZVPSJMq0TImjGf5Mvc3nDgI572Hdo2aku0YXM
+6WjSWkpYtxpg7Cqxfl6hPSefLPUnBqlIoM2qbrE7MSKEVD6+2bW9GqYPFVg4qQLL
+sOxnxJIMfOvLFfd7guL+iLH424XfiUUxaf8EdZE4u2IEl4REvkS1FoEGwyA4BEGM
+SeVPedQCbZ0qY7Pc2tmZE3XfEUhIsyStG0Nb6i6AKcAFYGapbgE6kAB0gwsYcHlW
+MOvsvdAfcTq6jwtHlO1s68qtvkWquTQ9lpX+fzddUUNxEHSqv5eU3oo6fT3Vj5ZF
+IVlaA5ThZMrI5PgRPuwJM4GL8/VLwY5mbDLFqn/irGeEvP99J3S87ornLLunjpxS
+y1/AymcVep2H32Tj82WS/IRQXBOzz4EnQRJGszKxAV6tY+Zje3sWyTTgObhlsiTQ
+TDgnvtSW8RvVHqKrwgkxxEsRHg7u8UdzZ0jg+O5+3F8B6/NWMyts0OaFqT9wvI8y
+O7VIy3dUtGdz7Hde6Ggp/iTn1LbgdJ3N8Hzxf1j6NMWUKHVsadvwpRJbUeqq9c3+
+QuxsJi8wWemxxQCE+tPyc1dP+ej5/M7bERbSOHMGgX03758IvP7A/fy2DjGPv2+l
+AwlEke0Uze1367QKgxM0nc3SZDlptY7zPIJC5saWXb8Rt2bw2JxEBOTavrp+ZwJ8
+tcH961onq8Tme2ICaCzk
+-----END PKCS7-----
+END
+    pki_msg = OpenSSL::PKCS7.new(pki_message_pem)
+    store = OpenSSL::X509::Store.new
+    pki_msg.verify(nil, store, nil, OpenSSL::PKCS7::NOVERIFY)
+    p7enc = OpenSSL::PKCS7.new(pki_msg.data)
+    assert_equal(pki_message_content_pem, p7enc.to_pem)
+  end
+end
+
+end
diff --git a/jni/ruby/test/openssl/test_pkey_dh.rb b/jni/ruby/test/openssl/test_pkey_dh.rb
new file mode 100644
index 0000000..67dd3e7
--- /dev/null
+++ b/jni/ruby/test/openssl/test_pkey_dh.rb
@@ -0,0 +1,82 @@
+require_relative 'utils'
+
+if defined?(OpenSSL::TestUtils)
+
+class OpenSSL::TestPKeyDH < Test::Unit::TestCase
+
+  NEW_KEYLEN = 256
+
+  def test_new
+    dh = OpenSSL::PKey::DH.new(NEW_KEYLEN)
+    assert_key(dh)
+  end
+
+  def test_new_break
+    assert_nil(OpenSSL::PKey::DH.new(NEW_KEYLEN) { break })
+    assert_raises(RuntimeError) do
+      OpenSSL::PKey::DH.new(NEW_KEYLEN) { raise }
+    end
+  end
+
+  def test_to_der
+    dh = OpenSSL::TestUtils::TEST_KEY_DH1024
+    der = dh.to_der
+    dh2 = OpenSSL::PKey::DH.new(der)
+    assert_equal_params(dh, dh2)
+    assert_no_key(dh2)
+  end
+
+  def test_to_pem
+    dh = OpenSSL::TestUtils::TEST_KEY_DH1024
+    pem = dh.to_pem
+    dh2 = OpenSSL::PKey::DH.new(pem)
+    assert_equal_params(dh, dh2)
+    assert_no_key(dh2)
+  end
+
+  def test_public_key
+    dh = OpenSSL::TestUtils::TEST_KEY_DH1024
+    public_key = dh.public_key
+    assert_no_key(public_key) #implies public_key.public? is false!
+    assert_equal(dh.to_der, public_key.to_der)
+    assert_equal(dh.to_pem, public_key.to_pem)
+  end
+
+  def test_generate_key
+    dh = OpenSSL::TestUtils::TEST_KEY_DH512_PUB.public_key # creates a copy
+    assert_no_key(dh)
+    dh.generate_key!
+    assert_key(dh)
+  end
+
+  def test_key_exchange
+    dh = OpenSSL::TestUtils::TEST_KEY_DH512_PUB
+    dh2 = dh.public_key
+    dh.generate_key!
+    dh2.generate_key!
+    assert_equal(dh.compute_key(dh2.pub_key), dh2.compute_key(dh.pub_key))
+  end
+
+  private
+
+  def assert_equal_params(dh1, dh2)
+    assert_equal(dh1.g, dh2.g)
+    assert_equal(dh1.p, dh2.p)
+  end
+
+  def assert_no_key(dh)
+    assert_equal(false, dh.public?)
+    assert_equal(false, dh.private?)
+    assert_equal(nil, dh.pub_key)
+    assert_equal(nil, dh.priv_key)
+  end
+
+  def assert_key(dh)
+    assert(dh.public?)
+    assert(dh.private?)
+    assert(dh.pub_key)
+    assert(dh.priv_key)
+  end
+end
+
+end
diff --git a/jni/ruby/test/openssl/test_pkey_dsa.rb b/jni/ruby/test/openssl/test_pkey_dsa.rb
new file mode 100644
index 0000000..e4ea1b5
--- /dev/null
+++ b/jni/ruby/test/openssl/test_pkey_dsa.rb
@@ -0,0 +1,240 @@
+require_relative 'utils'
+require 'base64'
+
+if defined?(OpenSSL::TestUtils)
+
+class OpenSSL::TestPKeyDSA < Test::Unit::TestCase
+  def test_private
+    key = OpenSSL::PKey::DSA.new(256)
+    assert(key.private?)
+    key2 = OpenSSL::PKey::DSA.new(key.to_der)
+    assert(key2.private?)
+    key3 = key.public_key
+    assert(!key3.private?)
+    key4 = OpenSSL::PKey::DSA.new(key3.to_der)
+    assert(!key4.private?)
+  end
+
+  def test_new
+    key = OpenSSL::PKey::DSA.new 256
+    pem  = key.public_key.to_pem
+    OpenSSL::PKey::DSA.new pem
+    assert_equal([], OpenSSL.errors)
+  end
+
+  def test_new_break
+    assert_nil(OpenSSL::PKey::DSA.new(512) { break })
+    assert_raise(RuntimeError) do
+      OpenSSL::PKey::DSA.new(512) { raise }
+    end
+  end
+
+  def test_sys_sign_verify
+    key = OpenSSL::TestUtils::TEST_KEY_DSA256
+    data = 'Sign me!'
+    digest = OpenSSL::Digest::SHA1.digest(data)
+    sig = key.syssign(digest)
+    assert(key.sysverify(digest, sig))
+  end
+
+  def test_sign_verify
+    check_sign_verify(OpenSSL::Digest::DSS1.new)
+  end
+
+if (OpenSSL::OPENSSL_VERSION_NUMBER > 0x10000000)
+  def test_sign_verify_sha1
+    check_sign_verify(OpenSSL::Digest::SHA1.new)
+  end
+
+  def test_sign_verify_sha256
+    check_sign_verify(OpenSSL::Digest::SHA256.new)
+  end
+end
+
+  def test_digest_state_irrelevant_verify
+    key = OpenSSL::TestUtils::TEST_KEY_DSA256
+    digest1 = OpenSSL::Digest::DSS1.new
+    digest2 = OpenSSL::Digest::DSS1.new
+    data = 'Sign me!'
+    sig = key.sign(digest1, data)
+    digest1.reset
+    digest1 << 'Change state of digest1'
+    assert(key.verify(digest1, sig, data))
+    assert(key.verify(digest2, sig, data))
+  end
+
+  def test_read_DSA_PUBKEY
+    p = 7188211954100152441468596248707152960171255279130004340103875772401008316444412091945435731597638374542374929457672178957081124632837356913990200866056699
+    q = 957032439192465935099784319494405376402293318491
+    g = 122928973717064636255205666162891733518376475981809749897454444301389338825906076467196186192907631719698166056821519884939865041993585844526937010746285
+    y = 1235756183583465414789073313502727057075641172514181938731172021825149551960029708596057102104063395063907739571546165975727369183495540798749742124846271
+    algo = OpenSSL::ASN1::ObjectId.new('DSA')
+    params = OpenSSL::ASN1::Sequence.new([OpenSSL::ASN1::Integer.new(p),
+                                          OpenSSL::ASN1::Integer.new(q),
+                                          OpenSSL::ASN1::Integer.new(g)])
+    algo_id = OpenSSL::ASN1::Sequence.new ([algo, params])
+    pub_key = OpenSSL::ASN1::Integer.new(y)
+    seq = OpenSSL::ASN1::Sequence.new([algo_id, OpenSSL::ASN1::BitString.new(pub_key.to_der)])
+    key = OpenSSL::PKey::DSA.new(seq.to_der)
+    assert(key.public?)
+    assert(!key.private?)
+    assert_equal(p, key.p)
+    assert_equal(q, key.q)
+    assert_equal(g, key.g)
+    assert_equal(y, key.pub_key)
+    assert_equal(nil, key.priv_key)
+    assert_equal([], OpenSSL.errors)
+  end
+
+  def test_read_DSAPublicKey_pem
+    p = 12260055936871293565827712385212529106400444521449663325576634579961635627321079536132296996623400607469624537382977152381984332395192110731059176842635699
+    q = 979494906553787301107832405790107343409973851677
+    g = 3731695366899846297271147240305742456317979984190506040697507048095553842519347835107669437969086119948785140453492839427038591924536131566350847469993845
+    y = 10505239074982761504240823422422813362721498896040719759460296306305851824586095328615844661273887569281276387605297130014564808567159023649684010036304695
+    pem = <<-EOF
+-----BEGIN DSA PUBLIC KEY-----
+MIHfAkEAyJSJ+g+P/knVcgDwwTzC7Pwg/pWs2EMd/r+lYlXhNfzg0biuXRul8VR4
+VUC/phySExY0PdcqItkR/xYAYNMbNwJBAOoV57X0FxKO/PrNa/MkoWzkCKV/hzhE
+p0zbFdsicw+hIjJ7S6Sd/FlDlo89HQZ2FuvWJ6wGLM1j00r39+F2qbMCFQCrkhIX
+SG+is37hz1IaBeEudjB2HQJAR0AloavBvtsng8obsjLb7EKnB+pSeHr/BdIQ3VH7
+fWLOqqkzFeRrYMDzUpl36XktY6Yq8EJYlW9pCMmBVNy/dQ==
+-----END DSA PUBLIC KEY-----
+    EOF
+    key = OpenSSL::PKey::DSA.new(pem)
+    assert(key.public?)
+    assert(!key.private?)
+    assert_equal(p, key.p)
+    assert_equal(q, key.q)
+    assert_equal(g, key.g)
+    assert_equal(y, key.pub_key)
+    assert_equal(nil, key.priv_key)
+    assert_equal([], OpenSSL.errors)
+  end
+
+  def test_read_DSA_PUBKEY_pem
+    p = 12260055936871293565827712385212529106400444521449663325576634579961635627321079536132296996623400607469624537382977152381984332395192110731059176842635699
+    q = 979494906553787301107832405790107343409973851677
+    g = 3731695366899846297271147240305742456317979984190506040697507048095553842519347835107669437969086119948785140453492839427038591924536131566350847469993845
+    y = 10505239074982761504240823422422813362721498896040719759460296306305851824586095328615844661273887569281276387605297130014564808567159023649684010036304695
+    pem = <<-EOF
+-----BEGIN PUBLIC KEY-----
+MIHxMIGoBgcqhkjOOAQBMIGcAkEA6hXntfQXEo78+s1r8yShbOQIpX+HOESnTNsV
+2yJzD6EiMntLpJ38WUOWjz0dBnYW69YnrAYszWPTSvf34XapswIVAKuSEhdIb6Kz
+fuHPUhoF4S52MHYdAkBHQCWhq8G+2yeDyhuyMtvsQqcH6lJ4ev8F0hDdUft9Ys6q
+qTMV5GtgwPNSmXfpeS1jpirwQliVb2kIyYFU3L91A0QAAkEAyJSJ+g+P/knVcgDw
+wTzC7Pwg/pWs2EMd/r+lYlXhNfzg0biuXRul8VR4VUC/phySExY0PdcqItkR/xYA
+YNMbNw==
+-----END PUBLIC KEY-----
+    EOF
+    key = OpenSSL::PKey::DSA.new(pem)
+    assert(key.public?)
+    assert(!key.private?)
+    assert_equal(p, key.p)
+    assert_equal(q, key.q)
+    assert_equal(g, key.g)
+    assert_equal(y, key.pub_key)
+    assert_equal(nil, key.priv_key)
+    assert_equal([], OpenSSL.errors)
+  end
+
+  def test_export_format_is_DSA_PUBKEY_pem
+    key = OpenSSL::TestUtils::TEST_KEY_DSA256
+    pem = key.public_key.to_pem
+    pem.gsub!(/^-+(\w|\s)+-+$/, "") # eliminate --------BEGIN...-------
+    asn1 = OpenSSL::ASN1.decode(Base64.decode64(pem))
+    assert_equal(OpenSSL::ASN1::SEQUENCE, asn1.tag)
+    assert_equal(2, asn1.value.size)
+    seq = asn1.value
+    assert_equal(OpenSSL::ASN1::SEQUENCE, seq[0].tag)
+    assert_equal(2, seq[0].value.size)
+    algo_id = seq[0].value
+    assert_equal(OpenSSL::ASN1::OBJECT, algo_id[0].tag)
+    assert_equal('DSA', algo_id[0].value)
+    assert_equal(OpenSSL::ASN1::SEQUENCE, algo_id[1].tag)
+    assert_equal(3, algo_id[1].value.size)
+    params = algo_id[1].value
+    assert_equal(OpenSSL::ASN1::INTEGER, params[0].tag)
+    assert_equal(key.p, params[0].value)
+    assert_equal(OpenSSL::ASN1::INTEGER, params[1].tag)
+    assert_equal(key.q, params[1].value)
+    assert_equal(OpenSSL::ASN1::INTEGER, params[2].tag)
+    assert_equal(key.g, params[2].value)
+    assert_equal(OpenSSL::ASN1::BIT_STRING, seq[1].tag)
+    assert_equal(0, seq[1].unused_bits)
+    pub_key = OpenSSL::ASN1.decode(seq[1].value)
+    assert_equal(OpenSSL::ASN1::INTEGER, pub_key.tag)
+    assert_equal(key.pub_key, pub_key.value)
+    assert_equal([], OpenSSL.errors)
+  end
+
+  def test_read_private_key_der
+    key = OpenSSL::TestUtils::TEST_KEY_DSA256
+    der = key.to_der
+    key2 = OpenSSL::PKey.read(der)
+    assert(key2.private?)
+    assert_equal(der, key2.to_der)
+    assert_equal([], OpenSSL.errors)
+  end
+
+  def test_read_private_key_pem
+    key = OpenSSL::TestUtils::TEST_KEY_DSA256
+    pem = key.to_pem
+    key2 = OpenSSL::PKey.read(pem)
+    assert(key2.private?)
+    assert_equal(pem, key2.to_pem)
+    assert_equal([], OpenSSL.errors)
+  end
+
+  def test_read_public_key_der
+    key = OpenSSL::TestUtils::TEST_KEY_DSA256.public_key
+    der = key.to_der
+    key2 = OpenSSL::PKey.read(der)
+    assert(!key2.private?)
+    assert_equal(der, key2.to_der)
+    assert_equal([], OpenSSL.errors)
+  end
+
+  def test_read_public_key_pem
+    key = OpenSSL::TestUtils::TEST_KEY_DSA256.public_key
+    pem = key.to_pem
+    key2 = OpenSSL::PKey.read(pem)
+    assert(!key2.private?)
+    assert_equal(pem, key2.to_pem)
+    assert_equal([], OpenSSL.errors)
+  end
+
+  def test_read_private_key_pem_pw
+    key = OpenSSL::TestUtils::TEST_KEY_DSA256
+    pem = key.to_pem(OpenSSL::Cipher.new('AES-128-CBC'), 'secret')
+    #callback form for password
+    key2 = OpenSSL::PKey.read(pem) do
+      'secret'
+    end
+    assert(key2.private?)
+    # pass password directly
+    key2 = OpenSSL::PKey.read(pem, 'secret')
+    assert(key2.private?)
+    #omit pem equality check, will be different due to cipher iv
+    assert_equal([], OpenSSL.errors)
+  end
+
+  def test_export_password_length
+    key = OpenSSL::TestUtils::TEST_KEY_DSA256
+    assert_raise(OpenSSL::OpenSSLError) do
+      key.export(OpenSSL::Cipher.new('AES-128-CBC'), 'sec')
+    end
+    pem = key.export(OpenSSL::Cipher.new('AES-128-CBC'), 'secr')
+    assert(pem)
+  end
+
+  private
+
+  def check_sign_verify(digest)
+    key = OpenSSL::TestUtils::TEST_KEY_DSA256
+    data = 'Sign me!'
+    sig = key.sign(digest, data)
+    assert(key.verify(digest, sig, data))
+  end
+end
+
+end
diff --git a/jni/ruby/test/openssl/test_pkey_ec.rb b/jni/ruby/test/openssl/test_pkey_ec.rb
new file mode 100644
index 0000000..1693ace
--- /dev/null
+++ b/jni/ruby/test/openssl/test_pkey_ec.rb
@@ -0,0 +1,211 @@
+require_relative 'utils'
+
+if defined?(OpenSSL::TestUtils) && defined?(OpenSSL::PKey::EC)
+
+class OpenSSL::TestEC < Test::Unit::TestCase
+  def setup
+    @data1 = 'foo'
+    @data2 = 'bar' * 1000 # data too long for DSA sig
+
+    @groups = []
+    @keys = []
+
+    OpenSSL::PKey::EC.builtin_curves.each do |curve, comment|
+      next if curve.start_with?("Oakley") # Oakley curves are not suitable for ECDSA
+      group = OpenSSL::PKey::EC::Group.new(curve)
+
+      key = OpenSSL::PKey::EC.new(group)
+      key.generate_key
+
+      @groups << group
+      @keys << key
+    end
+  end
+
+  def compare_keys(k1, k2)
+    assert_equal(k1.to_pem, k2.to_pem)
+  end
+
+  def test_builtin_curves
+    assert(!OpenSSL::PKey::EC.builtin_curves.empty?)
+  end
+
+  def test_curve_names
+    @groups.each_with_index do |group, idx|
+      key = @keys[idx]
+      assert_equal(group.curve_name, key.group.curve_name)
+    end
+  end
+
+  def test_check_key
+    for key in @keys
+      assert_equal(key.check_key, true)
+      assert_equal(key.private_key?, true)
+      assert_equal(key.public_key?, true)
+    end
+  end
+
+  def test_group_encoding
+    for group in @groups
+      for meth in [:to_der, :to_pem]
+        txt = group.send(meth)
+        gr = OpenSSL::PKey::EC::Group.new(txt)
+
+        assert_equal(txt, gr.send(meth))
+
+        assert_equal(group.generator.to_bn, gr.generator.to_bn)
+        assert_equal(group.cofactor, gr.cofactor)
+        assert_equal(group.order, gr.order)
+        assert_equal(group.seed, gr.seed)
+        assert_equal(group.degree, gr.degree)
+      end
+    end
+  end
+
+  def test_key_encoding
+    for key in @keys
+      group = key.group
+
+      for meth in [:to_der, :to_pem]
+        txt = key.send(meth)
+        assert_equal(txt, OpenSSL::PKey::EC.new(txt).send(meth))
+      end
+
+      bn = key.public_key.to_bn
+      assert_equal(bn, OpenSSL::PKey::EC::Point.new(group, bn).to_bn)
+    end
+  end
+
+  def test_set_keys
+    for key in @keys
+      k = OpenSSL::PKey::EC.new
+      k.group = key.group
+      k.private_key = key.private_key
+      k.public_key = key.public_key
+
+      compare_keys(key, k)
+    end
+  end
+
+  def test_dsa_sign_verify
+    for key in @keys
+      sig = key.dsa_sign_asn1(@data1)
+      assert(key.dsa_verify_asn1(@data1, sig))
+    end
+  end
+
+  def test_dsa_sign_asn1_FIPS186_3
+    for key in @keys
+      size = key.group.order.num_bits / 8 + 1
+      dgst = (1..size).to_a.pack('C*')
+      begin
+        sig = key.dsa_sign_asn1(dgst)
+        # dgst is auto-truncated according to FIPS186-3 after openssl-0.9.8m
+        assert(key.dsa_verify_asn1(dgst + "garbage", sig))
+      rescue OpenSSL::PKey::ECError => e
+        # just an exception for longer dgst before openssl-0.9.8m
+        assert_equal('ECDSA_sign: data too large for key size', e.message)
+        # no need to do following tests
+        return
+      end
+    end
+  end
+
+  def test_dh_compute_key
+    for key in @keys
+      k = OpenSSL::PKey::EC.new(key.group)
+      k.generate_key
+
+      puba = key.public_key
+      pubb = k.public_key
+      a = key.dh_compute_key(pubb)
+      b = k.dh_compute_key(puba)
+      assert_equal(a, b)
+    end
+  end
+
+  def test_read_private_key_der
+    ec = OpenSSL::TestUtils::TEST_KEY_EC_P256V1
+    der = ec.to_der
+    ec2 = OpenSSL::PKey.read(der)
+    assert(ec2.private_key?)
+    assert_equal(der, ec2.to_der)
+    assert_equal([], OpenSSL.errors)
+  end
+
+  def test_read_private_key_pem
+    ec = OpenSSL::TestUtils::TEST_KEY_EC_P256V1
+    pem = ec.to_pem
+    ec2 = OpenSSL::PKey.read(pem)
+    assert(ec2.private_key?)
+    assert_equal(pem, ec2.to_pem)
+    assert_equal([], OpenSSL.errors)
+  end
+
+  def test_read_public_key_der
+    ec = OpenSSL::TestUtils::TEST_KEY_EC_P256V1
+    ec2 = OpenSSL::PKey::EC.new(ec.group)
+    ec2.public_key = ec.public_key
+    der = ec2.to_der
+    ec3 = OpenSSL::PKey.read(der)
+    assert(!ec3.private_key?)
+    assert_equal(der, ec3.to_der)
+    assert_equal([], OpenSSL.errors)
+  end
+
+  def test_read_public_key_pem
+    ec = OpenSSL::TestUtils::TEST_KEY_EC_P256V1
+    ec2 = OpenSSL::PKey::EC.new(ec.group)
+    ec2.public_key = ec.public_key
+    pem = ec2.to_pem
+    ec3 = OpenSSL::PKey.read(pem)
+    assert(!ec3.private_key?)
+    assert_equal(pem, ec3.to_pem)
+    assert_equal([], OpenSSL.errors)
+  end
+
+  def test_read_private_key_pem_pw
+    ec = OpenSSL::TestUtils::TEST_KEY_EC_P256V1
+    pem = ec.to_pem(OpenSSL::Cipher.new('AES-128-CBC'), 'secret')
+    #callback form for password
+    ec2 = OpenSSL::PKey.read(pem) do
+      'secret'
+    end
+    assert(ec2.private_key?)
+    # pass password directly
+    ec2 = OpenSSL::PKey.read(pem, 'secret')
+    assert(ec2.private_key?)
+    #omit pem equality check, will be different due to cipher iv
+    assert_equal([], OpenSSL.errors)
+  end
+
+  def test_export_password_length
+    key = OpenSSL::TestUtils::TEST_KEY_EC_P256V1
+    assert_raise(OpenSSL::OpenSSLError) do
+      key.export(OpenSSL::Cipher.new('AES-128-CBC'), 'sec')
+    end
+    pem = key.export(OpenSSL::Cipher.new('AES-128-CBC'), 'secr')
+    assert(pem)
+  end
+
+  def test_ec_point_mul
+    ec = OpenSSL::TestUtils::TEST_KEY_EC_P256V1
+    p1 = ec.public_key
+    bn1 = OpenSSL::BN.new('10')
+    bn2 = OpenSSL::BN.new('20')
+
+    p2 = p1.mul(bn1)
+    assert(p1.group == p2.group)
+    p2 = p1.mul(bn1, bn2)
+    assert(p1.group == p2.group)
+    p2 = p1.mul([bn1, bn2], [p1])
+    assert(p1.group == p2.group)
+    p2 = p1.mul([bn1, bn2], [p1], bn2)
+    assert(p1.group == p2.group)
+  end
+
+# test Group: asn1_flag, point_conversion
+
+end
+
+end
diff --git a/jni/ruby/test/openssl/test_pkey_rsa.rb b/jni/ruby/test/openssl/test_pkey_rsa.rb
new file mode 100644
index 0000000..ea042c2
--- /dev/null
+++ b/jni/ruby/test/openssl/test_pkey_rsa.rb
@@ -0,0 +1,313 @@
+require_relative 'utils'
+require 'base64'
+
+if defined?(OpenSSL::TestUtils)
+
+class OpenSSL::TestPKeyRSA < Test::Unit::TestCase
+  def test_padding
+    key = OpenSSL::PKey::RSA.new(512, 3)
+
+    # Need right size for raw mode
+    plain0 = "x" * (512/8)
+    cipher = key.private_encrypt(plain0, OpenSSL::PKey::RSA::NO_PADDING)
+    plain1 = key.public_decrypt(cipher, OpenSSL::PKey::RSA::NO_PADDING)
+    assert_equal(plain0, plain1)
+
+    # Need smaller size for pkcs1 mode
+    plain0 = "x" * (512/8 - 11)
+    cipher1 = key.private_encrypt(plain0, OpenSSL::PKey::RSA::PKCS1_PADDING)
+    plain1 = key.public_decrypt(cipher1, OpenSSL::PKey::RSA::PKCS1_PADDING)
+    assert_equal(plain0, plain1)
+
+    cipherdef = key.private_encrypt(plain0) # PKCS1_PADDING is default
+    plain1 = key.public_decrypt(cipherdef)
+    assert_equal(plain0, plain1)
+    assert_equal(cipher1, cipherdef)
+
+    # Failure cases
+    assert_raise(ArgumentError){ key.private_encrypt() }
+    assert_raise(ArgumentError){ key.private_encrypt("hi", 1, nil) }
+    assert_raise(OpenSSL::PKey::RSAError){ key.private_encrypt(plain0, 666) }
+  end
+
+  def test_private
+    key = OpenSSL::PKey::RSA.new(512, 3)
+    assert(key.private?)
+    key2 = OpenSSL::PKey::RSA.new(key.to_der)
+    assert(key2.private?)
+    key3 = key.public_key
+    assert(!key3.private?)
+    key4 = OpenSSL::PKey::RSA.new(key3.to_der)
+    assert(!key4.private?)
+  end
+
+  def test_new
+    key = OpenSSL::PKey::RSA.new 512
+    pem  = key.public_key.to_pem
+    OpenSSL::PKey::RSA.new pem
+    assert_equal([], OpenSSL.errors)
+  end
+
+  def test_new_exponent_default
+    assert_equal(65537, OpenSSL::PKey::RSA.new(512).e)
+  end
+
+  def test_new_with_exponent
+    1.upto(30) do |idx|
+      e = (2 ** idx) + 1
+      key = OpenSSL::PKey::RSA.new(512, e)
+      assert_equal(e, key.e)
+    end
+  end
+
+  def test_new_break
+    assert_nil(OpenSSL::PKey::RSA.new(1024) { break })
+    assert_raise(RuntimeError) do
+      OpenSSL::PKey::RSA.new(1024) { raise }
+    end
+  end
+
+  def test_sign_verify
+    key = OpenSSL::TestUtils::TEST_KEY_RSA1024
+    digest = OpenSSL::Digest::SHA1.new
+    data = 'Sign me!'
+    sig = key.sign(digest, data)
+    assert(key.verify(digest, sig, data))
+  end
+
+  def test_sign_verify_memory_leak
+    bug9743 = '[ruby-core:62038] [Bug #9743]'
+    assert_no_memory_leak(%w[-ropenssl], <<-PREP, <<-CODE, bug9743, rss: true, timeout: 30)
+    data = 'Sign me!'
+    digest = OpenSSL::Digest::SHA512.new
+    pkey = OpenSSL::PKey::RSA.new(2048)
+    signature = pkey.sign(digest, data)
+    pub_key = pkey.public_key
+    PREP
+    20_000.times {
+      pub_key.verify(digest, signature, data)
+    }
+    CODE
+
+    assert_no_memory_leak(%w[-ropenssl], <<-PREP, <<-CODE, bug9743, rss: true, timeout: 30)
+    data = 'Sign me!'
+    digest = OpenSSL::Digest::SHA512.new
+    pkey = OpenSSL::PKey::RSA.new(2048)
+    signature = pkey.sign(digest, data)
+    pub_key = pkey.public_key
+    PREP
+    20_000.times {
+      begin
+        pub_key.verify(digest, signature, 1)
+      rescue TypeError
+      end
+    }
+    CODE
+  end
+
+  def test_digest_state_irrelevant_sign
+    key = OpenSSL::TestUtils::TEST_KEY_RSA1024
+    digest1 = OpenSSL::Digest::SHA1.new
+    digest2 = OpenSSL::Digest::SHA1.new
+    data = 'Sign me!'
+    digest1 << 'Change state of digest1'
+    sig1 = key.sign(digest1, data)
+    sig2 = key.sign(digest2, data)
+    assert_equal(sig1, sig2)
+  end
+
+  def test_digest_state_irrelevant_verify
+    key = OpenSSL::TestUtils::TEST_KEY_RSA1024
+    digest1 = OpenSSL::Digest::SHA1.new
+    digest2 = OpenSSL::Digest::SHA1.new
+    data = 'Sign me!'
+    sig = key.sign(digest1, data)
+    digest1.reset
+    digest1 << 'Change state of digest1'
+    assert(key.verify(digest1, sig, data))
+    assert(key.verify(digest2, sig, data))
+  end
+
+  def test_read_RSAPublicKey
+    modulus = 10664264882656732240315063514678024569492171560814833397008094754351396057398262071307709191731289492697968568138092052265293364132872019762410446076526351
+    exponent = 65537
+    seq = OpenSSL::ASN1::Sequence.new([OpenSSL::ASN1::Integer.new(modulus), OpenSSL::ASN1::Integer.new(exponent)])
+    key = OpenSSL::PKey::RSA.new(seq.to_der)
+    assert(key.public?)
+    assert(!key.private?)
+    assert_equal(modulus, key.n)
+    assert_equal(exponent, key.e)
+    assert_equal(nil, key.d)
+    assert_equal(nil, key.p)
+    assert_equal(nil, key.q)
+    assert_equal([], OpenSSL.errors)
+  end
+
+  def test_read_RSA_PUBKEY
+    modulus = 10664264882656732240315063514678024569492171560814833397008094754351396057398262071307709191731289492697968568138092052265293364132872019762410446076526351
+    exponent = 65537
+    algo = OpenSSL::ASN1::ObjectId.new('rsaEncryption')
+    null_params = OpenSSL::ASN1::Null.new(nil)
+    algo_id = OpenSSL::ASN1::Sequence.new ([algo, null_params])
+    pub_key = OpenSSL::ASN1::Sequence.new([OpenSSL::ASN1::Integer.new(modulus), OpenSSL::ASN1::Integer.new(exponent)])
+    seq = OpenSSL::ASN1::Sequence.new([algo_id, OpenSSL::ASN1::BitString.new(pub_key.to_der)])
+    key = OpenSSL::PKey::RSA.new(seq.to_der)
+    assert(key.public?)
+    assert(!key.private?)
+    assert_equal(modulus, key.n)
+    assert_equal(exponent, key.e)
+    assert_equal(nil, key.d)
+    assert_equal(nil, key.p)
+    assert_equal(nil, key.q)
+    assert_equal([], OpenSSL.errors)
+  end
+
+  def test_read_RSAPublicKey_pem
+    modulus = 9416340886363418692990906464787534854462163316648195510702927337693641649864839352187127240942127674615733815606532506566068276485089353644309497938966061
+    exponent = 65537
+    pem = <<-EOF
+-----BEGIN RSA PUBLIC KEY-----
+MEgCQQCzyh2RIZK62E2PbTWqUljD+K23XR9AGBKNtXjal6WD2yRGcLqzPJLNCa60
+AudJR1JobbIbDJrQu6AXnWh5k/YtAgMBAAE=
+-----END RSA PUBLIC KEY-----
+    EOF
+    key = OpenSSL::PKey::RSA.new(pem)
+    assert(key.public?)
+    assert(!key.private?)
+    assert_equal(modulus, key.n)
+    assert_equal(exponent, key.e)
+    assert_equal(nil, key.d)
+    assert_equal(nil, key.p)
+    assert_equal(nil, key.q)
+    assert_equal([], OpenSSL.errors)
+  end
+
+  def test_read_RSA_PUBKEY_pem
+    modulus = 9416340886363418692990906464787534854462163316648195510702927337693641649864839352187127240942127674615733815606532506566068276485089353644309497938966061
+    exponent = 65537
+    pem = <<-EOF
+-----BEGIN PUBLIC KEY-----
+MFwwDQYJKoZIhvcNAQEBBQADSwAwSAJBALPKHZEhkrrYTY9tNapSWMP4rbdd
+H0AYEo21eNqXpYPbJEZwurM8ks0JrrQC50lHUmhtshsMmtC7oBedaHmT9i0C
+AwEAAQ==
+-----END PUBLIC KEY-----
+    EOF
+    key = OpenSSL::PKey::RSA.new(pem)
+    assert(key.public?)
+    assert(!key.private?)
+    assert_equal(modulus, key.n)
+    assert_equal(exponent, key.e)
+    assert_equal(nil, key.d)
+    assert_equal(nil, key.p)
+    assert_equal(nil, key.q)
+    assert_equal([], OpenSSL.errors)
+  end
+
+  def test_export_format_is_RSA_PUBKEY
+    key = OpenSSL::PKey::RSA.new(512)
+    asn1 = OpenSSL::ASN1.decode(key.public_key.to_der)
+    check_PUBKEY(asn1, key)
+  end
+
+  def test_export_format_is_RSA_PUBKEY_pem
+    key = OpenSSL::PKey::RSA.new(512)
+    pem = key.public_key.to_pem
+    pem.gsub!(/^-+(\w|\s)+-+$/, "") # eliminate --------BEGIN...-------
+    asn1 = OpenSSL::ASN1.decode(Base64.decode64(pem))
+    check_PUBKEY(asn1, key)
+  end
+
+  def test_read_private_key_der
+    der = OpenSSL::TestUtils::TEST_KEY_RSA1024.to_der
+    key = OpenSSL::PKey.read(der)
+    assert(key.private?)
+    assert_equal(der, key.to_der)
+    assert_equal([], OpenSSL.errors)
+  end
+
+  def test_read_private_key_pem
+    pem = OpenSSL::TestUtils::TEST_KEY_RSA1024.to_pem
+    key = OpenSSL::PKey.read(pem)
+    assert(key.private?)
+    assert_equal(pem, key.to_pem)
+    assert_equal([], OpenSSL.errors)
+  end
+
+  def test_read_public_key_der
+    der = OpenSSL::TestUtils::TEST_KEY_RSA1024.public_key.to_der
+    key = OpenSSL::PKey.read(der)
+    assert(!key.private?)
+    assert_equal(der, key.to_der)
+    assert_equal([], OpenSSL.errors)
+  end
+
+  def test_read_public_key_pem
+    pem = OpenSSL::TestUtils::TEST_KEY_RSA1024.public_key.to_pem
+    key = OpenSSL::PKey.read(pem)
+    assert(!key.private?)
+    assert_equal(pem, key.to_pem)
+    assert_equal([], OpenSSL.errors)
+  end
+
+  def test_read_private_key_pem_pw
+    pem = OpenSSL::TestUtils::TEST_KEY_RSA1024.to_pem(OpenSSL::Cipher.new('AES-128-CBC'), 'secret')
+    #callback form for password
+    key = OpenSSL::PKey.read(pem) do
+      'secret'
+    end
+    assert(key.private?)
+    # pass password directly
+    key = OpenSSL::PKey.read(pem, 'secret')
+    assert(key.private?)
+    #omit pem equality check, will be different due to cipher iv
+    assert_equal([], OpenSSL.errors)
+  end
+
+  def test_read_private_key_pem_pw_exception
+    pem = OpenSSL::TestUtils::TEST_KEY_RSA1024.to_pem(OpenSSL::Cipher.new('AES-128-CBC'), 'secret')
+    # it raises an ArgumentError from PEM reading. The exception raised inside are ignored for now.
+    assert_raise(ArgumentError) do
+      OpenSSL::PKey.read(pem) do
+        raise RuntimeError
+      end
+    end
+    assert_equal([], OpenSSL.errors)
+  end
+
+  def test_export_password_length
+    key = OpenSSL::TestUtils::TEST_KEY_RSA1024
+    assert_raise(OpenSSL::OpenSSLError) do
+      key.export(OpenSSL::Cipher.new('AES-128-CBC'), 'sec')
+    end
+    pem = key.export(OpenSSL::Cipher.new('AES-128-CBC'), 'secr')
+    assert(pem)
+  end
+
+  private
+
+  def check_PUBKEY(asn1, key)
+    assert_equal(OpenSSL::ASN1::SEQUENCE, asn1.tag)
+    assert_equal(2, asn1.value.size)
+    seq = asn1.value
+    assert_equal(OpenSSL::ASN1::SEQUENCE, seq[0].tag)
+    assert_equal(2, seq[0].value.size)
+    algo_id = seq[0].value
+    assert_equal(OpenSSL::ASN1::OBJECT, algo_id[0].tag)
+    assert_equal('rsaEncryption', algo_id[0].value)
+    assert_equal(OpenSSL::ASN1::NULL, algo_id[1].tag)
+    assert_equal(nil, algo_id[1].value)
+    assert_equal(OpenSSL::ASN1::BIT_STRING, seq[1].tag)
+    assert_equal(0, seq[1].unused_bits)
+    pub_key = OpenSSL::ASN1.decode(seq[1].value)
+    assert_equal(OpenSSL::ASN1::SEQUENCE, pub_key.tag)
+    assert_equal(2, pub_key.value.size)
+    assert_equal(OpenSSL::ASN1::INTEGER, pub_key.value[0].tag)
+    assert_equal(key.n, pub_key.value[0].value)
+    assert_equal(OpenSSL::ASN1::INTEGER, pub_key.value[1].tag)
+    assert_equal(key.e, pub_key.value[1].value)
+    assert_equal([], OpenSSL.errors)
+  end
+
+end
+
+end
diff --git a/jni/ruby/test/openssl/test_ssl.rb b/jni/ruby/test/openssl/test_ssl.rb
new file mode 100644
index 0000000..8aa18e5
--- /dev/null
+++ b/jni/ruby/test/openssl/test_ssl.rb
@@ -0,0 +1,933 @@
+require_relative "utils"
+
+if defined?(OpenSSL::TestUtils)
+
+class OpenSSL::TestSSL < OpenSSL::SSLTestCase
+
+  def test_ctx_setup
+    ctx = OpenSSL::SSL::SSLContext.new
+    assert_equal(ctx.setup, true)
+    assert_equal(ctx.setup, nil)
+  end
+
+  def test_ctx_setup_no_compression
+    ctx = OpenSSL::SSL::SSLContext.new
+    ctx.options = OpenSSL::SSL::OP_ALL | OpenSSL::SSL::OP_NO_COMPRESSION
+    assert_equal(ctx.setup, true)
+    assert_equal(ctx.setup, nil)
+    assert_equal(OpenSSL::SSL::OP_NO_COMPRESSION,
+                 ctx.options & OpenSSL::SSL::OP_NO_COMPRESSION)
+  end if defined?(OpenSSL::SSL::OP_NO_COMPRESSION)
+
+  def test_ctx_setup_with_extra_chain_cert
+    ctx = OpenSSL::SSL::SSLContext.new
+    ctx.extra_chain_cert = [@ca_cert, @cli_cert]
+    assert_equal(ctx.setup, true)
+    assert_equal(ctx.setup, nil)
+  end
+
+  def test_not_started_session
+    skip "non socket argument of SSLSocket.new is not supported on this platform" if /mswin|mingw/ =~ RUBY_PLATFORM
+    open(__FILE__) do |f|
+      assert_nil OpenSSL::SSL::SSLSocket.new(f).cert
+    end
+  end
+
+  def test_ssl_gets
+    start_server(OpenSSL::SSL::VERIFY_NONE, true) { |server, port|
+      server_connect(port) { |ssl|
+        ssl.write "abc\n"
+        IO.select [ssl]
+
+        line = ssl.gets
+
+        assert_equal "abc\n", line
+        assert_equal Encoding::BINARY, line.encoding
+      }
+    }
+  end
+
+  def test_ssl_read_nonblock
+    start_server(OpenSSL::SSL::VERIFY_NONE, true) { |server, port|
+      server_connect(port) { |ssl|
+        assert_raise(IO::WaitReadable) { ssl.read_nonblock(100) }
+        ssl.write("abc\n")
+        IO.select [ssl]
+        assert_equal('a', ssl.read_nonblock(1))
+        assert_equal("bc\n", ssl.read_nonblock(100))
+        assert_raise(IO::WaitReadable) { ssl.read_nonblock(100) }
+      }
+    }
+  end
+
+  def test_connect_and_close
+    start_server(OpenSSL::SSL::VERIFY_NONE, true){|server, port|
+      sock = TCPSocket.new("127.0.0.1", port)
+      ssl = OpenSSL::SSL::SSLSocket.new(sock)
+      assert(ssl.connect)
+      ssl.close
+      assert(!sock.closed?)
+      sock.close
+
+      sock = TCPSocket.new("127.0.0.1", port)
+      ssl = OpenSSL::SSL::SSLSocket.new(sock)
+      ssl.sync_close = true  # !!
+      assert(ssl.connect)
+      ssl.close
+      assert(sock.closed?)
+    }
+  end
+
+  def test_read_and_write
+    start_server(OpenSSL::SSL::VERIFY_NONE, true){|server, port|
+      server_connect(port) { |ssl|
+        # syswrite and sysread
+        ITERATIONS.times{|i|
+          str = "x" * 100 + "\n"
+          ssl.syswrite(str)
+          newstr = ''
+          newstr << ssl.sysread(str.size - newstr.size) until newstr.size == str.size
+          assert_equal(str, newstr)
+
+          str = "x" * i * 100 + "\n"
+          buf = ""
+          ssl.syswrite(str)
+          assert_equal(buf.object_id, ssl.sysread(str.size, buf).object_id)
+          newstr = buf
+          newstr << ssl.sysread(str.size - newstr.size) until newstr.size == str.size
+          assert_equal(str, newstr)
+        }
+
+        # puts and gets
+        ITERATIONS.times{
+          str = "x" * 100 + "\n"
+          ssl.puts(str)
+          assert_equal(str, ssl.gets)
+
+          str = "x" * 100
+          ssl.puts(str)
+          assert_equal(str, ssl.gets("\n", 100))
+          assert_equal("\n", ssl.gets)
+        }
+
+        # read and write
+        ITERATIONS.times{|i|
+          str = "x" * 100 + "\n"
+          ssl.write(str)
+          assert_equal(str, ssl.read(str.size))
+
+          str = "x" * i * 100 + "\n"
+          buf = ""
+          ssl.write(str)
+          assert_equal(buf.object_id, ssl.read(str.size, buf).object_id)
+          assert_equal(str, buf)
+        }
+      }
+    }
+  end
+
+  def test_client_auth_failure
+    vflag = OpenSSL::SSL::VERIFY_PEER|OpenSSL::SSL::VERIFY_FAIL_IF_NO_PEER_CERT
+    start_server(vflag, true, :ignore_listener_error => true){|server, port|
+      assert_raise(OpenSSL::SSL::SSLError, Errno::ECONNRESET){
+        sock = TCPSocket.new("127.0.0.1", port)
+        ssl = OpenSSL::SSL::SSLSocket.new(sock)
+        ssl.sync_close = true
+        begin
+          ssl.connect
+        ensure
+          ssl.close
+        end
+      }
+    }
+  end
+
+  def test_client_auth_success
+    vflag = OpenSSL::SSL::VERIFY_PEER|OpenSSL::SSL::VERIFY_FAIL_IF_NO_PEER_CERT
+    start_server(vflag, true){|server, port|
+      ctx = OpenSSL::SSL::SSLContext.new
+      ctx.key = @cli_key
+      ctx.cert = @cli_cert
+
+      server_connect(port, ctx) { |ssl|
+        ssl.puts("foo")
+        assert_equal("foo\n", ssl.gets)
+      }
+
+      called = nil
+      ctx = OpenSSL::SSL::SSLContext.new
+      ctx.client_cert_cb = Proc.new{ |sslconn|
+        called = true
+        [@cli_cert, @cli_key]
+      }
+
+      server_connect(port, ctx) { |ssl|
+        assert(called)
+        ssl.puts("foo")
+        assert_equal("foo\n", ssl.gets)
+      }
+    }
+  end
+
+  def test_client_ca
+    ctx_proc = Proc.new do |ctx|
+      ctx.client_ca = [@ca_cert]
+    end
+
+    vflag = OpenSSL::SSL::VERIFY_PEER|OpenSSL::SSL::VERIFY_FAIL_IF_NO_PEER_CERT
+    start_server(vflag, true, :ctx_proc => ctx_proc){|server, port|
+      ctx = OpenSSL::SSL::SSLContext.new
+      client_ca_from_server = nil
+      ctx.client_cert_cb = Proc.new do |sslconn|
+        client_ca_from_server = sslconn.client_ca
+        [@cli_cert, @cli_key]
+      end
+      server_connect(port, ctx) { |ssl| assert_equal([@ca], client_ca_from_server) }
+    }
+  end
+
+  def test_read_nonblock_without_session
+    OpenSSL::TestUtils.silent do
+      start_server(OpenSSL::SSL::VERIFY_NONE, false){|server, port|
+        sock = TCPSocket.new("127.0.0.1", port)
+        ssl = OpenSSL::SSL::SSLSocket.new(sock)
+        ssl.sync_close = true
+
+        assert_equal :wait_readable, ssl.read_nonblock(100, exception: false)
+        ssl.write("abc\n")
+        IO.select [ssl]
+        assert_equal('a', ssl.read_nonblock(1))
+        assert_equal("bc\n", ssl.read_nonblock(100))
+        assert_equal :wait_readable, ssl.read_nonblock(100, exception: false)
+        ssl.close
+      }
+    end
+  end
+
+  def test_starttls
+    OpenSSL::TestUtils.silent do
+      start_server(OpenSSL::SSL::VERIFY_NONE, false){|server, port|
+        sock = TCPSocket.new("127.0.0.1", port)
+        ssl = OpenSSL::SSL::SSLSocket.new(sock)
+        ssl.sync_close = true
+        str = "x" * 1000 + "\n"
+
+        ITERATIONS.times{
+          ssl.puts(str)
+          assert_equal(str, ssl.gets)
+        }
+        starttls(ssl)
+
+        ITERATIONS.times{
+          ssl.puts(str)
+          assert_equal(str, ssl.gets)
+        }
+
+        ssl.close
+      }
+    end
+  end
+
+  def test_parallel
+    GC.start
+    start_server(OpenSSL::SSL::VERIFY_NONE, true){|server, port|
+      ssls = []
+      10.times{
+        sock = TCPSocket.new("127.0.0.1", port)
+        ssl = OpenSSL::SSL::SSLSocket.new(sock)
+        ssl.connect
+        ssl.sync_close = true
+        ssls << ssl
+      }
+      str = "x" * 1000 + "\n"
+      ITERATIONS.times{
+        ssls.each{|ssl|
+          ssl.puts(str)
+          assert_equal(str, ssl.gets)
+        }
+      }
+      ssls.each{|ssl| ssl.close }
+    }
+  end
+
+  def test_verify_result
+    start_server(OpenSSL::SSL::VERIFY_NONE, true, :ignore_listener_error => true){|server, port|
+      sock = TCPSocket.new("127.0.0.1", port)
+      ctx = OpenSSL::SSL::SSLContext.new
+      ctx.set_params
+      ssl = OpenSSL::SSL::SSLSocket.new(sock, ctx)
+      ssl.sync_close = true
+      begin
+        assert_raise(OpenSSL::SSL::SSLError){ ssl.connect }
+        assert_equal(OpenSSL::X509::V_ERR_SELF_SIGNED_CERT_IN_CHAIN, ssl.verify_result)
+      ensure
+        ssl.close
+      end
+    }
+
+    start_server(OpenSSL::SSL::VERIFY_NONE, true){|server, port|
+      sock = TCPSocket.new("127.0.0.1", port)
+      ctx = OpenSSL::SSL::SSLContext.new
+      ctx.set_params(
+        :verify_callback => Proc.new do |preverify_ok, store_ctx|
+          store_ctx.error = OpenSSL::X509::V_OK
+          true
+        end
+      )
+      ssl = OpenSSL::SSL::SSLSocket.new(sock, ctx)
+      ssl.sync_close = true
+      begin
+        ssl.connect
+        assert_equal(OpenSSL::X509::V_OK, ssl.verify_result)
+      ensure
+        ssl.close
+      end
+    }
+
+    start_server(OpenSSL::SSL::VERIFY_NONE, true, :ignore_listener_error => true){|server, port|
+      sock = TCPSocket.new("127.0.0.1", port)
+      ctx = OpenSSL::SSL::SSLContext.new
+      ctx.set_params(
+        :verify_callback => Proc.new do |preverify_ok, store_ctx|
+          store_ctx.error = OpenSSL::X509::V_ERR_APPLICATION_VERIFICATION
+          false
+        end
+      )
+      ssl = OpenSSL::SSL::SSLSocket.new(sock, ctx)
+      ssl.sync_close = true
+      begin
+        assert_raise(OpenSSL::SSL::SSLError){ ssl.connect }
+        assert_equal(OpenSSL::X509::V_ERR_APPLICATION_VERIFICATION, ssl.verify_result)
+      ensure
+        ssl.close
+      end
+    }
+  end
+
+  def test_exception_in_verify_callback_is_ignored
+    start_server(OpenSSL::SSL::VERIFY_NONE, true, :ignore_listener_error => true){|server, port|
+      sock = TCPSocket.new("127.0.0.1", port)
+      ctx = OpenSSL::SSL::SSLContext.new
+      ctx.set_params(
+        :verify_callback => Proc.new do |preverify_ok, store_ctx|
+          store_ctx.error = OpenSSL::X509::V_OK
+          raise RuntimeError
+        end
+      )
+      ssl = OpenSSL::SSL::SSLSocket.new(sock, ctx)
+      ssl.sync_close = true
+      begin
+        OpenSSL::TestUtils.silent do
+          # SSLError, not RuntimeError
+          assert_raise(OpenSSL::SSL::SSLError) { ssl.connect }
+        end
+        assert_equal(OpenSSL::X509::V_ERR_CERT_REJECTED, ssl.verify_result)
+      ensure
+        ssl.close
+      end
+    }
+  end
+
+  def test_sslctx_set_params
+    start_server(OpenSSL::SSL::VERIFY_NONE, true, :ignore_listener_error => true){|server, port|
+      sock = TCPSocket.new("127.0.0.1", port)
+      ctx = OpenSSL::SSL::SSLContext.new
+      ctx.set_params
+      assert_equal(OpenSSL::SSL::VERIFY_PEER, ctx.verify_mode)
+      assert_equal(OpenSSL::SSL::SSLContext::DEFAULT_PARAMS[:options], ctx.options)
+      ciphers = ctx.ciphers
+      ciphers_versions = ciphers.collect{|_, v, _, _| v }
+      ciphers_names = ciphers.collect{|v, _, _, _| v }
+      assert(ciphers_names.all?{|v| /ADH/ !~ v })
+      assert(ciphers_versions.all?{|v| /SSLv2/ !~ v })
+      ssl = OpenSSL::SSL::SSLSocket.new(sock, ctx)
+      ssl.sync_close = true
+      begin
+        assert_raise(OpenSSL::SSL::SSLError){ ssl.connect }
+        assert_equal(OpenSSL::X509::V_ERR_SELF_SIGNED_CERT_IN_CHAIN, ssl.verify_result)
+      ensure
+        ssl.close
+      end
+    }
+  end
+
+  def test_post_connect_check_with_anon_ciphers
+    sslerr = OpenSSL::SSL::SSLError
+
+    start_server(OpenSSL::SSL::VERIFY_NONE, true, {use_anon_cipher: true}){|server, port|
+      ctx = OpenSSL::SSL::SSLContext.new
+      ctx.ciphers = "aNULL"
+      server_connect(port, ctx) { |ssl|
+        msg = "Peer verification enabled, but no certificate received. Anonymous cipher suite " \
+          "ADH-AES256-GCM-SHA384 was negotiated. Anonymous suites must be disabled to use peer verification."
+        assert_raise_with_message(sslerr,msg){ssl.post_connection_check("localhost.localdomain")}
+      }
+    }
+  end
+
+  def test_post_connection_check
+    sslerr = OpenSSL::SSL::SSLError
+
+    start_server(OpenSSL::SSL::VERIFY_NONE, true){|server, port|
+      server_connect(port) { |ssl|
+        assert_raise(sslerr){ssl.post_connection_check("localhost.localdomain")}
+        assert_raise(sslerr){ssl.post_connection_check("127.0.0.1")}
+        assert(ssl.post_connection_check("localhost"))
+        assert_raise(sslerr){ssl.post_connection_check("foo.example.com")}
+
+        cert = ssl.peer_cert
+        assert(!OpenSSL::SSL.verify_certificate_identity(cert, "localhost.localdomain"))
+        assert(!OpenSSL::SSL.verify_certificate_identity(cert, "127.0.0.1"))
+        assert(OpenSSL::SSL.verify_certificate_identity(cert, "localhost"))
+        assert(!OpenSSL::SSL.verify_certificate_identity(cert, "foo.example.com"))
+      }
+    }
+
+    now = Time.now
+    exts = [
+      ["keyUsage","keyEncipherment,digitalSignature",true],
+      ["subjectAltName","DNS:localhost.localdomain",false],
+      ["subjectAltName","IP:127.0.0.1",false],
+    ]
+    @svr_cert = issue_cert(@svr, @svr_key, 4, now, now+1800, exts,
+                           @ca_cert, @ca_key, OpenSSL::Digest::SHA1.new)
+    start_server(OpenSSL::SSL::VERIFY_NONE, true){|server, port|
+      server_connect(port) { |ssl|
+        assert(ssl.post_connection_check("localhost.localdomain"))
+        assert(ssl.post_connection_check("127.0.0.1"))
+        assert_raise(sslerr){ssl.post_connection_check("localhost")}
+        assert_raise(sslerr){ssl.post_connection_check("foo.example.com")}
+
+        cert = ssl.peer_cert
+        assert(OpenSSL::SSL.verify_certificate_identity(cert, "localhost.localdomain"))
+        assert(OpenSSL::SSL.verify_certificate_identity(cert, "127.0.0.1"))
+        assert(!OpenSSL::SSL.verify_certificate_identity(cert, "localhost"))
+        assert(!OpenSSL::SSL.verify_certificate_identity(cert, "foo.example.com"))
+      }
+    }
+
+    now = Time.now
+    exts = [
+      ["keyUsage","keyEncipherment,digitalSignature",true],
+      ["subjectAltName","DNS:*.localdomain",false],
+    ]
+    @svr_cert = issue_cert(@svr, @svr_key, 5, now, now+1800, exts,
+                           @ca_cert, @ca_key, OpenSSL::Digest::SHA1.new)
+    start_server(OpenSSL::SSL::VERIFY_NONE, true){|server, port|
+      server_connect(port) { |ssl|
+        assert(ssl.post_connection_check("localhost.localdomain"))
+        assert_raise(sslerr){ssl.post_connection_check("127.0.0.1")}
+        assert_raise(sslerr){ssl.post_connection_check("localhost")}
+        assert_raise(sslerr){ssl.post_connection_check("foo.example.com")}
+        cert = ssl.peer_cert
+        assert(OpenSSL::SSL.verify_certificate_identity(cert, "localhost.localdomain"))
+        assert(!OpenSSL::SSL.verify_certificate_identity(cert, "127.0.0.1"))
+        assert(!OpenSSL::SSL.verify_certificate_identity(cert, "localhost"))
+        assert(!OpenSSL::SSL.verify_certificate_identity(cert, "foo.example.com"))
+      }
+    }
+  end
+
+  def test_verify_certificate_identity
+    [true, false].each do |criticality|
+      cert = create_null_byte_SAN_certificate(criticality)
+      assert_equal(false, OpenSSL::SSL.verify_certificate_identity(cert, 'www.example.com'))
+      assert_equal(true,  OpenSSL::SSL.verify_certificate_identity(cert, "www.example.com\0.evil.com"))
+      assert_equal(false, OpenSSL::SSL.verify_certificate_identity(cert, '192.168.7.255'))
+      assert_equal(true,  OpenSSL::SSL.verify_certificate_identity(cert, '192.168.7.1'))
+      assert_equal(false, OpenSSL::SSL.verify_certificate_identity(cert, '13::17'))
+      assert_equal(true,  OpenSSL::SSL.verify_certificate_identity(cert, '13:0:0:0:0:0:0:17'))
+    end
+  end
+
+  def test_verify_hostname
+    assert_equal(true,  OpenSSL::SSL.verify_hostname("www.example.com", "*.example.com"))
+    assert_equal(false, OpenSSL::SSL.verify_hostname("www.subdomain.example.com", "*.example.com"))
+  end
+
+  def test_verify_wildcard
+    assert_equal(false, OpenSSL::SSL.verify_wildcard("foo", "x*"))
+    assert_equal(true,  OpenSSL::SSL.verify_wildcard("foo", "foo"))
+    assert_equal(true,  OpenSSL::SSL.verify_wildcard("foo", "f*"))
+    assert_equal(true,  OpenSSL::SSL.verify_wildcard("foo", "*"))
+    assert_equal(false, OpenSSL::SSL.verify_wildcard("abc*bcd", "abcd"))
+    assert_equal(false, OpenSSL::SSL.verify_wildcard("xn--qdk4b9b", "x*"))
+    assert_equal(false, OpenSSL::SSL.verify_wildcard("xn--qdk4b9b", "*--qdk4b9b"))
+    assert_equal(true,  OpenSSL::SSL.verify_wildcard("xn--qdk4b9b", "xn--qdk4b9b"))
+  end
+
+  # Comments in this test is excerpted from http://tools.ietf.org/html/rfc6125#page-27
+  def test_post_connection_check_wildcard_san
+    # case-insensitive ASCII comparison
+    # RFC 6125, section 6.4.1
+    #
+    # "..matching of the reference identifier against the presented identifier
+    # is performed by comparing the set of domain name labels using a
+    # case-insensitive ASCII comparison, as clarified by [DNS-CASE] (e.g.,
+    # "WWW.Example.Com" would be lower-cased to "www.example.com" for
+    # comparison purposes)
+    assert_equal(true, OpenSSL::SSL.verify_certificate_identity(
+      create_cert_with_san('DNS:*.example.com'), 'www.example.com'))
+    assert_equal(true, OpenSSL::SSL.verify_certificate_identity(
+      create_cert_with_san('DNS:*.Example.COM'), 'www.example.com'))
+    assert_equal(true, OpenSSL::SSL.verify_certificate_identity(
+      create_cert_with_san('DNS:*.example.com'), 'WWW.Example.COM'))
+    # 1.  The client SHOULD NOT attempt to match a presented identifier in
+    #     which the wildcard character comprises a label other than the
+    #     left-most label (e.g., do not match bar.*.example.net).
+    assert_equal(false, OpenSSL::SSL.verify_certificate_identity(
+      create_cert_with_san('DNS:www.*.com'), 'www.example.com'))
+    # 2.  If the wildcard character is the only character of the left-most
+    #     label in the presented identifier, the client SHOULD NOT compare
+    #     against anything but the left-most label of the reference
+    #     identifier (e.g., *.example.com would match foo.example.com but
+    #     not bar.foo.example.com or example.com).
+    assert_equal(true, OpenSSL::SSL.verify_certificate_identity(
+      create_cert_with_san('DNS:*.example.com'), 'foo.example.com'))
+    assert_equal(false, OpenSSL::SSL.verify_certificate_identity(
+      create_cert_with_san('DNS:*.example.com'), 'bar.foo.example.com'))
+    # 3.  The client MAY match a presented identifier in which the wildcard
+    #     character is not the only character of the label (e.g.,
+    #     baz*.example.net and *baz.example.net and b*z.example.net would
+    #     be taken to match baz1.example.net and foobaz.example.net and
+    #     buzz.example.net, respectively).  ...
+    assert_equal(true, OpenSSL::SSL.verify_certificate_identity(
+      create_cert_with_san('DNS:baz*.example.com'), 'baz1.example.com'))
+    assert_equal(true, OpenSSL::SSL.verify_certificate_identity(
+      create_cert_with_san('DNS:*baz.example.com'), 'foobaz.example.com'))
+    assert_equal(true, OpenSSL::SSL.verify_certificate_identity(
+      create_cert_with_san('DNS:b*z.example.com'), 'buzz.example.com'))
+    # Section 6.4.3 of RFC6125 states that client should NOT match identifier
+    # where wildcard is other than left-most label.
+    #
+    # Also implicitly mentions the wildcard character only in singular form,
+    # and discourages matching against more than one wildcard.
+    #
+    # See RFC 6125, section 7.2, subitem 2.
+    assert_equal(false, OpenSSL::SSL.verify_certificate_identity(
+      create_cert_with_san('DNS:*b*.example.com'), 'abc.example.com'))
+    assert_equal(false, OpenSSL::SSL.verify_certificate_identity(
+      create_cert_with_san('DNS:*b*.example.com'), 'ab.example.com'))
+    assert_equal(false, OpenSSL::SSL.verify_certificate_identity(
+      create_cert_with_san('DNS:*b*.example.com'), 'bc.example.com'))
+    #                                ...  However, the client SHOULD NOT
+    #   attempt to match a presented identifier where the wildcard
+    #   character is embedded within an A-label or U-label [IDNA-DEFS] of
+    #   an internationalized domain name [IDNA-PROTO].
+    assert_equal(true, OpenSSL::SSL.verify_certificate_identity(
+      create_cert_with_san('DNS:xn*.example.com'), 'xn1ca.example.com'))
+    # part of A-label
+    assert_equal(false, OpenSSL::SSL.verify_certificate_identity(
+      create_cert_with_san('DNS:xn--*.example.com'), 'xn--1ca.example.com'))
+    # part of U-label
+    # dNSName in RFC5280 is an IA5String so U-label should NOT be allowed
+    # regardless of wildcard.
+    #
+    # See Section 7.2 of RFC 5280:
+    #   IA5String is limited to the set of ASCII characters.
+    assert_equal(false, OpenSSL::SSL.verify_certificate_identity(
+      create_cert_with_san('DNS:á*.example.com'), 'á1.example.com'))
+  end
+
+  def test_post_connection_check_wildcard_cn
+    assert_equal(true, OpenSSL::SSL.verify_certificate_identity(
+      create_cert_with_name('*.example.com'), 'www.example.com'))
+    assert_equal(true, OpenSSL::SSL.verify_certificate_identity(
+      create_cert_with_name('*.Example.COM'), 'www.example.com'))
+    assert_equal(true, OpenSSL::SSL.verify_certificate_identity(
+      create_cert_with_name('*.example.com'), 'WWW.Example.COM'))
+    assert_equal(false, OpenSSL::SSL.verify_certificate_identity(
+      create_cert_with_name('www.*.com'), 'www.example.com'))
+    assert_equal(true, OpenSSL::SSL.verify_certificate_identity(
+      create_cert_with_name('*.example.com'), 'foo.example.com'))
+    assert_equal(false, OpenSSL::SSL.verify_certificate_identity(
+      create_cert_with_name('*.example.com'), 'bar.foo.example.com'))
+    assert_equal(true, OpenSSL::SSL.verify_certificate_identity(
+      create_cert_with_name('baz*.example.com'), 'baz1.example.com'))
+    assert_equal(true, OpenSSL::SSL.verify_certificate_identity(
+      create_cert_with_name('*baz.example.com'), 'foobaz.example.com'))
+    assert_equal(true, OpenSSL::SSL.verify_certificate_identity(
+      create_cert_with_name('b*z.example.com'), 'buzz.example.com'))
+    # Section 6.4.3 of RFC6125 states that client should NOT match identifier
+    # where wildcard is other than left-most label.
+    #
+    # Also implicitly mentions the wildcard character only in singular form,
+    # and discourages matching against more than one wildcard.
+    #
+    # See RFC 6125, section 7.2, subitem 2.
+    assert_equal(false, OpenSSL::SSL.verify_certificate_identity(
+      create_cert_with_name('*b*.example.com'), 'abc.example.com'))
+    assert_equal(false, OpenSSL::SSL.verify_certificate_identity(
+      create_cert_with_name('*b*.example.com'), 'ab.example.com'))
+    assert_equal(false, OpenSSL::SSL.verify_certificate_identity(
+      create_cert_with_name('*b*.example.com'), 'bc.example.com'))
+    assert_equal(true, OpenSSL::SSL.verify_certificate_identity(
+      create_cert_with_name('xn*.example.com'), 'xn1ca.example.com'))
+    assert_equal(false, OpenSSL::SSL.verify_certificate_identity(
+      create_cert_with_name('xn--*.example.com'), 'xn--1ca.example.com'))
+    # part of U-label
+    # Subject in RFC5280 states case-insensitive ASCII comparison.
+    #
+    # See Section 7.2 of RFC 5280:
+    #   IA5String is limited to the set of ASCII characters.
+    assert_equal(false, OpenSSL::SSL.verify_certificate_identity(
+      create_cert_with_name('á*.example.com'), 'á1.example.com'))
+  end
+
+  def create_cert_with_san(san)
+    ef = OpenSSL::X509::ExtensionFactory.new
+    cert = OpenSSL::X509::Certificate.new
+    cert.subject = OpenSSL::X509::Name.parse("/DC=some/DC=site/CN=Some Site")
+    ext = ef.create_ext('subjectAltName', san)
+    cert.add_extension(ext)
+    cert
+  end
+
+  def create_cert_with_name(name)
+    cert = OpenSSL::X509::Certificate.new
+    cert.subject = OpenSSL::X509::Name.new([['DC', 'some'], ['DC', 'site'], ['CN', name]])
+    cert
+  end
+
+
+  # Create NULL byte SAN certificate
+  def create_null_byte_SAN_certificate(critical = false)
+    ef = OpenSSL::X509::ExtensionFactory.new
+    cert = OpenSSL::X509::Certificate.new
+    cert.subject = OpenSSL::X509::Name.parse "/DC=some/DC=site/CN=Some Site"
+    ext = ef.create_ext('subjectAltName', 'DNS:placeholder,IP:192.168.7.1,IP:13::17', critical)
+    ext_asn1 = OpenSSL::ASN1.decode(ext.to_der)
+    san_list_der = ext_asn1.value.reduce(nil) { |memo,val| val.tag == 4 ? val.value : memo }
+    san_list_asn1 = OpenSSL::ASN1.decode(san_list_der)
+    san_list_asn1.value[0].value = "www.example.com\0.evil.com"
+    pos = critical ? 2 : 1
+    ext_asn1.value[pos].value = san_list_asn1.to_der
+    real_ext = OpenSSL::X509::Extension.new ext_asn1
+    cert.add_extension(real_ext)
+    cert
+  end
+
+  def test_tlsext_hostname
+    return unless OpenSSL::SSL::SSLSocket.instance_methods.include?(:hostname)
+
+    ctx_proc = Proc.new do |ctx, ssl|
+      foo_ctx = ctx.dup
+
+      ctx.servername_cb = Proc.new do |ssl2, hostname|
+        case hostname
+        when 'foo.example.com'
+          foo_ctx
+        when 'bar.example.com'
+          nil
+        else
+          raise "unknown hostname #{hostname.inspect}"
+        end
+      end
+    end
+
+    server_proc = Proc.new do |ctx, ssl|
+      readwrite_loop(ctx, ssl)
+    end
+
+    start_server(OpenSSL::SSL::VERIFY_NONE, true, :ctx_proc => ctx_proc, :server_proc => server_proc) do |server, port|
+      2.times do |i|
+        ctx = OpenSSL::SSL::SSLContext.new
+        if defined?(OpenSSL::SSL::OP_NO_TICKET)
+          # disable RFC4507 support
+          ctx.options = OpenSSL::SSL::OP_NO_TICKET
+        end
+        server_connect(port, ctx) { |ssl|
+          ssl.hostname = (i & 1 == 0) ? 'foo.example.com' : 'bar.example.com'
+          str = "x" * 100 + "\n"
+          ssl.puts(str)
+          assert_equal(str, ssl.gets)
+        }
+      end
+    end
+  end
+
+  def test_multibyte_read_write
+    #German a umlaut
+    auml = [%w{ C3 A4 }.join('')].pack('H*')
+    auml.force_encoding(Encoding::UTF_8)
+
+    [10, 1000, 100000].each {|i|
+      str = nil
+      num_written = nil
+      server_proc = Proc.new {|ctx, ssl|
+        cmp = ssl.read
+        raw_size = cmp.size
+        cmp.force_encoding(Encoding::UTF_8)
+        assert_equal(str, cmp)
+        assert_equal(num_written, raw_size)
+        ssl.close
+      }
+      start_server(OpenSSL::SSL::VERIFY_NONE, true, :server_proc => server_proc){|server, port|
+        server_connect(port) { |ssl|
+          str = auml * i
+          num_written = ssl.write(str)
+        }
+      }
+    }
+  end
+
+  def test_unset_OP_ALL
+    ctx_proc = Proc.new { |ctx|
+      # If OP_DONT_INSERT_EMPTY_FRAGMENTS is not defined, this test is
+      # redundant because the default options already are equal to OP_ALL.
+      # But it also degrades gracefully, so keep it
+      ctx.options = OpenSSL::SSL::OP_ALL
+    }
+    start_server(OpenSSL::SSL::VERIFY_NONE, true, :ctx_proc => ctx_proc){|server, port|
+      server_connect(port) { |ssl|
+        ssl.puts('hello')
+        assert_equal("hello\n", ssl.gets)
+      }
+    }
+  end
+
+  # different OpenSSL versions react differently when facing a SSL/TLS version
+  # that has been marked as forbidden, therefore either of these may be raised
+  HANDSHAKE_ERRORS = [OpenSSL::SSL::SSLError, Errno::ECONNRESET]
+
+if OpenSSL::SSL::SSLContext::METHODS.include? :TLSv1
+
+  def test_forbid_ssl_v3_for_client
+    ctx_proc = Proc.new { |ctx| ctx.options = OpenSSL::SSL::OP_ALL | OpenSSL::SSL::OP_NO_SSLv3 }
+    start_server_version(:SSLv23, ctx_proc) { |server, port|
+      ctx = OpenSSL::SSL::SSLContext.new
+      ctx.ssl_version = :SSLv3
+      assert_raise(*HANDSHAKE_ERRORS) { server_connect(port, ctx) }
+    }
+  end
+
+  def test_forbid_ssl_v3_from_server
+    start_server_version(:SSLv3) { |server, port|
+      ctx = OpenSSL::SSL::SSLContext.new
+      ctx.options = OpenSSL::SSL::OP_ALL | OpenSSL::SSL::OP_NO_SSLv3
+      assert_raise(*HANDSHAKE_ERRORS) { server_connect(port, ctx) }
+    }
+  end
+
+end
+
+if OpenSSL::SSL::SSLContext::METHODS.include? :TLSv1_1
+
+  def test_tls_v1_1
+    start_server_version(:TLSv1_1) { |server, port|
+      server_connect(port) { |ssl| assert_equal("TLSv1.1", ssl.ssl_version) }
+    }
+  end
+
+  def test_forbid_tls_v1_for_client
+    ctx_proc = Proc.new { |ctx| ctx.options = OpenSSL::SSL::OP_ALL | OpenSSL::SSL::OP_NO_TLSv1 }
+    start_server_version(:SSLv23, ctx_proc) { |server, port|
+      ctx = OpenSSL::SSL::SSLContext.new
+      ctx.ssl_version = :TLSv1
+      assert_raise(*HANDSHAKE_ERRORS) { server_connect(port, ctx) }
+    }
+  end
+
+  def test_forbid_tls_v1_from_server
+    start_server_version(:TLSv1) { |server, port|
+      ctx = OpenSSL::SSL::SSLContext.new
+      ctx.options = OpenSSL::SSL::OP_ALL | OpenSSL::SSL::OP_NO_TLSv1
+      assert_raise(*HANDSHAKE_ERRORS) { server_connect(port, ctx) }
+    }
+  end
+
+end
+
+if OpenSSL::SSL::SSLContext::METHODS.include? :TLSv1_2
+
+  def test_tls_v1_2
+    start_server_version(:TLSv1_2) { |server, port|
+      ctx = OpenSSL::SSL::SSLContext.new
+      ctx.ssl_version = :TLSv1_2_client
+      server_connect(port, ctx) { |ssl| assert_equal("TLSv1.2", ssl.ssl_version) }
+    }
+  end if OpenSSL::OPENSSL_VERSION_NUMBER > 0x10001000
+
+  def test_forbid_tls_v1_1_for_client
+    ctx_proc = Proc.new { |ctx| ctx.options = OpenSSL::SSL::OP_ALL | OpenSSL::SSL::OP_NO_TLSv1_1 }
+    start_server_version(:SSLv23, ctx_proc) { |server, port|
+      ctx = OpenSSL::SSL::SSLContext.new
+      ctx.ssl_version = :TLSv1_1
+      assert_raise(*HANDSHAKE_ERRORS) { server_connect(port, ctx) }
+    }
+  end if defined?(OpenSSL::SSL::OP_NO_TLSv1_1)
+
+  def test_forbid_tls_v1_1_from_server
+    start_server_version(:TLSv1_1) { |server, port|
+      ctx = OpenSSL::SSL::SSLContext.new
+      ctx.options = OpenSSL::SSL::OP_ALL | OpenSSL::SSL::OP_NO_TLSv1_1
+      assert_raise(*HANDSHAKE_ERRORS) { server_connect(port, ctx) }
+    }
+  end if defined?(OpenSSL::SSL::OP_NO_TLSv1_1)
+
+  def test_forbid_tls_v1_2_for_client
+    ctx_proc = Proc.new { |ctx| ctx.options = OpenSSL::SSL::OP_ALL | OpenSSL::SSL::OP_NO_TLSv1_2 }
+    start_server_version(:SSLv23, ctx_proc) { |server, port|
+      ctx = OpenSSL::SSL::SSLContext.new
+      ctx.ssl_version = :TLSv1_2
+      assert_raise(*HANDSHAKE_ERRORS) { server_connect(port, ctx) }
+    }
+  end if defined?(OpenSSL::SSL::OP_NO_TLSv1_2)
+
+  def test_forbid_tls_v1_2_from_server
+    start_server_version(:TLSv1_2) { |server, port|
+      ctx = OpenSSL::SSL::SSLContext.new
+      ctx.options = OpenSSL::SSL::OP_ALL | OpenSSL::SSL::OP_NO_TLSv1_2
+      assert_raise(*HANDSHAKE_ERRORS) { server_connect(port, ctx) }
+    }
+  end if defined?(OpenSSL::SSL::OP_NO_TLSv1_2)
+
+end
+
+  def test_renegotiation_cb
+    num_handshakes = 0
+    renegotiation_cb = Proc.new { |ssl| num_handshakes += 1 }
+    ctx_proc = Proc.new { |ctx| ctx.renegotiation_cb = renegotiation_cb }
+    start_server_version(:SSLv23, ctx_proc) { |server, port|
+      server_connect(port) { |ssl|
+        assert_equal(1, num_handshakes)
+      }
+    }
+  end
+
+if OpenSSL::OPENSSL_VERSION_NUMBER > 0x10001000
+
+  def test_npn_protocol_selection_ary
+    advertised = ["http/1.1", "spdy/2"]
+    ctx_proc = Proc.new { |ctx| ctx.npn_protocols = advertised }
+    start_server_version(:SSLv23, ctx_proc) { |server, port|
+      selector = lambda { |which|
+        ctx = OpenSSL::SSL::SSLContext.new
+        ctx.npn_select_cb = -> (protocols) { protocols.send(which) }
+        server_connect(port, ctx) { |ssl|
+          assert_equal(advertised.send(which), ssl.npn_protocol)
+        }
+      }
+      selector.call(:first)
+      selector.call(:last)
+    }
+  end
+
+  def test_npn_protocol_selection_enum
+    advertised = Object.new
+    def advertised.each
+      yield "http/1.1"
+      yield "spdy/2"
+    end
+    ctx_proc = Proc.new { |ctx| ctx.npn_protocols = advertised }
+    start_server_version(:SSLv23, ctx_proc) { |server, port|
+      selector = lambda { |selected, which|
+        ctx = OpenSSL::SSL::SSLContext.new
+        ctx.npn_select_cb = -> (protocols) { protocols.to_a.send(which) }
+        server_connect(port, ctx) { |ssl|
+          assert_equal(selected, ssl.npn_protocol)
+        }
+      }
+      selector.call("http/1.1", :first)
+      selector.call("spdy/2", :last)
+    }
+  end
+
+  def test_npn_protocol_selection_cancel
+    ctx_proc = Proc.new { |ctx| ctx.npn_protocols = ["http/1.1"] }
+    start_server_version(:SSLv23, ctx_proc) { |server, port|
+      ctx = OpenSSL::SSL::SSLContext.new
+      ctx.npn_select_cb = -> (protocols) { raise RuntimeError.new }
+      assert_raise(RuntimeError) { server_connect(port, ctx) }
+    }
+  end
+
+  def test_npn_advertised_protocol_too_long
+    ctx_proc = Proc.new { |ctx| ctx.npn_protocols = ["a" * 256] }
+    start_server_version(:SSLv23, ctx_proc) { |server, port|
+      ctx = OpenSSL::SSL::SSLContext.new
+      ctx.npn_select_cb = -> (protocols) { protocols.first }
+      assert_raise(*HANDSHAKE_ERRORS) { server_connect(port, ctx) }
+    }
+  end
+
+  def test_npn_selected_protocol_too_long
+    ctx_proc = Proc.new { |ctx| ctx.npn_protocols = ["http/1.1"] }
+    start_server_version(:SSLv23, ctx_proc) { |server, port|
+      ctx = OpenSSL::SSL::SSLContext.new
+      ctx.npn_select_cb = -> (protocols) { "a" * 256 }
+      assert_raise(*HANDSHAKE_ERRORS) { server_connect(port, ctx) }
+    }
+  end
+
+end
+
+  def test_invalid_shutdown_by_gc
+    assert_nothing_raised {
+      start_server(OpenSSL::SSL::VERIFY_NONE, true){|server, port|
+        10.times {
+          sock = TCPSocket.new("127.0.0.1", port)
+          ssl = OpenSSL::SSL::SSLSocket.new(sock)
+          GC.start
+          ssl.connect
+          sock.close
+        }
+      }
+    }
+  end
+
+  def test_close_after_socket_close
+    start_server(OpenSSL::SSL::VERIFY_NONE, true){|server, port|
+      sock = TCPSocket.new("127.0.0.1", port)
+      ssl = OpenSSL::SSL::SSLSocket.new(sock)
+      ssl.sync_close = true
+      ssl.connect
+      sock.close
+      assert_nothing_raised do
+        ssl.close
+      end
+    }
+  end
+
+  def test_sync_close_without_connect
+    Socket.open(:INET, :STREAM) {|s|
+      ssl = OpenSSL::SSL::SSLSocket.new(s)
+      ssl.sync_close = true
+      ssl.close
+      assert(s.closed?)
+    }
+  end
+
+  private
+
+  def start_server_version(version, ctx_proc=nil, server_proc=nil, &blk)
+    ctx_wrap = Proc.new { |ctx|
+      ctx.ssl_version = version
+      ctx_proc.call(ctx) if ctx_proc
+    }
+    start_server(
+      OpenSSL::SSL::VERIFY_NONE,
+      true,
+      :ctx_proc => ctx_wrap,
+      :server_proc => server_proc,
+      :ignore_listener_error => true,
+      &blk
+    )
+  end
+
+  def server_connect(port, ctx=nil)
+    sock = TCPSocket.new("127.0.0.1", port)
+    ssl = ctx ? OpenSSL::SSL::SSLSocket.new(sock, ctx) : OpenSSL::SSL::SSLSocket.new(sock)
+    ssl.sync_close = true
+    ssl.connect
+    yield ssl if block_given?
+  ensure
+    if ssl
+      ssl.close
+    elsif sock
+      sock.close
+    end
+  end
+end
+
+end
diff --git a/jni/ruby/test/openssl/test_ssl_session.rb b/jni/ruby/test/openssl/test_ssl_session.rb
new file mode 100644
index 0000000..d4c2220
--- /dev/null
+++ b/jni/ruby/test/openssl/test_ssl_session.rb
@@ -0,0 +1,380 @@
+require_relative "utils"
+
+if defined?(OpenSSL::TestUtils)
+
+class OpenSSL::TestSSLSession < OpenSSL::SSLTestCase
+  def test_session_equals
+    session = OpenSSL::SSL::Session.new <<-SESSION
+-----BEGIN SSL SESSION PARAMETERS-----
+MIIDFgIBAQICAwEEAgA5BCCY3pW6iTkPoD5SENuztz/gZjhvey6XnHbsxd22k0Ol
+dgQw8uaN3hCRnlhoIKPWInCFzrp/tQsDRFs9jDjc9pwpy/oKHmJdQQMQA1g8FYnO
+gpdVoQYCBE52ikKiBAICASyjggKOMIICijCCAXKgAwIBAgIBAjANBgkqhkiG9w0B
+AQUFADA9MRMwEQYKCZImiZPyLGQBGRYDb3JnMRkwFwYKCZImiZPyLGQBGRYJcnVi
+eS1sYW5nMQswCQYDVQQDDAJDQTAeFw0xMTA5MTkwMDE4MTBaFw0xMTA5MTkwMDQ4
+MTBaMEQxEzARBgoJkiaJk/IsZAEZFgNvcmcxGTAXBgoJkiaJk/IsZAEZFglydWJ5
+LWxhbmcxEjAQBgNVBAMMCWxvY2FsaG9zdDCBnzANBgkqhkiG9w0BAQEFAAOBjQAw
+gYkCgYEAy8LEsNRApz7U/j5DoB4XBgO9Z8Atv5y/OVQRp0ag8Tqo1YewsWijxEWB
+7JOATwpBN267U4T1nPZIxxEEO7n/WNa2ws9JWsjah8ssEBFSxZqdXKSLf0N4Hi7/
+GQ/aYoaMCiQ8jA4jegK2FJmXM71uPe+jFN/peeBOpRfyXxRFOYcCAwEAAaMSMBAw
+DgYDVR0PAQH/BAQDAgWgMA0GCSqGSIb3DQEBBQUAA4IBAQARC7GP7InX1t7VEXz2
+I8RI57S0/HSJL4fDIYP3zFpitHX1PZeo+7XuzMilvPjjBo/ky9Jzo8TYiY+N+JEz
+mY/A/zPA4ZsJ7KYj6/FEdIc/vRlS0CvsbClbNjw1jl/PoB2FLr2b3uuBcZEsyZeP
+yq154ijq37Ajf8K5Mi5FgshoP41BPtRPj+VVf61rv1IcEnNWdDCS6DR4XsaNC+zt
+G6AqCqkytIXWRuDw6n6vYLF3A/tn2sldLo7/scY0PMDNbo63O/LTxkDHmPhSkD68
+8m9SsMeTR+RCiDEZWFPVcAH/8mDfi+5k8uN3qS+gOU/PPrmHGgl5ykiSFgqs4v61
+tddwpBAEDjcwMzA5NTYzMTU1MzAwpQMCARM=
+-----END SSL SESSION PARAMETERS-----
+    SESSION
+
+    start_server(OpenSSL::SSL::VERIFY_NONE, true, :ignore_listener_error => true) { |_, port|
+      ctx = OpenSSL::SSL::SSLContext.new
+      ctx.session_cache_mode = OpenSSL::SSL::SSLContext::SESSION_CACHE_CLIENT
+      ctx.session_id_context = self.object_id.to_s
+
+      sock = TCPSocket.new '127.0.0.1', port
+      begin
+        ssl = OpenSSL::SSL::SSLSocket.new sock, ctx
+        ssl.session = session
+
+        assert_equal session, ssl.session
+      ensure
+        sock.close
+      end
+    }
+  end
+
+  def test_session
+    timeout(5) do
+      start_server(OpenSSL::SSL::VERIFY_NONE, true) do |server, port|
+        sock = TCPSocket.new("127.0.0.1", port)
+        ctx = OpenSSL::SSL::SSLContext.new("TLSv1")
+        ssl = OpenSSL::SSL::SSLSocket.new(sock, ctx)
+        ssl.sync_close = true
+        ssl.connect
+        session = ssl.session
+        assert(session == OpenSSL::SSL::Session.new(session.to_pem))
+        assert(session == OpenSSL::SSL::Session.new(ssl))
+        assert_equal(300, session.timeout)
+        session.timeout = 5
+        assert_equal(5, session.timeout)
+        assert_not_nil(session.time)
+        # SSL_SESSION_time keeps long value so we can't keep nsec fragment.
+        session.time = t1 = Time.now.to_i
+        assert_equal(Time.at(t1), session.time)
+        if session.respond_to?(:id)
+          assert_not_nil(session.id)
+        end
+        pem = session.to_pem
+        assert_match(/\A-----BEGIN SSL SESSION PARAMETERS-----/, pem)
+        assert_match(/-----END SSL SESSION PARAMETERS-----\Z/, pem)
+        pem.gsub!(/-----(BEGIN|END) SSL SESSION PARAMETERS-----/, '').gsub!(/[\r\n]+/m, '')
+        assert_equal(session.to_der, pem.unpack('m*')[0])
+        assert_not_nil(session.to_text)
+        ssl.close
+      end
+    end
+  end
+
+  DUMMY_SESSION = <<__EOS__
+-----BEGIN SSL SESSION PARAMETERS-----
+MIIDzQIBAQICAwEEAgA5BCAF219w9ZEV8dNA60cpEGOI34hJtIFbf3bkfzSgMyad
+MQQwyGLbkCxE4OiMLdKKem+pyh8V7ifoP7tCxhdmwoDlJxI1v6nVCjai+FGYuncy
+NNSWoQYCBE4DDWuiAwIBCqOCAo4wggKKMIIBcqADAgECAgECMA0GCSqGSIb3DQEB
+BQUAMD0xEzARBgoJkiaJk/IsZAEZFgNvcmcxGTAXBgoJkiaJk/IsZAEZFglydWJ5
+LWxhbmcxCzAJBgNVBAMMAkNBMB4XDTExMDYyMzA5NTQ1MVoXDTExMDYyMzEwMjQ1
+MVowRDETMBEGCgmSJomT8ixkARkWA29yZzEZMBcGCgmSJomT8ixkARkWCXJ1Ynkt
+bGFuZzESMBAGA1UEAwwJbG9jYWxob3N0MIGfMA0GCSqGSIb3DQEBAQUAA4GNADCB
+iQKBgQDLwsSw1ECnPtT+PkOgHhcGA71nwC2/nL85VBGnRqDxOqjVh7CxaKPERYHs
+k4BPCkE3brtThPWc9kjHEQQ7uf9Y1rbCz0layNqHyywQEVLFmp1cpIt/Q3geLv8Z
+D9pihowKJDyMDiN6ArYUmZczvW4976MU3+l54E6lF/JfFEU5hwIDAQABoxIwEDAO
+BgNVHQ8BAf8EBAMCBaAwDQYJKoZIhvcNAQEFBQADggEBACj5WhoZ/ODVeHpwgq1d
+8fW/13ICRYHYpv6dzlWihyqclGxbKMlMnaVCPz+4JaVtMz3QB748KJQgL3Llg3R1
+ek+f+n1MBCMfFFsQXJ2gtLB84zD6UCz8aaCWN5/czJCd7xMz7fRLy3TOIW5boXAU
+zIa8EODk+477K1uznHm286ab0Clv+9d304hwmBZgkzLg6+31Of6d6s0E0rwLGiS2
+sOWYg34Y3r4j8BS9Ak4jzpoLY6cJ0QAKCOJCgmjGr4XHpyXMLbicp3ga1uSbwtVO
+gF/gTfpLhJC+y0EQ5x3Ftl88Cq7ZJuLBDMo/TLIfReJMQu/HlrTT7+LwtneSWGmr
+KkSkAgQApQMCAROqgcMEgcAuDkAVfj6QAJMz9yqTzW5wPFyty7CxUEcwKjUqj5UP
+/Yvky1EkRuM/eQfN7ucY+MUvMqv+R8ZSkHPsnjkBN5ChvZXjrUSZKFVjR4eFVz2V
+jismLEJvIFhQh6pqTroRrOjMfTaM5Lwoytr2FTGobN9rnjIRsXeFQW1HLFbXn7Dh
+8uaQkMwIVVSGRB8T7t6z6WIdWruOjCZ6G5ASI5XoqAHwGezhLodZuvJEfsVyCF9y
+j+RBGfCFrrQbBdnkFI/ztgM=
+-----END SSL SESSION PARAMETERS-----
+__EOS__
+
+  DUMMY_SESSION_NO_EXT = <<-__EOS__
+-----BEGIN SSL SESSION PARAMETERS-----
+MIIDCAIBAQICAwAEAgA5BCDyAW7rcpzMjDSosH+Tv6sukymeqgq3xQVVMez628A+
+lAQw9TrKzrIqlHEh6ltuQaqv/Aq83AmaAlogYktZgXAjOGnhX7ifJDNLMuCfQq53
+hPAaoQYCBE4iDeeiBAICASyjggKOMIICijCCAXKgAwIBAgIBAjANBgkqhkiG9w0B
+AQUFADA9MRMwEQYKCZImiZPyLGQBGRYDb3JnMRkwFwYKCZImiZPyLGQBGRYJcnVi
+eS1sYW5nMQswCQYDVQQDDAJDQTAeFw0xMTA3MTYyMjE3MTFaFw0xMTA3MTYyMjQ3
+MTFaMEQxEzARBgoJkiaJk/IsZAEZFgNvcmcxGTAXBgoJkiaJk/IsZAEZFglydWJ5
+LWxhbmcxEjAQBgNVBAMMCWxvY2FsaG9zdDCBnzANBgkqhkiG9w0BAQEFAAOBjQAw
+gYkCgYEAy8LEsNRApz7U/j5DoB4XBgO9Z8Atv5y/OVQRp0ag8Tqo1YewsWijxEWB
+7JOATwpBN267U4T1nPZIxxEEO7n/WNa2ws9JWsjah8ssEBFSxZqdXKSLf0N4Hi7/
+GQ/aYoaMCiQ8jA4jegK2FJmXM71uPe+jFN/peeBOpRfyXxRFOYcCAwEAAaMSMBAw
+DgYDVR0PAQH/BAQDAgWgMA0GCSqGSIb3DQEBBQUAA4IBAQA3TRzABRG3kz8jEEYr
+tDQqXgsxwTsLhTT5d1yF0D8uFw+y15hJAJnh6GJHjqhWBrF4zNoTApFo+4iIL6g3
+q9C3mUsxIVAHx41DwZBh/FI7J4FqlAoGOguu7892CNVY3ZZjc3AXMTdKjcNoWPzz
+FCdj5fNT24JMMe+ZdGZK97ChahJsdn/6B3j6ze9NK9mfYEbiJhejGTPLOFVHJCGR
+KYYZ3ZcKhLDr9ql4d7cCo1gBtemrmFQGPui7GttNEqmXqUKvV8mYoa8farf5i7T4
+L6a/gp2cVZTaDIS1HjbJsA/Ag7AajZqiN6LfqShNUVsrMZ+5CoV8EkBDTZPJ9MSr
+a3EqpAIEAKUDAgET
+-----END SSL SESSION PARAMETERS-----
+__EOS__
+
+
+  def test_session_time
+    sess = OpenSSL::SSL::Session.new(DUMMY_SESSION_NO_EXT)
+    sess.time = (now = Time.now)
+    assert_equal(now.to_i, sess.time.to_i)
+    sess.time = 1
+    assert_equal(1, sess.time.to_i)
+    sess.time = 1.2345
+    assert_equal(1, sess.time.to_i)
+    # Can OpenSSL handle t>2038y correctly? Version?
+    sess.time = 2**31 - 1
+    assert_equal(2**31 - 1, sess.time.to_i)
+  end
+
+  def test_session_timeout
+    sess = OpenSSL::SSL::Session.new(DUMMY_SESSION_NO_EXT)
+    assert_raise(TypeError) do
+      sess.timeout = Time.now
+    end
+    sess.timeout = 1
+    assert_equal(1, sess.timeout.to_i)
+    sess.timeout = 1.2345
+    assert_equal(1, sess.timeout.to_i)
+    sess.timeout = 2**31 - 1
+    assert_equal(2**31 - 1, sess.timeout.to_i)
+  end
+
+  def test_session_exts_read
+    assert(OpenSSL::SSL::Session.new(DUMMY_SESSION))
+  end if OpenSSL::OPENSSL_VERSION_NUMBER >= 0x009080bf
+
+  def test_client_session
+    last_session = nil
+    start_server(OpenSSL::SSL::VERIFY_NONE, true) do |server, port|
+      2.times do
+        sock = TCPSocket.new("127.0.0.1", port)
+        # Debian's openssl 0.9.8g-13 failed at assert(ssl.session_reused?),
+        # when use default SSLContext. [ruby-dev:36167]
+        ctx = OpenSSL::SSL::SSLContext.new("TLSv1")
+        ssl = OpenSSL::SSL::SSLSocket.new(sock, ctx)
+        ssl.sync_close = true
+        ssl.session = last_session if last_session
+        ssl.connect
+
+        session = ssl.session
+        if last_session
+          assert(ssl.session_reused?)
+
+          if session.respond_to?(:id)
+            assert_equal(session.id, last_session.id)
+          end
+          assert_equal(session.to_pem, last_session.to_pem)
+          assert_equal(session.to_der, last_session.to_der)
+          # Older version of OpenSSL may not be consistent.  Look up which versions later.
+          assert_equal(session.to_text, last_session.to_text)
+        else
+          assert(!ssl.session_reused?)
+        end
+        last_session = session
+
+        str = "x" * 100 + "\n"
+        ssl.puts(str)
+        assert_equal(str, ssl.gets)
+
+        ssl.close
+      end
+    end
+  end
+
+  def test_server_session
+    connections = 0
+    saved_session = nil
+
+    ctx_proc = Proc.new do |ctx, ssl|
+# add test for session callbacks here
+    end
+
+    server_proc = Proc.new do |ctx, ssl|
+      session = ssl.session
+      stats = ctx.session_cache_stats
+
+      case connections
+      when 0
+        assert_equal(stats[:cache_num], 1)
+        assert_equal(stats[:cache_hits], 0)
+        assert_equal(stats[:cache_misses], 0)
+        assert(!ssl.session_reused?)
+      when 1
+        assert_equal(stats[:cache_num], 1)
+        assert_equal(stats[:cache_hits], 1)
+        assert_equal(stats[:cache_misses], 0)
+        assert(ssl.session_reused?)
+        ctx.session_remove(session)
+        saved_session = session
+      when 2
+        assert_equal(stats[:cache_num], 1)
+        assert_equal(stats[:cache_hits], 1)
+        assert_equal(stats[:cache_misses], 1)
+        assert(!ssl.session_reused?)
+        ctx.session_add(saved_session)
+      when 3
+        assert_equal(stats[:cache_num], 2)
+        assert_equal(stats[:cache_hits], 2)
+        assert_equal(stats[:cache_misses], 1)
+        assert(ssl.session_reused?)
+        ctx.flush_sessions(Time.now + 5000)
+      when 4
+        assert_equal(stats[:cache_num], 1)
+        assert_equal(stats[:cache_hits], 2)
+        assert_equal(stats[:cache_misses], 2)
+        assert(!ssl.session_reused?)
+        ctx.session_add(saved_session)
+      end
+      connections += 1
+
+      readwrite_loop(ctx, ssl)
+    end
+
+    first_session = nil
+    start_server(OpenSSL::SSL::VERIFY_NONE, true, :ctx_proc => ctx_proc, :server_proc => server_proc) do |server, port|
+      10.times do |i|
+        sock = TCPSocket.new("127.0.0.1", port)
+        ctx = OpenSSL::SSL::SSLContext.new
+        if defined?(OpenSSL::SSL::OP_NO_TICKET)
+          # disable RFC4507 support
+          ctx.options = OpenSSL::SSL::OP_NO_TICKET
+        end
+        ssl = OpenSSL::SSL::SSLSocket.new(sock, ctx)
+        ssl.sync_close = true
+        ssl.session = first_session if first_session
+        ssl.connect
+
+        session = ssl.session
+        if first_session
+          case i
+          when 1; assert(ssl.session_reused?)
+          when 2; assert(!ssl.session_reused?)
+          when 3; assert(ssl.session_reused?)
+          when 4; assert(!ssl.session_reused?)
+          when 5..10; assert(ssl.session_reused?)
+          end
+        end
+        first_session ||= session
+
+        str = "x" * 100 + "\n"
+        ssl.puts(str)
+        assert_equal(str, ssl.gets)
+
+        ssl.close
+      end
+    end
+  end
+
+  def test_ctx_client_session_cb
+    called = {}
+    ctx = OpenSSL::SSL::SSLContext.new("SSLv3")
+    ctx.session_cache_mode = OpenSSL::SSL::SSLContext::SESSION_CACHE_CLIENT
+
+    ctx.session_new_cb = lambda { |ary|
+      sock, sess = ary
+      called[:new] = [sock, sess]
+    }
+
+    ctx.session_remove_cb = lambda { |ary|
+      ctx, sess = ary
+      called[:remove] = [ctx, sess]
+      # any resulting value is OK (ignored)
+    }
+
+    start_server(OpenSSL::SSL::VERIFY_NONE, true) do |server, port|
+      sock = TCPSocket.new("127.0.0.1", port)
+      begin
+        ssl = OpenSSL::SSL::SSLSocket.new(sock, ctx)
+        ssl.sync_close = true
+        ssl.connect
+        assert_equal(1, ctx.session_cache_stats[:cache_num])
+        assert_equal(1, ctx.session_cache_stats[:connect_good])
+        assert_equal([ssl, ssl.session], called[:new])
+        assert(ctx.session_remove(ssl.session))
+        assert(!ctx.session_remove(ssl.session))
+        assert_equal([ctx, ssl.session], called[:remove])
+        ssl.close
+      ensure
+        sock.close if !sock.closed?
+      end
+    end
+  end
+
+  def test_ctx_server_session_cb
+    called = {}
+
+    ctx_proc = Proc.new { |ctx, ssl|
+      ctx.session_cache_mode = OpenSSL::SSL::SSLContext::SESSION_CACHE_SERVER
+      last_server_session = nil
+
+      # get_cb is called whenever a client proposed to resume a session but
+      # the session could not be found in the internal session cache.
+      ctx.session_get_cb = lambda { |ary|
+        sess, data = ary
+        if last_server_session
+          called[:get2] = [sess, data]
+          last_server_session
+        else
+          called[:get1] = [sess, data]
+          last_server_session = sess
+          nil
+        end
+      }
+
+      ctx.session_new_cb = lambda { |ary|
+        sock, sess = ary
+        called[:new] = [sock, sess]
+        # SSL server doesn't cache sessions so get_cb is called next time.
+        ctx.session_remove(sess)
+      }
+
+      ctx.session_remove_cb = lambda { |ary|
+        ctx, sess = ary
+        called[:remove] = [ctx, sess]
+      }
+    }
+
+    server_proc = Proc.new { |c, ssl|
+      ssl.session
+      c.session_cache_stats
+      readwrite_loop(c, ssl)
+    }
+    start_server(OpenSSL::SSL::VERIFY_NONE, true, :ctx_proc => ctx_proc, :server_proc => server_proc) do |server, port|
+      last_client_session = nil
+      3.times do
+        sock = TCPSocket.new("127.0.0.1", port)
+        begin
+          ssl = OpenSSL::SSL::SSLSocket.new(sock, OpenSSL::SSL::SSLContext.new("SSLv3"))
+          ssl.sync_close = true
+          ssl.session = last_client_session if last_client_session
+          ssl.connect
+          last_client_session = ssl.session
+          ssl.close
+          timeout(5) do
+            Thread.pass until called.key?(:new)
+            assert(called.delete(:new))
+            Thread.pass until called.key?(:remove)
+            assert(called.delete(:remove))
+          end
+        ensure
+          sock.close if !sock.closed?
+        end
+      end
+    end
+    assert(called[:get1])
+    assert(called[:get2])
+  end
+end
+
+end
diff --git a/jni/ruby/test/openssl/test_x509cert.rb b/jni/ruby/test/openssl/test_x509cert.rb
new file mode 100644
index 0000000..783677a
--- /dev/null
+++ b/jni/ruby/test/openssl/test_x509cert.rb
@@ -0,0 +1,226 @@
+require_relative "utils"
+
+if defined?(OpenSSL::TestUtils)
+
+class OpenSSL::TestX509Certificate < Test::Unit::TestCase
+  def setup
+    @rsa1024 = OpenSSL::TestUtils::TEST_KEY_RSA1024
+    @rsa2048 = OpenSSL::TestUtils::TEST_KEY_RSA2048
+    @dsa256  = OpenSSL::TestUtils::TEST_KEY_DSA256
+    @dsa512  = OpenSSL::TestUtils::TEST_KEY_DSA512
+    @ca = OpenSSL::X509::Name.parse("/DC=org/DC=ruby-lang/CN=CA")
+    @ee1 = OpenSSL::X509::Name.parse("/DC=org/DC=ruby-lang/CN=EE1")
+    @ee2 = OpenSSL::X509::Name.parse("/DC=org/DC=ruby-lang/CN=EE2")
+  end
+
+  def teardown
+  end
+
+  def issue_cert(*args)
+    OpenSSL::TestUtils.issue_cert(*args)
+  end
+
+  def test_serial
+    [1, 2**32, 2**100].each{|s|
+      cert = issue_cert(@ca, @rsa2048, s, Time.now, Time.now+3600, [],
+                        nil, nil, OpenSSL::Digest::SHA1.new)
+      assert_equal(s, cert.serial)
+      cert = OpenSSL::X509::Certificate.new(cert.to_der)
+      assert_equal(s, cert.serial)
+    }
+  end
+
+  def test_public_key
+    exts = [
+      ["basicConstraints","CA:TRUE",true],
+      ["subjectKeyIdentifier","hash",false],
+      ["authorityKeyIdentifier","keyid:always",false],
+    ]
+
+    sha1 = OpenSSL::Digest::SHA1.new
+    dsa_digest = OpenSSL::TestUtils::DSA_SIGNATURE_DIGEST.new
+
+    [
+      [@rsa1024, sha1], [@rsa2048, sha1], [@dsa256, dsa_digest], [@dsa512, dsa_digest]
+    ].each{|pk, digest|
+      cert = issue_cert(@ca, pk, 1, Time.now, Time.now+3600, exts,
+                        nil, nil, digest)
+      assert_equal(cert.extensions.sort_by(&:to_s)[2].value,
+                   OpenSSL::TestUtils.get_subject_key_id(cert))
+      cert = OpenSSL::X509::Certificate.new(cert.to_der)
+      assert_equal(cert.extensions.sort_by(&:to_s)[2].value,
+                   OpenSSL::TestUtils.get_subject_key_id(cert))
+    }
+  end
+
+  def test_validity
+    now = Time.now until now && now.usec != 0
+    cert = issue_cert(@ca, @rsa2048, 1, now, now+3600, [],
+                      nil, nil, OpenSSL::Digest::SHA1.new)
+    assert_not_equal(now, cert.not_before)
+    assert_not_equal(now+3600, cert.not_after)
+
+    now = Time.at(now.to_i)
+    cert = issue_cert(@ca, @rsa2048, 1, now, now+3600, [],
+                      nil, nil, OpenSSL::Digest::SHA1.new)
+    assert_equal(now.getutc, cert.not_before)
+    assert_equal((now+3600).getutc, cert.not_after)
+
+    now = Time.at(0)
+    cert = issue_cert(@ca, @rsa2048, 1, now, now, [],
+                      nil, nil, OpenSSL::Digest::SHA1.new)
+    assert_equal(now.getutc, cert.not_before)
+    assert_equal(now.getutc, cert.not_after)
+
+    now = Time.at(0x7fffffff)
+    cert = issue_cert(@ca, @rsa2048, 1, now, now, [],
+                      nil, nil, OpenSSL::Digest::SHA1.new)
+    assert_equal(now.getutc, cert.not_before)
+    assert_equal(now.getutc, cert.not_after)
+  end
+
+  def test_extension
+    ca_exts = [
+      ["basicConstraints","CA:TRUE",true],
+      ["keyUsage","keyCertSign, cRLSign",true],
+      ["subjectKeyIdentifier","hash",false],
+      ["authorityKeyIdentifier","keyid:always",false],
+    ]
+    ca_cert = issue_cert(@ca, @rsa2048, 1, Time.now, Time.now+3600, ca_exts,
+                         nil, nil, OpenSSL::Digest::SHA1.new)
+    ca_cert.extensions.each_with_index{|ext, i|
+      assert_equal(ca_exts[i].first, ext.oid)
+      assert_equal(ca_exts[i].last, ext.critical?)
+    }
+
+    ee1_exts = [
+      ["keyUsage","Non Repudiation, Digital Signature, Key Encipherment",true],
+      ["subjectKeyIdentifier","hash",false],
+      ["authorityKeyIdentifier","keyid:always",false],
+      ["extendedKeyUsage","clientAuth, emailProtection, codeSigning",false],
+      ["subjectAltName","email:ee1@ruby-lang.org",false],
+    ]
+    ee1_cert = issue_cert(@ee1, @rsa1024, 2, Time.now, Time.now+1800, ee1_exts,
+                          ca_cert, @rsa2048, OpenSSL::Digest::SHA1.new)
+    assert_equal(ca_cert.subject.to_der, ee1_cert.issuer.to_der)
+    ee1_cert.extensions.each_with_index{|ext, i|
+      assert_equal(ee1_exts[i].first, ext.oid)
+      assert_equal(ee1_exts[i].last, ext.critical?)
+    }
+
+    ee2_exts = [
+      ["keyUsage","Non Repudiation, Digital Signature, Key Encipherment",true],
+      ["subjectKeyIdentifier","hash",false],
+      ["authorityKeyIdentifier","issuer:always",false],
+      ["extendedKeyUsage","clientAuth, emailProtection, codeSigning",false],
+      ["subjectAltName","email:ee2@ruby-lang.org",false],
+    ]
+    ee2_cert = issue_cert(@ee2, @rsa1024, 3, Time.now, Time.now+1800, ee2_exts,
+                          ca_cert, @rsa2048, OpenSSL::Digest::MD5.new)
+    assert_equal(ca_cert.subject.to_der, ee2_cert.issuer.to_der)
+    ee2_cert.extensions.each_with_index{|ext, i|
+      assert_equal(ee2_exts[i].first, ext.oid)
+      assert_equal(ee2_exts[i].last, ext.critical?)
+    }
+
+  end
+
+  def test_sign_and_verify_rsa_sha1
+    cert = issue_cert(@ca, @rsa2048, 1, Time.now, Time.now+3600, [],
+                      nil, nil, OpenSSL::Digest::SHA1.new)
+    assert_equal(false, cert.verify(@rsa1024))
+    assert_equal(true,  cert.verify(@rsa2048))
+    assert_equal(false, certificate_error_returns_false { cert.verify(@dsa256) })
+    assert_equal(false, certificate_error_returns_false { cert.verify(@dsa512) })
+    cert.serial = 2
+    assert_equal(false, cert.verify(@rsa2048))
+  end
+
+  def test_sign_and_verify_rsa_md5
+    cert = issue_cert(@ca, @rsa2048, 1, Time.now, Time.now+3600, [],
+                      nil, nil, OpenSSL::Digest::MD5.new)
+    assert_equal(false, cert.verify(@rsa1024))
+    assert_equal(true, cert.verify(@rsa2048))
+
+    assert_equal(false, certificate_error_returns_false { cert.verify(@dsa256) })
+    assert_equal(false, certificate_error_returns_false { cert.verify(@dsa512) })
+    cert.subject = @ee1
+    assert_equal(false, cert.verify(@rsa2048))
+  rescue OpenSSL::X509::CertificateError # RHEL7 disables MD5
+  end
+
+  def test_sign_and_verify_dsa
+    cert = issue_cert(@ca, @dsa512, 1, Time.now, Time.now+3600, [],
+                      nil, nil, OpenSSL::TestUtils::DSA_SIGNATURE_DIGEST.new)
+    assert_equal(false, certificate_error_returns_false { cert.verify(@rsa1024) })
+    assert_equal(false, certificate_error_returns_false { cert.verify(@rsa2048) })
+    assert_equal(false, cert.verify(@dsa256))
+    assert_equal(true,  cert.verify(@dsa512))
+    cert.not_after = Time.now
+    assert_equal(false, cert.verify(@dsa512))
+  end
+
+  def test_sign_and_verify_rsa_dss1
+    cert = issue_cert(@ca, @rsa2048, 1, Time.now, Time.now+3600, [],
+                      nil, nil, OpenSSL::Digest::DSS1.new)
+    assert_equal(false, cert.verify(@rsa1024))
+    assert_equal(true, cert.verify(@rsa2048))
+    assert_equal(false, certificate_error_returns_false { cert.verify(@dsa256) })
+    assert_equal(false, certificate_error_returns_false { cert.verify(@dsa512) })
+    cert.subject = @ee1
+    assert_equal(false, cert.verify(@rsa2048))
+  rescue OpenSSL::X509::CertificateError
+  end
+
+  def test_sign_and_verify_dsa_md5
+    assert_raise(OpenSSL::X509::CertificateError){
+      issue_cert(@ca, @dsa512, 1, Time.now, Time.now+3600, [],
+                 nil, nil, OpenSSL::Digest::MD5.new)
+    }
+  end
+
+  def test_dsig_algorithm_mismatch
+    assert_raise(OpenSSL::X509::CertificateError) do
+      issue_cert(@ca, @rsa2048, 1, Time.now, Time.now+3600, [],
+                 nil, nil, OpenSSL::Digest::DSS1.new)
+    end if OpenSSL::OPENSSL_VERSION_NUMBER < 0x10001000 # [ruby-core:42949]
+
+    assert_raise(OpenSSL::X509::CertificateError) do
+      issue_cert(@ca, @dsa512, 1, Time.now, Time.now+3600, [],
+                 nil, nil, OpenSSL::Digest::MD5.new)
+    end
+  end
+
+  def test_dsa_with_sha2
+    begin
+      cert = issue_cert(@ca, @dsa256, 1, Time.now, Time.now+3600, [],
+                        nil, nil, OpenSSL::Digest::SHA256.new)
+      assert_equal("dsa_with_SHA256", cert.signature_algorithm)
+    rescue OpenSSL::X509::CertificateError
+      # dsa_with_sha2 not supported. skip following test.
+      return
+    end
+    # TODO: need more tests for dsa + sha2
+
+    # SHA1 is allowed from OpenSSL 1.0.0 (0.9.8 requires DSS1)
+    cert = issue_cert(@ca, @dsa256, 1, Time.now, Time.now+3600, [],
+                      nil, nil, OpenSSL::Digest::SHA1.new)
+    assert_equal("dsaWithSHA1", cert.signature_algorithm)
+  end if defined?(OpenSSL::Digest::SHA256)
+
+  def test_check_private_key
+    cert = issue_cert(@ca, @rsa2048, 1, Time.now, Time.now+3600, [],
+                      nil, nil, OpenSSL::Digest::SHA1.new)
+    assert_equal(true, cert.check_private_key(@rsa2048))
+  end
+
+  private
+
+  def certificate_error_returns_false
+    yield
+  rescue OpenSSL::X509::CertificateError
+    false
+  end
+end
+
+end
diff --git a/jni/ruby/test/openssl/test_x509crl.rb b/jni/ruby/test/openssl/test_x509crl.rb
new file mode 100644
index 0000000..9dc1b1c
--- /dev/null
+++ b/jni/ruby/test/openssl/test_x509crl.rb
@@ -0,0 +1,220 @@
+require_relative "utils"
+
+if defined?(OpenSSL::TestUtils)
+
+class OpenSSL::TestX509CRL < Test::Unit::TestCase
+  def setup
+    @rsa1024 = OpenSSL::TestUtils::TEST_KEY_RSA1024
+    @rsa2048 = OpenSSL::TestUtils::TEST_KEY_RSA2048
+    @dsa256  = OpenSSL::TestUtils::TEST_KEY_DSA256
+    @dsa512  = OpenSSL::TestUtils::TEST_KEY_DSA512
+    @ca = OpenSSL::X509::Name.parse("/DC=org/DC=ruby-lang/CN=CA")
+    @ee1 = OpenSSL::X509::Name.parse("/DC=org/DC=ruby-lang/CN=EE1")
+    @ee2 = OpenSSL::X509::Name.parse("/DC=org/DC=ruby-lang/CN=EE2")
+  end
+
+  def teardown
+  end
+
+  def issue_crl(*args)
+    OpenSSL::TestUtils.issue_crl(*args)
+  end
+
+  def issue_cert(*args)
+    OpenSSL::TestUtils.issue_cert(*args)
+  end
+
+  def test_basic
+    now = Time.at(Time.now.to_i)
+
+    cert = issue_cert(@ca, @rsa2048, 1, now, now+3600, [],
+                      nil, nil, OpenSSL::Digest::SHA1.new)
+    crl = issue_crl([], 1, now, now+1600, [],
+                    cert, @rsa2048, OpenSSL::Digest::SHA1.new)
+    assert_equal(1, crl.version)
+    assert_equal(cert.issuer.to_der, crl.issuer.to_der)
+    assert_equal(now, crl.last_update)
+    assert_equal(now+1600, crl.next_update)
+
+    crl = OpenSSL::X509::CRL.new(crl.to_der)
+    assert_equal(1, crl.version)
+    assert_equal(cert.issuer.to_der, crl.issuer.to_der)
+    assert_equal(now, crl.last_update)
+    assert_equal(now+1600, crl.next_update)
+  end
+
+  def test_revoked
+
+    # CRLReason ::= ENUMERATED {
+    #      unspecified             (0),
+    #      keyCompromise           (1),
+    #      cACompromise            (2),
+    #      affiliationChanged      (3),
+    #      superseded              (4),
+    #      cessationOfOperation    (5),
+    #      certificateHold         (6),
+    #      removeFromCRL           (8),
+    #      privilegeWithdrawn      (9),
+    #      aACompromise           (10) }
+
+    now = Time.at(Time.now.to_i)
+    revoke_info = [
+      [1, Time.at(0),          1],
+      [2, Time.at(0x7fffffff), 2],
+      [3, now,                 3],
+      [4, now,                 4],
+      [5, now,                 5],
+    ]
+    cert = issue_cert(@ca, @rsa2048, 1, Time.now, Time.now+3600, [],
+                      nil, nil, OpenSSL::Digest::SHA1.new)
+    crl = issue_crl(revoke_info, 1, Time.now, Time.now+1600, [],
+                    cert, @rsa2048, OpenSSL::Digest::SHA1.new)
+    revoked = crl.revoked
+    assert_equal(5, revoked.size)
+    assert_equal(1, revoked[0].serial)
+    assert_equal(2, revoked[1].serial)
+    assert_equal(3, revoked[2].serial)
+    assert_equal(4, revoked[3].serial)
+    assert_equal(5, revoked[4].serial)
+
+    assert_equal(Time.at(0), revoked[0].time)
+    assert_equal(Time.at(0x7fffffff), revoked[1].time)
+    assert_equal(now, revoked[2].time)
+    assert_equal(now, revoked[3].time)
+    assert_equal(now, revoked[4].time)
+
+    assert_equal("CRLReason", revoked[0].extensions[0].oid)
+    assert_equal("CRLReason", revoked[1].extensions[0].oid)
+    assert_equal("CRLReason", revoked[2].extensions[0].oid)
+    assert_equal("CRLReason", revoked[3].extensions[0].oid)
+    assert_equal("CRLReason", revoked[4].extensions[0].oid)
+
+    assert_equal("Key Compromise", revoked[0].extensions[0].value)
+    assert_equal("CA Compromise", revoked[1].extensions[0].value)
+    assert_equal("Affiliation Changed", revoked[2].extensions[0].value)
+    assert_equal("Superseded", revoked[3].extensions[0].value)
+    assert_equal("Cessation Of Operation", revoked[4].extensions[0].value)
+
+    assert_equal(false, revoked[0].extensions[0].critical?)
+    assert_equal(false, revoked[1].extensions[0].critical?)
+    assert_equal(false, revoked[2].extensions[0].critical?)
+    assert_equal(false, revoked[3].extensions[0].critical?)
+    assert_equal(false, revoked[4].extensions[0].critical?)
+
+    assert_equal("Key Compromise", revoked[0].extensions[0].value)
+    assert_equal("CA Compromise", revoked[1].extensions[0].value)
+    assert_equal("Affiliation Changed", revoked[2].extensions[0].value)
+    assert_equal("Superseded", revoked[3].extensions[0].value)
+    assert_equal("Cessation Of Operation", revoked[4].extensions[0].value)
+
+    revoke_info = (1..1000).collect{|i| [i, now, 0] }
+    crl = issue_crl(revoke_info, 1, Time.now, Time.now+1600, [],
+                    cert, @rsa2048, OpenSSL::Digest::SHA1.new)
+    revoked = crl.revoked
+    assert_equal(1000, revoked.size)
+    assert_equal(1, revoked[0].serial)
+    assert_equal(1000, revoked[999].serial)
+  end
+
+  def test_extension
+    cert_exts = [
+      ["basicConstraints", "CA:TRUE", true],
+      ["subjectKeyIdentifier", "hash", false],
+      ["authorityKeyIdentifier", "keyid:always", false],
+      ["subjectAltName", "email:xyzzy@ruby-lang.org", false],
+      ["keyUsage", "cRLSign, keyCertSign", true],
+    ]
+    crl_exts = [
+      ["authorityKeyIdentifier", "keyid:always", false],
+      ["issuerAltName", "issuer:copy", false],
+    ]
+
+    cert = issue_cert(@ca, @rsa2048, 1, Time.now, Time.now+3600, cert_exts,
+                      nil, nil, OpenSSL::Digest::SHA1.new)
+    crl = issue_crl([], 1, Time.now, Time.now+1600, crl_exts,
+                    cert, @rsa2048, OpenSSL::Digest::SHA1.new)
+    exts = crl.extensions
+    assert_equal(3, exts.size)
+    assert_equal("1", exts[0].value)
+    assert_equal("crlNumber", exts[0].oid)
+    assert_equal(false, exts[0].critical?)
+
+    assert_equal("authorityKeyIdentifier", exts[1].oid)
+    keyid = OpenSSL::TestUtils.get_subject_key_id(cert)
+    assert_match(/^keyid:#{keyid}/, exts[1].value)
+    assert_equal(false, exts[1].critical?)
+
+    assert_equal("issuerAltName", exts[2].oid)
+    assert_equal("email:xyzzy@ruby-lang.org", exts[2].value)
+    assert_equal(false, exts[2].critical?)
+
+    crl = OpenSSL::X509::CRL.new(crl.to_der)
+    exts = crl.extensions
+    assert_equal(3, exts.size)
+    assert_equal("1", exts[0].value)
+    assert_equal("crlNumber", exts[0].oid)
+    assert_equal(false, exts[0].critical?)
+
+    assert_equal("authorityKeyIdentifier", exts[1].oid)
+    keyid = OpenSSL::TestUtils.get_subject_key_id(cert)
+    assert_match(/^keyid:#{keyid}/, exts[1].value)
+    assert_equal(false, exts[1].critical?)
+
+    assert_equal("issuerAltName", exts[2].oid)
+    assert_equal("email:xyzzy@ruby-lang.org", exts[2].value)
+    assert_equal(false, exts[2].critical?)
+  end
+
+  def test_crlnumber
+    cert = issue_cert(@ca, @rsa2048, 1, Time.now, Time.now+3600, [],
+                      nil, nil, OpenSSL::Digest::SHA1.new)
+    crl = issue_crl([], 1, Time.now, Time.now+1600, [],
+                    cert, @rsa2048, OpenSSL::Digest::SHA1.new)
+    assert_match(1.to_s, crl.extensions[0].value)
+    assert_match(/X509v3 CRL Number:\s+#{1}/m, crl.to_text)
+
+    crl = issue_crl([], 2**32, Time.now, Time.now+1600, [],
+                    cert, @rsa2048, OpenSSL::Digest::SHA1.new)
+    assert_match((2**32).to_s, crl.extensions[0].value)
+    assert_match(/X509v3 CRL Number:\s+#{2**32}/m, crl.to_text)
+
+    crl = issue_crl([], 2**100, Time.now, Time.now+1600, [],
+                    cert, @rsa2048, OpenSSL::Digest::SHA1.new)
+    assert_match(/X509v3 CRL Number:\s+#{2**100}/m, crl.to_text)
+    assert_match((2**100).to_s, crl.extensions[0].value)
+  end
+
+  def test_sign_and_verify
+    cert = issue_cert(@ca, @rsa2048, 1, Time.now, Time.now+3600, [],
+                      nil, nil, OpenSSL::Digest::SHA1.new)
+    crl = issue_crl([], 1, Time.now, Time.now+1600, [],
+                    cert, @rsa2048, OpenSSL::Digest::SHA1.new)
+    assert_equal(false, crl.verify(@rsa1024))
+    assert_equal(true,  crl.verify(@rsa2048))
+    assert_equal(false, crl_error_returns_false { crl.verify(@dsa256) })
+    assert_equal(false, crl_error_returns_false { crl.verify(@dsa512) })
+    crl.version = 0
+    assert_equal(false, crl.verify(@rsa2048))
+
+    cert = issue_cert(@ca, @dsa512, 1, Time.now, Time.now+3600, [],
+                      nil, nil, OpenSSL::TestUtils::DSA_SIGNATURE_DIGEST.new)
+    crl = issue_crl([], 1, Time.now, Time.now+1600, [],
+                    cert, @dsa512, OpenSSL::TestUtils::DSA_SIGNATURE_DIGEST.new)
+    assert_equal(false, crl_error_returns_false { crl.verify(@rsa1024) })
+    assert_equal(false, crl_error_returns_false { crl.verify(@rsa2048) })
+    assert_equal(false, crl.verify(@dsa256))
+    assert_equal(true,  crl.verify(@dsa512))
+    crl.version = 0
+    assert_equal(false, crl.verify(@dsa512))
+  end
+
+  private
+
+  def crl_error_returns_false
+    yield
+  rescue OpenSSL::X509::CRLError
+    false
+  end
+end
+
+end
diff --git a/jni/ruby/test/openssl/test_x509ext.rb b/jni/ruby/test/openssl/test_x509ext.rb
new file mode 100644
index 0000000..29e9f1d
--- /dev/null
+++ b/jni/ruby/test/openssl/test_x509ext.rb
@@ -0,0 +1,69 @@
+require_relative 'utils'
+
+if defined?(OpenSSL::TestUtils)
+
+class OpenSSL::TestX509Extension < Test::Unit::TestCase
+  def setup
+    @basic_constraints_value = OpenSSL::ASN1::Sequence([
+      OpenSSL::ASN1::Boolean(true),   # CA
+      OpenSSL::ASN1::Integer(2)       # pathlen
+    ])
+    @basic_constraints = OpenSSL::ASN1::Sequence([
+      OpenSSL::ASN1::ObjectId("basicConstraints"),
+      OpenSSL::ASN1::Boolean(true),
+      OpenSSL::ASN1::OctetString(@basic_constraints_value.to_der),
+    ])
+  end
+
+  def teardown
+  end
+
+  def test_new
+    ext = OpenSSL::X509::Extension.new(@basic_constraints.to_der)
+    assert_equal("basicConstraints", ext.oid)
+    assert_equal(true, ext.critical?)
+    assert_equal("CA:TRUE, pathlen:2", ext.value)
+
+    ext = OpenSSL::X509::Extension.new("2.5.29.19",
+                                       @basic_constraints_value.to_der, true)
+    assert_equal(@basic_constraints.to_der, ext.to_der)
+  end
+
+  def test_create_by_factory
+    ef = OpenSSL::X509::ExtensionFactory.new
+
+    bc = ef.create_extension("basicConstraints", "critical, CA:TRUE, pathlen:2")
+    assert_equal(@basic_constraints.to_der, bc.to_der)
+
+    bc = ef.create_extension("basicConstraints", "CA:TRUE, pathlen:2", true)
+    assert_equal(@basic_constraints.to_der, bc.to_der)
+
+    begin
+      ef.config = OpenSSL::Config.parse(<<-_end_of_cnf_)
+      [crlDistPts]
+      URI.1 = http://www.example.com/crl
+      URI.2 = ldap://ldap.example.com/cn=ca?certificateRevocationList;binary
+      _end_of_cnf_
+    rescue NotImplementedError
+      return
+    end
+
+    cdp = ef.create_extension("crlDistributionPoints", "@crlDistPts")
+    assert_equal(false, cdp.critical?)
+    assert_equal("crlDistributionPoints", cdp.oid)
+    assert_match(%{URI:http://www\.example\.com/crl}, cdp.value)
+    assert_match(
+      %r{URI:ldap://ldap\.example\.com/cn=ca\?certificateRevocationList;binary},
+      cdp.value)
+
+    cdp = ef.create_extension("crlDistributionPoints", "critical, @crlDistPts")
+    assert_equal(true, cdp.critical?)
+    assert_equal("crlDistributionPoints", cdp.oid)
+    assert_match(%{URI:http://www.example.com/crl}, cdp.value)
+    assert_match(
+      %r{URI:ldap://ldap.example.com/cn=ca\?certificateRevocationList;binary},
+      cdp.value)
+  end
+end
+
+end
diff --git a/jni/ruby/test/openssl/test_x509name.rb b/jni/ruby/test/openssl/test_x509name.rb
new file mode 100644
index 0000000..a92af53
--- /dev/null
+++ b/jni/ruby/test/openssl/test_x509name.rb
@@ -0,0 +1,367 @@
+# coding: US-ASCII
+require_relative 'utils'
+
+if defined?(OpenSSL::TestUtils)
+
+class OpenSSL::TestX509Name < Test::Unit::TestCase
+  OpenSSL::ASN1::ObjectId.register(
+    "1.2.840.113549.1.9.1", "emailAddress", "emailAddress")
+  OpenSSL::ASN1::ObjectId.register(
+    "2.5.4.5", "serialNumber", "serialNumber")
+
+  def setup
+    @obj_type_tmpl = Hash.new(OpenSSL::ASN1::PRINTABLESTRING)
+    @obj_type_tmpl.update(OpenSSL::X509::Name::OBJECT_TYPE_TEMPLATE)
+  end
+
+  def teardown
+  end
+
+  def test_s_new
+    dn = [ ["C", "JP"], ["O", "example"], ["CN", "www.example.jp"] ]
+    name = OpenSSL::X509::Name.new(dn)
+    ary = name.to_a
+    assert_equal("/C=JP/O=example/CN=www.example.jp", name.to_s)
+    assert_equal("C", ary[0][0])
+    assert_equal("O", ary[1][0])
+    assert_equal("CN", ary[2][0])
+    assert_equal("JP", ary[0][1])
+    assert_equal("example", ary[1][1])
+    assert_equal("www.example.jp", ary[2][1])
+    assert_equal(OpenSSL::ASN1::PRINTABLESTRING, ary[0][2])
+    assert_equal(OpenSSL::ASN1::UTF8STRING, ary[1][2])
+    assert_equal(OpenSSL::ASN1::UTF8STRING, ary[2][2])
+
+    dn = [
+      ["countryName", "JP"],
+      ["organizationName", "example"],
+      ["commonName", "www.example.jp"]
+    ]
+    name = OpenSSL::X509::Name.new(dn)
+    ary = name.to_a
+    assert_equal("/C=JP/O=example/CN=www.example.jp", name.to_s)
+    assert_equal("C", ary[0][0])
+    assert_equal("O", ary[1][0])
+    assert_equal("CN", ary[2][0])
+    assert_equal("JP", ary[0][1])
+    assert_equal("example", ary[1][1])
+    assert_equal("www.example.jp", ary[2][1])
+    assert_equal(OpenSSL::ASN1::PRINTABLESTRING, ary[0][2])
+    assert_equal(OpenSSL::ASN1::UTF8STRING, ary[1][2])
+    assert_equal(OpenSSL::ASN1::UTF8STRING, ary[2][2])
+
+    name = OpenSSL::X509::Name.new(dn, @obj_type_tmpl)
+    ary = name.to_a
+    assert_equal("/C=JP/O=example/CN=www.example.jp", name.to_s)
+    assert_equal(OpenSSL::ASN1::PRINTABLESTRING, ary[0][2])
+    assert_equal(OpenSSL::ASN1::PRINTABLESTRING, ary[1][2])
+    assert_equal(OpenSSL::ASN1::PRINTABLESTRING, ary[2][2])
+
+    dn = [
+      ["countryName", "JP", OpenSSL::ASN1::PRINTABLESTRING],
+      ["organizationName", "example", OpenSSL::ASN1::PRINTABLESTRING],
+      ["commonName", "www.example.jp", OpenSSL::ASN1::PRINTABLESTRING]
+    ]
+    name = OpenSSL::X509::Name.new(dn)
+    ary = name.to_a
+    assert_equal("/C=JP/O=example/CN=www.example.jp", name.to_s)
+    assert_equal(OpenSSL::ASN1::PRINTABLESTRING, ary[0][2])
+    assert_equal(OpenSSL::ASN1::PRINTABLESTRING, ary[1][2])
+    assert_equal(OpenSSL::ASN1::PRINTABLESTRING, ary[2][2])
+
+    dn = [
+      ["DC", "org"],
+      ["DC", "ruby-lang"],
+      ["CN", "GOTOU Yuuzou"],
+      ["emailAddress", "gotoyuzo@ruby-lang.org"],
+      ["serialNumber", "123"],
+    ]
+    name = OpenSSL::X509::Name.new(dn)
+    ary = name.to_a
+    assert_equal("/DC=org/DC=ruby-lang/CN=GOTOU Yuuzou/emailAddress=gotoyuzo@ruby-lang.org/serialNumber=123", name.to_s)
+    assert_equal("DC", ary[0][0])
+    assert_equal("DC", ary[1][0])
+    assert_equal("CN", ary[2][0])
+    assert_equal("emailAddress", ary[3][0])
+    assert_equal("serialNumber", ary[4][0])
+    assert_equal("org", ary[0][1])
+    assert_equal("ruby-lang", ary[1][1])
+    assert_equal("GOTOU Yuuzou", ary[2][1])
+    assert_equal("gotoyuzo@ruby-lang.org", ary[3][1])
+    assert_equal("123", ary[4][1])
+    assert_equal(OpenSSL::ASN1::IA5STRING, ary[0][2])
+    assert_equal(OpenSSL::ASN1::IA5STRING, ary[1][2])
+    assert_equal(OpenSSL::ASN1::UTF8STRING, ary[2][2])
+    assert_equal(OpenSSL::ASN1::IA5STRING, ary[3][2])
+    assert_equal(OpenSSL::ASN1::PRINTABLESTRING, ary[4][2])
+
+    name_from_der = OpenSSL::X509::Name.new(name.to_der)
+    assert_equal(name_from_der.to_s, name.to_s)
+    assert_equal(name_from_der.to_a, name.to_a)
+    assert_equal(name_from_der.to_der, name.to_der)
+  end
+
+  def test_unrecognized_oid
+    dn = [ ["1.2.3.4.5.6.7.8.9.7.5.3.1", "Unknown OID 1"],
+           ["1.1.2.3.5.8.13.21.34", "Unknown OID 2"],
+           ["C", "US"],
+           ["postalCode", "60602"],
+           ["ST", "Illinois"],
+           ["L", "Chicago"],
+           #["street", "123 Fake St"],
+           ["O", "Some Company LLC"],
+           ["CN", "mydomain.com"] ]
+
+    name = OpenSSL::X509::Name.new(dn)
+    ary = name.to_a
+    #assert_equal("/1.2.3.4.5.6.7.8.9.7.5.3.1=Unknown OID 1/1.1.2.3.5.8.13.21.34=Unknown OID 2/C=US/postalCode=60602/ST=Illinois/L=Chicago/street=123 Fake St/O=Some Company LLC/CN=mydomain.com", name.to_s)
+    assert_equal("/1.2.3.4.5.6.7.8.9.7.5.3.1=Unknown OID 1/1.1.2.3.5.8.13.21.34=Unknown OID 2/C=US/postalCode=60602/ST=Illinois/L=Chicago/O=Some Company LLC/CN=mydomain.com", name.to_s)
+    assert_equal("1.2.3.4.5.6.7.8.9.7.5.3.1", ary[0][0])
+    assert_equal("1.1.2.3.5.8.13.21.34", ary[1][0])
+    assert_equal("C", ary[2][0])
+    assert_equal("postalCode", ary[3][0])
+    assert_equal("ST", ary[4][0])
+    assert_equal("L", ary[5][0])
+    #assert_equal("street", ary[6][0])
+    assert_equal("O", ary[6][0])
+    assert_equal("CN", ary[7][0])
+    assert_equal("Unknown OID 1", ary[0][1])
+    assert_equal("Unknown OID 2", ary[1][1])
+    assert_equal("US", ary[2][1])
+    assert_equal("60602", ary[3][1])
+    assert_equal("Illinois", ary[4][1])
+    assert_equal("Chicago", ary[5][1])
+    #assert_equal("123 Fake St", ary[6][1])
+    assert_equal("Some Company LLC", ary[6][1])
+    assert_equal("mydomain.com", ary[7][1])
+  end
+
+  def test_unrecognized_oid_parse_encode_equality
+    dn = [ ["1.2.3.4.5.6.7.8.9.7.5.3.2", "Unknown OID1"],
+           ["1.1.2.3.5.8.13.21.35", "Unknown OID2"],
+           ["C", "US"],
+           ["postalCode", "60602"],
+           ["ST", "Illinois"],
+           ["L", "Chicago"],
+           #["street", "123 Fake St"],
+           ["O", "Some Company LLC"],
+           ["CN", "mydomain.com"] ]
+
+    name1 = OpenSSL::X509::Name.new(dn)
+    name2 = OpenSSL::X509::Name.parse(name1.to_s)
+    assert_equal(name1.to_s, name2.to_s)
+    assert_equal(name1.to_a, name2.to_a)
+  end
+
+  def test_s_parse
+    dn = "/DC=org/DC=ruby-lang/CN=www.ruby-lang.org"
+    name = OpenSSL::X509::Name.parse(dn)
+    assert_equal(dn, name.to_s)
+    ary = name.to_a
+    assert_equal("DC", ary[0][0])
+    assert_equal("DC", ary[1][0])
+    assert_equal("CN", ary[2][0])
+    assert_equal("org", ary[0][1])
+    assert_equal("ruby-lang", ary[1][1])
+    assert_equal("www.ruby-lang.org", ary[2][1])
+    assert_equal(OpenSSL::ASN1::IA5STRING, ary[0][2])
+    assert_equal(OpenSSL::ASN1::IA5STRING, ary[1][2])
+    assert_equal(OpenSSL::ASN1::UTF8STRING, ary[2][2])
+
+    dn2 = "DC=org, DC=ruby-lang, CN=www.ruby-lang.org"
+    name = OpenSSL::X509::Name.parse(dn2)
+    ary = name.to_a
+    assert_equal(dn, name.to_s)
+    assert_equal("org", ary[0][1])
+    assert_equal("ruby-lang", ary[1][1])
+    assert_equal("www.ruby-lang.org", ary[2][1])
+
+    name = OpenSSL::X509::Name.parse(dn2, @obj_type_tmpl)
+    ary = name.to_a
+    assert_equal(OpenSSL::ASN1::IA5STRING, ary[0][2])
+    assert_equal(OpenSSL::ASN1::IA5STRING, ary[1][2])
+    assert_equal(OpenSSL::ASN1::PRINTABLESTRING, ary[2][2])
+  end
+
+  def test_s_parse_rfc2253
+    scanner = OpenSSL::X509::Name::RFC2253DN.method(:scan)
+
+    assert_equal([["C", "JP"]], scanner.call("C=JP"))
+    assert_equal([
+        ["DC", "org"],
+        ["DC", "ruby-lang"],
+        ["CN", "GOTOU Yuuzou"],
+        ["emailAddress", "gotoyuzo@ruby-lang.org"],
+      ],
+      scanner.call(
+        "emailAddress=gotoyuzo@ruby-lang.org,CN=GOTOU Yuuzou,"+
+        "DC=ruby-lang,DC=org")
+    )
+
+    u8 = OpenSSL::ASN1::UTF8STRING
+    assert_equal([
+        ["DC", "org"],
+        ["DC", "ruby-lang"],
+        ["O", ",=+<>#;"],
+        ["O", ",=+<>#;"],
+        ["OU", ""],
+        ["OU", ""],
+        ["L", "aaa=\"bbb, ccc\""],
+        ["L", "aaa=\"bbb, ccc\""],
+        ["CN", "\345\276\214\350\227\244\350\243\225\350\224\265"],
+        ["CN", "\345\276\214\350\227\244\350\243\225\350\224\265"],
+        ["CN", "\345\276\214\350\227\244\350\243\225\350\224\265"],
+        ["CN", "\345\276\214\350\227\244\350\243\225\350\224\265", u8],
+        ["2.5.4.3", "GOTOU, Yuuzou"],
+        ["2.5.4.3", "GOTOU, Yuuzou"],
+        ["2.5.4.3", "GOTOU, Yuuzou"],
+        ["2.5.4.3", "GOTOU, Yuuzou"],
+        ["CN", "GOTOU \"gotoyuzo\" Yuuzou"],
+        ["CN", "GOTOU \"gotoyuzo\" Yuuzou"],
+        ["1.2.840.113549.1.9.1", "gotoyuzo@ruby-lang.org"],
+        ["emailAddress", "gotoyuzo@ruby-lang.org"],
+      ],
+      scanner.call(
+        "emailAddress=gotoyuzo@ruby-lang.org," +
+        "1.2.840.113549.1.9.1=gotoyuzo@ruby-lang.org," +
+        'CN=GOTOU \"gotoyuzo\" Yuuzou,' +
+        'CN="GOTOU \"gotoyuzo\" Yuuzou",' +
+        '2.5.4.3=GOTOU\,\20Yuuzou,' +
+        '2.5.4.3=GOTOU\, Yuuzou,' +
+        '2.5.4.3="GOTOU, Yuuzou",' +
+        '2.5.4.3="GOTOU\, Yuuzou",' +
+        "CN=#0C0CE5BE8CE897A4E8A395E894B5," +
+        'CN=\E5\BE\8C\E8\97\A4\E8\A3\95\E8\94\B5,' +
+        "CN=\"\xE5\xBE\x8C\xE8\x97\xA4\xE8\xA3\x95\xE8\x94\xB5\"," +
+        "CN=\xE5\xBE\x8C\xE8\x97\xA4\xE8\xA3\x95\xE8\x94\xB5," +
+        'L=aaa\=\"bbb\, ccc\",' +
+        'L="aaa=\"bbb, ccc\"",' +
+        'OU=,' +
+        'OU="",' +
+        'O=\,\=\+\<\>\#\;,' +
+        'O=",=+<>#;",' +
+        "DC=ruby-lang," +
+        "DC=org")
+    )
+
+    [
+      "DC=org+DC=jp",
+      "DC=org,DC=ruby-lang+DC=rubyist,DC=www"
+    ].each{|dn|
+      ex = scanner.call(dn) rescue $!
+      dn_r = Regexp.escape(dn)
+      assert_match(/^multi-valued RDN is not supported: #{dn_r}/, ex.message)
+    }
+
+    [
+      ["DC=org,DC=exapmle,CN", "CN"],
+      ["DC=org,DC=example,", ""],
+      ["DC=org,DC=exapmle,CN=www.example.org;", "CN=www.example.org;"],
+      ["DC=org,DC=exapmle,CN=#www.example.org", "CN=#www.example.org"],
+      ["DC=org,DC=exapmle,CN=#777777.example.org", "CN=#777777.example.org"],
+      ["DC=org,DC=exapmle,CN=\"www.example\".org", "CN=\"www.example\".org"],
+      ["DC=org,DC=exapmle,CN=www.\"example.org\"", "CN=www.\"example.org\""],
+      ["DC=org,DC=exapmle,CN=www.\"example\".org", "CN=www.\"example\".org"],
+    ].each{|dn, msg|
+      ex = scanner.call(dn) rescue $!
+      assert_match(/^malformed RDN: .*=>#{Regexp.escape(msg)}/, ex.message)
+    }
+
+    dn = "CN=www.ruby-lang.org,DC=ruby-lang,DC=org"
+    name = OpenSSL::X509::Name.parse_rfc2253(dn)
+    assert_equal(dn, name.to_s(OpenSSL::X509::Name::RFC2253))
+    ary = name.to_a
+    assert_equal("DC", ary[0][0])
+    assert_equal("DC", ary[1][0])
+    assert_equal("CN", ary[2][0])
+    assert_equal("org", ary[0][1])
+    assert_equal("ruby-lang", ary[1][1])
+    assert_equal("www.ruby-lang.org", ary[2][1])
+    assert_equal(OpenSSL::ASN1::IA5STRING, ary[0][2])
+    assert_equal(OpenSSL::ASN1::IA5STRING, ary[1][2])
+    assert_equal(OpenSSL::ASN1::UTF8STRING, ary[2][2])
+  end
+
+  def test_add_entry
+    dn = [
+      ["DC", "org"],
+      ["DC", "ruby-lang"],
+      ["CN", "GOTOU Yuuzou"],
+      ["emailAddress", "gotoyuzo@ruby-lang.org"],
+      ["serialNumber", "123"],
+    ]
+    name = OpenSSL::X509::Name.new
+    dn.each{|attr| name.add_entry(*attr) }
+    ary = name.to_a
+    assert_equal("/DC=org/DC=ruby-lang/CN=GOTOU Yuuzou/emailAddress=gotoyuzo@ruby-lang.org/serialNumber=123", name.to_s)
+    assert_equal("DC", ary[0][0])
+    assert_equal("DC", ary[1][0])
+    assert_equal("CN", ary[2][0])
+    assert_equal("emailAddress", ary[3][0])
+    assert_equal("serialNumber", ary[4][0])
+    assert_equal("org", ary[0][1])
+    assert_equal("ruby-lang", ary[1][1])
+    assert_equal("GOTOU Yuuzou", ary[2][1])
+    assert_equal("gotoyuzo@ruby-lang.org", ary[3][1])
+    assert_equal("123", ary[4][1])
+    assert_equal(OpenSSL::ASN1::IA5STRING, ary[0][2])
+    assert_equal(OpenSSL::ASN1::IA5STRING, ary[1][2])
+    assert_equal(OpenSSL::ASN1::UTF8STRING, ary[2][2])
+    assert_equal(OpenSSL::ASN1::IA5STRING, ary[3][2])
+    assert_equal(OpenSSL::ASN1::PRINTABLESTRING, ary[4][2])
+  end
+
+  def test_add_entry_street
+    return if OpenSSL::OPENSSL_VERSION_NUMBER < 0x009080df # 0.9.8m
+    # openssl/crypto/objects/obj_mac.h 1.83
+    dn = [
+      ["DC", "org"],
+      ["DC", "ruby-lang"],
+      ["CN", "GOTOU Yuuzou"],
+      ["emailAddress", "gotoyuzo@ruby-lang.org"],
+      ["serialNumber", "123"],
+      ["street", "Namiki"],
+    ]
+    name = OpenSSL::X509::Name.new
+    dn.each{|attr| name.add_entry(*attr) }
+    ary = name.to_a
+    assert_equal("/DC=org/DC=ruby-lang/CN=GOTOU Yuuzou/emailAddress=gotoyuzo@ruby-lang.org/serialNumber=123/street=Namiki", name.to_s)
+    assert_equal("Namiki", ary[5][1])
+  end
+
+  def test_equals2
+    n1 = OpenSSL::X509::Name.parse 'CN=a'
+    n2 = OpenSSL::X509::Name.parse 'CN=a'
+
+    assert_equal n1, n2
+  end
+
+  def test_spaceship
+    n1 = OpenSSL::X509::Name.parse 'CN=a'
+    n2 = OpenSSL::X509::Name.parse 'CN=b'
+
+    assert_equal(-1, n1 <=> n2)
+  end
+
+  def name_hash(name)
+    # OpenSSL 1.0.0 uses SHA1 for canonical encoding (not just a der) of
+    # X509Name for X509_NAME_hash.
+    name.respond_to?(:hash_old) ? name.hash_old : name.hash
+  end
+
+  def test_hash
+    dn = "/DC=org/DC=ruby-lang/CN=www.ruby-lang.org"
+    name = OpenSSL::X509::Name.parse(dn)
+    d = Digest::MD5.digest(name.to_der)
+    expected = (d[0].ord & 0xff) | (d[1].ord & 0xff) << 8 | (d[2].ord & 0xff) << 16 | (d[3].ord & 0xff) << 24
+    assert_equal(expected, name_hash(name))
+    #
+    dn = "/DC=org/DC=ruby-lang/CN=baz.ruby-lang.org"
+    name = OpenSSL::X509::Name.parse(dn)
+    d = Digest::MD5.digest(name.to_der)
+    expected = (d[0].ord & 0xff) | (d[1].ord & 0xff) << 8 | (d[2].ord & 0xff) << 16 | (d[3].ord & 0xff) << 24
+    assert_equal(expected, name_hash(name))
+  end
+end
+
+end
diff --git a/jni/ruby/test/openssl/test_x509req.rb b/jni/ruby/test/openssl/test_x509req.rb
new file mode 100644
index 0000000..27040cb
--- /dev/null
+++ b/jni/ruby/test/openssl/test_x509req.rb
@@ -0,0 +1,158 @@
+require_relative "utils"
+
+if defined?(OpenSSL::TestUtils)
+
+class OpenSSL::TestX509Request < Test::Unit::TestCase
+  def setup
+    @rsa1024 = OpenSSL::TestUtils::TEST_KEY_RSA1024
+    @rsa2048 = OpenSSL::TestUtils::TEST_KEY_RSA2048
+    @dsa256  = OpenSSL::TestUtils::TEST_KEY_DSA256
+    @dsa512  = OpenSSL::TestUtils::TEST_KEY_DSA512
+    @dn = OpenSSL::X509::Name.parse("/DC=org/DC=ruby-lang/CN=GOTOU Yuuzou")
+  end
+
+  def issue_csr(ver, dn, key, digest)
+    req = OpenSSL::X509::Request.new
+    req.version = ver
+    req.subject = dn
+    req.public_key = key.public_key
+    req.sign(key, digest)
+    req
+  end
+
+  def test_public_key
+    req = issue_csr(0, @dn, @rsa1024, OpenSSL::Digest::SHA1.new)
+    assert_equal(@rsa1024.public_key.to_der, req.public_key.to_der)
+    req = OpenSSL::X509::Request.new(req.to_der)
+    assert_equal(@rsa1024.public_key.to_der, req.public_key.to_der)
+
+    req = issue_csr(0, @dn, @dsa512, OpenSSL::TestUtils::DSA_SIGNATURE_DIGEST.new)
+    assert_equal(@dsa512.public_key.to_der, req.public_key.to_der)
+    req = OpenSSL::X509::Request.new(req.to_der)
+    assert_equal(@dsa512.public_key.to_der, req.public_key.to_der)
+  end
+
+  def test_version
+    req = issue_csr(0, @dn, @rsa1024, OpenSSL::Digest::SHA1.new)
+    assert_equal(0, req.version)
+    req = OpenSSL::X509::Request.new(req.to_der)
+    assert_equal(0, req.version)
+
+    req = issue_csr(1, @dn, @rsa1024, OpenSSL::Digest::SHA1.new)
+    assert_equal(1, req.version)
+    req = OpenSSL::X509::Request.new(req.to_der)
+    assert_equal(1, req.version)
+  end
+
+  def test_subject
+    req = issue_csr(0, @dn, @rsa1024, OpenSSL::Digest::SHA1.new)
+    assert_equal(@dn.to_der, req.subject.to_der)
+    req = OpenSSL::X509::Request.new(req.to_der)
+    assert_equal(@dn.to_der, req.subject.to_der)
+  end
+
+  def create_ext_req(exts)
+    ef = OpenSSL::X509::ExtensionFactory.new
+    exts = exts.collect{|e| ef.create_extension(*e) }
+    return OpenSSL::ASN1::Set([OpenSSL::ASN1::Sequence(exts)])
+  end
+
+  def get_ext_req(ext_req_value)
+    set = OpenSSL::ASN1.decode(ext_req_value)
+    seq = set.value[0]
+    seq.value.collect{|asn1ext|
+      OpenSSL::X509::Extension.new(asn1ext).to_a
+    }
+  end
+
+  def test_attr
+    exts = [
+      ["keyUsage", "Digital Signature, Key Encipherment", true],
+      ["subjectAltName", "email:gotoyuzo@ruby-lang.org", false],
+    ]
+    attrval = create_ext_req(exts)
+    attrs = [
+      OpenSSL::X509::Attribute.new("extReq", attrval),
+      OpenSSL::X509::Attribute.new("msExtReq", attrval),
+    ]
+
+    req0 = issue_csr(0, @dn, @rsa1024, OpenSSL::Digest::SHA1.new)
+    attrs.each{|attr| req0.add_attribute(attr) }
+    req1 = issue_csr(0, @dn, @rsa1024, OpenSSL::Digest::SHA1.new)
+    req1.attributes = attrs
+    assert_equal(req0.to_der, req1.to_der)
+
+    attrs = req0.attributes
+    assert_equal(2, attrs.size)
+    assert_equal("extReq", attrs[0].oid)
+    assert_equal("msExtReq", attrs[1].oid)
+    assert_equal(exts, get_ext_req(attrs[0].value))
+    assert_equal(exts, get_ext_req(attrs[1].value))
+
+    req = OpenSSL::X509::Request.new(req0.to_der)
+    attrs = req.attributes
+    assert_equal(2, attrs.size)
+    assert_equal("extReq", attrs[0].oid)
+    assert_equal("msExtReq", attrs[1].oid)
+    assert_equal(exts, get_ext_req(attrs[0].value))
+    assert_equal(exts, get_ext_req(attrs[1].value))
+  end
+
+  def test_sign_and_verify_rsa_sha1
+    req = issue_csr(0, @dn, @rsa1024, OpenSSL::Digest::SHA1.new)
+    assert_equal(true,  req.verify(@rsa1024))
+    assert_equal(false, req.verify(@rsa2048))
+    assert_equal(false, request_error_returns_false { req.verify(@dsa256) })
+    assert_equal(false, request_error_returns_false { req.verify(@dsa512) })
+    req.version = 1
+    assert_equal(false, req.verify(@rsa1024))
+  end
+
+  def test_sign_and_verify_rsa_md5
+    req = issue_csr(0, @dn, @rsa2048, OpenSSL::Digest::MD5.new)
+    assert_equal(false, req.verify(@rsa1024))
+    assert_equal(true,  req.verify(@rsa2048))
+    assert_equal(false, request_error_returns_false { req.verify(@dsa256) })
+    assert_equal(false, request_error_returns_false { req.verify(@dsa512) })
+    req.subject = OpenSSL::X509::Name.parse("/C=JP/CN=FooBar")
+    assert_equal(false, req.verify(@rsa2048))
+  rescue OpenSSL::X509::RequestError # RHEL7 disables MD5
+  end
+
+  def test_sign_and_verify_dsa
+    req = issue_csr(0, @dn, @dsa512, OpenSSL::TestUtils::DSA_SIGNATURE_DIGEST.new)
+    assert_equal(false, request_error_returns_false { req.verify(@rsa1024) })
+    assert_equal(false, request_error_returns_false { req.verify(@rsa2048) })
+    assert_equal(false, req.verify(@dsa256))
+    assert_equal(true,  req.verify(@dsa512))
+    req.public_key = @rsa1024.public_key
+    assert_equal(false, req.verify(@dsa512))
+  end
+
+  def test_sign_and_verify_rsa_dss1
+    req = issue_csr(0, @dn, @rsa1024, OpenSSL::Digest::DSS1.new)
+    assert_equal(true,  req.verify(@rsa1024))
+    assert_equal(false, req.verify(@rsa2048))
+    assert_equal(false, request_error_returns_false { req.verify(@dsa256) })
+    assert_equal(false, request_error_returns_false { req.verify(@dsa512) })
+    req.version = 1
+    assert_equal(false, req.verify(@rsa1024))
+  rescue OpenSSL::X509::RequestError
+    skip
+  end
+
+  def test_sign_and_verify_dsa_md5
+    assert_raise(OpenSSL::X509::RequestError){
+      issue_csr(0, @dn, @dsa512, OpenSSL::Digest::MD5.new) }
+  end
+
+  private
+
+  def request_error_returns_false
+    yield
+  rescue OpenSSL::X509::RequestError
+    false
+  end
+end
+
+end
diff --git a/jni/ruby/test/openssl/test_x509store.rb b/jni/ruby/test/openssl/test_x509store.rb
new file mode 100644
index 0000000..f3e144f
--- /dev/null
+++ b/jni/ruby/test/openssl/test_x509store.rb
@@ -0,0 +1,231 @@
+require_relative "utils"
+
+if defined?(OpenSSL::TestUtils)
+
+class OpenSSL::TestX509Store < Test::Unit::TestCase
+  def setup
+    @rsa1024 = OpenSSL::TestUtils::TEST_KEY_RSA1024
+    @rsa2048 = OpenSSL::TestUtils::TEST_KEY_RSA2048
+    @dsa256  = OpenSSL::TestUtils::TEST_KEY_DSA256
+    @dsa512  = OpenSSL::TestUtils::TEST_KEY_DSA512
+    @ca1 = OpenSSL::X509::Name.parse("/DC=org/DC=ruby-lang/CN=CA1")
+    @ca2 = OpenSSL::X509::Name.parse("/DC=org/DC=ruby-lang/CN=CA2")
+    @ee1 = OpenSSL::X509::Name.parse("/DC=org/DC=ruby-lang/CN=EE1")
+    @ee2 = OpenSSL::X509::Name.parse("/DC=org/DC=ruby-lang/CN=EE2")
+  end
+
+  def teardown
+  end
+
+  def test_nosegv_on_cleanup
+    cert  = OpenSSL::X509::Certificate.new
+    store = OpenSSL::X509::Store.new
+    ctx   = OpenSSL::X509::StoreContext.new(store, cert, [])
+    EnvUtil.suppress_warning do
+      ctx.cleanup
+    end
+    ctx.verify
+  end
+
+  def issue_cert(*args)
+    OpenSSL::TestUtils.issue_cert(*args)
+  end
+
+  def issue_crl(*args)
+    OpenSSL::TestUtils.issue_crl(*args)
+  end
+
+  def test_verify
+    now = Time.at(Time.now.to_i)
+    ca_exts = [
+      ["basicConstraints","CA:TRUE",true],
+      ["keyUsage","cRLSign,keyCertSign",true],
+    ]
+    ee_exts = [
+      ["keyUsage","keyEncipherment,digitalSignature",true],
+    ]
+    ca1_cert = issue_cert(@ca1, @rsa2048, 1, now, now+3600, ca_exts,
+                          nil, nil, OpenSSL::Digest::SHA1.new)
+    ca2_cert = issue_cert(@ca2, @rsa1024, 2, now, now+1800, ca_exts,
+                          ca1_cert, @rsa2048, OpenSSL::Digest::SHA1.new)
+    ee1_cert = issue_cert(@ee1, @dsa256, 10, now, now+1800, ee_exts,
+                          ca2_cert, @rsa1024, OpenSSL::Digest::SHA1.new)
+    ee2_cert = issue_cert(@ee2, @dsa512, 20, now, now+1800, ee_exts,
+                          ca2_cert, @rsa1024, OpenSSL::Digest::SHA1.new)
+    ee3_cert = issue_cert(@ee2, @dsa512, 30, now-100, now-1, ee_exts,
+                          ca2_cert, @rsa1024, OpenSSL::Digest::SHA1.new)
+    ee4_cert = issue_cert(@ee2, @dsa512, 40, now+1000, now+2000, ee_exts,
+                          ca2_cert, @rsa1024, OpenSSL::Digest::SHA1.new)
+
+    revoke_info = []
+    crl1   = issue_crl(revoke_info, 1, now, now+1800, [],
+                       ca1_cert, @rsa2048, OpenSSL::Digest::SHA1.new)
+    revoke_info = [ [2, now, 1], ]
+    crl1_2 = issue_crl(revoke_info, 2, now, now+1800, [],
+                       ca1_cert, @rsa2048, OpenSSL::Digest::SHA1.new)
+    revoke_info = [ [20, now, 1], ]
+    crl2   = issue_crl(revoke_info, 1, now, now+1800, [],
+                       ca2_cert, @rsa1024, OpenSSL::Digest::SHA1.new)
+    revoke_info = []
+    crl2_2 = issue_crl(revoke_info, 2, now-100, now-1, [],
+                       ca2_cert, @rsa1024, OpenSSL::Digest::SHA1.new)
+
+    assert_equal(true, ca1_cert.verify(ca1_cert.public_key))   # self signed
+    assert_equal(true, ca2_cert.verify(ca1_cert.public_key))   # issued by ca1
+    assert_equal(true, ee1_cert.verify(ca2_cert.public_key))   # issued by ca2
+    assert_equal(true, ee2_cert.verify(ca2_cert.public_key))   # issued by ca2
+    assert_equal(true, ee3_cert.verify(ca2_cert.public_key))   # issued by ca2
+    assert_equal(true, crl1.verify(ca1_cert.public_key))       # issued by ca1
+    assert_equal(true, crl1_2.verify(ca1_cert.public_key))     # issued by ca1
+    assert_equal(true, crl2.verify(ca2_cert.public_key))       # issued by ca2
+    assert_equal(true, crl2_2.verify(ca2_cert.public_key))     # issued by ca2
+
+    store = OpenSSL::X509::Store.new
+    assert_equal(false, store.verify(ca1_cert))
+    assert_not_equal(OpenSSL::X509::V_OK, store.error)
+
+    assert_equal(false, store.verify(ca2_cert))
+    assert_not_equal(OpenSSL::X509::V_OK, store.error)
+
+    store.add_cert(ca1_cert)
+    assert_equal(true, store.verify(ca2_cert))
+    assert_equal(OpenSSL::X509::V_OK, store.error)
+    assert_equal("ok", store.error_string)
+    chain = store.chain
+    assert_equal(2, chain.size)
+    assert_equal(@ca2.to_der, chain[0].subject.to_der)
+    assert_equal(@ca1.to_der, chain[1].subject.to_der)
+
+    store.purpose = OpenSSL::X509::PURPOSE_SSL_CLIENT
+    assert_equal(false, store.verify(ca2_cert))
+    assert_not_equal(OpenSSL::X509::V_OK, store.error)
+
+    store.purpose = OpenSSL::X509::PURPOSE_CRL_SIGN
+    assert_equal(true, store.verify(ca2_cert))
+    assert_equal(OpenSSL::X509::V_OK, store.error)
+
+    store.add_cert(ca2_cert)
+    store.purpose = OpenSSL::X509::PURPOSE_SSL_CLIENT
+    assert_equal(true, store.verify(ee1_cert))
+    assert_equal(true, store.verify(ee2_cert))
+    assert_equal(OpenSSL::X509::V_OK, store.error)
+    assert_equal("ok", store.error_string)
+    chain = store.chain
+    assert_equal(3, chain.size)
+    assert_equal(@ee2.to_der, chain[0].subject.to_der)
+    assert_equal(@ca2.to_der, chain[1].subject.to_der)
+    assert_equal(@ca1.to_der, chain[2].subject.to_der)
+    assert_equal(false, store.verify(ee3_cert))
+    assert_equal(OpenSSL::X509::V_ERR_CERT_HAS_EXPIRED, store.error)
+    assert_match(/expire/i, store.error_string)
+    assert_equal(false, store.verify(ee4_cert))
+    assert_equal(OpenSSL::X509::V_ERR_CERT_NOT_YET_VALID, store.error)
+    assert_match(/not yet valid/i, store.error_string)
+
+    store = OpenSSL::X509::Store.new
+    store.add_cert(ca1_cert)
+    store.add_cert(ca2_cert)
+    store.time = now + 1500
+    assert_equal(true, store.verify(ca1_cert))
+    assert_equal(true, store.verify(ca2_cert))
+    assert_equal(true, store.verify(ee4_cert))
+    store.time = now + 1900
+    assert_equal(true, store.verify(ca1_cert))
+    assert_equal(false, store.verify(ca2_cert))
+    assert_equal(OpenSSL::X509::V_ERR_CERT_HAS_EXPIRED, store.error)
+    assert_equal(false, store.verify(ee4_cert))
+    assert_equal(OpenSSL::X509::V_ERR_CERT_HAS_EXPIRED, store.error)
+    store.time = now + 4000
+    assert_equal(false, store.verify(ee1_cert))
+    assert_equal(OpenSSL::X509::V_ERR_CERT_HAS_EXPIRED, store.error)
+    assert_equal(false, store.verify(ee4_cert))
+    assert_equal(OpenSSL::X509::V_ERR_CERT_HAS_EXPIRED, store.error)
+
+    # the underlying X509 struct caches the result of the last
+    # verification for signature and not-before. so the following code
+    # rebuilds new objects to avoid site effect.
+    store.time = Time.now - 4000
+    assert_equal(false, store.verify(OpenSSL::X509::Certificate.new(ca2_cert)))
+    assert_equal(OpenSSL::X509::V_ERR_CERT_NOT_YET_VALID, store.error)
+    assert_equal(false, store.verify(OpenSSL::X509::Certificate.new(ee1_cert)))
+    assert_equal(OpenSSL::X509::V_ERR_CERT_NOT_YET_VALID, store.error)
+
+    return unless defined?(OpenSSL::X509::V_FLAG_CRL_CHECK)
+
+    store = OpenSSL::X509::Store.new
+    store.purpose = OpenSSL::X509::PURPOSE_ANY
+    store.flags = OpenSSL::X509::V_FLAG_CRL_CHECK
+    store.add_cert(ca1_cert)
+    store.add_crl(crl1)   # revoke no cert
+    store.add_crl(crl2)   # revoke ee2_cert
+    assert_equal(true,  store.verify(ca1_cert))
+    assert_equal(true,  store.verify(ca2_cert))
+    assert_equal(true,  store.verify(ee1_cert, [ca2_cert]))
+    assert_equal(false, store.verify(ee2_cert, [ca2_cert]))
+
+    store = OpenSSL::X509::Store.new
+    store.purpose = OpenSSL::X509::PURPOSE_ANY
+    store.flags = OpenSSL::X509::V_FLAG_CRL_CHECK
+    store.add_cert(ca1_cert)
+    store.add_crl(crl1_2) # revoke ca2_cert
+    store.add_crl(crl2)   # revoke ee2_cert
+    assert_equal(true,  store.verify(ca1_cert))
+    assert_equal(false, store.verify(ca2_cert))
+    assert_equal(true,  store.verify(ee1_cert, [ca2_cert]),
+      "This test is expected to be success with OpenSSL 0.9.7c or later.")
+    assert_equal(false, store.verify(ee2_cert, [ca2_cert]))
+
+    store.flags =
+      OpenSSL::X509::V_FLAG_CRL_CHECK|OpenSSL::X509::V_FLAG_CRL_CHECK_ALL
+    assert_equal(true,  store.verify(ca1_cert))
+    assert_equal(false, store.verify(ca2_cert))
+    assert_equal(false, store.verify(ee1_cert, [ca2_cert]))
+    assert_equal(false, store.verify(ee2_cert, [ca2_cert]))
+
+    store = OpenSSL::X509::Store.new
+    store.purpose = OpenSSL::X509::PURPOSE_ANY
+    store.flags =
+      OpenSSL::X509::V_FLAG_CRL_CHECK|OpenSSL::X509::V_FLAG_CRL_CHECK_ALL
+    store.add_cert(ca1_cert)
+    store.add_cert(ca2_cert)
+    store.add_crl(crl1)
+    store.add_crl(crl2_2) # issued by ca2 but expired.
+    assert_equal(true, store.verify(ca1_cert))
+    assert_equal(true, store.verify(ca2_cert))
+    assert_equal(false, store.verify(ee1_cert))
+    assert_equal(OpenSSL::X509::V_ERR_CRL_HAS_EXPIRED, store.error)
+    assert_equal(false, store.verify(ee2_cert))
+  end
+
+  def test_set_errors
+    now = Time.now
+    ca1_cert = issue_cert(@ca1, @rsa2048, 1, now, now+3600, [],
+                          nil, nil, OpenSSL::Digest::SHA1.new)
+    store = OpenSSL::X509::Store.new
+    store.add_cert(ca1_cert)
+    assert_raise(OpenSSL::X509::StoreError){
+      store.add_cert(ca1_cert)  # add same certificate twice
+    }
+
+    revoke_info = []
+    crl1 = issue_crl(revoke_info, 1, now, now+1800, [],
+                     ca1_cert, @rsa2048, OpenSSL::Digest::SHA1.new)
+    revoke_info = [ [2, now, 1], ]
+    crl2 = issue_crl(revoke_info, 2, now+1800, now+3600, [],
+                     ca1_cert, @rsa2048, OpenSSL::Digest::SHA1.new)
+    store.add_crl(crl1)
+    if /0\.9\.8.*-rhel/ =~ OpenSSL::OPENSSL_VERSION
+      # RedHat is distributing a patched version of OpenSSL that allows
+      # multiple CRL for a key (multi-crl.patch)
+      assert_nothing_raised do
+        store.add_crl(crl2) # add CRL issued by same CA twice.
+      end
+    else
+      assert_raise(OpenSSL::X509::StoreError){
+        store.add_crl(crl2) # add CRL issued by same CA twice.
+      }
+    end
+  end
+end
+
+end
diff --git a/jni/ruby/test/openssl/utils.rb b/jni/ruby/test/openssl/utils.rb
new file mode 100644
index 0000000..bd936be
--- /dev/null
+++ b/jni/ruby/test/openssl/utils.rb
@@ -0,0 +1,333 @@
+begin
+  require "openssl"
+
+  # Disable FIPS mode for tests for installations
+  # where FIPS mode would be enabled by default.
+  # Has no effect on all other installations.
+  OpenSSL.fips_mode=false
+rescue LoadError
+end
+require "test/unit"
+require "digest/md5"
+require 'tempfile'
+require "rbconfig"
+require "socket"
+
+module OpenSSL::TestUtils
+  TEST_KEY_RSA1024 = OpenSSL::PKey::RSA.new <<-_end_of_pem_
+-----BEGIN RSA PRIVATE KEY-----
+MIICXgIBAAKBgQDLwsSw1ECnPtT+PkOgHhcGA71nwC2/nL85VBGnRqDxOqjVh7Cx
+aKPERYHsk4BPCkE3brtThPWc9kjHEQQ7uf9Y1rbCz0layNqHyywQEVLFmp1cpIt/
+Q3geLv8ZD9pihowKJDyMDiN6ArYUmZczvW4976MU3+l54E6lF/JfFEU5hwIDAQAB
+AoGBAKSl/MQarye1yOysqX6P8fDFQt68VvtXkNmlSiKOGuzyho0M+UVSFcs6k1L0
+maDE25AMZUiGzuWHyaU55d7RXDgeskDMakD1v6ZejYtxJkSXbETOTLDwUWTn618T
+gnb17tU1jktUtU67xK/08i/XodlgnQhs6VoHTuCh3Hu77O6RAkEA7+gxqBuZR572
+74/akiW/SuXm0SXPEviyO1MuSRwtI87B02D0qgV8D1UHRm4AhMnJ8MCs1809kMQE
+JiQUCrp9mQJBANlt2ngBO14us6NnhuAseFDTBzCHXwUUu1YKHpMMmxpnGqaldGgX
+sOZB3lgJsT9VlGf3YGYdkLTNVbogQKlKpB8CQQDiSwkb4vyQfDe8/NpU5Not0fII
+8jsDUCb+opWUTMmfbxWRR3FBNu8wnym/m19N4fFj8LqYzHX4KY0oVPu6qvJxAkEA
+wa5snNekFcqONLIE4G5cosrIrb74sqL8GbGb+KuTAprzj5z1K8Bm0UW9lTjVDjDi
+qRYgZfZSL+x1P/54+xTFSwJAY1FxA/N3QPCXCjPh5YqFxAMQs2VVYTfg+t0MEcJD
+dPMQD5JX6g5HKnHFg2mZtoXQrWmJSn7p8GJK8yNTopEErA==
+-----END RSA PRIVATE KEY-----
+  _end_of_pem_
+
+  TEST_KEY_RSA2048 = OpenSSL::PKey::RSA.new <<-_end_of_pem_
+-----BEGIN RSA PRIVATE KEY-----
+MIIEpAIBAAKCAQEAuV9ht9J7k4NBs38jOXvvTKY9gW8nLICSno5EETR1cuF7i4pN
+s9I1QJGAFAX0BEO4KbzXmuOvfCpD3CU+Slp1enenfzq/t/e/1IRW0wkJUJUFQign
+4CtrkJL+P07yx18UjyPlBXb81ApEmAB5mrJVSrWmqbjs07JbuS4QQGGXLc+Su96D
+kYKmSNVjBiLxVVSpyZfAY3hD37d60uG+X8xdW5v68JkRFIhdGlb6JL8fllf/A/bl
+NwdJOhVr9mESHhwGjwfSeTDPfd8ZLE027E5lyAVX9KZYcU00mOX+fdxOSnGqS/8J
+DRh0EPHDL15RcJjV2J6vZjPb0rOYGDoMcH+94wIDAQABAoIBAAzsamqfYQAqwXTb
+I0CJtGg6msUgU7HVkOM+9d3hM2L791oGHV6xBAdpXW2H8LgvZHJ8eOeSghR8+dgq
+PIqAffo4x1Oma+FOg3A0fb0evyiACyrOk+EcBdbBeLo/LcvahBtqnDfiUMQTpy6V
+seSoFCwuN91TSCeGIsDpRjbG1vxZgtx+uI+oH5+ytqJOmfCksRDCkMglGkzyfcl0
+Xc5CUhIJ0my53xijEUQl19rtWdMnNnnkdbG8PT3LZlOta5Do86BElzUYka0C6dUc
+VsBDQ0Nup0P6rEQgy7tephHoRlUGTYamsajGJaAo1F3IQVIrRSuagi7+YpSpCqsW
+wORqorkCgYEA7RdX6MDVrbw7LePnhyuaqTiMK+055/R1TqhB1JvvxJ1CXk2rDL6G
+0TLHQ7oGofd5LYiemg4ZVtWdJe43BPZlVgT6lvL/iGo8JnrncB9Da6L7nrq/+Rvj
+XGjf1qODCK+LmreZWEsaLPURIoR/Ewwxb9J2zd0CaMjeTwafJo1CZvcCgYEAyCgb
+aqoWvUecX8VvARfuA593Lsi50t4MEArnOXXcd1RnXoZWhbx5rgO8/ATKfXr0BK/n
+h2GF9PfKzHFm/4V6e82OL7gu/kLy2u9bXN74vOvWFL5NOrOKPM7Kg+9I131kNYOw
+Ivnr/VtHE5s0dY7JChYWE1F3vArrOw3T00a4CXUCgYEA0SqY+dS2LvIzW4cHCe9k
+IQqsT0yYm5TFsUEr4sA3xcPfe4cV8sZb9k/QEGYb1+SWWZ+AHPV3UW5fl8kTbSNb
+v4ng8i8rVVQ0ANbJO9e5CUrepein2MPL0AkOATR8M7t7dGGpvYV0cFk8ZrFx0oId
+U0PgYDotF/iueBWlbsOM430CgYEAqYI95dFyPI5/AiSkY5queeb8+mQH62sdcCCr
+vd/w/CZA/K5sbAo4SoTj8dLk4evU6HtIa0DOP63y071eaxvRpTNqLUOgmLh+D6gS
+Cc7TfLuFrD+WDBatBd5jZ+SoHccVrLR/4L8jeodo5FPW05A+9gnKXEXsTxY4LOUC
+9bS4e1kCgYAqVXZh63JsMwoaxCYmQ66eJojKa47VNrOeIZDZvd2BPVf30glBOT41
+gBoDG3WMPZoQj9pb7uMcrnvs4APj2FIhMU8U15LcPAj59cD6S6rWnAxO8NFK7HQG
+4Jxg3JNNf8ErQoCHb1B3oVdXJkmbJkARoDpBKmTCgKtP8ADYLmVPQw==
+-----END RSA PRIVATE KEY-----
+  _end_of_pem_
+
+  TEST_KEY_DSA256 = OpenSSL::PKey::DSA.new <<-_end_of_pem_
+-----BEGIN DSA PRIVATE KEY-----
+MIH3AgEAAkEAhk2libbY2a8y2Pt21+YPYGZeW6wzaW2yfj5oiClXro9XMR7XWLkE
+9B7XxLNFCS2gmCCdMsMW1HulaHtLFQmB2wIVAM43JZrcgpu6ajZ01VkLc93gu/Ed
+AkAOhujZrrKV5CzBKutKLb0GVyVWmdC7InoNSMZEeGU72rT96IjM59YzoqmD0pGM
+3I1o4cGqg1D1DfM1rQlnN1eSAkBq6xXfEDwJ1mLNxF6q8Zm/ugFYWR5xcX/3wFiT
+b4+EjHP/DbNh9Vm5wcfnDBJ1zKvrMEf2xqngYdrV/3CiGJeKAhRvL57QvJZcQGvn
+ISNX5cMzFHRW3Q==
+-----END DSA PRIVATE KEY-----
+  _end_of_pem_
+
+  TEST_KEY_DSA512 = OpenSSL::PKey::DSA.new <<-_end_of_pem_
+-----BEGIN DSA PRIVATE KEY-----
+MIH4AgEAAkEA5lB4GvEwjrsMlGDqGsxrbqeFRh6o9OWt6FgTYiEEHaOYhkIxv0Ok
+RZPDNwOG997mDjBnvDJ1i56OmS3MbTnovwIVAJgub/aDrSDB4DZGH7UyarcaGy6D
+AkB9HdFw/3td8K4l1FZHv7TCZeJ3ZLb7dF3TWoGUP003RCqoji3/lHdKoVdTQNuR
+S/m6DlCwhjRjiQ/lBRgCLCcaAkEAjN891JBjzpMj4bWgsACmMggFf57DS0Ti+5++
+Q1VB8qkJN7rA7/2HrCR3gTsWNb1YhAsnFsoeRscC+LxXoXi9OAIUBG98h4tilg6S
+55jreJD3Se3slps=
+-----END DSA PRIVATE KEY-----
+  _end_of_pem_
+
+if defined?(OpenSSL::PKey::EC)
+
+  TEST_KEY_EC_P256V1 = OpenSSL::PKey::EC.new <<-_end_of_pem_
+-----BEGIN EC PRIVATE KEY-----
+MHcCAQEEIID49FDqcf1O1eO8saTgG70UbXQw9Fqwseliit2aWhH1oAoGCCqGSM49
+AwEHoUQDQgAEFglk2c+oVUIKQ64eZG9bhLNPWB7lSZ/ArK41eGy5wAzU/0G51Xtt
+CeBUl+MahZtn9fO1JKdF4qJmS39dXnpENg==
+-----END EC PRIVATE KEY-----
+  _end_of_pem_
+
+end
+
+  TEST_KEY_DH512_PUB = OpenSSL::PKey::DH.new <<-_end_of_pem_
+-----BEGIN DH PARAMETERS-----
+MEYCQQDmWXGPqk76sKw/edIOdhAQD4XzjJ+AR/PTk2qzaGs+u4oND2yU5D2NN4wr
+aPgwHyJBiK1/ebK3tYcrSKrOoRyrAgEC
+-----END DH PARAMETERS-----
+  _end_of_pem_
+
+  TEST_KEY_DH1024 = OpenSSL::PKey::DH.new <<-_end_of_pem_
+-----BEGIN DH PARAMETERS-----
+MIGHAoGBAKnKQ8MNK6nYZzLrrcuTsLxuiJGXoOO5gT+tljOTbHBuiktdMTITzIY0
+pFxIvjG05D7HoBZQfrR0c92NGWPkAiCkhQKB8JCbPVzwNLDy6DZ0pmofDKrEsYHG
+AQjjxMXhwULlmuR/K+WwlaZPiLIBYalLAZQ7ZbOPeVkJ8ePao0eLAgEC
+-----END DH PARAMETERS-----
+  _end_of_pem_
+
+  TEST_KEY_DH1024.priv_key = OpenSSL::BN.new("48561834C67E65FFD2A9B47F41E5E78FDC95C387428FDB1E4B0188B64D1643C3A8D3455B945B7E8C4D166010C7C2CE23BFB9BEF43D0348FE7FA5284B0225E7FE1537546D114E3D8A4411B9B9351AB451E1A358F50ED61B1F00DA29336EEBBD649980AC86D76AF8BBB065298C2052672EEF3EF13AB47A15275FC2836F3AC74CEA", 16)
+
+  DSA_SIGNATURE_DIGEST = OpenSSL::OPENSSL_VERSION_NUMBER > 0x10000000 ?
+                         OpenSSL::Digest::SHA1 :
+                         OpenSSL::Digest::DSS1
+
+  module_function
+
+  def issue_cert(dn, key, serial, not_before, not_after, extensions,
+                 issuer, issuer_key, digest)
+    cert = OpenSSL::X509::Certificate.new
+    issuer = cert unless issuer
+    issuer_key = key unless issuer_key
+    cert.version = 2
+    cert.serial = serial
+    cert.subject = dn
+    cert.issuer = issuer.subject
+    cert.public_key = key.public_key
+    cert.not_before = not_before
+    cert.not_after = not_after
+    ef = OpenSSL::X509::ExtensionFactory.new
+    ef.subject_certificate = cert
+    ef.issuer_certificate = issuer
+    extensions.each{|oid, value, critical|
+      cert.add_extension(ef.create_extension(oid, value, critical))
+    }
+    cert.sign(issuer_key, digest)
+    cert
+  end
+
+  def issue_crl(revoke_info, serial, lastup, nextup, extensions,
+                issuer, issuer_key, digest)
+    crl = OpenSSL::X509::CRL.new
+    crl.issuer = issuer.subject
+    crl.version = 1
+    crl.last_update = lastup
+    crl.next_update = nextup
+    revoke_info.each{|rserial, time, reason_code|
+      revoked = OpenSSL::X509::Revoked.new
+      revoked.serial = rserial
+      revoked.time = time
+      enum = OpenSSL::ASN1::Enumerated(reason_code)
+      ext = OpenSSL::X509::Extension.new("CRLReason", enum)
+      revoked.add_extension(ext)
+      crl.add_revoked(revoked)
+    }
+    ef = OpenSSL::X509::ExtensionFactory.new
+    ef.issuer_certificate = issuer
+    ef.crl = crl
+    crlnum = OpenSSL::ASN1::Integer(serial)
+    crl.add_extension(OpenSSL::X509::Extension.new("crlNumber", crlnum))
+    extensions.each{|oid, value, critical|
+      crl.add_extension(ef.create_extension(oid, value, critical))
+    }
+    crl.sign(issuer_key, digest)
+    crl
+  end
+
+  def get_subject_key_id(cert)
+    asn1_cert = OpenSSL::ASN1.decode(cert)
+    tbscert   = asn1_cert.value[0]
+    pkinfo    = tbscert.value[6]
+    publickey = pkinfo.value[1]
+    pkvalue   = publickey.value
+    OpenSSL::Digest::SHA1.hexdigest(pkvalue).scan(/../).join(":").upcase
+  end
+
+  def silent
+    begin
+      back, $VERBOSE = $VERBOSE, nil
+      yield
+    ensure
+      $VERBOSE = back
+    end
+  end
+
+  class OpenSSL::SSLTestCase < Test::Unit::TestCase
+    RUBY = EnvUtil.rubybin
+    ITERATIONS = ($0 == __FILE__) ? 100 : 10
+
+    def setup
+      @ca_key  = OpenSSL::TestUtils::TEST_KEY_RSA2048
+      @svr_key = OpenSSL::TestUtils::TEST_KEY_RSA1024
+      @cli_key = OpenSSL::TestUtils::TEST_KEY_DSA256
+      @ca  = OpenSSL::X509::Name.parse("/DC=org/DC=ruby-lang/CN=CA")
+      @svr = OpenSSL::X509::Name.parse("/DC=org/DC=ruby-lang/CN=localhost")
+      @cli = OpenSSL::X509::Name.parse("/DC=org/DC=ruby-lang/CN=localhost")
+      now = Time.at(Time.now.to_i)
+      ca_exts = [
+        ["basicConstraints","CA:TRUE",true],
+        ["keyUsage","cRLSign,keyCertSign",true],
+      ]
+      ee_exts = [
+        ["keyUsage","keyEncipherment,digitalSignature",true],
+      ]
+      @ca_cert  = issue_cert(@ca, @ca_key, 1, now, now+3600, ca_exts, nil, nil, OpenSSL::Digest::SHA1.new)
+      @svr_cert = issue_cert(@svr, @svr_key, 2, now, now+1800, ee_exts, @ca_cert, @ca_key, OpenSSL::Digest::SHA1.new)
+      @cli_cert = issue_cert(@cli, @cli_key, 3, now, now+1800, ee_exts, @ca_cert, @ca_key, OpenSSL::Digest::SHA1.new)
+      @server = nil
+    end
+
+    def teardown
+    end
+
+    def issue_cert(*arg)
+      OpenSSL::TestUtils.issue_cert(*arg)
+    end
+
+    def issue_crl(*arg)
+      OpenSSL::TestUtils.issue_crl(*arg)
+    end
+
+    def readwrite_loop(ctx, ssl)
+      while line = ssl.gets
+        if line =~ /^STARTTLS$/
+          ssl.accept
+          next
+        end
+        ssl.write(line)
+      end
+    rescue OpenSSL::SSL::SSLError
+    rescue IOError
+    ensure
+      ssl.close rescue nil
+    end
+
+    def server_loop(ctx, ssls, stop_pipe_r, ignore_listener_error, server_proc, threads)
+      loop do
+        ssl = nil
+        begin
+          readable, = IO.select([ssls, stop_pipe_r])
+          if readable.include? stop_pipe_r
+            return
+          end
+          ssl = ssls.accept
+        rescue OpenSSL::SSL::SSLError
+          if ignore_listener_error
+            retry
+          else
+            raise
+          end
+        end
+
+        th = Thread.start do
+          server_proc.call(ctx, ssl)
+        end
+        threads << th
+      end
+    rescue Errno::EBADF, IOError, Errno::EINVAL, Errno::ECONNABORTED, Errno::ENOTSOCK, Errno::ECONNRESET
+      if !ignore_listener_error
+        raise
+      end
+    end
+
+    def start_server(verify_mode, start_immediately, args = {}, &block)
+      IO.pipe {|stop_pipe_r, stop_pipe_w|
+        ctx_proc = args[:ctx_proc]
+        server_proc = args[:server_proc]
+        ignore_listener_error = args.fetch(:ignore_listener_error, false)
+        use_anon_cipher = args.fetch(:use_anon_cipher, false)
+        server_proc ||= method(:readwrite_loop)
+
+        store = OpenSSL::X509::Store.new
+        store.add_cert(@ca_cert)
+        store.purpose = OpenSSL::X509::PURPOSE_SSL_CLIENT
+        ctx = OpenSSL::SSL::SSLContext.new
+        ctx.ciphers = "ADH-AES256-GCM-SHA384" if use_anon_cipher
+        ctx.cert_store = store
+        #ctx.extra_chain_cert = [ ca_cert ]
+        ctx.cert = @svr_cert
+        ctx.key = @svr_key
+        ctx.tmp_dh_callback = proc { OpenSSL::TestUtils::TEST_KEY_DH1024 }
+        ctx.verify_mode = verify_mode
+        ctx_proc.call(ctx) if ctx_proc
+
+        Socket.do_not_reverse_lookup = true
+        tcps = nil
+        tcps = TCPServer.new("127.0.0.1", 0)
+        port = tcps.connect_address.ip_port
+
+        ssls = OpenSSL::SSL::SSLServer.new(tcps, ctx)
+        ssls.start_immediately = start_immediately
+
+        threads = []
+        begin
+          server = Thread.new do
+            begin
+              server_loop(ctx, ssls, stop_pipe_r, ignore_listener_error, server_proc, threads)
+            ensure
+              tcps.close
+            end
+          end
+          threads.unshift server
+
+          $stderr.printf("SSL server started: pid=%d port=%d\n", $$, port) if $DEBUG
+
+          client = Thread.new do
+            begin
+              block.call(server, port.to_i)
+            ensure
+              stop_pipe_w.close
+            end
+          end
+          threads.unshift client
+        ensure
+          assert_join_threads(threads)
+        end
+      }
+    end
+
+    def starttls(ssl)
+      ssl.puts("STARTTLS")
+      sleep 1   # When this line is eliminated, process on Cygwin blocks
+                # forever at ssl.connect. But I don't know why it does.
+      ssl.connect
+    end
+  end
+
+end if defined?(OpenSSL::OPENSSL_LIBRARY_VERSION) and
+  /\AOpenSSL +0\./ !~ OpenSSL::OPENSSL_LIBRARY_VERSION
-- 
cgit v1.2.3-70-g09d2