diff --git a/.github/workflows/ci-test.yml b/.github/workflows/ci-test.yml
new file mode 100644
index 00000000..fcea30ed
--- /dev/null
+++ b/.github/workflows/ci-test.yml
@@ -0,0 +1,77 @@
+name: rake test
+
+on: [push, pull_request]
+
+env:
+ JAVA_OPTS: '-XX:+TieredCompilation -XX:TieredStopAtLevel=1'
+ JRUBY_OPTS: '-J-ea'
+
+jobs:
+
+ maven-test:
+ runs-on: ubuntu-22.04
+
+ strategy:
+ matrix:
+ ruby-version: [ jruby-9.4.13.0 ]
+ java-version: [ 8, 11, 21, 23 ]
+ distribution: [ temurin ]
+ include:
+ - java-version: 8
+ distribution: temurin
+ ruby-version: jruby-9.2.19.0
+ - java-version: 11
+ distribution: temurin
+ ruby-version: jruby-9.2.20.1
+ - java-version: 8
+ distribution: temurin
+ ruby-version: jruby-9.3.3.0
+ - java-version: 11
+ distribution: temurin
+ ruby-version: jruby-9.3.13.0
+ - java-version: 21
+ distribution: oracle
+ ruby-version: jruby-9.3.13.0
+ - java-version: 11
+ distribution: zulu
+ ruby-version: jruby-9.4.5.0
+ - java-version: 21
+ distribution: oracle
+ ruby-version: jruby-9.4.8.0
+ - java-version: 21
+ distribution: temurin
+ ruby-version: jruby-10.0.1.0
+ - java-version: 24
+ distribution: zulu
+ ruby-version: jruby-10.0.1.0
+ - java-version: 21
+ distribution: corretto
+ ruby-version: jruby-head # 10.0
+ fail-fast: false
+
+ steps:
+ - name: checkout
+ uses: actions/checkout@v2
+
+ - name: set up java ${{ matrix.java-version }}
+ uses: actions/setup-java@v4
+ with:
+ java-version: ${{ matrix.java-version }}
+ distribution: ${{ matrix.distribution }}
+
+ - name: set up ${{ matrix.ruby-version }}
+ uses: ruby/setup-ruby@v1
+ with:
+ ruby-version: ${{ matrix.ruby-version }}
+
+ - name: install bundler
+ run: jruby -S gem install bundler -v "~>2.2.28"
+
+ - name: bundle install
+ run: jruby -S bundle install
+
+ - name: rake test_prepare
+ run: jruby -rbundler/setup -S rake test_prepare
+
+ - name: rake test
+ run: jruby -rbundler/setup -S rake test
diff --git a/.github/workflows/ci-test_provider.yml b/.github/workflows/ci-test_provider.yml
new file mode 100644
index 00000000..42a17c83
--- /dev/null
+++ b/.github/workflows/ci-test_provider.yml
@@ -0,0 +1,56 @@
+name: rake test (with provider)
+
+on: [push, pull_request]
+
+env:
+ JAVA_OPTS: '-Djruby.openssl.provider.register=true -Djruby.openssl.warn=true '
+ JRUBY_OPTS: '-J-ea -J--add-opens=java.base/java.security=org.jruby.dist -Xjit.threshold=0'
+
+jobs:
+
+ maven-test:
+ runs-on: ubuntu-24.04 # ubuntu-latest
+
+ strategy:
+ matrix:
+ ruby-version: [ jruby-9.4.12.0 ]
+ java-version: [ 21, 23 ]
+ distribution: [ temurin, oracle ]
+ include:
+ - ruby-version: jruby-9.4.8.0
+ java-version: 11
+ distribution: corretto
+ - ruby-version: jruby-9.4.8.0
+ java-version: 11
+ distribution: zulu
+ - ruby-version: jruby-9.4.8.0
+ java-version: 11
+ distribution: temurin
+ fail-fast: false
+
+ steps:
+ - name: checkout
+ uses: actions/checkout@v2
+
+ - name: set up java ${{ matrix.java-version }}
+ uses: actions/setup-java@v4
+ with:
+ java-version: ${{ matrix.java-version }}
+ distribution: ${{ matrix.distribution }}
+
+ - name: set up ${{ matrix.ruby-version }}
+ uses: ruby/setup-ruby@v1
+ with:
+ ruby-version: ${{ matrix.ruby-version }}
+
+ - name: install bundler
+ run: jruby -S gem install bundler -v "~>2.2.28"
+
+ - name: bundle install
+ run: jruby -S bundle install
+
+ - name: rake test_prepare
+ run: jruby -rbundler/setup -S rake test_prepare
+
+ - name: rake test
+ run: jruby -rbundler/setup -S rake test
diff --git a/.mvn/extensions.xml b/.mvn/extensions.xml
new file mode 100644
index 00000000..86abdc76
--- /dev/null
+++ b/.mvn/extensions.xml
@@ -0,0 +1,13 @@
+
+
+
+ org.jruby.maven
+ mavengem-wagon
+ 2.0.2
+
+
+ io.takari.polyglot
+ polyglot-ruby
+ 0.7.0
+
+
diff --git a/.mvn/wrapper/maven-wrapper.properties b/.mvn/wrapper/maven-wrapper.properties
new file mode 100644
index 00000000..70f4f50f
--- /dev/null
+++ b/.mvn/wrapper/maven-wrapper.properties
@@ -0,0 +1,18 @@
+# Licensed to the Apache Software Foundation (ASF) under one
+# or more contributor license agreements. See the NOTICE file
+# distributed with this work for additional information
+# regarding copyright ownership. The ASF licenses this file
+# to you under the Apache License, Version 2.0 (the
+# "License"); you may not use this file except in compliance
+# with the License. You may obtain a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing,
+# software distributed under the License is distributed on an
+# "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+# KIND, either express or implied. See the License for the
+# specific language governing permissions and limitations
+# under the License.
+distributionUrl=https://repo.maven.apache.org/maven2/org/apache/maven/apache-maven/3.8.8/apache-maven-3.8.8-bin.zip
+wrapperUrl=https://repo.maven.apache.org/maven2/org/apache/maven/wrapper/maven-wrapper/3.2.0/maven-wrapper-3.2.0.jar
diff --git a/.travis.yml b/.travis.yml
deleted file mode 100644
index 1003fa7d..00000000
--- a/.travis.yml
+++ /dev/null
@@ -1,87 +0,0 @@
-language: ruby
-
-dist: precise # due OpenJDK 7
-jdk:
- - openjdk7
- - oraclejdk8
-
-env:
- - TEST_PROFILE=test-1.7.20
- - TEST_PROFILE=test-1.7.26
- - TEST_PROFILE=test-9.0.5.0
- - TEST_PROFILE=test-9.1.8.0
- - TEST_PROFILE=test-9.1.17.0
-
-before_install:
- - unset _JAVA_OPTIONS
- - rvm @default,@global do gem uninstall bundler -a -x -I || true
- - gem install bundler -v "~>1.17.3"
-
-install: if [[ -v BUNDLE_INSTALL ]]; then jruby -S bundle install; else echo ""; fi
-
-script: if [[ -v TEST_COMMAND ]]; then $TEST_COMMAND; else mvn verify -P $TEST_PROFILE; fi
-
-matrix:
- allow_failures:
- - jdk: openjdk7
- - jdk: oraclejdk11
- env: TEST_COMMAND="jruby -S rake integration:install integration:test"
- - rvm: jruby-head
- include:
- # since maven runit fails to boot with test-unit being a default gem on 9.2 :
- - jdk: oraclejdk8
- env: TEST_COMMAND="jruby -rbundler/setup -S rmvn test-compile && jruby -S rake test" BUNDLE_INSTALL=true
- rvm: jruby-9.2.5.0
- - jdk: oraclejdk8
- env: TEST_COMMAND="jruby -rbundler/setup -S rmvn test-compile && jruby -S rake test" BUNDLE_INSTALL=true
- rvm: jruby-9.2.9.0
- #
- - jdk: openjdk7
- env: TEST_COMMAND="jruby -rbundler/setup -S rmvn test-compile && jruby -S rake test" BUNDLE_INSTALL=true
- rvm: jruby-1.7.24
- - jdk: oraclejdk8
- env: TEST_COMMAND="jruby -rbundler/setup -S rmvn test-compile && jruby -S rake test" BUNDLE_INSTALL=true
- rvm: jruby-1.7.27
- - jdk: openjdk7
- env: TEST_COMMAND="jruby -rbundler/setup -S rmvn test-compile && jruby -S rake test" BUNDLE_INSTALL=true
- rvm: jruby-1.7.27
- #
- - jdk: openjdk7
- env: TEST_COMMAND="jruby -rbundler/setup -S rmvn verify -P test-1.7.26" BUNDLE_INSTALL=true RUBY_MAVEN_VERSION=3.3.8
- rvm: jruby-1.7.26
- - jdk: oraclejdk8
- env: TEST_COMMAND="jruby -rbundler/setup -S rmvn test-compile && jruby -S rake test" BUNDLE_INSTALL=true
- rvm: jruby-9.2.8.0
- - jdk: oraclejdk11
- env: TEST_COMMAND="jruby -rbundler/setup -S rmvn test-compile && jruby -S rake test" BUNDLE_INSTALL=true
- rvm: jruby-9.2.9.0
- - jdk: oraclejdk8
- env: TEST_COMMAND="jruby -rbundler/setup -S rmvn test-compile && jruby -S rake test" BUNDLE_INSTALL=true
- rvm: jruby-head
- - jdk: oraclejdk11
- env: TEST_COMMAND="jruby -rbundler/setup -S rmvn test-compile && jruby -S rake test" BUNDLE_INSTALL=true
- rvm: jruby-head
- #
- - jdk: oraclejdk8
- env: TEST_COMMAND="jruby -S rake integration:install integration:test"
- rvm: jruby-1.7.26
- - jdk: openjdk7
- env: TEST_COMMAND="jruby -S rake integration:install integration:test"
- rvm: jruby-1.7.27
- - jdk: oraclejdk8
- env: TEST_COMMAND="jruby -S rake integration:install integration:test"
- rvm: jruby-9.2.8.0
- - jdk: openjdk7
- env: TEST_COMMAND="jruby -S rake integration:install integration:test"
- rvm: jruby-9.1.17.0
- - jdk: oraclejdk11
- env: TEST_COMMAND="jruby -S rake integration:install integration:test"
- rvm: jruby-9.2.9.0
-notifications:
- irc:
- channels:
- - "irc.freenode.org#jruby"
- on_success: change
- template:
- - "%{repository} (%{branch}:%{commit} by %{author}): %{message} (%{build_url})"
- skip_join: true
diff --git a/BUILDING.md b/BUILDING.md
index 4a98c003..ff035177 100644
--- a/BUILDING.md
+++ b/BUILDING.md
@@ -10,15 +10,19 @@ If you're coming from a Ruby world and do not have Maven setup, you can alternat
### Building
-The usual `mvn package` builds a .gem that includes the JRuby extension .jar
+The usual `./mvnw package -Dmaven.test.skip=true` builds a .gem that includes the JRuby extension .jar
+There's a rake target as well that shells out: `jruby -S rake jar`
### Testing
-Tests `mvn test` are run by default with Maven using JRuby plugins.
-When the ext .jar is build (`rake jar` or `mvn package -Dmaven.test.skip=true`)
-one can run a tests Ruby-style e.g. `jruby -Ilib:. src/test/ruby/test_bn.rb`
+NOTE: the ext .jar needs to be build (see the Building section above on `rake jar`)
+The full unit test suite can be boostraped using Rake: `jruby -S rake test`
+
+Tests can also be run individually e.g. `jruby -Ilib:src/test/ruby src/test/ruby/test_bn.rb`
+
+NOTE: make sure to **-Ilib** otherwise you end up using the OpenSSL default gem shipped with JRuby.
### Releasing
@@ -28,39 +32,13 @@ one can run a tests Ruby-style e.g. `jruby -Ilib:. src/test/ruby/test_bn.rb`
make sure [pom.xml](pom.xml) is regenerated e.g. using `rmvn validate`
and `git commit` the changes
-* (optional) signing artifacts is preferred thus find your GPG key
-
-* `mvn -Prelease -DupdateReleaseInfo=true -Dgpg.keyname=A7A374B9 clean deploy`
-
-* (advised) examine and close the repository to push it to Sonatype's staging
-
-* (advised) run JRuby's full suite using the staged new jruby-openssl gem
- e.g. https://github.com/jruby/jruby/commit/1df6315e9145195f19ad862be5e3a5
-
-* (advised) release the staging repository at Sonatype's if all is well
-
-* (optional) update JRuby to bundle new jruby-openssl gem (remove staging)
- e.g. https://github.com/jruby/jruby/commit/8750e736491825eec1d1954a07d492
+* `./mvnw -Prelease -DupdateReleaseInfo=true -Dmaven.test.skip=true clean package`
* gem push the build gem from pkg/ e.g. `gem push pkg/jruby-openssl-0.9.15.gem`
* tag the release e.g. `git tag v0.9.15`
* update `VERSION` to next SNAPSHOT (e.g. `"0.9.16.dev"`) and commit
- make sure [pom.xml](pom.xml) is regenerated (`rmvn validate`)
+ make sure [pom.xml](pom.xml) is regenerated (`./mvnw validate`)
* `git push origin master --tags`
-
-* (advised) ... take the rest of the day off!
-
-
-#### Manually Deploying
-
-When a release went by only pushing to http://rubygems.org/ one can still push
-to Sonatype's Maven repos, rename *jruby-openssl-x.x.x-java.gem* (when it is
-downloaded from https://rubygems.org/gems/jruby-openssl) to follow Maven's
-naming conventions (stripping the *-java* suffix) and "mvn deploy" by hand :
-
-```
-mvn deploy:deploy-file -Dfile=jruby-openssl-0.9.15.gem -DpomFile=pom.xml -DrepositoryId=ossrh -Durl=https://oss.sonatype.org/service/local/staging/deploy/maven2/
-```
\ No newline at end of file
diff --git a/Gemfile b/Gemfile
index 72781685..6f4de2aa 100644
--- a/Gemfile
+++ b/Gemfile
@@ -1,10 +1,10 @@
source 'https://rubygems.org'
# Specify your gem's dependencies in the gemspec
-gemspec
+gemspec if defined? JRUBY_VERSION
-# for less surprises with newer releases
-gem 'jar-dependencies', '~> 0.3.11', :require => nil
+gem "rake", require: false
+gem 'mocha', '~> 1.4', '< 2.0'
-# for the rake task
-gem 'ruby-maven', ENV['RUBY_MAVEN_VERSION'] || '~> 3.3.8'
+# NOTE: runit-maven-plugin will use it's own :
+gem 'test-unit'
diff --git a/History.md b/History.md
index 10992eaf..d81fc667 100644
--- a/History.md
+++ b/History.md
@@ -1,3 +1,264 @@
+## 0.15.5
+
+* [deps] upgrade BC to version 1.81
+* Improving completeness of ASN1 encoding/decoding (#335)
+* [fix] OpenSSL::X509::CRL#to_pem when building CRL from scratch (#163)
+* [fix] OpenSSL::ASN1::ASN1Data encoding/decoding compatibility (#265)
+
+## 0.15.4
+
+* Verify hostname by default
+
+This addresses **CVE-2025-46551** and **GHSA-72qj-48g4-5xgx**.
+
+Users can work around this by applying this patch manually to their
+own jruby-openssl and jruby installs, or by re-enabling hostname
+verification with the following code early in application boot:
+```ruby
+require 'openssl'
+
+OpenSSL::SSL::SSLContext::DEFAULT_PARAMS[:verify_hostname] = true
+```
+
+## 0.15.3
+
+* [fix] keep curve name when group is set into another key
+* [fix] make sure `OpenSSL::PKey::EC#dup` (copying) works
+* [compat] make sure `OpenSSL::PKey::EC#generate_key!` exists
+* [compat] missing OpenSSL:BN `to_int`, `-@`, `+@`, `abs`, `negative?`
+* [compat] implement PKey::EC `public_to_pem` and `xxx_to_der`
+* [fix] initialize @unused_bits = 0 for BitString
+* [fix] raise ASN1Error when unused_bits out of range
+* [fix] respect @unused_bits in BitString (#323)
+* [fix] missing `OpenSSL::ASN1::ObjectId#==` (#311)
+* [compat] implement PKey::DSA `public_to_der` and `public_to_pem`
+* [compat] implement PKey::RSA `public_to_der` and `public_to_pem`
+* [fix] DSA private key should generate after `set_key`
+* [refactor] RSA key internals to always consider params
+* [fix] DSA key compatibility when `set_pqg`
+* [fix] RSA private key should generate after `set_key`
+* [compat] add private? and public? methods on `PKey::EC`
+
+## 0.15.2
+
+* [deps] upgrade BC to version 1.79
+* [fix] avoid PKey::EC.new failing with specific DER (#318)
+* [fix] have a useful OPENSSL_VERSION_NUMBER
+
+## 0.15.1
+
+* [deps] upgrade BC to version 1.78.1
+
+## 0.15.0
+
+This version upgraded to latest Bouncy-Castle (1.78) and the minimum supported
+JRuby is now 9.2.
+
+* [refactor] propagate IOError from selector exception
+* [fix] convert IOException to Ruby exception correctly
+ follow up on the fix (#242) in 0.14.6
+* [fix] implement `OpenSSL::PKey::EC::Point#mul` and `#add` (#307)
+* [fix] ASN.1 BitString pad bits being out of range
+* [compat] support base64digest on `OpenSSL::HMAC`
+* [compat] add `Buffering#getbyte` for `SSLSocket`
+* [refactor] drop (unused) Config native impl
+* [refactor] less locking when there's a shared SSLContext
+* [fix] encoding of ASN1::Null primitive to_der
+* [fix] ASN.1 tagged object tag-class encoding/decoding
+* [fix] ASN1 primitive tagging (encoding) part (#122)
+* [fix] encoding/decoding of all ASN1 string types
+* [fix] ASN1Data encoding with Array primitive value (#119)
+* [refactor] drop security restriction JCE work-around
+* [refactor] drop long deprecated OpenSSLReal Java class
+* [deps] upgrade BC to version 1.78
+
+## 0.14.6
+
+* [compat] OpenSSL::ConfigError and DEFAULT_CONFIG_FILE (#304)
+* [fix] `OpenSSL::PKey::DH#set_pqg` regression (#300)
+* Convert `IOException` to Ruby exception correctly (#242)
+* [refactor] add exception debugging within SSLSocket#waitSelect
+* [fix] sync `SSLContext#setup` as it could be shared (#302)
+* [refactor] organize i-var sets (set `@context` after setup)
+
+## 0.14.5
+
+* [fix] `OpenSSL::X509::Request#verify` with DSA public key
+ (this was a regression introduced in JOSSL 0.14.4)
+
+## 0.14.4
+
+* [fix] convert `OpenSSL::ASN1::Sequence` to an array on #to_der (#265)
+* [feat] implement `PKey::DH.generate` and (dummy) `q` reader (#254)
+* [fix] raise `TypeError` when arg isn't a `Group`
+* [refactor] make sure `ASN1Error` has native cause
+* [fix] stop assuming (JDK) EC key identifier
+ "EC" with Sun provider but "ECDSA" with BC
+* [fix] do not check empty string as curve name
+* [fix] make sure `PKeyEC#group.curve_name` is always set
+* [refactor] `PKey.read` to use BC fully when reading public keys
+* [fix] `OpenSSL::X509::CRL#sign` to accept string digest
+* [fix] `OpenSSL::X509::Request#version` default is -1
+* [fix] resolving EC key from `X509::Request.new(pem)`
+* [feat] implement `OpenSSL::X509::Request#signature_algorithm`
+* [fix] work-around CSR failing with EC key (#294)
+* [feat] implement `OpenSSL::PKey::EC#to_text` (#280)
+* [feat] partial support for `PKey::EC::Point#to_octet_string(form)`
+* [feat] implement `OpenSSL::PKCS7::SignerInfo#signed_time` (#269)
+* [feat] implement #oid method for `PKey` classes (#281)
+* [fix] raise `PKeyError` from `PKey.read` when no key (#285)
+* [fix] restore PKCS#8 EC key handling (see #292)
+* [fix] revert `readPrivateKey` so public key is not lost (#292)
+
+## 0.14.3
+
+* [fix] `SSLSocket#alpn_protocol` to be nil when not used (#287)
+* [feat] try resolving curve-name from EC public key
+* [feat] implement missing `PKey::EC#dsa_verify_asn1` (#241)
+* [feat] implement support for `PKey::EC.generate` (#255)
+* [refactor] make sure curveName is set when using `PKey.read` (#289)
+* [fix] add `Cipher#auth_data(arg)` override (Rails 7.x compatibility) (#290)
+* [fix] raise `TypeError` when arg not of expected type (jruby/jruby#7875)
+
+## 0.14.2
+
+* [deps] upgrade BC to latest 1.74
+* [fix] for CRL verify when signed with EC key (#276)
+* [fix] `OpenSSL::X509::Certificate#public_key` raises for EC keys (#273)
+
+## 0.14.1
+
+* [refactor] improve performance of Diffie-Hellman key exchange (#272)
+* Try to use JDK console to prompt for pass (#270)
+* [fix] for PKCS8 EC private key support (#267)
+* ~~"[fix] handle potential buffer overflow on write" (#242)~~
+
+## 0.14.1 (CR2)
+
+* [fix] Java's default session timeout in 24h
+* [fix] handle ArgumentError on `SSLSession#timeout=`
+* [fix] handle potential buffer overflow on write (#242)
+* [fix] buffer overflow after wrap-ing data - wait
+* [refactor] try a few tricks to detect session re-use
+
+## 0.14.0
+
+This version upgraded to latest Bouncy-Castle (1.71) and is only compatible with
+the new version mostly due artifact naming and breaking chances in BC itself.
+
+* [deps] upgrade BC to latest 1.71
+* [fix] make set_minmax_proto_version private
+
+## 0.13.0
+
+* [fix] ASN1::EndOfContent ancestor hierarchy (#228)
+* [fix] handle X509::Name type conversion (#206)
+* [fix] handle invalid type when creating `X509::Name`
+* [fix] `OpenSSL::X509::Name#inspect` compatibility
+* [fix] escaping with `OpenSSL::X509::Name::RFC2253`
+* [feat] implement `OpenSSL::X509::Name#to_utf8`
+* [fix] compat missing `OpenSSL::SSL::OP_NO_TLSv1_3`
+* [refactor] performance - do not encode/decode cert objects
+* [fix] make sure `Context.ciphers` are not mutated (#219)
+* [feat] support `to_java` conversion for CRL
+* [feat] support `to_java` protocol for PKey (#250)
+
+## 0.12.2
+
+* [fix] work-around JRuby 9.2 autoload behavior (#248)
+ to be able to install jruby-openssl >= 0.12 on JRuby 9.2
+ while the default gem (shipped with JRuby) is < 0.12
+* [feat] support alpn negotiation in ssl context (#247)
+* [feat] support Java cipher names on `SSLContext#ciphers=`
+* [fix] properly handle `require_jar` fallback
+
+## 0.12.1
+
+* improved compatibility with the openssl gem (version 2.2.1)
+* JOSSL now ships with a single set of openssl .rb files
+ - providing compat with `required_ruby_version = '>= 2.3.0'`
+ - flat set of .rb files at *lib/openssl/* (based on openssl gem)
+* revisited `OpenSSL::SSL::SSLContext::DEFAULT_PARAMS` defaults
+ - implicit `verify_hostname` default .rb callback still a noop
+ - TLS continues to rely on the Java SSL engine for hostname checks
+* working TLS 1.3 support
+* droped Java 1.7 support (at least Java 8 needed to use the gem)
+* fixed `SSLContext#options` matches C OpenSSL (using `OP_ALL`)
+* no longer filter out SSLv2 (for improved OpenSSL compatibility)
+* implemented naive `SSLContext#ciphers` caching to speed-up TLS
+* `StoreError` raised due a Java exception now retain native cause
+
+## 0.12.0 (yanked)
+
+There were Java 8 and JRuby 9.3 regressions in this release, use 0.12.1 instead.
+
+## 0.11.0
+
+NOTE: This release aims to adapt the certificate verification logic to be aligned
+with OpenSSL 1.1.1 as a resolution to issues due *DST Root CA X3* expiration, more
+details at: https://letsencrypt.org/docs/dst-root-ca-x3-expiration-september-2021/
+
+The port is expected to be superior compared to the simple legacy verification,
+however in case of issues the previous algorithm is still around and can be toggled
+using `JRUBY_OPTS="-J-Djruby.openssl.x509.store.verify=legacy"` system property.
+
+* **OpenSSL 1.1.1 cert verification port** (fixes #236) (#239)
+ - as a side-effect part of the PR to "allow multiple certs with same SubjectDN"
+ (#198) got reverted, this has been causing verification regressions (since 0.10.5)
+ for some users (#232) and is expected to be fixed
+* [fix] replace deprecated getPeerCertificateChain (#231)
+
+## 0.10.7
+
+* [feat] upgrade BC library to 1.68
+* [fix] SSLContext#ciphers= (fixes #221 and jruby/jruby#3100) (#222)
+* [fix] Java::JavaLang::StringIndexOutOfBoundsException on ctx.cipher=[] (fixes #220) (#223)
+* [fix] SSLContext#ciphers= compatibility (fixes #223) (#220)
+* [fix] Match OpenSSL::X509::Name.hash implementation with Ruby (#216, #218)
+* [fix] OpenSSL::SSL::SSLContext#min_version= failure (#215)
+* [fix] adds OpenSSL::Cipher#iv_len= setter (#208)
+
+## 0.10.6 (yanked)
+
+Due several regressions please update to version 0.10.7 or higher.
+
+## 0.10.5
+
+* [fix] EC key sign/verify (#193)
+* [feat] upgrade BC library to 1.65
+* [refactor] clean security helpers to avoid reflection (#197)
+* Just use normal getInstance to get KeyFactory (fixes #197)
+* Allow multiple Certificates with the same SubjectDN in the store (#198)
+* Try direct path for MessageDigest before invasive path (#194)
+ (relates to jruby/jruby#6098)
+* [refactor] avoid NativeException usage (jruby/jruby#5646)
+
+## 0.10.4
+
+* Use CertificateFactory.getInstance rather than reflection
+ eliminates one of the module warnings we have been seeing (#161)
+
+## 0.10.3
+
+* [fix] implement (missing) PKey::DSA#params
+* [fix] authorityKeyIdentifier ext (general-name) value
+* [fix] authority keyid extension's :always part optional (#174)
+* [fix] work-around for not setting certificate serial
+ raise a more friendly error (jruby/jruby#1691)
+* [fix] PKey.read not parsing RSA pub-key (#176)
+* [feat] support reading DSA (public key) in full DER
+* [fix] RSA key DER format to closely follow OpenSSL
+* [fix] add missing ASN1 factory methods (Null, EndOfContent)
+* [fix] support getting password from block for PKeys
+* [fix] incorrect ASN.1 for wrapped Integer type
+* [fix] correct public key for subjectKeyIdentifier ext (#173)
+* [fix] invalid Cert#sign handling -> raise (instead of ClassCastException)
+* [feat] more TLS (GCM) ciphers - supported on Java 8+
+* [feat] add ECDHE-RSA-AES128-GCM-SHA256 as supported cipher (#185)
+* [feat] add support for ECDHE-RSA-AES256-GCM-SHA384 (#187)
+* [fix] try hard not to fail on unkown oids (OpenSSL::X509::Certificate#to_text)
+* update Bouncy-Castle to 1.62 (and handle supported BC compatibility)
+
## 0.10.2
* update Bouncy-Castle to 1.61 (and handle supported BC compatibility)
diff --git a/LICENSE.txt b/LICENSE.txt
index 1b78015c..8f2413b4 100644
--- a/LICENSE.txt
+++ b/LICENSE.txt
@@ -18,7 +18,7 @@ ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
DEALINGS IN THE SOFTWARE.
Copyright (C) 2007-2009 Ola Bini
- Copyright (C) 2009-2018 The JRuby Team
+ Copyright (C) 2009-2024 The JRuby Team
Alternatively, the contents of this file may be used under the terms of
either of the GNU General Public License Version 2 or later (the "GPL"),
diff --git a/Mavenfile b/Mavenfile
index f14cc84e..25e36a7b 100644
--- a/Mavenfile
+++ b/Mavenfile
@@ -7,7 +7,7 @@ distribution_management do
repository :id => :ossrh, :url => 'https://oss.sonatype.org/service/local/staging/deploy/maven2/'
end
-java_target = '1.7'
+java_target = '1.8'
gen_sources = '${basedir}/target/generated-sources' # hard-coded in AnnotationBinder
plugin( 'org.codehaus.mojo:exec-maven-plugin', '1.3.2' ) do
@@ -45,15 +45,18 @@ plugin( 'org.codehaus.mojo:build-helper-maven-plugin', '1.9' ) do
execute_goal 'add-source', :phase => 'process-classes', :sources => [ gen_sources ]
end
-plugin( :compiler, '3.1',
- :source => '1.7', :target => java_target,
- :encoding => 'UTF-8', :debug => true,
- :showWarnings => true, :showDeprecation => true,
- :excludes => [ 'module-info.java' ],
+compiler_configuration = {
+ :source => '1.8', :target => java_target, :release => '8',
+ :encoding => 'UTF-8', :debug => true,
+ :showWarnings => true, :showDeprecation => true,
+ :excludes => [ 'module-info.java' ],
+ #:jdkToolchain => { :version => '[1.7,11)' },
+ :generatedSourcesDirectory => gen_sources,
+ :annotationProcessors => [ 'org.jruby.anno.AnnotationBinder' ]
+}
+compiler_configuration.delete(:release) if ENV_JAVA['java.specification.version'] == '1.8'
- :generatedSourcesDirectory => gen_sources,
- :annotationProcessors => [ 'org.jruby.anno.AnnotationBinder' ],
- :compilerArgs => [ '-XDignore.symbol.file=true' ] ) do
+plugin( :compiler, '3.9.0', compiler_configuration) do
#execute_goal :compile, :id => 'annotation-binder', :phase => 'compile',
# :generatedSourcesDirectory => gen_sources, #:outputDirectory => gen_sources,
@@ -62,15 +65,11 @@ plugin( :compiler, '3.1',
# :useIncrementalCompilation => false, :fork => true, :verbose => true,
# :compilerArgs => [ '-XDignore.symbol.file=true', '-J-Dfile.encoding=UTF-8' ]
- execute_goal :compile, :id => 'compile-populators', :phase => 'process-classes',
- :includes => [ 'org/jruby/gen/**/*.java' ], :optimize => true,
- :compilerArgs => [ '-XDignore.symbol.file=true' ]
- # NOTE: maybe '-J-Xbootclasspath/p:${unsafe.jar}' ... as well ?!
-end
-
-profile 'module-info' do
- activation { jdk '[9,)' }
- plugin :compiler, '3.1', :source => '9', :target => java_target, :includes => [ 'module-info.java' ]
+ execute_goal :compile,
+ :id => 'compile-populators', :phase => 'process-classes',
+ :includes => [ 'org/jruby/gen/**/*.java' ],
+ :optimize => true,
+ :compilerArgs => [ '', '-XDignore.symbol.file=true' ]
end
plugin :clean do
@@ -83,13 +82,18 @@ plugin :clean do
'failOnError' => 'false' )
end
-jar 'org.jruby:jruby-core', '1.7.20', :scope => :provided
-jar 'junit:junit', '4.11', :scope => :test
+jar 'org.jruby:jruby-core', '9.1.11.0', :scope => :provided
+# for invoker generated classes we need to add javax.annotation when on Java > 8
+jar 'javax.annotation:javax.annotation-api', '1.3.1', :scope => :compile
+jar 'junit:junit', '[4.13.1,)', :scope => :test
+
+# NOTE: to build on Java 11 - installing gems fails (due old jossl) with:
+# load error: jopenssl/load -- java.lang.StringIndexOutOfBoundsException
+MVN_JRUBY_VERSION = ENV_JAVA['java.version'].to_i >= 9 ? '9.2.19.0' : '9.1.17.0'
jruby_plugin! :gem do
# when installing dependent gems we want to use the built in openssl not the one from this lib directory
- # we compile against jruby-core-1.7.20 and want to keep this out of the plugin execution here
- execute_goal :id => 'default-initialize', :addProjectClasspath => false, :libDirectory => 'something-which-does-not-exists'
+ execute_goal :id => 'default-package', :addProjectClasspath => false, :libDirectory => 'something-which-does-not-exists'
execute_goals :id => 'default-push', :skip => true
end
@@ -98,24 +102,24 @@ plugin :deploy, '2.8.1' do
execute_goals( :deploy, :skip => false )
end
-supported_bc_versions = %w{ 1.55 1.56 1.57 1.58 1.59 1.60 1.61 }
+supported_bc_versions = %w{ 1.60 1.61 1.62 1.63 1.64 1.65 1.66 1.67 1.68 }
default_bc_version = File.read File.expand_path('lib/jopenssl/version.rb', File.dirname(__FILE__))
default_bc_version = default_bc_version[/BOUNCY_CASTLE_VERSION\s?=\s?'(.*?)'/, 1]
-properties( 'jruby.plugins.version' => '1.1.6',
- 'jruby.versions' => '9.1.17.0',
+properties( 'jruby.plugins.version' => '3.0.2',
'jruby.switches' => '-W0', # https://github.com/torquebox/jruby-maven-plugins/issues/94
'bc.versions' => default_bc_version,
'invoker.test' => '${bc.versions}',
# allow to skip all tests with -Dmaven.test.skip
'invoker.skip' => '${maven.test.skip}',
'runit.dir' => 'src/test/ruby/**/test_*.rb',
- # use this version of jruby for ALL the jruby-maven-plugins
- 'jruby.version' => '9.1.17.0', # Java 7 compatible till supporting JRuby 1.7
- # dump pom.xml as readonly when running 'rmvn'
- 'polyglot.dump.pom' => 'pom.xml',
- 'polyglot.dump.readonly' => true )
+ 'mavengem.wagon.version' => '2.0.2', # for jruby plugin
+ 'mavengem-wagon.version' => '2.0.2', # for polyglot-ruby
+ # use this version of jruby for the jruby-maven-plugins
+ 'jruby.versions' => MVN_JRUBY_VERSION, 'jruby.version' => MVN_JRUBY_VERSION,
+ # dump pom.xml when running 'rmvn'
+ 'polyglot.dump.pom' => 'pom.xml', 'polyglot.dump.readonly' => false )
# make sure we have the embedded jars in place before we run runit plugin
plugin! :dependency do
@@ -142,21 +146,8 @@ invoker_run_options = {
'runit.dir' => '${runit.dir}' }
}
-jruby_1_7_versions = %w{ 1.7.20 1.7.22 1.7.23 1.7.24 1.7.25 1.7.26 1.7.27 }
-
-jruby_1_7_versions.each { |version|
-profile :id => "test-#{version}" do
- plugin :invoker, '1.8' do
- execute_goals( :install, :run, invoker_run_options )
- end
- properties 'jruby.versions' => version,
- 'jruby.modes' => '1.9,2.0',
- 'bc.versions' => supported_bc_versions.join(',')
-end
-}
-
-jruby_9_K_versions = %w{ 9.0.1.0 9.0.5.0 9.1.2.0 9.1.8.0 9.1.12.0 9.1.16.0 9.1.17.0 }
-jruby_9_K_versions += %w{ 9.2.0.0 9.2.5.0 9.2.6.0 }
+jruby_9_K_versions = %w{ 9.1.2.0 9.1.8.0 9.1.12.0 9.1.16.0 9.1.17.0 }
+jruby_9_K_versions += %w{ 9.2.0.0 9.2.5.0 9.2.10.0 9.2.17.0 9.2.19.0 }
jruby_9_K_versions.each { |version|
profile :id => "test-#{version}" do
diff --git a/README.md b/README.md
index 477ec5f4..fc82183a 100644
--- a/README.md
+++ b/README.md
@@ -1,9 +1,9 @@
# JRuby-OpenSSL
[JRuby-OpenSSL](https://github.com/jruby/jruby-openssl) is an add-on gem for
-[JRuby](http://jruby.org) that emulates the Ruby OpenSSL native library.
+[JRuby](https://www.jruby.org/) that emulates the Ruby OpenSSL native library.
-Under the hood uses the [Bouncy Castle Crypto APIs](http://www.bouncycastle.org/).
+Under the hood uses the [Bouncy Castle Crypto APIs](https://www.bouncycastle.org/java.html).
Each jruby-openssl gem release includes a certain version, usually the latest available,
of the library (namely BC Provider and PKIX/CMS/EAC/PKCS/OCSP/TSP/OPENSSL jars).
@@ -13,23 +13,29 @@ the JRuby [mailing list][1] or the [bug tracker][2].
## Compatibility
-
-| JRuby-OpenSSL | JRuby compat | JVM compat | supported BC |
-| ------------- |:-------------:| ----------:| ------------:|
-| 0.9.6 | 1.6.8-9.0.2 | Java 6-8 | 1.47-1.50 |
-| 0.9.12 | 1.6.8-9.0.5 | Java 6-8 | 1.47-1.52 |
-| 0.9.13 | 1.6.8-9.1.2 | Java 6-8 | 1.49-1.52 |
-| 0.9.14 | 1.6.8-9.1.5 | Java 6-8 | 1.49-1.54 |
-| 0.9.17 | 1.6.8-9.1.5 | Java 6-8 | 1.50-1.54 |
-| ~>0.9.18 | 1.6.8-9.1.x | Java 6-8 | 1.50-1.55 |
-| 0.10.0 | 1.7.20-9.2.x | Java 7-10 | 1.55-1.59 |
+| JRuby-OpenSSL | JRuby compat | JVM compat | supported BC |
+|---------------|:------------:|-----------:|-------------:|
+| 0.9.6 | 1.6.8-9.0.2 | Java 6-8 | 1.47-1.50 |
+| 0.9.12 | 1.6.8-9.0.5 | Java 6-8 | 1.47-1.52 |
+| 0.9.13 | 1.6.8-9.1.2 | Java 6-8 | 1.49-1.52 |
+| 0.9.14 | 1.6.8-9.1.5 | Java 6-8 | 1.49-1.54 |
+| 0.9.17 | 1.6.8-9.1.5 | Java 6-8 | 1.50-1.54 |
+| ~>0.9.18 | 1.6.8-9.1.x | Java 6-8 | 1.50-1.55 |
+| 0.10.0 | 1.7.20-9.2.x | Java 7-10 | 1.55-1.59 |
+| 0.10.3 | 1.7.20-9.2.x | Java 7-11 | 1.56-1.62 |
+| ~>0.10.5 | 1.7.20-9.3.x | Java 7-11 | 1.60-1.68 |
+| ~>0.11.x | 9.0.x-9.3.x | Java 7-11 | 1.62-1.68 |
+| ~>0.12.x | 9.1.x-9.3.x | Java 8-15 | 1.65-1.68 |
+| ~>0.13.x | 9.1.x-9.4.x | Java 8-17 | 1.68-1.69 |
+| ~>0.14.x | 9.1.x-9.4.x | Java 8-21 | 1.71-1.74 |
+| ~>0.15.x | 9.2.x-9.4.x | Java 8-21 | 1.76-1.79 |
NOTE: backwards JRuby compatibility was not handled for versions <= **0.9.6**
## Security
-JRuby-OpenSSL is an essential part of [JRuby](http://jruby.org), please report security
-vulnerabilities to `security@jruby.org` as detailed on JRuby's [security page](http://jruby.org/security).
+JRuby-OpenSSL is an essential part of [JRuby](https://www.jruby.org/), please report security vulnerabilities to
+`security@jruby.org` as detailed on JRuby's [security page](https://www.jruby.org/security) or using [GitHub][0].
Please note that most OpenSSL vulnerabilities do not effect JRuby since its not using
any of OpenSSL's C code, only Ruby parts (*.rb) are the same as in MRI's OpenSSL library.
@@ -42,32 +48,28 @@ any of OpenSSL's C code, only Ruby parts (*.rb) are the same as in MRI's OpenSSL
mvn test
will run (junit as well as ruby) tests and a some ruby tests against the default
-jruby version. to pick a different version and/or modes (1.8, 1.9, 2.0, 2.1) run
+jruby version. to pick a different JRuby version run
- mvn test -Djruby.versions=1.7.12 -Djruby.modes=1.8
+ mvn test -Djruby.versions=9.2.8.0
for running integration-tests the gem will be first installed and then the same
tests run for each possible bouncy-castle version (see [listing][3]), run with
- mvn verify -P test-9.0.4.0,test-1.7.22
+ mvn verify -P test-9.2.9.0,test-9.1.17.0
or pick a bouncy-castle version
- mvn verify -P test-1.6.8 -Dbc.versions=1.50
-
-or simply be more picky
-
- mvn verify -P test-1.7.4 -Dbc.versions=1.49 -Djruby.modes=1.9
+ mvn verify -P test-9.2.9.0 -Dbc.versions=1.60
NOTE: you can pick any jruby version which is on [central][4] or on [ci.jruby][5]
## License
-(c) 2009-2018 JRuby distributed under EPL 1.0/GPL 2.0/LGPL 2.1
+(c) 2009-2024 JRuby distributed under EPL 1.0/GPL 2.0/LGPL 2.1
-[0]: https://secure.travis-ci.org/jruby/jruby-openssl.svg
-[1]: http://xircles.codehaus.org/projects/jruby/lists
-[2]: https://github.com/jruby/jruby/issues
+[0]: https://github.com/jruby/jruby-openssl/security
+[1]: https://github.com/jruby/jruby/wiki/MailingLists
+[2]: https://github.com/jruby/jruby-openssl/issues/new
[3]: https://github.com/jruby/jruby-openssl/tree/master/integration
[4]: http://central.maven.org/maven2/org/jruby/
-[5]: http://ci.jruby.org/snapshots/maven/org.jruby/
+[5]: https://www.jruby.org/nightly
diff --git a/Rakefile b/Rakefile
index 67c47b25..ef83d8f5 100644
--- a/Rakefile
+++ b/Rakefile
@@ -1,36 +1,27 @@
#-*- mode: ruby -*-
-begin
- require 'ruby-maven'
-rescue LoadError
- warn "ruby-maven not available - some tasks will not work " <<
- "either `gem install ruby-maven' or use mvn instead of rake"
- desc "Package jopenssl.jar with the compiled classes"
- task :jar do
- sh "mvn prepare-package -Dmaven.test.skip=true"
- end
- namespace :jar do
- desc "Package jopenssl.jar file (and dependendent jars)"
- task :all do
- sh "mvn package -Dmaven.test.skip=true"
- end
- end
-else
- #Rake::Task[:jar].clear rescue nil
- desc "Package jopenssl.jar with the compiled classes"
- task :jar do
- RubyMaven.exec( 'prepare-package -Dmaven.test.skip=true' )
- end
- namespace :jar do
- desc "Package jopenssl.jar file (and dependendent jars)"
- task :all do
- RubyMaven.exec( 'package -Dmaven.test.skip=true' )
- end
+#Rake::Task[:jar].clear rescue nil
+desc "Package jopenssl.jar with the compiled classes"
+task :jar do
+ sh( './mvnw prepare-package -Dmaven.test.skip=true' )
+end
+namespace :jar do
+ desc "Package jopenssl.jar file (and dependendent jars)"
+ task :all do
+ sh( './mvnw package -Dmaven.test.skip=true' )
end
end
+task :test_prepare do
+ sh( './mvnw prepare-package -Dmaven.test.skip=true' )
+ sh( './mvnw test-compile' ) # separate step due -Dmaven.test.skip=true
+end
+
+task :clean do
+ sh( './mvnw clean' )
+end
task :build do
- RubyMaven.exec('package -Dmaven.test.skip')
+ sh( './mvnw clean package -Dmaven.test.skip=true' )
end
task :default => :build
@@ -51,16 +42,15 @@ task :test => 'lib/jopenssl.jar'
namespace :integration do
it_path = File.expand_path('../src/test/integration', __FILE__)
task :install do
- Dir.chdir(it_path) do
- ruby "-S bundle install --gemfile '#{it_path}/Gemfile'"
- end
+ ruby "-C #{it_path} -S bundle install"
end
# desc "Run IT tests"
task :test => 'lib/jopenssl.jar' do
unless File.exist?(File.join(it_path, 'Gemfile.lock'))
raise "bundle not installed, run `rake integration:install'"
end
- loader = "ARGV.each { |f| require f }" ; lib = [ 'lib', it_path ]
+ loader = "ARGV.each { |f| require f }"
+ lib = [ File.expand_path('../lib', __FILE__), it_path ]
test_files = FileList['src/test/integration/*_test.rb'].map { |path| path.sub('src/test/integration/', '') }
ruby "-I#{lib.join(':')} -C src/test/integration -e \"#{loader}\" #{test_files.map { |f| "\"#{f}\"" }.join(' ')}"
end
diff --git a/integration/pom.xml b/integration/pom.xml
index bc0941a9..1f0a89a2 100644
--- a/integration/pom.xml
+++ b/integration/pom.xml
@@ -26,13 +26,13 @@
true
jruby
- http://ci.jruby.org/snapshots/maven
+ https://ci.jruby.org/snapshots/maven
rubygems-releases
- http://rubygems-proxy.torquebox.org/releases
+ https://rubygems-proxy.torquebox.org/releases
diff --git a/jruby-openssl.gemspec b/jruby-openssl.gemspec
index 42affb8c..195ccd9e 100644
--- a/jruby-openssl.gemspec
+++ b/jruby-openssl.gemspec
@@ -25,19 +25,13 @@ Gem::Specification.new do |s|
bc_version = version_rb.match( /.*\sBOUNCY_CASTLE_VERSION\s*=\s*['"](.*)['"]/ )[1]
raise 'BOUNCY_CASTLE_VERSION not matched' if (bc_version || '').empty?
- s.requirements << "jar org.bouncycastle:bcprov-jdk15on, #{bc_version}" # Provider
- s.requirements << "jar org.bouncycastle:bcpkix-jdk15on, #{bc_version}" # PKIX/CMS/EAC/PKCSOCSP/TSP/OPENSSL
- s.requirements << "jar org.bouncycastle:bctls-jdk15on, #{bc_version}" # DTLS/TLS API/JSSE Provider
+ s.required_ruby_version = '>= 2.5.0' # JRuby >= 9.2
- s.required_ruby_version = '>= 1.9.3'
- s.required_rubygems_version = '>= 2.4.8'
+ s.requirements << "jar org.bouncycastle:bcprov-jdk18on, #{bc_version}" # Provider
+ s.requirements << "jar org.bouncycastle:bcpkix-jdk18on, #{bc_version}" # PKIX/CMS/EAC/PKCSOCSP/TSP/OPENSSL
+ s.requirements << "jar org.bouncycastle:bctls-jdk18on, #{bc_version}" # DTLS/TLS API/JSSE Provider
+ s.requirements << "jar org.bouncycastle:bcutil-jdk18on, #{bc_version}"
- s.add_development_dependency 'jar-dependencies', '~> 0.1'
-
- s.add_development_dependency 'mocha', '~> 1.4', '< 2.0'
- s.add_development_dependency 'ruby-maven', '~> 3.0'
- # NOTE: runit-maven-plugin will use it's own :
- #s.add_development_dependency 'test-unit', '2.5.5'
end
# vim: syntax=Ruby
diff --git a/lib/jopenssl/_compat23.rb b/lib/jopenssl/_compat23.rb
deleted file mode 100644
index 8da642c6..00000000
--- a/lib/jopenssl/_compat23.rb
+++ /dev/null
@@ -1,71 +0,0 @@
-# frozen_string_literal: false
-
-module OpenSSL
-
- module PKey
-
- class DH
-
- def set_key(pub_key, priv_key)
- self.pub_key = pub_key
- self.priv_key = priv_key
- self
- end
-
- def set_pqg(p, q, g)
- self.p = p
- if respond_to?(:q)
- self.q = q
- else # TODO self.q = q
- OpenSSL.warn "JRuby-OpenSSL does not support setting q param on #{inspect}" if q
- end
- self.g = g
- self
- end
-
- end
-
- class DSA
-
- def set_key(pub_key, priv_key)
- self.pub_key = pub_key
- self.priv_key = priv_key
- self
- end
-
- def set_pqg(p, q, g)
- self.p = p
- self.q = q
- self.g = g
- self
- end
-
- end
-
- class RSA
-
- def set_key(n, e, d)
- self.n = n
- self.e = e
- self.d = d
- self
- end
-
- def set_factors(p, q)
- self.p = p
- self.q = q
- self
- end
-
- def set_crt_params(dmp1, dmq1, iqmp)
- self.dmp1 = dmp1
- self.dmq1 = dmq1
- self.iqmp = iqmp
- self
- end
-
- end
-
- end
-
-end
diff --git a/lib/jopenssl/load.rb b/lib/jopenssl/load.rb
index 6e681ccc..6d38b8a7 100644
--- a/lib/jopenssl/load.rb
+++ b/lib/jopenssl/load.rb
@@ -1,50 +1,71 @@
-warn 'Loading jruby-openssl gem in a non-JRuby interpreter' unless defined? JRUBY_VERSION
-
require 'jopenssl/version'
-warn "JRuby #{JRUBY_VERSION} is not supported by jruby-openssl #{JOpenSSL::VERSION}" if JRUBY_VERSION < '1.7.20'
-
# NOTE: assuming user does pull in BC .jars from somewhere else on the CP
unless ENV_JAVA['jruby.openssl.load.jars'].eql?('false')
version = JOpenSSL::BOUNCY_CASTLE_VERSION
- bc_jars = nil
begin
require 'jar-dependencies'
# if we have jar-dependencies we let it track the jars
- require_jar( 'org.bouncycastle', 'bcprov-jdk15on', version )
- require_jar( 'org.bouncycastle', 'bcpkix-jdk15on', version )
- require_jar( 'org.bouncycastle', 'bctls-jdk15on', version )
+ require_jar 'org.bouncycastle', 'bcprov-jdk18on', version
+ require_jar 'org.bouncycastle', 'bcpkix-jdk18on', version
+ require_jar 'org.bouncycastle', 'bcutil-jdk18on', version
+ require_jar 'org.bouncycastle', 'bctls-jdk18on', version
bc_jars = true
- rescue LoadError
+ rescue LoadError, RuntimeError
bc_jars = false
end
unless bc_jars
- load "org/bouncycastle/bcprov-jdk15on/#{version}/bcprov-jdk15on-#{version}.jar"
- load "org/bouncycastle/bcpkix-jdk15on/#{version}/bcpkix-jdk15on-#{version}.jar"
- load "org/bouncycastle/bctls-jdk15on/#{version}/bctls-jdk15on-#{version}.jar"
+ load "org/bouncycastle/bcprov-jdk18on/#{version}/bcprov-jdk18on-#{version}.jar"
+ load "org/bouncycastle/bcpkix-jdk18on/#{version}/bcpkix-jdk18on-#{version}.jar"
+ load "org/bouncycastle/bcutil-jdk18on/#{version}/bcutil-jdk18on-#{version}.jar"
+ load "org/bouncycastle/bctls-jdk18on/#{version}/bctls-jdk18on-#{version}.jar"
end
end
require 'jopenssl.jar'
+JRuby::Util.load_ext('org.jruby.ext.openssl.OpenSSL')
-if JRuby::Util.respond_to?(:load_ext) # JRuby 9.2
- JRuby::Util.load_ext('org.jruby.ext.openssl.OpenSSL')
-else; require 'jruby'
- org.jruby.ext.openssl.OpenSSL.load(JRuby.runtime)
-end
-
-if RUBY_VERSION > '2.3'
- load 'jopenssl23/openssl.rb'
- load 'jopenssl/_compat23.rb'
-elsif RUBY_VERSION > '2.2'
- load 'jopenssl22/openssl.rb'
-elsif RUBY_VERSION > '2.1'
- load 'jopenssl21/openssl.rb'
-else
- load 'jopenssl19/openssl.rb'
-end
+# NOTE: content bellow should live in *lib/openssl.rb* but due RubyGems/Bundler
+# `autoload :OpenSSL` this will cause issues if an older version (0.11) is the
+# default gem under JRuby 9.2 (which on auto-load does not trigger a dynamic
+# require - this is only fixed in JRuby 9.3)
module OpenSSL
autoload :Config, 'openssl/config' unless const_defined?(:Config, false)
+ autoload :ConfigError, 'openssl/config' unless const_defined?(:ConfigError, false)
autoload :PKCS12, 'openssl/pkcs12'
end
+
+=begin
+= Info
+ 'OpenSSL for Ruby 2' project
+ Copyright (C) 2002 Michal Rokos
+ All rights reserved.
+
+= Licence
+ This program is licensed under the same licence as Ruby.
+ (See the file 'LICENCE'.)
+=end
+
+require 'openssl/bn'
+require 'openssl/pkey'
+require 'openssl/cipher'
+require 'openssl/digest'
+require 'openssl/hmac'
+require 'openssl/x509'
+require 'openssl/ssl'
+require 'openssl/pkcs5'
+
+module OpenSSL
+ # call-seq:
+ # OpenSSL.secure_compare(string, string) -> boolean
+ #
+ # Constant time memory comparison. Inputs are hashed using SHA-256 to mask
+ # the length of the secret. Returns +true+ if the strings are identical,
+ # +false+ otherwise.
+ def self.secure_compare(a, b)
+ hashed_a = OpenSSL::Digest.digest('SHA256', a)
+ hashed_b = OpenSSL::Digest.digest('SHA256', b)
+ OpenSSL.fixed_length_secure_compare(hashed_a, hashed_b) && a == b
+ end
+end
diff --git a/lib/jopenssl/version.rb b/lib/jopenssl/version.rb
index 30fa8c27..d5766787 100644
--- a/lib/jopenssl/version.rb
+++ b/lib/jopenssl/version.rb
@@ -1,9 +1,10 @@
module JOpenSSL
- VERSION = '0.10.3.dev'
- BOUNCY_CASTLE_VERSION = '1.62'
+ VERSION = '0.15.6.dev'
+ BOUNCY_CASTLE_VERSION = '1.81'
end
Object.class_eval do
Jopenssl = JOpenSSL
private_constant :Jopenssl if respond_to?(:private_constant)
+ deprecate_constant :Jopenssl if respond_to?(:deprecate_constant)
end
diff --git a/lib/jopenssl19/openssl.rb b/lib/jopenssl19/openssl.rb
deleted file mode 100644
index 3efdd1ee..00000000
--- a/lib/jopenssl19/openssl.rb
+++ /dev/null
@@ -1,22 +0,0 @@
-=begin
-= $RCSfile$ -- Loader for all OpenSSL C-space and Ruby-space definitions
-
-= Info
- 'OpenSSL for Ruby 2' project
- Copyright (C) 2002 Michal Rokos
- All rights reserved.
-
-= Licence
- This program is licenced under the same licence as Ruby.
- (See the file 'LICENCE'.)
-
-= Version
- $Id$
-=end
-
-require 'openssl/bn'
-require 'openssl/cipher'
-require 'openssl/config'
-require 'openssl/digest'
-require 'openssl/ssl-internal'
-require 'openssl/x509-internal'
diff --git a/lib/jopenssl19/openssl/bn.rb b/lib/jopenssl19/openssl/bn.rb
deleted file mode 100644
index 3933ee3c..00000000
--- a/lib/jopenssl19/openssl/bn.rb
+++ /dev/null
@@ -1,29 +0,0 @@
-#--
-#
-# $RCSfile$
-#
-# = Ruby-space definitions that completes C-space funcs for BN
-#
-# = Info
-# 'OpenSSL for Ruby 2' project
-# Copyright (C) 2002 Michal Rokos
-# All rights reserved.
-#
-# = Licence
-# This program is licenced under the same licence as Ruby.
-# (See the file 'LICENCE'.)
-#
-# = Version
-# $Id$
-#
-#++
-
-##
-# Add double dispatch to Integer
-#
-class Integer
- def to_bn
- OpenSSL::BN::new(self)
- end
-end # Integer
-
diff --git a/lib/jopenssl19/openssl/buffering.rb b/lib/jopenssl19/openssl/buffering.rb
deleted file mode 100644
index 51bc968e..00000000
--- a/lib/jopenssl19/openssl/buffering.rb
+++ /dev/null
@@ -1,449 +0,0 @@
-=begin
-= $RCSfile$ -- Buffering mix-in module.
-
-= Info
- 'OpenSSL for Ruby 2' project
- Copyright (C) 2001 GOTOU YUUZOU
- All rights reserved.
-
-= Licence
- This program is licenced under the same licence as Ruby.
- (See the file 'LICENCE'.)
-
-= Version
- $Id$
-=end
-
-##
-# OpenSSL IO buffering mix-in module.
-#
-# This module allows an OpenSSL::SSL::SSLSocket to behave like an IO.
-
-module OpenSSL::Buffering
- include Enumerable
-
- ##
- # The "sync mode" of the SSLSocket.
- #
- # See IO#sync for full details.
-
- attr_accessor :sync
-
- ##
- # Default size to read from or write to the SSLSocket for buffer operations.
-
- BLOCK_SIZE = 1024*16
-
- def initialize(*args)
- @eof = false
- @rbuffer = ""
- @sync = @io.sync
- end
-
- #
- # for reading.
- #
- private
-
- ##
- # Fills the buffer from the underlying SSLSocket
-
- def fill_rbuff
- begin
- @rbuffer << self.sysread(BLOCK_SIZE)
- rescue Errno::EAGAIN
- retry
- rescue EOFError
- @eof = true
- end
- end
-
- ##
- # Consumes +size+ bytes from the buffer
-
- def consume_rbuff(size=nil)
- if @rbuffer.empty?
- nil
- else
- size = @rbuffer.size unless size
- ret = @rbuffer[0, size]
- @rbuffer[0, size] = ""
- ret
- end
- end
-
- public
-
- ##
- # Reads +size+ bytes from the stream. If +buf+ is provided it must
- # reference a string which will receive the data.
- #
- # See IO#read for full details.
-
- def read(size=nil, buf=nil)
- if size == 0
- if buf
- buf.clear
- return buf
- else
- return ""
- end
- end
- until @eof
- break if size && size <= @rbuffer.size
- fill_rbuff
- end
- ret = consume_rbuff(size) || ""
- if buf
- buf.replace(ret)
- ret = buf
- end
- (size && ret.empty?) ? nil : ret
- end
-
- ##
- # Reads at most +maxlen+ bytes from the stream. If +buf+ is provided it
- # must reference a string which will receive the data.
- #
- # See IO#readpartial for full details.
-
- def readpartial(maxlen, buf=nil)
- if maxlen == 0
- if buf
- buf.clear
- return buf
- else
- return ""
- end
- end
- if @rbuffer.empty?
- begin
- return sysread(maxlen, buf)
- rescue Errno::EAGAIN
- retry
- end
- end
- ret = consume_rbuff(maxlen)
- if buf
- buf.replace(ret)
- ret = buf
- end
- raise EOFError if ret.empty?
- ret
- end
-
- ##
- # Reads at most +maxlen+ bytes in the non-blocking manner.
- #
- # When no data can be read without blocking it raises
- # OpenSSL::SSL::SSLError extended by IO::WaitReadable or IO::WaitWritable.
- #
- # IO::WaitReadable means SSL needs to read internally so read_nonblock
- # should be called again when the underlying IO is readable.
- #
- # IO::WaitWritable means SSL needs to write internally so read_nonblock
- # should be called again after the underlying IO is writable.
- #
- # OpenSSL::Buffering#read_nonblock needs two rescue clause as follows:
- #
- # # emulates blocking read (readpartial).
- # begin
- # result = ssl.read_nonblock(maxlen)
- # rescue IO::WaitReadable
- # IO.select([io])
- # retry
- # rescue IO::WaitWritable
- # IO.select(nil, [io])
- # retry
- # end
- #
- # Note that one reason that read_nonblock writes to the underlying IO is
- # when the peer requests a new TLS/SSL handshake. See openssl the FAQ for
- # more details. http://www.openssl.org/support/faq.html
-
- def read_nonblock(maxlen, buf=nil)
- if maxlen == 0
- if buf
- buf.clear
- return buf
- else
- return ""
- end
- end
- if @rbuffer.empty?
- return sysread_nonblock(maxlen, buf)
- end
- ret = consume_rbuff(maxlen)
- if buf
- buf.replace(ret)
- ret = buf
- end
- raise EOFError if ret.empty?
- ret
- end
-
- ##
- # Reads the next "line+ from the stream. Lines are separated by +eol+. If
- # +limit+ is provided the result will not be longer than the given number of
- # bytes.
- #
- # +eol+ may be a String or Regexp.
- #
- # Unlike IO#gets the line read will not be assigned to +$_+.
- #
- # Unlike IO#gets the separator must be provided if a limit is provided.
-
- def gets(eol=$/, limit=nil)
- idx = @rbuffer.index(eol)
- until @eof
- break if idx
- fill_rbuff
- idx = @rbuffer.index(eol)
- end
- if eol.is_a?(Regexp)
- size = idx ? idx+$&.size : nil
- else
- size = idx ? idx+eol.size : nil
- end
- if limit and limit >= 0
- size = [size, limit].min
- end
- consume_rbuff(size)
- end
-
- ##
- # Executes the block for every line in the stream where lines are separated
- # by +eol+.
- #
- # See also #gets
-
- def each(eol=$/)
- while line = self.gets(eol)
- yield line
- end
- end
- alias each_line each
-
- ##
- # Reads lines from the stream which are separated by +eol+.
- #
- # See also #gets
-
- def readlines(eol=$/)
- ary = []
- while line = self.gets(eol)
- ary << line
- end
- ary
- end
-
- ##
- # Reads a line from the stream which is separated by +eol+.
- #
- # Raises EOFError if at end of file.
-
- def readline(eol=$/)
- raise EOFError if eof?
- gets(eol)
- end
-
- ##
- # Reads one character from the stream. Returns nil if called at end of
- # file.
-
- def getc
- read(1)
- end
-
- ##
- # Calls the given block once for each byte in the stream.
-
- def each_byte # :yields: byte
- while c = getc
- yield(c.ord)
- end
- end
-
- ##
- # Reads a one-character string from the stream. Raises an EOFError at end
- # of file.
-
- def readchar
- raise EOFError if eof?
- getc
- end
-
- ##
- # Pushes character +c+ back onto the stream such that a subsequent buffered
- # character read will return it.
- #
- # Unlike IO#getc multiple bytes may be pushed back onto the stream.
- #
- # Has no effect on unbuffered reads (such as #sysread).
-
- def ungetc(c)
- @rbuffer[0,0] = c.chr
- end
-
- ##
- # Returns true if the stream is at file which means there is no more data to
- # be read.
-
- def eof?
- fill_rbuff if !@eof && @rbuffer.empty?
- @eof && @rbuffer.empty?
- end
- alias eof eof?
-
- #
- # for writing.
- #
- private
-
- ##
- # Writes +s+ to the buffer. When the buffer is full or #sync is true the
- # buffer is flushed to the underlying socket.
-
- def do_write(s)
- @wbuffer = "" unless defined? @wbuffer
- @wbuffer << s
- @wbuffer.force_encoding(Encoding::BINARY)
- @sync ||= false
- if @sync or @wbuffer.size > BLOCK_SIZE or idx = @wbuffer.rindex($/)
- remain = idx ? idx + $/.size : @wbuffer.length
- nwritten = 0
- while remain > 0
- str = @wbuffer[nwritten,remain]
- begin
- nwrote = syswrite(str)
- rescue Errno::EAGAIN
- retry
- end
- remain -= nwrote
- nwritten += nwrote
- end
- @wbuffer[0,nwritten] = ""
- end
- end
-
- public
-
- ##
- # Writes +s+ to the stream. If the argument is not a string it will be
- # converted using String#to_s. Returns the number of bytes written.
-
- def write(s)
- do_write(s)
- s.bytesize
- end
-
- ##
- # Writes +str+ in the non-blocking manner.
- #
- # If there is buffered data, it is flushed first. This may block.
- #
- # write_nonblock returns number of bytes written to the SSL connection.
- #
- # When no data can be written without blocking it raises
- # OpenSSL::SSL::SSLError extended by IO::WaitReadable or IO::WaitWritable.
- #
- # IO::WaitReadable means SSL needs to read internally so write_nonblock
- # should be called again after the underlying IO is readable.
- #
- # IO::WaitWritable means SSL needs to write internally so write_nonblock
- # should be called again after underlying IO is writable.
- #
- # So OpenSSL::Buffering#write_nonblock needs two rescue clause as follows.
- #
- # # emulates blocking write.
- # begin
- # result = ssl.write_nonblock(str)
- # rescue IO::WaitReadable
- # IO.select([io])
- # retry
- # rescue IO::WaitWritable
- # IO.select(nil, [io])
- # retry
- # end
- #
- # Note that one reason that write_nonblock reads from the underlying IO
- # is when the peer requests a new TLS/SSL handshake. See the openssl FAQ
- # for more details. http://www.openssl.org/support/faq.html
-
- def write_nonblock(s)
- flush
- syswrite_nonblock(s)
- end
-
- ##
- # Writes +s+ to the stream. +s+ will be converted to a String using
- # String#to_s.
-
- def << (s)
- do_write(s)
- self
- end
-
- ##
- # Writes +args+ to the stream along with a record separator.
- #
- # See IO#puts for full details.
-
- def puts(*args)
- s = ""
- if args.empty?
- s << "\n"
- end
- args.each{|arg|
- s << arg.to_s
- if $/ && /\n\z/ !~ s
- s << "\n"
- end
- }
- do_write(s)
- nil
- end
-
- ##
- # Writes +args+ to the stream.
- #
- # See IO#print for full details.
-
- def print(*args)
- s = ""
- args.each{ |arg| s << arg.to_s }
- do_write(s)
- nil
- end
-
- ##
- # Formats and writes to the stream converting parameters under control of
- # the format string.
- #
- # See Kernel#sprintf for format string details.
-
- def printf(s, *args)
- do_write(s % args)
- nil
- end
-
- ##
- # Flushes buffered data to the SSLSocket.
-
- def flush
- osync = @sync
- @sync = true
- do_write ""
- return self
- ensure
- @sync = osync
- end
-
- ##
- # Closes the SSLSocket and flushes any unwritten data.
-
- def close
- flush rescue nil
- sysclose
- end
-end
diff --git a/lib/jopenssl19/openssl/cipher.rb b/lib/jopenssl19/openssl/cipher.rb
deleted file mode 100644
index b254a238..00000000
--- a/lib/jopenssl19/openssl/cipher.rb
+++ /dev/null
@@ -1,28 +0,0 @@
-#--
-#
-# $RCSfile$
-#
-# = Ruby-space predefined Cipher subclasses
-#
-# = Info
-# 'OpenSSL for Ruby 2' project
-# Copyright (C) 2002 Michal Rokos
-# All rights reserved.
-#
-# = Licence
-# This program is licenced under the same licence as Ruby.
-# (See the file 'LICENCE'.)
-#
-# = Version
-# $Id$
-#
-#++
-
-module OpenSSL
- class Cipher
- # This class is only provided for backwards compatibility. Use OpenSSL::Cipher in the future.
- class Cipher < Cipher
- # add warning
- end
- end # Cipher
-end # OpenSSL
\ No newline at end of file
diff --git a/lib/jopenssl19/openssl/config.rb b/lib/jopenssl19/openssl/config.rb
deleted file mode 100644
index 5716d59f..00000000
--- a/lib/jopenssl19/openssl/config.rb
+++ /dev/null
@@ -1,472 +0,0 @@
-=begin
-= Ruby-space definitions that completes C-space funcs for Config
-
-= Info
- Copyright (C) 2010 Hiroshi Nakamura
-
-= Licence
- This program is licenced under the same licence as Ruby.
- (See the file 'LICENCE'.)
-
-=end
-
-require 'stringio'
-
-module OpenSSL
- ##
- # = OpenSSL::Config
- #
- # Configuration for the openssl library.
- #
- # Many system's installation of openssl library will depend on your system
- # configuration. See the value of OpenSSL::Config::DEFAULT_CONFIG_FILE for
- # the location of the file for your host.
- #
- # See also http://www.openssl.org/docs/apps/config.html
- class Config
- include Enumerable
-
- class << self
-
- ##
- # Parses a given +string+ as a blob that contains configuration for openssl.
- #
- # If the source of the IO is a file, then consider using #parse_config.
- def parse(string)
- c = new()
- parse_config(StringIO.new(string)).each do |section, hash|
- c[section] = hash
- end
- c
- end
-
- ##
- # load is an alias to ::new
- alias load new
-
- ##
- # Parses the configuration data read from +io+, see also #parse.
- #
- # Raises a ConfigError on invalid configuration data.
- def parse_config(io)
- begin
- parse_config_lines(io)
- rescue ConfigError => e
- e.message.replace("error in line #{io.lineno}: " + e.message)
- raise
- end
- end
-
- def get_key_string(data, section, key) # :nodoc:
- if v = data[section] && data[section][key]
- return v
- elsif section == 'ENV'
- if v = ENV[key]
- return v
- end
- end
- if v = data['default'] && data['default'][key]
- return v
- end
- end
-
- private
-
- def parse_config_lines(io)
- section = 'default'
- data = {section => {}}
- while definition = get_definition(io)
- definition = clear_comments(definition)
- next if definition.empty?
- if definition[0] == ?[
- if /\[([^\]]*)\]/ =~ definition
- section = $1.strip
- data[section] ||= {}
- else
- raise ConfigError, "missing close square bracket"
- end
- else
- if /\A([^:\s]*)(?:::([^:\s]*))?\s*=(.*)\z/ =~ definition
- if $2
- section = $1
- key = $2
- else
- key = $1
- end
- value = unescape_value(data, section, $3)
- (data[section] ||= {})[key] = value.strip
- else
- raise ConfigError, "missing equal sign"
- end
- end
- end
- data
- end
-
- # escape with backslash
- QUOTE_REGEXP_SQ = /\A([^'\\]*(?:\\.[^'\\]*)*)'/
- # escape with backslash and doubled dq
- QUOTE_REGEXP_DQ = /\A([^"\\]*(?:""[^"\\]*|\\.[^"\\]*)*)"/
- # escaped char map
- ESCAPE_MAP = {
- "r" => "\r",
- "n" => "\n",
- "b" => "\b",
- "t" => "\t",
- }
-
- def unescape_value(data, section, value)
- scanned = []
- while m = value.match(/['"\\$]/)
- scanned << m.pre_match
- c = m[0]
- value = m.post_match
- case c
- when "'"
- if m = value.match(QUOTE_REGEXP_SQ)
- scanned << m[1].gsub(/\\(.)/, '\\1')
- value = m.post_match
- else
- break
- end
- when '"'
- if m = value.match(QUOTE_REGEXP_DQ)
- scanned << m[1].gsub(/""/, '').gsub(/\\(.)/, '\\1')
- value = m.post_match
- else
- break
- end
- when "\\"
- c = value.slice!(0, 1)
- scanned << (ESCAPE_MAP[c] || c)
- when "$"
- ref, value = extract_reference(value)
- refsec = section
- if ref.index('::')
- refsec, ref = ref.split('::', 2)
- end
- if v = get_key_string(data, refsec, ref)
- scanned << v
- else
- raise ConfigError, "variable has no value"
- end
- else
- raise 'must not reaced'
- end
- end
- scanned << value
- scanned.join
- end
-
- def extract_reference(value)
- rest = ''
- if m = value.match(/\(([^)]*)\)|\{([^}]*)\}/)
- value = m[1] || m[2]
- rest = m.post_match
- elsif [?(, ?{].include?(value[0])
- raise ConfigError, "no close brace"
- end
- if m = value.match(/[a-zA-Z0-9_]*(?:::[a-zA-Z0-9_]*)?/)
- return m[0], m.post_match + rest
- else
- raise
- end
- end
-
- def clear_comments(line)
- # FCOMMENT
- if m = line.match(/\A([\t\n\f ]*);.*\z/)
- return m[1]
- end
- # COMMENT
- scanned = []
- while m = line.match(/[#'"\\]/)
- scanned << m.pre_match
- c = m[0]
- line = m.post_match
- case c
- when '#'
- line = nil
- break
- when "'", '"'
- regexp = (c == "'") ? QUOTE_REGEXP_SQ : QUOTE_REGEXP_DQ
- scanned << c
- if m = line.match(regexp)
- scanned << m[0]
- line = m.post_match
- else
- scanned << line
- line = nil
- break
- end
- when "\\"
- scanned << c
- scanned << line.slice!(0, 1)
- else
- raise 'must not reaced'
- end
- end
- scanned << line
- scanned.join
- end
-
- def get_definition(io)
- if line = get_line(io)
- while /[^\\]\\\z/ =~ line
- if extra = get_line(io)
- line += extra
- else
- break
- end
- end
- return line.strip
- end
- end
-
- def get_line(io)
- if line = io.gets
- line.gsub(/[\r\n]*/, '')
- end
- end
- end
-
- ##
- # Creates an instance of OpenSSL's configuration class.
- #
- # This can be used in contexts like OpenSSL::X509::ExtensionFactory.config=
- #
- # If the optional +filename+ parameter is provided, then it is read in and
- # parsed via #parse_config.
- #
- # This can raise IO exceptions based on the access, or availability of the
- # file. A ConfigError exception may be raised depending on the validity of
- # the data being configured.
- #
- def initialize(filename = nil)
- @data = {}
- if filename
- File.open(filename.to_s) do |file|
- Config.parse_config(file).each do |section, hash|
- self[section] = hash
- end
- end
- end
- end
-
- ##
- # Gets the value of +key+ from the given +section+
- #
- # Given the following configurating file being loaded:
- #
- # config = OpenSSL::Config.load('foo.cnf')
- # #=> #
- # puts config.to_s
- # #=> [ default ]
- # # foo=bar
- #
- # You can get a specific value from the config if you know the +section+
- # and +key+ like so:
- #
- # config.get_value('default','foo')
- # #=> "bar"
- #
- def get_value(section, key)
- if section.nil?
- raise TypeError.new('nil not allowed')
- end
- section = 'default' if section.empty?
- get_key_string(section, key)
- end
-
- ##
- #
- # *Deprecated*
- #
- # Use #get_value instead
- def value(arg1, arg2 = nil) # :nodoc:
- warn('Config#value is deprecated; use Config#get_value')
- if arg2.nil?
- section, key = 'default', arg1
- else
- section, key = arg1, arg2
- end
- section ||= 'default'
- section = 'default' if section.empty?
- get_key_string(section, key)
- end
-
- ##
- # Set the target +key+ with a given +value+ under a specific +section+.
- #
- # Given the following configurating file being loaded:
- #
- # config = OpenSSL::Config.load('foo.cnf')
- # #=> #
- # puts config.to_s
- # #=> [ default ]
- # # foo=bar
- #
- # You can set the value of +foo+ under the +default+ section to a new
- # value:
- #
- # config.add_value('default', 'foo', 'buzz')
- # #=> "buzz"
- # puts config.to_s
- # #=> [ default ]
- # # foo=buzz
- #
- def add_value(section, key, value)
- check_modify
- (@data[section] ||= {})[key] = value
- end
-
- ##
- # Get a specific +section+ from the current configuration
- #
- # Given the following configurating file being loaded:
- #
- # config = OpenSSL::Config.load('foo.cnf')
- # #=> #
- # puts config.to_s
- # #=> [ default ]
- # # foo=bar
- #
- # You can get a hash of the specific section like so:
- #
- # config['default']
- # #=> {"foo"=>"bar"}
- #
- def [](section)
- @data[section] || {}
- end
-
- ##
- # Deprecated
- #
- # Use #[] instead
- def section(name) # :nodoc:
- warn('Config#section is deprecated; use Config#[]')
- @data[name] || {}
- end
-
- ##
- # Sets a specific +section+ name with a Hash +pairs+
- #
- # Given the following configuration being created:
- #
- # config = OpenSSL::Config.new
- # #=> #
- # config['default'] = {"foo"=>"bar","baz"=>"buz"}
- # #=> {"foo"=>"bar", "baz"=>"buz"}
- # puts config.to_s
- # #=> [ default ]
- # # foo=bar
- # # baz=buz
- #
- # It's important to note that this will essentially merge any of the keys
- # in +pairs+ with the existing +section+. For example:
- #
- # config['default']
- # #=> {"foo"=>"bar", "baz"=>"buz"}
- # config['default'] = {"foo" => "changed"}
- # #=> {"foo"=>"changed"}
- # config['default']
- # #=> {"foo"=>"changed", "baz"=>"buz"}
- #
- def []=(section, pairs)
- check_modify
- @data[section] ||= {}
- pairs.each do |key, value|
- self.add_value(section, key, value)
- end
- end
-
- ##
- # Get the names of all sections in the current configuration
- def sections
- @data.keys
- end
-
- ##
- # Get the parsable form of the current configuration
- #
- # Given the following configuration being created:
- #
- # config = OpenSSL::Config.new
- # #=> #
- # config['default'] = {"foo"=>"bar","baz"=>"buz"}
- # #=> {"foo"=>"bar", "baz"=>"buz"}
- # puts config.to_s
- # #=> [ default ]
- # # foo=bar
- # # baz=buz
- #
- # You can parse get the serialized configuration using #to_s and then parse
- # it later:
- #
- # serialized_config = config.to_s
- # # much later...
- # new_config = OpenSSL::Config.parse(serialized_config)
- # #=> #
- # puts new_config
- # #=> [ default ]
- # foo=bar
- # baz=buz
- #
- def to_s
- ary = []
- @data.keys.sort.each do |section|
- ary << "[ #{section} ]\n"
- @data[section].keys.each do |key|
- ary << "#{key}=#{@data[section][key]}\n"
- end
- ary << "\n"
- end
- ary.join
- end
-
- ##
- # For a block.
- #
- # Receive the section and its pairs for the current configuration.
- #
- # config.each do |section, key, value|
- # # ...
- # end
- #
- def each
- @data.each do |section, hash|
- hash.each do |key, value|
- yield [section, key, value]
- end
- end
- end
-
- ##
- # String representation of this configuration object, including the class
- # name and its sections.
- def inspect
- "#<#{self.class.name} sections=#{sections.inspect}>"
- end
-
- protected
-
- def data # :nodoc:
- @data
- end
-
- private
-
- def initialize_copy(other)
- @data = other.data.dup
- end
-
- def check_modify
- raise TypeError.new("Insecure: can't modify OpenSSL config") if frozen?
- end
-
- def get_key_string(section, key)
- Config.get_key_string(@data, section, key)
- end
- end
-end
diff --git a/lib/jopenssl19/openssl/digest.rb b/lib/jopenssl19/openssl/digest.rb
deleted file mode 100644
index d62993b2..00000000
--- a/lib/jopenssl19/openssl/digest.rb
+++ /dev/null
@@ -1,32 +0,0 @@
-#--
-#
-# $RCSfile$
-#
-# = Ruby-space predefined Digest subclasses
-#
-# = Info
-# 'OpenSSL for Ruby 2' project
-# Copyright (C) 2002 Michal Rokos
-# All rights reserved.
-#
-# = Licence
-# This program is licenced under the same licence as Ruby.
-# (See the file 'LICENCE'.)
-#
-# = Version
-# $Id$
-#
-#++
-
-module OpenSSL
- class Digest
- # This class is only provided for backwards compatibility. Use OpenSSL::Digest in the future.
- class Digest < Digest
- def initialize(*args)
- # add warning
- super(*args)
- end
- end
- end # Digest
-end # OpenSSL
-
diff --git a/lib/jopenssl19/openssl/ssl-internal.rb b/lib/jopenssl19/openssl/ssl-internal.rb
deleted file mode 100644
index f8dce80c..00000000
--- a/lib/jopenssl19/openssl/ssl-internal.rb
+++ /dev/null
@@ -1,223 +0,0 @@
-=begin
-= $RCSfile$ -- Ruby-space definitions that completes C-space funcs for SSL
-
-= Info
- 'OpenSSL for Ruby 2' project
- Copyright (C) 2001 GOTOU YUUZOU
- All rights reserved.
-
-= Licence
- This program is licenced under the same licence as Ruby.
- (See the file 'LICENCE'.)
-
-= Version
- $Id$
-=end
-
-require "openssl/buffering"
-require 'fcntl' # used by OpenSSL::SSL::Nonblock (if loaded)
-
-module OpenSSL
- module SSL
- class SSLContext
- DEFAULT_PARAMS = {
- :ssl_version => "SSLv23",
- :verify_mode => OpenSSL::SSL::VERIFY_PEER,
- :ciphers => %w{
- ECDHE-ECDSA-AES128-GCM-SHA256
- ECDHE-RSA-AES128-GCM-SHA256
- ECDHE-ECDSA-AES256-GCM-SHA384
- ECDHE-RSA-AES256-GCM-SHA384
- DHE-RSA-AES128-GCM-SHA256
- DHE-DSS-AES128-GCM-SHA256
- DHE-RSA-AES256-GCM-SHA384
- DHE-DSS-AES256-GCM-SHA384
- ECDHE-ECDSA-AES128-SHA256
- ECDHE-RSA-AES128-SHA256
- ECDHE-ECDSA-AES128-SHA
- ECDHE-RSA-AES128-SHA
- ECDHE-ECDSA-AES256-SHA384
- ECDHE-RSA-AES256-SHA384
- ECDHE-ECDSA-AES256-SHA
- ECDHE-RSA-AES256-SHA
- DHE-RSA-AES128-SHA256
- DHE-RSA-AES256-SHA256
- DHE-RSA-AES128-SHA
- DHE-RSA-AES256-SHA
- DHE-DSS-AES128-SHA256
- DHE-DSS-AES256-SHA256
- DHE-DSS-AES128-SHA
- DHE-DSS-AES256-SHA
- AES128-GCM-SHA256
- AES256-GCM-SHA384
- AES128-SHA256
- AES256-SHA256
- AES128-SHA
- AES256-SHA
- ECDHE-ECDSA-RC4-SHA
- ECDHE-RSA-RC4-SHA
- RC4-SHA
- }.join(":"),
- :options => -> {
- opts = OpenSSL::SSL::OP_ALL
- opts &= ~OpenSSL::SSL::OP_DONT_INSERT_EMPTY_FRAGMENTS if defined?(OpenSSL::SSL::OP_DONT_INSERT_EMPTY_FRAGMENTS)
- opts |= OpenSSL::SSL::OP_NO_COMPRESSION if defined?(OpenSSL::SSL::OP_NO_COMPRESSION)
- opts |= OpenSSL::SSL::OP_NO_SSLv2 if defined?(OpenSSL::SSL::OP_NO_SSLv2)
- opts |= OpenSSL::SSL::OP_NO_SSLv3 if defined?(OpenSSL::SSL::OP_NO_SSLv3)
- opts
- }.call
- } unless const_defined? :DEFAULT_PARAMS # JRuby does it in Java
-
- begin
- DEFAULT_CERT_STORE = OpenSSL::X509::Store.new
- DEFAULT_CERT_STORE.set_default_paths
- if defined?(OpenSSL::X509::V_FLAG_CRL_CHECK_ALL)
- DEFAULT_CERT_STORE.flags = OpenSSL::X509::V_FLAG_CRL_CHECK_ALL
- end
- end unless const_defined? :DEFAULT_CERT_STORE
-
- def set_params(params={})
- params = DEFAULT_PARAMS.merge(params)
- params.each{|name, value| self.__send__("#{name}=", value) }
- if self.verify_mode != OpenSSL::SSL::VERIFY_NONE
- unless self.ca_file or self.ca_path or self.cert_store
- self.cert_store = DEFAULT_CERT_STORE
- end
- end
- return params
- end unless method_defined? :set_params
- end
-
- module SocketForwarder
- def addr
- to_io.addr
- end
-
- def peeraddr
- to_io.peeraddr
- end
-
- def setsockopt(level, optname, optval)
- to_io.setsockopt(level, optname, optval)
- end
-
- def getsockopt(level, optname)
- to_io.getsockopt(level, optname)
- end
-
- def fcntl(*args)
- to_io.fcntl(*args)
- end
-
- def closed?
- to_io.closed?
- end
-
- def do_not_reverse_lookup=(flag)
- to_io.do_not_reverse_lookup = flag
- end
- end
-
- def verify_certificate_identity(cert, hostname)
- should_verify_common_name = true
- cert.extensions.each { |ext|
- next if ext.oid != "subjectAltName"
- ext.value.split(/,\s+/).each { |general_name|
- # MRI 1.9.3 (since we parse ASN.1 differently)
- # when 2 # dNSName in GeneralName (RFC5280)
- if /\ADNS:(.*)/ =~ general_name
- should_verify_common_name = false
- reg = Regexp.escape($1).gsub(/\\\*/, "[^.]+")
- return true if /\A#{reg}\z/i =~ hostname
- # MRI 1.9.3 (since we parse ASN.1 differently)
- # when 7 # iPAddress in GeneralName (RFC5280)
- elsif /\AIP(?: Address)?:(.*)/ =~ general_name
- should_verify_common_name = false
- return true if $1 == hostname
- # NOTE: bellow logic makes little sense as we read exts differently
- #value = $1 # follows GENERAL_NAME_print() in x509v3/v3_alt.c
- #if value.size == 4
- # return true if value.unpack('C*').join('.') == hostname
- #elsif value.size == 16
- # return true if value.unpack('n*').map { |e| sprintf("%X", e) }.join(':') == hostname
- #end
- end
- }
- }
- if should_verify_common_name
- cert.subject.to_a.each { |oid, value|
- if oid == "CN"
- reg = Regexp.escape(value).gsub(/\\\*/, "[^.]+")
- return true if /\A#{reg}\z/i =~ hostname
- end
- }
- end
- return false
- end
- module_function :verify_certificate_identity
-
- class SSLSocket
- include Buffering
- include SocketForwarder
- include Nonblock
-
- def sysclose
- return if closed?
- stop
- io.close if sync_close
- end unless method_defined? :sysclose
-
- def post_connection_check(hostname)
- unless OpenSSL::SSL.verify_certificate_identity(peer_cert, hostname)
- raise SSLError, "hostname does not match the server certificate"
- end
- return true
- end
-
- end
-
- class SSLServer
- include SocketForwarder
- attr_accessor :start_immediately
-
- def initialize(svr, ctx)
- @svr = svr
- @ctx = ctx
- unless ctx.session_id_context
- session_id = OpenSSL::Digest::MD5.hexdigest($0)
- @ctx.session_id_context = session_id
- end
- @start_immediately = true
- end
-
- def to_io
- @svr
- end
-
- def listen(backlog=5)
- @svr.listen(backlog)
- end
-
- def shutdown(how=Socket::SHUT_RDWR)
- @svr.shutdown(how)
- end
-
- def accept
- sock = @svr.accept
- begin
- ssl = OpenSSL::SSL::SSLSocket.new(sock, @ctx)
- ssl.sync_close = true
- ssl.accept if @start_immediately
- ssl
- rescue SSLError => ex
- sock.close
- raise ex
- end
- end
-
- def close
- @svr.close
- end
- end
- end
-end
diff --git a/lib/jopenssl19/openssl/ssl.rb b/lib/jopenssl19/openssl/ssl.rb
deleted file mode 100644
index 15f42d60..00000000
--- a/lib/jopenssl19/openssl/ssl.rb
+++ /dev/null
@@ -1,2 +0,0 @@
-warn 'deprecated openssl/ssl use: require "openssl" instead of "openssl/ssl"'
-require 'openssl'
diff --git a/lib/jopenssl19/openssl/x509-internal.rb b/lib/jopenssl19/openssl/x509-internal.rb
deleted file mode 100644
index 24eeff7d..00000000
--- a/lib/jopenssl19/openssl/x509-internal.rb
+++ /dev/null
@@ -1,115 +0,0 @@
-=begin
-= $RCSfile$ -- Ruby-space definitions that completes C-space funcs for X509 and subclasses
-
-= Info
- 'OpenSSL for Ruby 2' project
- Copyright (C) 2002 Michal Rokos
- All rights reserved.
-
-= Licence
- This program is licenced under the same licence as Ruby.
- (See the file 'LICENCE'.)
-
-= Version
- $Id$
-=end
-
-module OpenSSL
- module X509
- class Name
- module RFC2253DN
- Special = ',=+<>#;'
- HexChar = /[0-9a-fA-F]/
- HexPair = /#{HexChar}#{HexChar}/
- HexString = /#{HexPair}+/
- Pair = /\\(?:[#{Special}]|\\|"|#{HexPair})/
- StringChar = /[^#{Special}\\"]/
- QuoteChar = /[^\\"]/
- AttributeType = /[a-zA-Z][0-9a-zA-Z]*|[0-9]+(?:\.[0-9]+)*/
- AttributeValue = /
- (?!["#])((?:#{StringChar}|#{Pair})*)|
- \#(#{HexString})|
- "((?:#{QuoteChar}|#{Pair})*)"
- /x
- TypeAndValue = /\A(#{AttributeType})=#{AttributeValue}/
-
- module_function
-
- def expand_pair(str)
- return nil unless str
- return str.gsub(Pair){
- pair = $&
- case pair.size
- when 2 then pair[1,1]
- when 3 then Integer("0x#{pair[1,2]}").chr
- else raise OpenSSL::X509::NameError, "invalid pair: #{str}"
- end
- }
- end
-
- def expand_hexstring(str)
- return nil unless str
- der = str.gsub(HexPair){$&.to_i(16).chr }
- a1 = OpenSSL::ASN1.decode(der)
- return a1.value, a1.tag
- end
-
- def expand_value(str1, str2, str3)
- value = expand_pair(str1)
- value, tag = expand_hexstring(str2) unless value
- value = expand_pair(str3) unless value
- return value, tag
- end
-
- def scan(dn)
- str = dn
- ary = []
- while true
- if md = TypeAndValue.match(str)
- remain = md.post_match
- type = md[1]
- value, tag = expand_value(md[2], md[3], md[4]) rescue nil
- if value
- type_and_value = [type, value]
- type_and_value.push(tag) if tag
- ary.unshift(type_and_value)
- if remain.length > 2 && remain[0] == ?,
- str = remain[1..-1]
- next
- elsif remain.length > 2 && remain[0] == ?+
- raise OpenSSL::X509::NameError,
- "multi-valued RDN is not supported: #{dn}"
- elsif remain.empty?
- break
- end
- end
- end
- msg_dn = dn[0, dn.length - str.length] + " =>" + str
- raise OpenSSL::X509::NameError, "malformed RDN: #{msg_dn}"
- end
- return ary
- end
- end
-
- class << self
- def parse_rfc2253(str, template=OBJECT_TYPE_TEMPLATE)
- ary = OpenSSL::X509::Name::RFC2253DN.scan(str)
- self.new(ary, template)
- end
-
- def parse_openssl(str, template=OBJECT_TYPE_TEMPLATE)
- ary = str.scan(/\s*([^\/,]+)\s*/).collect{|i| i[0].split("=", 2) }
- self.new(ary, template)
- end
-
- alias parse parse_openssl
- end
- end
-
- class StoreContext
- def cleanup
- warn "(#{caller.first}) OpenSSL::X509::StoreContext#cleanup is deprecated with no replacement" if $VERBOSE
- end
- end
- end
-end
diff --git a/lib/jopenssl19/openssl/x509.rb b/lib/jopenssl19/openssl/x509.rb
deleted file mode 100644
index f1777cdf..00000000
--- a/lib/jopenssl19/openssl/x509.rb
+++ /dev/null
@@ -1,2 +0,0 @@
-warn 'deprecated openssl/x509 use: require "openssl" instead of "openssl/x509"'
-require 'openssl'
diff --git a/lib/jopenssl21/openssl.rb b/lib/jopenssl21/openssl.rb
deleted file mode 100644
index b1d7406c..00000000
--- a/lib/jopenssl21/openssl.rb
+++ /dev/null
@@ -1,22 +0,0 @@
-=begin
-= $RCSfile$ -- Loader for all OpenSSL C-space and Ruby-space definitions
-
-= Info
- 'OpenSSL for Ruby 2' project
- Copyright (C) 2002 Michal Rokos
- All rights reserved.
-
-= Licence
- This program is licenced under the same licence as Ruby.
- (See the file 'LICENCE'.)
-
-= Version
- $Id$
-=end
-
-require 'openssl/bn'
-require 'openssl/cipher'
-require 'openssl/config'
-require 'openssl/digest'
-require 'openssl/x509'
-require 'openssl/ssl'
diff --git a/lib/jopenssl21/openssl/bn.rb b/lib/jopenssl21/openssl/bn.rb
deleted file mode 100644
index a8a8d8ac..00000000
--- a/lib/jopenssl21/openssl/bn.rb
+++ /dev/null
@@ -1,28 +0,0 @@
-#--
-#
-# $RCSfile$
-#
-# = Ruby-space definitions that completes C-space funcs for BN
-#
-# = Info
-# 'OpenSSL for Ruby 2' project
-# Copyright (C) 2002 Michal Rokos
-# All rights reserved.
-#
-# = Licence
-# This program is licenced under the same licence as Ruby.
-# (See the file 'LICENCE'.)
-#
-# = Version
-# $Id$
-#
-#++
-
-##
-# Add double dispatch to Integer
-#
-class Integer
- def to_bn
- OpenSSL::BN::new(self)
- end
-end # Integer
diff --git a/lib/jopenssl21/openssl/buffering.rb b/lib/jopenssl21/openssl/buffering.rb
deleted file mode 100644
index f6133b54..00000000
--- a/lib/jopenssl21/openssl/buffering.rb
+++ /dev/null
@@ -1 +0,0 @@
-load 'jopenssl22/openssl/buffering.rb'
\ No newline at end of file
diff --git a/lib/jopenssl21/openssl/cipher.rb b/lib/jopenssl21/openssl/cipher.rb
deleted file mode 100644
index 14a02292..00000000
--- a/lib/jopenssl21/openssl/cipher.rb
+++ /dev/null
@@ -1 +0,0 @@
-load 'jopenssl22/openssl/cipher.rb'
\ No newline at end of file
diff --git a/lib/jopenssl21/openssl/config.rb b/lib/jopenssl21/openssl/config.rb
deleted file mode 100644
index 613e2e8f..00000000
--- a/lib/jopenssl21/openssl/config.rb
+++ /dev/null
@@ -1 +0,0 @@
-load 'jopenssl22/openssl/config.rb'
\ No newline at end of file
diff --git a/lib/jopenssl21/openssl/digest.rb b/lib/jopenssl21/openssl/digest.rb
deleted file mode 100644
index ebdeba29..00000000
--- a/lib/jopenssl21/openssl/digest.rb
+++ /dev/null
@@ -1 +0,0 @@
-load 'jopenssl22/openssl/digest.rb'
\ No newline at end of file
diff --git a/lib/jopenssl21/openssl/ssl.rb b/lib/jopenssl21/openssl/ssl.rb
deleted file mode 100644
index e02f25e9..00000000
--- a/lib/jopenssl21/openssl/ssl.rb
+++ /dev/null
@@ -1 +0,0 @@
-load 'jopenssl22/openssl/ssl.rb'
\ No newline at end of file
diff --git a/lib/jopenssl21/openssl/x509.rb b/lib/jopenssl21/openssl/x509.rb
deleted file mode 100644
index eabd676e..00000000
--- a/lib/jopenssl21/openssl/x509.rb
+++ /dev/null
@@ -1,119 +0,0 @@
-#--
-#
-# $RCSfile$
-#
-# = Ruby-space definitions that completes C-space funcs for X509 and subclasses
-#
-# = Info
-# 'OpenSSL for Ruby 2' project
-# Copyright (C) 2002 Michal Rokos
-# All rights reserved.
-#
-# = Licence
-# This program is licenced under the same licence as Ruby.
-# (See the file 'LICENCE'.)
-#
-# = Version
-# $Id$
-#
-#++
-
-module OpenSSL
- module X509
- class Name
- module RFC2253DN
- Special = ',=+<>#;'
- HexChar = /[0-9a-fA-F]/
- HexPair = /#{HexChar}#{HexChar}/
- HexString = /#{HexPair}+/
- Pair = /\\(?:[#{Special}]|\\|"|#{HexPair})/
- StringChar = /[^#{Special}\\"]/
- QuoteChar = /[^\\"]/
- AttributeType = /[a-zA-Z][0-9a-zA-Z]*|[0-9]+(?:\.[0-9]+)*/
- AttributeValue = /
- (?!["#])((?:#{StringChar}|#{Pair})*)|
- \#(#{HexString})|
- "((?:#{QuoteChar}|#{Pair})*)"
- /x
- TypeAndValue = /\A(#{AttributeType})=#{AttributeValue}/
-
- module_function
-
- def expand_pair(str)
- return nil unless str
- return str.gsub(Pair){
- pair = $&
- case pair.size
- when 2 then pair[1,1]
- when 3 then Integer("0x#{pair[1,2]}").chr
- else raise OpenSSL::X509::NameError, "invalid pair: #{str}"
- end
- }
- end
-
- def expand_hexstring(str)
- return nil unless str
- der = str.gsub(HexPair){$&.to_i(16).chr }
- a1 = OpenSSL::ASN1.decode(der)
- return a1.value, a1.tag
- end
-
- def expand_value(str1, str2, str3)
- value = expand_pair(str1)
- value, tag = expand_hexstring(str2) unless value
- value = expand_pair(str3) unless value
- return value, tag
- end
-
- def scan(dn)
- str = dn
- ary = []
- while true
- if md = TypeAndValue.match(str)
- remain = md.post_match
- type = md[1]
- value, tag = expand_value(md[2], md[3], md[4]) rescue nil
- if value
- type_and_value = [type, value]
- type_and_value.push(tag) if tag
- ary.unshift(type_and_value)
- if remain.length > 2 && remain[0] == ?,
- str = remain[1..-1]
- next
- elsif remain.length > 2 && remain[0] == ?+
- raise OpenSSL::X509::NameError,
- "multi-valued RDN is not supported: #{dn}"
- elsif remain.empty?
- break
- end
- end
- end
- msg_dn = dn[0, dn.length - str.length] + " =>" + str
- raise OpenSSL::X509::NameError, "malformed RDN: #{msg_dn}"
- end
- return ary
- end
- end
-
- class << self
- def parse_rfc2253(str, template=OBJECT_TYPE_TEMPLATE)
- ary = OpenSSL::X509::Name::RFC2253DN.scan(str)
- self.new(ary, template)
- end
-
- def parse_openssl(str, template=OBJECT_TYPE_TEMPLATE)
- ary = str.scan(/\s*([^\/,]+)\s*/).collect{|i| i[0].split("=", 2) }
- self.new(ary, template)
- end
-
- alias parse parse_openssl
- end
- end
-
- class StoreContext
- def cleanup
- warn "(#{caller.first}) OpenSSL::X509::StoreContext#cleanup is deprecated with no replacement" if $VERBOSE
- end
- end
- end
-end
diff --git a/lib/jopenssl22/openssl.rb b/lib/jopenssl22/openssl.rb
deleted file mode 100644
index b1d7406c..00000000
--- a/lib/jopenssl22/openssl.rb
+++ /dev/null
@@ -1,22 +0,0 @@
-=begin
-= $RCSfile$ -- Loader for all OpenSSL C-space and Ruby-space definitions
-
-= Info
- 'OpenSSL for Ruby 2' project
- Copyright (C) 2002 Michal Rokos
- All rights reserved.
-
-= Licence
- This program is licenced under the same licence as Ruby.
- (See the file 'LICENCE'.)
-
-= Version
- $Id$
-=end
-
-require 'openssl/bn'
-require 'openssl/cipher'
-require 'openssl/config'
-require 'openssl/digest'
-require 'openssl/x509'
-require 'openssl/ssl'
diff --git a/lib/jopenssl22/openssl/bn.rb b/lib/jopenssl22/openssl/bn.rb
deleted file mode 100644
index 781ae9c8..00000000
--- a/lib/jopenssl22/openssl/bn.rb
+++ /dev/null
@@ -1,39 +0,0 @@
-#--
-#
-# $RCSfile$
-#
-# = Ruby-space definitions that completes C-space funcs for BN
-#
-# = Info
-# 'OpenSSL for Ruby 2' project
-# Copyright (C) 2002 Michal Rokos
-# All rights reserved.
-#
-# = Licence
-# This program is licenced under the same licence as Ruby.
-# (See the file 'LICENCE'.)
-#
-# = Version
-# $Id$
-#
-#++
-
-module OpenSSL
- class BN
- def pretty_print(q)
- q.object_group(self) {
- q.text ' '
- q.text to_i.to_s
- }
- end
- end # BN
-end # OpenSSL
-
-##
-# Add double dispatch to Integer
-#
-class Integer
- def to_bn
- OpenSSL::BN::new(self)
- end
-end # Integer
diff --git a/lib/jopenssl22/openssl/buffering.rb b/lib/jopenssl22/openssl/buffering.rb
deleted file mode 100644
index 5248ab45..00000000
--- a/lib/jopenssl22/openssl/buffering.rb
+++ /dev/null
@@ -1,456 +0,0 @@
-# coding: binary
-#--
-#= $RCSfile$ -- Buffering mix-in module.
-#
-#= Info
-# 'OpenSSL for Ruby 2' project
-# Copyright (C) 2001 GOTOU YUUZOU
-# All rights reserved.
-#
-#= Licence
-# This program is licenced under the same licence as Ruby.
-# (See the file 'LICENCE'.)
-#
-#= Version
-# $Id$
-#++
-
-##
-# OpenSSL IO buffering mix-in module.
-#
-# This module allows an OpenSSL::SSL::SSLSocket to behave like an IO.
-#
-# You typically won't use this module directly, you can see it implemented in
-# OpenSSL::SSL::SSLSocket.
-
-module OpenSSL::Buffering
- include Enumerable
-
- ##
- # The "sync mode" of the SSLSocket.
- #
- # See IO#sync for full details.
-
- attr_accessor :sync
-
- ##
- # Default size to read from or write to the SSLSocket for buffer operations.
-
- BLOCK_SIZE = 1024*16
-
- ##
- # Creates an instance of OpenSSL's buffering IO module.
-
- def initialize(*)
- @eof = false
- @rbuffer = ""
- @sync = @io.sync
- end
-
- #
- # for reading.
- #
- private
-
- ##
- # Fills the buffer from the underlying SSLSocket
-
- def fill_rbuff
- begin
- @rbuffer << self.sysread(BLOCK_SIZE)
- rescue Errno::EAGAIN
- retry
- rescue EOFError
- @eof = true
- end
- end
-
- ##
- # Consumes +size+ bytes from the buffer
-
- def consume_rbuff(size=nil)
- if @rbuffer.empty?
- nil
- else
- size = @rbuffer.size unless size
- ret = @rbuffer[0, size]
- @rbuffer[0, size] = ""
- ret
- end
- end
-
- public
-
- ##
- # Reads +size+ bytes from the stream. If +buf+ is provided it must
- # reference a string which will receive the data.
- #
- # See IO#read for full details.
-
- def read(size=nil, buf=nil)
- if size == 0
- if buf
- buf.clear
- return buf
- else
- return ""
- end
- end
- until @eof
- break if size && size <= @rbuffer.size
- fill_rbuff
- end
- ret = consume_rbuff(size) || ""
- if buf
- buf.replace(ret)
- ret = buf
- end
- (size && ret.empty?) ? nil : ret
- end
-
- ##
- # Reads at most +maxlen+ bytes from the stream. If +buf+ is provided it
- # must reference a string which will receive the data.
- #
- # See IO#readpartial for full details.
-
- def readpartial(maxlen, buf=nil)
- if maxlen == 0
- if buf
- buf.clear
- return buf
- else
- return ""
- end
- end
- if @rbuffer.empty?
- begin
- return sysread(maxlen, buf)
- rescue Errno::EAGAIN
- retry
- end
- end
- ret = consume_rbuff(maxlen)
- if buf
- buf.replace(ret)
- ret = buf
- end
- raise EOFError if ret.empty?
- ret
- end
-
- ##
- # Reads at most +maxlen+ bytes in the non-blocking manner.
- #
- # When no data can be read without blocking it raises
- # OpenSSL::SSL::SSLError extended by IO::WaitReadable or IO::WaitWritable.
- #
- # IO::WaitReadable means SSL needs to read internally so read_nonblock
- # should be called again when the underlying IO is readable.
- #
- # IO::WaitWritable means SSL needs to write internally so read_nonblock
- # should be called again after the underlying IO is writable.
- #
- # OpenSSL::Buffering#read_nonblock needs two rescue clause as follows:
- #
- # # emulates blocking read (readpartial).
- # begin
- # result = ssl.read_nonblock(maxlen)
- # rescue IO::WaitReadable
- # IO.select([io])
- # retry
- # rescue IO::WaitWritable
- # IO.select(nil, [io])
- # retry
- # end
- #
- # Note that one reason that read_nonblock writes to the underlying IO is
- # when the peer requests a new TLS/SSL handshake. See openssl the FAQ for
- # more details. http://www.openssl.org/support/faq.html
-
- def read_nonblock(maxlen, buf=nil, exception: true)
- if maxlen == 0
- if buf
- buf.clear
- return buf
- else
- return ""
- end
- end
- if @rbuffer.empty?
- return sysread_nonblock(maxlen, buf, exception: exception)
- end
- ret = consume_rbuff(maxlen)
- if buf
- buf.replace(ret)
- ret = buf
- end
- raise EOFError if ret.empty?
- ret
- end
-
- ##
- # Reads the next "line+ from the stream. Lines are separated by +eol+. If
- # +limit+ is provided the result will not be longer than the given number of
- # bytes.
- #
- # +eol+ may be a String or Regexp.
- #
- # Unlike IO#gets the line read will not be assigned to +$_+.
- #
- # Unlike IO#gets the separator must be provided if a limit is provided.
-
- def gets(eol=$/, limit=nil)
- idx = @rbuffer.index(eol)
- until @eof
- break if idx
- fill_rbuff
- idx = @rbuffer.index(eol)
- end
- if eol.is_a?(Regexp)
- size = idx ? idx+$&.size : nil
- else
- size = idx ? idx+eol.size : nil
- end
- if limit and limit >= 0
- size = [size, limit].min
- end
- consume_rbuff(size)
- end
-
- ##
- # Executes the block for every line in the stream where lines are separated
- # by +eol+.
- #
- # See also #gets
-
- def each(eol=$/)
- while line = self.gets(eol)
- yield line
- end
- end
- alias each_line each
-
- ##
- # Reads lines from the stream which are separated by +eol+.
- #
- # See also #gets
-
- def readlines(eol=$/)
- ary = []
- while line = self.gets(eol)
- ary << line
- end
- ary
- end
-
- ##
- # Reads a line from the stream which is separated by +eol+.
- #
- # Raises EOFError if at end of file.
-
- def readline(eol=$/)
- raise EOFError if eof?
- gets(eol)
- end
-
- ##
- # Reads one character from the stream. Returns nil if called at end of
- # file.
-
- def getc
- read(1)
- end
-
- ##
- # Calls the given block once for each byte in the stream.
-
- def each_byte # :yields: byte
- while c = getc
- yield(c.ord)
- end
- end
-
- ##
- # Reads a one-character string from the stream. Raises an EOFError at end
- # of file.
-
- def readchar
- raise EOFError if eof?
- getc
- end
-
- ##
- # Pushes character +c+ back onto the stream such that a subsequent buffered
- # character read will return it.
- #
- # Unlike IO#getc multiple bytes may be pushed back onto the stream.
- #
- # Has no effect on unbuffered reads (such as #sysread).
-
- def ungetc(c)
- @rbuffer[0,0] = c.chr
- end
-
- ##
- # Returns true if the stream is at file which means there is no more data to
- # be read.
-
- def eof?
- fill_rbuff if !@eof && @rbuffer.empty?
- @eof && @rbuffer.empty?
- end
- alias eof eof?
-
- #
- # for writing.
- #
- private
-
- ##
- # Writes +s+ to the buffer. When the buffer is full or #sync is true the
- # buffer is flushed to the underlying socket.
-
- def do_write(s)
- @wbuffer = "" unless defined? @wbuffer
- @wbuffer << s
- @wbuffer.force_encoding(Encoding::BINARY)
- @sync ||= false
- if @sync or @wbuffer.size > BLOCK_SIZE or idx = @wbuffer.rindex($/)
- remain = idx ? idx + $/.size : @wbuffer.length
- nwritten = 0
- while remain > 0
- str = @wbuffer[nwritten,remain]
- begin
- nwrote = syswrite(str)
- rescue Errno::EAGAIN
- retry
- end
- remain -= nwrote
- nwritten += nwrote
- end
- @wbuffer[0,nwritten] = ""
- end
- end
-
- public
-
- ##
- # Writes +s+ to the stream. If the argument is not a string it will be
- # converted using String#to_s. Returns the number of bytes written.
-
- def write(s)
- do_write(s)
- s.bytesize
- end
-
- ##
- # Writes +str+ in the non-blocking manner.
- #
- # If there is buffered data, it is flushed first. This may block.
- #
- # write_nonblock returns number of bytes written to the SSL connection.
- #
- # When no data can be written without blocking it raises
- # OpenSSL::SSL::SSLError extended by IO::WaitReadable or IO::WaitWritable.
- #
- # IO::WaitReadable means SSL needs to read internally so write_nonblock
- # should be called again after the underlying IO is readable.
- #
- # IO::WaitWritable means SSL needs to write internally so write_nonblock
- # should be called again after underlying IO is writable.
- #
- # So OpenSSL::Buffering#write_nonblock needs two rescue clause as follows.
- #
- # # emulates blocking write.
- # begin
- # result = ssl.write_nonblock(str)
- # rescue IO::WaitReadable
- # IO.select([io])
- # retry
- # rescue IO::WaitWritable
- # IO.select(nil, [io])
- # retry
- # end
- #
- # Note that one reason that write_nonblock reads from the underlying IO
- # is when the peer requests a new TLS/SSL handshake. See the openssl FAQ
- # for more details. http://www.openssl.org/support/faq.html
-
- def write_nonblock(s, exception: true)
- flush
- syswrite_nonblock(s, exception: exception)
- end
-
- ##
- # Writes +s+ to the stream. +s+ will be converted to a String using
- # String#to_s.
-
- def << (s)
- do_write(s)
- self
- end
-
- ##
- # Writes +args+ to the stream along with a record separator.
- #
- # See IO#puts for full details.
-
- def puts(*args)
- s = ""
- if args.empty?
- s << "\n"
- end
- args.each{|arg|
- s << arg.to_s
- if $/ && /\n\z/ !~ s
- s << "\n"
- end
- }
- do_write(s)
- nil
- end
-
- ##
- # Writes +args+ to the stream.
- #
- # See IO#print for full details.
-
- def print(*args)
- s = ""
- args.each{ |arg| s << arg.to_s }
- do_write(s)
- nil
- end
-
- ##
- # Formats and writes to the stream converting parameters under control of
- # the format string.
- #
- # See Kernel#sprintf for format string details.
-
- def printf(s, *args)
- do_write(s % args)
- nil
- end
-
- ##
- # Flushes buffered data to the SSLSocket.
-
- def flush
- osync = @sync
- @sync = true
- do_write ""
- return self
- ensure
- @sync = osync
- end
-
- ##
- # Closes the SSLSocket and flushes any unwritten data.
-
- def close
- flush rescue nil
- sysclose
- end
-end
diff --git a/lib/jopenssl22/openssl/cipher.rb b/lib/jopenssl22/openssl/cipher.rb
deleted file mode 100644
index b254a238..00000000
--- a/lib/jopenssl22/openssl/cipher.rb
+++ /dev/null
@@ -1,28 +0,0 @@
-#--
-#
-# $RCSfile$
-#
-# = Ruby-space predefined Cipher subclasses
-#
-# = Info
-# 'OpenSSL for Ruby 2' project
-# Copyright (C) 2002 Michal Rokos
-# All rights reserved.
-#
-# = Licence
-# This program is licenced under the same licence as Ruby.
-# (See the file 'LICENCE'.)
-#
-# = Version
-# $Id$
-#
-#++
-
-module OpenSSL
- class Cipher
- # This class is only provided for backwards compatibility. Use OpenSSL::Cipher in the future.
- class Cipher < Cipher
- # add warning
- end
- end # Cipher
-end # OpenSSL
\ No newline at end of file
diff --git a/lib/jopenssl22/openssl/config.rb b/lib/jopenssl22/openssl/config.rb
deleted file mode 100644
index 24a54c91..00000000
--- a/lib/jopenssl22/openssl/config.rb
+++ /dev/null
@@ -1,313 +0,0 @@
-=begin
-= Ruby-space definitions that completes C-space funcs for Config
-
-= Info
- Copyright (C) 2010 Hiroshi Nakamura
-
-= Licence
- This program is licenced under the same licence as Ruby.
- (See the file 'LICENCE'.)
-
-=end
-
-require 'stringio'
-
-module OpenSSL
- class Config
- include Enumerable
-
- class << self
- def parse(str)
- c = new()
- parse_config(StringIO.new(str)).each do |section, hash|
- c[section] = hash
- end
- c
- end
-
- alias load new
-
- def parse_config(io)
- begin
- parse_config_lines(io)
- rescue ConfigError => e
- e.message.replace("error in line #{io.lineno}: " + e.message)
- raise
- end
- end
-
- def get_key_string(data, section, key) # :nodoc:
- if v = data[section] && data[section][key]
- return v
- elsif section == 'ENV'
- if v = ENV[key]
- return v
- end
- end
- if v = data['default'] && data['default'][key]
- return v
- end
- end
-
- private
-
- def parse_config_lines(io)
- section = 'default'
- data = {section => {}}
- while definition = get_definition(io)
- definition = clear_comments(definition)
- next if definition.empty?
- if definition[0] == ?[
- if /\[([^\]]*)\]/ =~ definition
- section = $1.strip
- data[section] ||= {}
- else
- raise ConfigError, "missing close square bracket"
- end
- else
- if /\A([^:\s]*)(?:::([^:\s]*))?\s*=(.*)\z/ =~ definition
- if $2
- section = $1
- key = $2
- else
- key = $1
- end
- value = unescape_value(data, section, $3)
- (data[section] ||= {})[key] = value.strip
- else
- raise ConfigError, "missing equal sign"
- end
- end
- end
- data
- end
-
- # escape with backslash
- QUOTE_REGEXP_SQ = /\A([^'\\]*(?:\\.[^'\\]*)*)'/
- # escape with backslash and doubled dq
- QUOTE_REGEXP_DQ = /\A([^"\\]*(?:""[^"\\]*|\\.[^"\\]*)*)"/
- # escaped char map
- ESCAPE_MAP = {
- "r" => "\r",
- "n" => "\n",
- "b" => "\b",
- "t" => "\t",
- }
-
- def unescape_value(data, section, value)
- scanned = []
- while m = value.match(/['"\\$]/)
- scanned << m.pre_match
- c = m[0]
- value = m.post_match
- case c
- when "'"
- if m = value.match(QUOTE_REGEXP_SQ)
- scanned << m[1].gsub(/\\(.)/, '\\1')
- value = m.post_match
- else
- break
- end
- when '"'
- if m = value.match(QUOTE_REGEXP_DQ)
- scanned << m[1].gsub(/""/, '').gsub(/\\(.)/, '\\1')
- value = m.post_match
- else
- break
- end
- when "\\"
- c = value.slice!(0, 1)
- scanned << (ESCAPE_MAP[c] || c)
- when "$"
- ref, value = extract_reference(value)
- refsec = section
- if ref.index('::')
- refsec, ref = ref.split('::', 2)
- end
- if v = get_key_string(data, refsec, ref)
- scanned << v
- else
- raise ConfigError, "variable has no value"
- end
- else
- raise 'must not reaced'
- end
- end
- scanned << value
- scanned.join
- end
-
- def extract_reference(value)
- rest = ''
- if m = value.match(/\(([^)]*)\)|\{([^}]*)\}/)
- value = m[1] || m[2]
- rest = m.post_match
- elsif [?(, ?{].include?(value[0])
- raise ConfigError, "no close brace"
- end
- if m = value.match(/[a-zA-Z0-9_]*(?:::[a-zA-Z0-9_]*)?/)
- return m[0], m.post_match + rest
- else
- raise
- end
- end
-
- def clear_comments(line)
- # FCOMMENT
- if m = line.match(/\A([\t\n\f ]*);.*\z/)
- return m[1]
- end
- # COMMENT
- scanned = []
- while m = line.match(/[#'"\\]/)
- scanned << m.pre_match
- c = m[0]
- line = m.post_match
- case c
- when '#'
- line = nil
- break
- when "'", '"'
- regexp = (c == "'") ? QUOTE_REGEXP_SQ : QUOTE_REGEXP_DQ
- scanned << c
- if m = line.match(regexp)
- scanned << m[0]
- line = m.post_match
- else
- scanned << line
- line = nil
- break
- end
- when "\\"
- scanned << c
- scanned << line.slice!(0, 1)
- else
- raise 'must not reaced'
- end
- end
- scanned << line
- scanned.join
- end
-
- def get_definition(io)
- if line = get_line(io)
- while /[^\\]\\\z/ =~ line
- if extra = get_line(io)
- line += extra
- else
- break
- end
- end
- return line.strip
- end
- end
-
- def get_line(io)
- if line = io.gets
- line.gsub(/[\r\n]*/, '')
- end
- end
- end
-
- def initialize(filename = nil)
- @data = {}
- if filename
- File.open(filename.to_s) do |file|
- Config.parse_config(file).each do |section, hash|
- self[section] = hash
- end
- end
- end
- end
-
- def get_value(section, key)
- if section.nil?
- raise TypeError.new('nil not allowed')
- end
- section = 'default' if section.empty?
- get_key_string(section, key)
- end
-
- def value(arg1, arg2 = nil)
- warn('Config#value is deprecated; use Config#get_value')
- if arg2.nil?
- section, key = 'default', arg1
- else
- section, key = arg1, arg2
- end
- section ||= 'default'
- section = 'default' if section.empty?
- get_key_string(section, key)
- end
-
- def add_value(section, key, value)
- check_modify
- (@data[section] ||= {})[key] = value
- end
-
- def [](section)
- @data[section] || {}
- end
-
- def section(name)
- warn('Config#section is deprecated; use Config#[]')
- @data[name] || {}
- end
-
- def []=(section, pairs)
- check_modify
- @data[section] ||= {}
- pairs.each do |key, value|
- self.add_value(section, key, value)
- end
- end
-
- def sections
- @data.keys
- end
-
- def to_s
- ary = []
- @data.keys.sort.each do |section|
- ary << "[ #{section} ]\n"
- @data[section].keys.each do |key|
- ary << "#{key}=#{@data[section][key]}\n"
- end
- ary << "\n"
- end
- ary.join
- end
-
- def each
- @data.each do |section, hash|
- hash.each do |key, value|
- yield [section, key, value]
- end
- end
- end
-
- def inspect
- "#<#{self.class.name} sections=#{sections.inspect}>"
- end
-
- protected
-
- def data
- @data
- end
-
- private
-
- def initialize_copy(other)
- @data = other.data.dup
- end
-
- def check_modify
- raise TypeError.new("Insecure: can't modify OpenSSL config") if frozen?
- end
-
- def get_key_string(section, key)
- Config.get_key_string(@data, section, key)
- end
- end
-end
diff --git a/lib/jopenssl22/openssl/digest.rb b/lib/jopenssl22/openssl/digest.rb
deleted file mode 100644
index c1d8bce6..00000000
--- a/lib/jopenssl22/openssl/digest.rb
+++ /dev/null
@@ -1,54 +0,0 @@
-#--
-#
-# $RCSfile$
-#
-# = Ruby-space predefined Digest subclasses
-#
-# = Info
-# 'OpenSSL for Ruby 2' project
-# Copyright (C) 2002 Michal Rokos
-# All rights reserved.
-#
-# = Licence
-# This program is licenced under the same licence as Ruby.
-# (See the file 'LICENCE'.)
-#
-# = Version
-# $Id$
-#
-#++
-
-module OpenSSL
- class Digest
- # Deprecated.
- #
- # This class is only provided for backwards compatibility.
- class Digest < Digest # :nodoc:
- # Deprecated.
- #
- # See OpenSSL::Digest.new
- def initialize(*args)
- warn('Digest::Digest is deprecated; use Digest')
- super(*args)
- end
- end
-
- end # Digest
-
- # Returns a Digest subclass by +name+.
- #
- # require 'openssl'
- #
- # OpenSSL::Digest("MD5")
- # # => OpenSSL::Digest::MD5
- #
- # Digest("Foo")
- # # => NameError: wrong constant name Foo
-
- def Digest(name)
- OpenSSL::Digest.const_get(name)
- end
-
- module_function :Digest
-
-end # OpenSSL
diff --git a/lib/jopenssl22/openssl/ssl.rb b/lib/jopenssl22/openssl/ssl.rb
deleted file mode 100644
index dc8707ec..00000000
--- a/lib/jopenssl22/openssl/ssl.rb
+++ /dev/null
@@ -1,330 +0,0 @@
-=begin
-= $RCSfile$ -- Ruby-space definitions that completes C-space funcs for SSL
-
-= Info
- 'OpenSSL for Ruby 2' project
- Copyright (C) 2001 GOTOU YUUZOU
- All rights reserved.
-
-= Licence
- This program is licenced under the same licence as Ruby.
- (See the file 'LICENCE'.)
-
-= Version
- $Id$
-=end
-
-require "openssl/buffering"
-require "fcntl"
-
-module OpenSSL
- module SSL
- class SSLContext
- DEFAULT_PARAMS = {
- :ssl_version => "SSLv23",
- :verify_mode => OpenSSL::SSL::VERIFY_PEER,
- :ciphers => %w{
- ECDHE-ECDSA-AES128-GCM-SHA256
- ECDHE-RSA-AES128-GCM-SHA256
- ECDHE-ECDSA-AES256-GCM-SHA384
- ECDHE-RSA-AES256-GCM-SHA384
- DHE-RSA-AES128-GCM-SHA256
- DHE-DSS-AES128-GCM-SHA256
- DHE-RSA-AES256-GCM-SHA384
- DHE-DSS-AES256-GCM-SHA384
- ECDHE-ECDSA-AES128-SHA256
- ECDHE-RSA-AES128-SHA256
- ECDHE-ECDSA-AES128-SHA
- ECDHE-RSA-AES128-SHA
- ECDHE-ECDSA-AES256-SHA384
- ECDHE-RSA-AES256-SHA384
- ECDHE-ECDSA-AES256-SHA
- ECDHE-RSA-AES256-SHA
- DHE-RSA-AES128-SHA256
- DHE-RSA-AES256-SHA256
- DHE-RSA-AES128-SHA
- DHE-RSA-AES256-SHA
- DHE-DSS-AES128-SHA256
- DHE-DSS-AES256-SHA256
- DHE-DSS-AES128-SHA
- DHE-DSS-AES256-SHA
- AES128-GCM-SHA256
- AES256-GCM-SHA384
- AES128-SHA256
- AES256-SHA256
- AES128-SHA
- AES256-SHA
- ECDHE-ECDSA-RC4-SHA
- ECDHE-RSA-RC4-SHA
- RC4-SHA
- }.join(":"),
- :options => -> {
- opts = OpenSSL::SSL::OP_ALL
- opts &= ~OpenSSL::SSL::OP_DONT_INSERT_EMPTY_FRAGMENTS if defined?(OpenSSL::SSL::OP_DONT_INSERT_EMPTY_FRAGMENTS)
- opts |= OpenSSL::SSL::OP_NO_COMPRESSION if defined?(OpenSSL::SSL::OP_NO_COMPRESSION)
- opts |= OpenSSL::SSL::OP_NO_SSLv2 if defined?(OpenSSL::SSL::OP_NO_SSLv2)
- opts |= OpenSSL::SSL::OP_NO_SSLv3 if defined?(OpenSSL::SSL::OP_NO_SSLv3)
- opts
- }.call
- } unless const_defined? :DEFAULT_PARAMS # JRuby does it in Java
-
- begin
- DEFAULT_CERT_STORE = OpenSSL::X509::Store.new
- DEFAULT_CERT_STORE.set_default_paths
- if defined?(OpenSSL::X509::V_FLAG_CRL_CHECK_ALL)
- DEFAULT_CERT_STORE.flags = OpenSSL::X509::V_FLAG_CRL_CHECK_ALL
- end
- end unless const_defined? :DEFAULT_CERT_STORE
-
- ##
- # Sets the parameters for this SSL context to the values in +params+.
- # The keys in +params+ must be assignment methods on SSLContext.
- #
- # If the verify_mode is not VERIFY_NONE and ca_file, ca_path and
- # cert_store are not set then the system default certificate store is
- # used.
-
- def set_params(params={})
- params = DEFAULT_PARAMS.merge(params)
- params.each { |name, value| self.__send__("#{name}=", value) }
- if self.verify_mode != OpenSSL::SSL::VERIFY_NONE
- unless self.ca_file or self.ca_path or self.cert_store
- self.cert_store = DEFAULT_CERT_STORE
- end
- end
- return params
- end unless method_defined? :set_params
- end
-
- module SocketForwarder
- def addr
- to_io.addr
- end
-
- def peeraddr
- to_io.peeraddr
- end
-
- def setsockopt(level, optname, optval)
- to_io.setsockopt(level, optname, optval)
- end
-
- def getsockopt(level, optname)
- to_io.getsockopt(level, optname)
- end
-
- def fcntl(*args)
- to_io.fcntl(*args)
- end
-
- def closed?
- to_io.closed?
- end
-
- def do_not_reverse_lookup=(flag)
- to_io.do_not_reverse_lookup = flag
- end
- end
-
- module Nonblock
- def initialize(*args)
- flag = File::NONBLOCK
- flag |= @io.fcntl(Fcntl::F_GETFL) if defined?(Fcntl::F_GETFL)
- @io.fcntl(Fcntl::F_SETFL, flag)
- super
- end
- end unless const_defined? :Nonblock # JRuby: hooked up in "native" Java
-
- def verify_certificate_identity(cert, hostname)
- should_verify_common_name = true
- cert.extensions.each { |ext|
- next if ext.oid != "subjectAltName"
- ext.value.split(/,\s+/).each { |general_name|
- #case san.tag
- # MRI 2.2.3 (JRuby parses ASN.1 differently)
- #when 2 # dNSName in GeneralName (RFC5280)
- if /\ADNS:(.*)/ =~ general_name
- should_verify_common_name = false
- return true if verify_hostname(hostname, $1)
- # MRI 2.2.3 (JRuby parses ASN.1 differently)
- #when 7 # iPAddress in GeneralName (RFC5280)
- elsif /\AIP(?: Address)?:(.*)/ =~ general_name
- should_verify_common_name = false
- return true if $1 == hostname
- # NOTE: bellow logic makes little sense JRuby reads exts differently
- # follows GENERAL_NAME_print() in x509v3/v3_alt.c
- #if san.value.size == 4
- # return true if san.value.unpack('C*').join('.') == hostname
- #elsif san.value.size == 16
- # return true if san.value.unpack('n*').map { |e| sprintf("%X", e) }.join(':') == hostname
- #end
- end
- }
- }
- if should_verify_common_name
- cert.subject.to_a.each{|oid, value|
- if oid == "CN"
- return true if verify_hostname(hostname, value)
- end
- }
- end
- return false
- end
- module_function :verify_certificate_identity
-
- def verify_hostname(hostname, san) # :nodoc:
- # RFC 5280, IA5String is limited to the set of ASCII characters
- return false unless san.ascii_only?
- return false unless hostname.ascii_only?
-
- # See RFC 6125, section 6.4.1
- # Matching is case-insensitive.
- san_parts = san.downcase.split(".")
-
- # TODO: this behavior should probably be more strict
- return san == hostname if san_parts.size < 2
-
- # Matching is case-insensitive.
- host_parts = hostname.downcase.split(".")
-
- # RFC 6125, section 6.4.3, subitem 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).
- return false unless san_parts.size == host_parts.size
-
- # RFC 6125, section 6.4.3, subitem 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).
- return false unless verify_wildcard(host_parts.shift, san_parts.shift)
-
- san_parts.join(".") == host_parts.join(".")
- end
- module_function :verify_hostname
-
- def verify_wildcard(domain_component, san_component) # :nodoc:
- parts = san_component.split("*", -1)
-
- return false if parts.size > 2
- return san_component == domain_component if parts.size == 1
-
- # RFC 6125, section 6.4.3, subitem 3.
- # The client SHOULD NOT attempt to match a presented identifier
- # where the wildcard character is embedded within an A-label or
- # U-label of an internationalized domain name.
- return false if domain_component.start_with?("xn--") && san_component != "*"
-
- parts[0].length + parts[1].length < domain_component.length &&
- domain_component.start_with?(parts[0]) &&
- domain_component.end_with?(parts[1])
- end
- module_function :verify_wildcard
-
- class SSLSocket
- include Buffering
- include SocketForwarder
- include Nonblock
-
- def sysclose
- return if closed?
- stop
- io.close if sync_close
- end unless method_defined? :sysclose
-
- ##
- # Perform hostname verification after an SSL connection is established
- #
- # This method MUST be called after calling #connect to ensure that the
- # hostname of a remote peer has been verified.
- def post_connection_check(hostname)
- if peer_cert.nil?
- msg = "Peer verification enabled, but no certificate received."
- if using_anon_cipher?
- msg += " Anonymous cipher suite #{cipher[0]} was negotiated. Anonymous suites must be disabled to use peer verification."
- end
- raise SSLError, msg
- end
-
- unless OpenSSL::SSL.verify_certificate_identity(peer_cert, hostname)
- raise SSLError, "hostname \"#{hostname}\" does not match the server certificate"
- end
- return true
- end
-
- private
-
- def using_anon_cipher?
- ctx = OpenSSL::SSL::SSLContext.new
- ctx.ciphers = "aNULL"
- ctx.ciphers.include?(cipher)
- end
- end
-
- ##
- # SSLServer represents a TCP/IP server socket with Secure Sockets Layer.
- class SSLServer
- include SocketForwarder
- # When true then #accept works exactly the same as TCPServer#accept
- attr_accessor :start_immediately
-
- # Creates a new instance of SSLServer.
- # * +srv+ is an instance of TCPServer.
- # * +ctx+ is an instance of OpenSSL::SSL::SSLContext.
- def initialize(svr, ctx)
- @svr = svr
- @ctx = ctx
- unless ctx.session_id_context
- # see #6137 - session id may not exceed 32 bytes
- prng = ::Random.new($0.hash)
- session_id = prng.bytes(16).unpack('H*')[0]
- @ctx.session_id_context = session_id
- end
- @start_immediately = true
- end
-
- # Returns the TCPServer passed to the SSLServer when initialized.
- def to_io
- @svr
- end
-
- # See TCPServer#listen for details.
- def listen(backlog=5)
- @svr.listen(backlog)
- end
-
- # See BasicSocket#shutdown for details.
- def shutdown(how=Socket::SHUT_RDWR)
- @svr.shutdown(how)
- end
-
- # Works similar to TCPServer#accept.
- def accept
- # Socket#accept returns [socket, addrinfo].
- # TCPServer#accept returns a socket.
- # The following comma strips addrinfo.
- sock, = @svr.accept
- begin
- ssl = OpenSSL::SSL::SSLSocket.new(sock, @ctx)
- ssl.sync_close = true
- ssl.accept if @start_immediately
- ssl
- rescue Exception => ex
- if ssl
- ssl.close
- else
- sock.close
- end
- raise ex
- end
- end
-
- # See IO#close for details.
- def close
- @svr.close
- end
- end
- end
-end
diff --git a/lib/jopenssl22/openssl/x509.rb b/lib/jopenssl22/openssl/x509.rb
deleted file mode 100644
index c8acf824..00000000
--- a/lib/jopenssl22/openssl/x509.rb
+++ /dev/null
@@ -1,139 +0,0 @@
-#--
-#
-# $RCSfile$
-#
-# = Ruby-space definitions that completes C-space funcs for X509 and subclasses
-#
-# = Info
-# 'OpenSSL for Ruby 2' project
-# Copyright (C) 2002 Michal Rokos
-# All rights reserved.
-#
-# = Licence
-# This program is licenced under the same licence as Ruby.
-# (See the file 'LICENCE'.)
-#
-# = Version
-# $Id$
-#
-#++
-
-module OpenSSL
- module X509
- class Name
- module RFC2253DN
- Special = ',=+<>#;'
- HexChar = /[0-9a-fA-F]/
- HexPair = /#{HexChar}#{HexChar}/
- HexString = /#{HexPair}+/
- Pair = /\\(?:[#{Special}]|\\|"|#{HexPair})/
- StringChar = /[^\\"#{Special}]/
- QuoteChar = /[^\\"]/
- AttributeType = /[a-zA-Z][0-9a-zA-Z]*|[0-9]+(?:\.[0-9]+)*/
- AttributeValue = /
- (?!["#])((?:#{StringChar}|#{Pair})*)|
- \#(#{HexString})|
- "((?:#{QuoteChar}|#{Pair})*)"
- /x
- TypeAndValue = /\A(#{AttributeType})=#{AttributeValue}/
-
- module_function
-
- def expand_pair(str)
- return nil unless str
- return str.gsub(Pair){
- pair = $&
- case pair.size
- when 2 then pair[1,1]
- when 3 then Integer("0x#{pair[1,2]}").chr
- else raise OpenSSL::X509::NameError, "invalid pair: #{str}"
- end
- }
- end
-
- def expand_hexstring(str)
- return nil unless str
- der = str.gsub(HexPair){$&.to_i(16).chr }
- a1 = OpenSSL::ASN1.decode(der)
- return a1.value, a1.tag
- end
-
- def expand_value(str1, str2, str3)
- value = expand_pair(str1)
- value, tag = expand_hexstring(str2) unless value
- value = expand_pair(str3) unless value
- return value, tag
- end
-
- def scan(dn)
- str = dn
- ary = []
- while true
- if md = TypeAndValue.match(str)
- remain = md.post_match
- type = md[1]
- value, tag = expand_value(md[2], md[3], md[4]) rescue nil
- if value
- type_and_value = [type, value]
- type_and_value.push(tag) if tag
- ary.unshift(type_and_value)
- if remain.length > 2 && remain[0] == ?,
- str = remain[1..-1]
- next
- elsif remain.length > 2 && remain[0] == ?+
- raise OpenSSL::X509::NameError,
- "multi-valued RDN is not supported: #{dn}"
- elsif remain.empty?
- break
- end
- end
- end
- msg_dn = dn[0, dn.length - str.length] + " =>" + str
- raise OpenSSL::X509::NameError, "malformed RDN: #{msg_dn}"
- end
- return ary
- end
- end
-
- class << self
- def parse_rfc2253(str, template=OBJECT_TYPE_TEMPLATE)
- ary = OpenSSL::X509::Name::RFC2253DN.scan(str)
- self.new(ary, template)
- end
-
- def parse_openssl(str, template=OBJECT_TYPE_TEMPLATE)
- ary = str.scan(/\s*([^\/,]+)\s*/).collect{|i| i[0].split("=", 2) }
- self.new(ary, template)
- end
-
- alias parse parse_openssl
- end
-
- def pretty_print(q)
- q.object_group(self) {
- q.text ' '
- q.text to_s(OpenSSL::X509::Name::RFC2253)
- }
- end
- end
-
- class StoreContext
- def cleanup
- warn "(#{caller.first}) OpenSSL::X509::StoreContext#cleanup is deprecated with no replacement" if $VERBOSE
- end
- end
-
- class Certificate
- def pretty_print(q)
- q.object_group(self) {
- q.breakable
- q.text 'subject='; q.pp self.subject; q.text ','; q.breakable
- q.text 'issuer='; q.pp self.issuer; q.text ','; q.breakable
- q.text 'serial='; q.pp self.serial; q.text ','; q.breakable
- q.text 'not_before='; q.pp self.not_before; q.text ','; q.breakable
- q.text 'not_after='; q.pp self.not_after
- }
- end
- end
- end
-end
diff --git a/lib/jopenssl23/openssl.rb b/lib/jopenssl23/openssl.rb
deleted file mode 100644
index 31502184..00000000
--- a/lib/jopenssl23/openssl.rb
+++ /dev/null
@@ -1,19 +0,0 @@
-# frozen_string_literal: false
-=begin
-= Info
- 'OpenSSL for Ruby 2' project
- Copyright (C) 2002 Michal Rokos
- All rights reserved.
-
-= Licence
- This program is licensed under the same licence as Ruby.
- (See the file 'LICENCE'.)
-=end
-
-require 'openssl/bn'
-require 'openssl/pkey'
-require 'openssl/cipher'
-require 'openssl/config' if OpenSSL.const_defined?(:Config, false)
-require 'openssl/digest'
-require 'openssl/x509'
-require 'openssl/ssl'
diff --git a/lib/jopenssl23/openssl/bn.rb b/lib/jopenssl23/openssl/bn.rb
deleted file mode 100644
index b900a18c..00000000
--- a/lib/jopenssl23/openssl/bn.rb
+++ /dev/null
@@ -1,38 +0,0 @@
-# frozen_string_literal: false
-#--
-#
-# = Ruby-space definitions that completes C-space funcs for BN
-#
-# = Info
-# 'OpenSSL for Ruby 2' project
-# Copyright (C) 2002 Michal Rokos
-# All rights reserved.
-#
-# = Licence
-# This program is licensed under the same licence as Ruby.
-# (See the file 'LICENCE'.)
-#++
-
-module OpenSSL
- class BN
- def pretty_print(q)
- q.object_group(self) {
- q.text ' '
- q.text to_i.to_s
- }
- end
- end # BN
-end # OpenSSL
-
-##
-#--
-# Add double dispatch to Integer
-#++
-class Integer
- # Casts an Integer as an OpenSSL::BN
- #
- # See `man bn` for more info.
- def to_bn
- OpenSSL::BN::new(self)
- end
-end # Integer
diff --git a/lib/jopenssl23/openssl/buffering.rb b/lib/jopenssl23/openssl/buffering.rb
deleted file mode 100644
index 07ad7cf2..00000000
--- a/lib/jopenssl23/openssl/buffering.rb
+++ /dev/null
@@ -1,455 +0,0 @@
-# coding: binary
-# frozen_string_literal: false
-#--
-#= Info
-# 'OpenSSL for Ruby 2' project
-# Copyright (C) 2001 GOTOU YUUZOU
-# All rights reserved.
-#
-#= Licence
-# This program is licensed under the same licence as Ruby.
-# (See the file 'LICENCE'.)
-#++
-
-##
-# OpenSSL IO buffering mix-in module.
-#
-# This module allows an OpenSSL::SSL::SSLSocket to behave like an IO.
-#
-# You typically won't use this module directly, you can see it implemented in
-# OpenSSL::SSL::SSLSocket.
-
-module OpenSSL::Buffering
- include Enumerable
-
- ##
- # The "sync mode" of the SSLSocket.
- #
- # See IO#sync for full details.
-
- attr_accessor :sync
-
- ##
- # Default size to read from or write to the SSLSocket for buffer operations.
-
- BLOCK_SIZE = 1024*16
-
- ##
- # Creates an instance of OpenSSL's buffering IO module.
-
- def initialize(*)
- # super
- @eof = false
- @rbuffer = ""
- @sync = @io.sync
- end
-
- #
- # for reading.
- #
- private
-
- ##
- # Fills the buffer from the underlying SSLSocket
-
- def fill_rbuff
- begin
- @rbuffer << self.sysread(BLOCK_SIZE)
- rescue Errno::EAGAIN
- retry
- rescue EOFError
- @eof = true
- end
- end
-
- ##
- # Consumes _size_ bytes from the buffer
-
- def consume_rbuff(size=nil)
- if @rbuffer.empty?
- nil
- else
- size = @rbuffer.size unless size
- ret = @rbuffer[0, size]
- @rbuffer[0, size] = ""
- ret
- end
- end
-
- public
-
- ##
- # Reads _size_ bytes from the stream. If _buf_ is provided it must
- # reference a string which will receive the data.
- #
- # See IO#read for full details.
-
- def read(size=nil, buf=nil)
- if size == 0
- if buf
- buf.clear
- return buf
- else
- return ""
- end
- end
- until @eof
- break if size && size <= @rbuffer.size
- fill_rbuff
- end
- ret = consume_rbuff(size) || ""
- if buf
- buf.replace(ret)
- ret = buf
- end
- (size && ret.empty?) ? nil : ret
- end
-
- ##
- # Reads at most _maxlen_ bytes from the stream. If _buf_ is provided it
- # must reference a string which will receive the data.
- #
- # See IO#readpartial for full details.
-
- def readpartial(maxlen, buf=nil)
- if maxlen == 0
- if buf
- buf.clear
- return buf
- else
- return ""
- end
- end
- if @rbuffer.empty?
- begin
- return sysread(maxlen, buf)
- rescue Errno::EAGAIN
- retry
- end
- end
- ret = consume_rbuff(maxlen)
- if buf
- buf.replace(ret)
- ret = buf
- end
- ret
- end
-
- ##
- # Reads at most _maxlen_ bytes in the non-blocking manner.
- #
- # When no data can be read without blocking it raises
- # OpenSSL::SSL::SSLError extended by IO::WaitReadable or IO::WaitWritable.
- #
- # IO::WaitReadable means SSL needs to read internally so read_nonblock
- # should be called again when the underlying IO is readable.
- #
- # IO::WaitWritable means SSL needs to write internally so read_nonblock
- # should be called again after the underlying IO is writable.
- #
- # OpenSSL::Buffering#read_nonblock needs two rescue clause as follows:
- #
- # # emulates blocking read (readpartial).
- # begin
- # result = ssl.read_nonblock(maxlen)
- # rescue IO::WaitReadable
- # IO.select([io])
- # retry
- # rescue IO::WaitWritable
- # IO.select(nil, [io])
- # retry
- # end
- #
- # Note that one reason that read_nonblock writes to the underlying IO is
- # when the peer requests a new TLS/SSL handshake. See openssl the FAQ for
- # more details. http://www.openssl.org/support/faq.html
- #
- # By specifying a keyword argument _exception_ to +false+, you can indicate
- # that read_nonblock should not raise an IO::Wait*able exception, but
- # return the symbol +:wait_writable+ or +:wait_readable+ instead. At EOF,
- # it will return +nil+ instead of raising EOFError.
-
- def read_nonblock(maxlen, buf=nil, exception: true)
- if maxlen == 0
- if buf
- buf.clear
- return buf
- else
- return ""
- end
- end
- if @rbuffer.empty?
- return sysread_nonblock(maxlen, buf, exception: exception)
- end
- ret = consume_rbuff(maxlen)
- if buf
- buf.replace(ret)
- ret = buf
- end
- ret
- end
-
- ##
- # Reads the next "line" from the stream. Lines are separated by _eol_. If
- # _limit_ is provided the result will not be longer than the given number of
- # bytes.
- #
- # _eol_ may be a String or Regexp.
- #
- # Unlike IO#gets the line read will not be assigned to +$_+.
- #
- # Unlike IO#gets the separator must be provided if a limit is provided.
-
- def gets(eol=$/, limit=nil)
- idx = @rbuffer.index(eol)
- until @eof
- break if idx
- fill_rbuff
- idx = @rbuffer.index(eol)
- end
- if eol.is_a?(Regexp)
- size = idx ? idx+$&.size : nil
- else
- size = idx ? idx+eol.size : nil
- end
- if size && limit && limit >= 0
- size = [size, limit].min
- end
- consume_rbuff(size)
- end
-
- ##
- # Executes the block for every line in the stream where lines are separated
- # by _eol_.
- #
- # See also #gets
-
- def each(eol=$/)
- while line = self.gets(eol)
- yield line
- end
- end
- alias each_line each
-
- ##
- # Reads lines from the stream which are separated by _eol_.
- #
- # See also #gets
-
- def readlines(eol=$/)
- ary = []
- while line = self.gets(eol)
- ary << line
- end
- ary
- end
-
- ##
- # Reads a line from the stream which is separated by _eol_.
- #
- # Raises EOFError if at end of file.
-
- def readline(eol=$/)
- raise EOFError if eof?
- gets(eol)
- end
-
- ##
- # Reads one character from the stream. Returns nil if called at end of
- # file.
-
- def getc
- read(1)
- end
-
- ##
- # Calls the given block once for each byte in the stream.
-
- def each_byte # :yields: byte
- while c = getc
- yield(c.ord)
- end
- end
-
- ##
- # Reads a one-character string from the stream. Raises an EOFError at end
- # of file.
-
- def readchar
- raise EOFError if eof?
- getc
- end
-
- ##
- # Pushes character _c_ back onto the stream such that a subsequent buffered
- # character read will return it.
- #
- # Unlike IO#getc multiple bytes may be pushed back onto the stream.
- #
- # Has no effect on unbuffered reads (such as #sysread).
-
- def ungetc(c)
- @rbuffer[0,0] = c.chr
- end
-
- ##
- # Returns true if the stream is at file which means there is no more data to
- # be read.
-
- def eof?
- fill_rbuff if !@eof && @rbuffer.empty?
- @eof && @rbuffer.empty?
- end
- alias eof eof?
-
- #
- # for writing.
- #
- private
-
- ##
- # Writes _s_ to the buffer. When the buffer is full or #sync is true the
- # buffer is flushed to the underlying socket.
-
- def do_write(s)
- @wbuffer = "" unless defined? @wbuffer
- @wbuffer << s
- @wbuffer.force_encoding(Encoding::BINARY)
- @sync ||= false
- if @sync or @wbuffer.size > BLOCK_SIZE
- until @wbuffer.empty?
- begin
- nwrote = syswrite(@wbuffer)
- rescue Errno::EAGAIN
- retry
- end
- @wbuffer[0, nwrote] = ""
- end
- end
- end
-
- public
-
- ##
- # Writes _s_ to the stream. If the argument is not a String it will be
- # converted using +.to_s+ method. Returns the number of bytes written.
-
- def write(*s)
- s.inject(0) do |written, str|
- do_write(str)
- written + str.bytesize
- end
- end
-
- ##
- # Writes _s_ in the non-blocking manner.
- #
- # If there is buffered data, it is flushed first. This may block.
- #
- # write_nonblock returns number of bytes written to the SSL connection.
- #
- # When no data can be written without blocking it raises
- # OpenSSL::SSL::SSLError extended by IO::WaitReadable or IO::WaitWritable.
- #
- # IO::WaitReadable means SSL needs to read internally so write_nonblock
- # should be called again after the underlying IO is readable.
- #
- # IO::WaitWritable means SSL needs to write internally so write_nonblock
- # should be called again after underlying IO is writable.
- #
- # So OpenSSL::Buffering#write_nonblock needs two rescue clause as follows.
- #
- # # emulates blocking write.
- # begin
- # result = ssl.write_nonblock(str)
- # rescue IO::WaitReadable
- # IO.select([io])
- # retry
- # rescue IO::WaitWritable
- # IO.select(nil, [io])
- # retry
- # end
- #
- # Note that one reason that write_nonblock reads from the underlying IO
- # is when the peer requests a new TLS/SSL handshake. See the openssl FAQ
- # for more details. http://www.openssl.org/support/faq.html
- #
- # By specifying a keyword argument _exception_ to +false+, you can indicate
- # that write_nonblock should not raise an IO::Wait*able exception, but
- # return the symbol +:wait_writable+ or +:wait_readable+ instead.
-
- def write_nonblock(s, exception: true)
- flush
- syswrite_nonblock(s, exception: exception)
- end
-
- ##
- # Writes _s_ to the stream. _s_ will be converted to a String using
- # +.to_s+ method.
-
- def <<(s)
- do_write(s)
- self
- end
-
- ##
- # Writes _args_ to the stream along with a record separator.
- #
- # See IO#puts for full details.
-
- def puts(*args)
- s = ""
- if args.empty?
- s << "\n"
- end
- args.each{|arg|
- s << arg.to_s
- s.sub!(/(?
-# All rights reserved.
-#
-# = Licence
-# This program is licensed under the same licence as Ruby.
-# (See the file 'LICENCE'.)
-#++
-
-module OpenSSL
- class Cipher
-
- # Deprecated.
- #
- # This class is only provided for backwards compatibility.
- # Use OpenSSL::Cipher.
- class Cipher < Cipher; end
- deprecate_constant :Cipher
- end # Cipher
-end # OpenSSL
diff --git a/lib/jopenssl23/openssl/config.rb b/lib/jopenssl23/openssl/config.rb
deleted file mode 100644
index 48d8be00..00000000
--- a/lib/jopenssl23/openssl/config.rb
+++ /dev/null
@@ -1,474 +0,0 @@
-# frozen_string_literal: false
-=begin
-= Ruby-space definitions that completes C-space funcs for Config
-
-= Info
- Copyright (C) 2010 Hiroshi Nakamura
-
-= Licence
- This program is licensed under the same licence as Ruby.
- (See the file 'LICENCE'.)
-
-=end
-
-require 'stringio'
-
-module OpenSSL
- ##
- # = OpenSSL::Config
- #
- # Configuration for the openssl library.
- #
- # Many system's installation of openssl library will depend on your system
- # configuration. See the value of OpenSSL::Config::DEFAULT_CONFIG_FILE for
- # the location of the file for your host.
- #
- # See also http://www.openssl.org/docs/apps/config.html
- class Config
- include Enumerable
-
- class << self
-
- ##
- # Parses a given _string_ as a blob that contains configuration for
- # OpenSSL.
- #
- # If the source of the IO is a file, then consider using #parse_config.
- def parse(string)
- c = new()
- parse_config(StringIO.new(string)).each do |section, hash|
- c[section] = hash
- end
- c
- end
-
- ##
- # load is an alias to ::new
- alias load new
-
- ##
- # Parses the configuration data read from _io_, see also #parse.
- #
- # Raises a ConfigError on invalid configuration data.
- def parse_config(io)
- begin
- parse_config_lines(io)
- rescue ConfigError => e
- e.message.replace("error in line #{io.lineno}: " + e.message)
- raise
- end
- end
-
- def get_key_string(data, section, key) # :nodoc:
- if v = data[section] && data[section][key]
- return v
- elsif section == 'ENV'
- if v = ENV[key]
- return v
- end
- end
- if v = data['default'] && data['default'][key]
- return v
- end
- end
-
- private
-
- def parse_config_lines(io)
- section = 'default'
- data = {section => {}}
- while definition = get_definition(io)
- definition = clear_comments(definition)
- next if definition.empty?
- if definition[0] == ?[
- if /\[([^\]]*)\]/ =~ definition
- section = $1.strip
- data[section] ||= {}
- else
- raise ConfigError, "missing close square bracket"
- end
- else
- if /\A([^:\s]*)(?:::([^:\s]*))?\s*=(.*)\z/ =~ definition
- if $2
- section = $1
- key = $2
- else
- key = $1
- end
- value = unescape_value(data, section, $3)
- (data[section] ||= {})[key] = value.strip
- else
- raise ConfigError, "missing equal sign"
- end
- end
- end
- data
- end
-
- # escape with backslash
- QUOTE_REGEXP_SQ = /\A([^'\\]*(?:\\.[^'\\]*)*)'/
- # escape with backslash and doubled dq
- QUOTE_REGEXP_DQ = /\A([^"\\]*(?:""[^"\\]*|\\.[^"\\]*)*)"/
- # escaped char map
- ESCAPE_MAP = {
- "r" => "\r",
- "n" => "\n",
- "b" => "\b",
- "t" => "\t",
- }
-
- def unescape_value(data, section, value)
- scanned = []
- while m = value.match(/['"\\$]/)
- scanned << m.pre_match
- c = m[0]
- value = m.post_match
- case c
- when "'"
- if m = value.match(QUOTE_REGEXP_SQ)
- scanned << m[1].gsub(/\\(.)/, '\\1')
- value = m.post_match
- else
- break
- end
- when '"'
- if m = value.match(QUOTE_REGEXP_DQ)
- scanned << m[1].gsub(/""/, '').gsub(/\\(.)/, '\\1')
- value = m.post_match
- else
- break
- end
- when "\\"
- c = value.slice!(0, 1)
- scanned << (ESCAPE_MAP[c] || c)
- when "$"
- ref, value = extract_reference(value)
- refsec = section
- if ref.index('::')
- refsec, ref = ref.split('::', 2)
- end
- if v = get_key_string(data, refsec, ref)
- scanned << v
- else
- raise ConfigError, "variable has no value"
- end
- else
- raise 'must not reaced'
- end
- end
- scanned << value
- scanned.join
- end
-
- def extract_reference(value)
- rest = ''
- if m = value.match(/\(([^)]*)\)|\{([^}]*)\}/)
- value = m[1] || m[2]
- rest = m.post_match
- elsif [?(, ?{].include?(value[0])
- raise ConfigError, "no close brace"
- end
- if m = value.match(/[a-zA-Z0-9_]*(?:::[a-zA-Z0-9_]*)?/)
- return m[0], m.post_match + rest
- else
- raise
- end
- end
-
- def clear_comments(line)
- # FCOMMENT
- if m = line.match(/\A([\t\n\f ]*);.*\z/)
- return m[1]
- end
- # COMMENT
- scanned = []
- while m = line.match(/[#'"\\]/)
- scanned << m.pre_match
- c = m[0]
- line = m.post_match
- case c
- when '#'
- line = nil
- break
- when "'", '"'
- regexp = (c == "'") ? QUOTE_REGEXP_SQ : QUOTE_REGEXP_DQ
- scanned << c
- if m = line.match(regexp)
- scanned << m[0]
- line = m.post_match
- else
- scanned << line
- line = nil
- break
- end
- when "\\"
- scanned << c
- scanned << line.slice!(0, 1)
- else
- raise 'must not reaced'
- end
- end
- scanned << line
- scanned.join
- end
-
- def get_definition(io)
- if line = get_line(io)
- while /[^\\]\\\z/ =~ line
- if extra = get_line(io)
- line += extra
- else
- break
- end
- end
- return line.strip
- end
- end
-
- def get_line(io)
- if line = io.gets
- line.gsub(/[\r\n]*/, '')
- end
- end
- end
-
- ##
- # Creates an instance of OpenSSL's configuration class.
- #
- # This can be used in contexts like OpenSSL::X509::ExtensionFactory.config=
- #
- # If the optional _filename_ parameter is provided, then it is read in and
- # parsed via #parse_config.
- #
- # This can raise IO exceptions based on the access, or availability of the
- # file. A ConfigError exception may be raised depending on the validity of
- # the data being configured.
- #
- def initialize(filename = nil)
- @data = {}
- if filename
- File.open(filename.to_s) do |file|
- Config.parse_config(file).each do |section, hash|
- self[section] = hash
- end
- end
- end
- end
-
- ##
- # Gets the value of _key_ from the given _section_
- #
- # Given the following configurating file being loaded:
- #
- # config = OpenSSL::Config.load('foo.cnf')
- # #=> #
- # puts config.to_s
- # #=> [ default ]
- # # foo=bar
- #
- # You can get a specific value from the config if you know the _section_
- # and _key_ like so:
- #
- # config.get_value('default','foo')
- # #=> "bar"
- #
- def get_value(section, key)
- if section.nil?
- raise TypeError.new('nil not allowed')
- end
- section = 'default' if section.empty?
- get_key_string(section, key)
- end
-
- ##
- #
- # *Deprecated*
- #
- # Use #get_value instead
- def value(arg1, arg2 = nil) # :nodoc:
- warn('Config#value is deprecated; use Config#get_value')
- if arg2.nil?
- section, key = 'default', arg1
- else
- section, key = arg1, arg2
- end
- section ||= 'default'
- section = 'default' if section.empty?
- get_key_string(section, key)
- end
-
- ##
- # Set the target _key_ with a given _value_ under a specific _section_.
- #
- # Given the following configurating file being loaded:
- #
- # config = OpenSSL::Config.load('foo.cnf')
- # #=> #
- # puts config.to_s
- # #=> [ default ]
- # # foo=bar
- #
- # You can set the value of _foo_ under the _default_ section to a new
- # value:
- #
- # config.add_value('default', 'foo', 'buzz')
- # #=> "buzz"
- # puts config.to_s
- # #=> [ default ]
- # # foo=buzz
- #
- def add_value(section, key, value)
- check_modify
- (@data[section] ||= {})[key] = value
- end
-
- ##
- # Get a specific _section_ from the current configuration
- #
- # Given the following configurating file being loaded:
- #
- # config = OpenSSL::Config.load('foo.cnf')
- # #=> #
- # puts config.to_s
- # #=> [ default ]
- # # foo=bar
- #
- # You can get a hash of the specific section like so:
- #
- # config['default']
- # #=> {"foo"=>"bar"}
- #
- def [](section)
- @data[section] || {}
- end
-
- ##
- # Deprecated
- #
- # Use #[] instead
- def section(name) # :nodoc:
- warn('Config#section is deprecated; use Config#[]')
- @data[name] || {}
- end
-
- ##
- # Sets a specific _section_ name with a Hash _pairs_.
- #
- # Given the following configuration being created:
- #
- # config = OpenSSL::Config.new
- # #=> #
- # config['default'] = {"foo"=>"bar","baz"=>"buz"}
- # #=> {"foo"=>"bar", "baz"=>"buz"}
- # puts config.to_s
- # #=> [ default ]
- # # foo=bar
- # # baz=buz
- #
- # It's important to note that this will essentially merge any of the keys
- # in _pairs_ with the existing _section_. For example:
- #
- # config['default']
- # #=> {"foo"=>"bar", "baz"=>"buz"}
- # config['default'] = {"foo" => "changed"}
- # #=> {"foo"=>"changed"}
- # config['default']
- # #=> {"foo"=>"changed", "baz"=>"buz"}
- #
- def []=(section, pairs)
- check_modify
- @data[section] ||= {}
- pairs.each do |key, value|
- self.add_value(section, key, value)
- end
- end
-
- ##
- # Get the names of all sections in the current configuration
- def sections
- @data.keys
- end
-
- ##
- # Get the parsable form of the current configuration
- #
- # Given the following configuration being created:
- #
- # config = OpenSSL::Config.new
- # #=> #
- # config['default'] = {"foo"=>"bar","baz"=>"buz"}
- # #=> {"foo"=>"bar", "baz"=>"buz"}
- # puts config.to_s
- # #=> [ default ]
- # # foo=bar
- # # baz=buz
- #
- # You can parse get the serialized configuration using #to_s and then parse
- # it later:
- #
- # serialized_config = config.to_s
- # # much later...
- # new_config = OpenSSL::Config.parse(serialized_config)
- # #=> #
- # puts new_config
- # #=> [ default ]
- # foo=bar
- # baz=buz
- #
- def to_s
- ary = []
- @data.keys.sort.each do |section|
- ary << "[ #{section} ]\n"
- @data[section].keys.each do |key|
- ary << "#{key}=#{@data[section][key]}\n"
- end
- ary << "\n"
- end
- ary.join
- end
-
- ##
- # For a block.
- #
- # Receive the section and its pairs for the current configuration.
- #
- # config.each do |section, key, value|
- # # ...
- # end
- #
- def each
- @data.each do |section, hash|
- hash.each do |key, value|
- yield [section, key, value]
- end
- end
- end
-
- ##
- # String representation of this configuration object, including the class
- # name and its sections.
- def inspect
- "#<#{self.class.name} sections=#{sections.inspect}>"
- end
-
- protected
-
- def data # :nodoc:
- @data
- end
-
- private
-
- def initialize_copy(other)
- @data = other.data.dup
- end
-
- def check_modify
- raise TypeError.new("Insecure: can't modify OpenSSL config") if frozen?
- end
-
- def get_key_string(section, key)
- Config.get_key_string(@data, section, key)
- end
- end
-end
diff --git a/lib/jopenssl23/openssl/digest.rb b/lib/jopenssl23/openssl/digest.rb
deleted file mode 100644
index c087bc0f..00000000
--- a/lib/jopenssl23/openssl/digest.rb
+++ /dev/null
@@ -1,43 +0,0 @@
-# frozen_string_literal: false
-#--
-# = Ruby-space predefined Digest subclasses
-#
-# = Info
-# 'OpenSSL for Ruby 2' project
-# Copyright (C) 2002 Michal Rokos
-# All rights reserved.
-#
-# = Licence
-# This program is licensed under the same licence as Ruby.
-# (See the file 'LICENCE'.)
-#++
-
-module OpenSSL
- class Digest
-
- # Deprecated.
- #
- # This class is only provided for backwards compatibility.
- # Use OpenSSL::Digest instead.
- class Digest < Digest; end # :nodoc:
- deprecate_constant :Digest
-
- end # Digest
-
- # Returns a Digest subclass by _name_
- #
- # require 'openssl'
- #
- # OpenSSL::Digest("MD5")
- # # => OpenSSL::Digest::MD5
- #
- # Digest("Foo")
- # # => NameError: wrong constant name Foo
-
- def Digest(name)
- OpenSSL::Digest.const_get(name)
- end
-
- module_function :Digest
-
-end # OpenSSL
diff --git a/lib/jopenssl23/openssl/pkey.rb b/lib/jopenssl23/openssl/pkey.rb
deleted file mode 100644
index a12ba8b6..00000000
--- a/lib/jopenssl23/openssl/pkey.rb
+++ /dev/null
@@ -1,25 +0,0 @@
-# frozen_string_literal: false
-#--
-# Ruby/OpenSSL Project
-# Copyright (C) 2017 Ruby/OpenSSL Project Authors
-#++
-
-module OpenSSL::PKey
- if defined?(EC)
- class EC::Point
- # :call-seq:
- # point.to_bn([conversion_form]) -> OpenSSL::BN
- #
- # Returns the octet string representation of the EC point as an instance of
- # OpenSSL::BN.
- #
- # If _conversion_form_ is not given, the _point_conversion_form_ attribute
- # set to the group is used.
- #
- # See #to_octet_string for more information.
- # def to_bn(conversion_form = group.point_conversion_form)
- # OpenSSL::BN.new(to_octet_string(conversion_form), 2)
- # end
- end
- end
-end
diff --git a/lib/jopenssl23/openssl/ssl.rb b/lib/jopenssl23/openssl/ssl.rb
deleted file mode 100644
index fbd04081..00000000
--- a/lib/jopenssl23/openssl/ssl.rb
+++ /dev/null
@@ -1,508 +0,0 @@
-# frozen_string_literal: false
-=begin
-= Info
- 'OpenSSL for Ruby 2' project
- Copyright (C) 2001 GOTOU YUUZOU
- All rights reserved.
-
-= Licence
- This program is licensed under the same licence as Ruby.
- (See the file 'LICENCE'.)
-=end
-
-require "openssl/buffering"
-require "io/nonblock"
-
-module OpenSSL
- module SSL
- class SSLContext
- unless const_defined? :DEFAULT_PARAMS # JRuby does it in Java
- DEFAULT_PARAMS = { # :nodoc:
- :min_version => OpenSSL::SSL::TLS1_VERSION,
- :verify_mode => OpenSSL::SSL::VERIFY_PEER,
- :verify_hostname => true,
- :options => -> {
- opts = OpenSSL::SSL::OP_ALL
- opts &= ~OpenSSL::SSL::OP_DONT_INSERT_EMPTY_FRAGMENTS
- opts |= OpenSSL::SSL::OP_NO_COMPRESSION
- opts
- }.call
- }
-
- if !(OpenSSL::OPENSSL_VERSION.start_with?("OpenSSL") &&
- OpenSSL::OPENSSL_VERSION_NUMBER >= 0x10100000)
- DEFAULT_PARAMS.merge!(
- ciphers: %w{
- ECDHE-ECDSA-AES128-GCM-SHA256
- ECDHE-RSA-AES128-GCM-SHA256
- ECDHE-ECDSA-AES256-GCM-SHA384
- ECDHE-RSA-AES256-GCM-SHA384
- DHE-RSA-AES128-GCM-SHA256
- DHE-DSS-AES128-GCM-SHA256
- DHE-RSA-AES256-GCM-SHA384
- DHE-DSS-AES256-GCM-SHA384
- ECDHE-ECDSA-AES128-SHA256
- ECDHE-RSA-AES128-SHA256
- ECDHE-ECDSA-AES128-SHA
- ECDHE-RSA-AES128-SHA
- ECDHE-ECDSA-AES256-SHA384
- ECDHE-RSA-AES256-SHA384
- ECDHE-ECDSA-AES256-SHA
- ECDHE-RSA-AES256-SHA
- DHE-RSA-AES128-SHA256
- DHE-RSA-AES256-SHA256
- DHE-RSA-AES128-SHA
- DHE-RSA-AES256-SHA
- DHE-DSS-AES128-SHA256
- DHE-DSS-AES256-SHA256
- DHE-DSS-AES128-SHA
- DHE-DSS-AES256-SHA
- AES128-GCM-SHA256
- AES256-GCM-SHA384
- AES128-SHA256
- AES256-SHA256
- AES128-SHA
- AES256-SHA
- }.join(":"),
- )
- end
- end
-
- if defined?(OpenSSL::PKey::DH)
- DEFAULT_2048 = OpenSSL::PKey::DH.new <<-_end_of_pem_
------BEGIN DH PARAMETERS-----
-MIIBCAKCAQEA7E6kBrYiyvmKAMzQ7i8WvwVk9Y/+f8S7sCTN712KkK3cqd1jhJDY
-JbrYeNV3kUIKhPxWHhObHKpD1R84UpL+s2b55+iMd6GmL7OYmNIT/FccKhTcveab
-VBmZT86BZKYyf45hUF9FOuUM9xPzuK3Vd8oJQvfYMCd7LPC0taAEljQLR4Edf8E6
-YoaOffgTf5qxiwkjnlVZQc3whgnEt9FpVMvQ9eknyeGB5KHfayAc3+hUAvI3/Cr3
-1bNveX5wInh5GDx1FGhKBZ+s1H+aedudCm7sCgRwv8lKWYGiHzObSma8A86KG+MD
-7Lo5JquQ3DlBodj3IDyPrxIv96lvRPFtAwIBAg==
------END DH PARAMETERS-----
- _end_of_pem_
- private_constant :DEFAULT_2048
-
- DEFAULT_TMP_DH_CALLBACK = lambda { |ctx, is_export, keylen| # :nodoc:
- warn "using default DH parameters." if $VERBOSE
- DEFAULT_2048
- }
- end
-
- begin
- DEFAULT_CERT_STORE = OpenSSL::X509::Store.new # :nodoc:
- DEFAULT_CERT_STORE.set_default_paths
- DEFAULT_CERT_STORE.flags = OpenSSL::X509::V_FLAG_CRL_CHECK_ALL
- end unless const_defined? :DEFAULT_CERT_STORE # JRuby
-
- # A callback invoked when DH parameters are required.
- #
- # The callback is invoked with the Session for the key exchange, an
- # flag indicating the use of an export cipher and the keylength
- # required.
- #
- # The callback must return an OpenSSL::PKey::DH instance of the correct
- # key length.
-
- attr_accessor :tmp_dh_callback
-
- # A callback invoked at connect time to distinguish between multiple
- # server names.
- #
- # The callback is invoked with an SSLSocket and a server name. The
- # callback must return an SSLContext for the server name or nil.
- attr_accessor :servername_cb
-
- # call-seq:
- # SSLContext.new -> ctx
- # SSLContext.new(:TLSv1) -> ctx
- # SSLContext.new("SSLv23") -> ctx
- #
- # Creates a new SSL context.
- #
- # If an argument is given, #ssl_version= is called with the value. Note
- # that this form is deprecated. New applications should use #min_version=
- # and #max_version= as necessary.
- # def initialize(version = nil)
- # self.options |= OpenSSL::SSL::OP_ALL
- # self.ssl_version = version if version
- # end
-
- ##
- # call-seq:
- # ctx.set_params(params = {}) -> params
- #
- # Sets saner defaults optimized for the use with HTTP-like protocols.
- #
- # If a Hash _params_ is given, the parameters are overridden with it.
- # The keys in _params_ must be assignment methods on SSLContext.
- #
- # If the verify_mode is not VERIFY_NONE and ca_file, ca_path and
- # cert_store are not set then the system default certificate store is
- # used.
- def set_params(params={})
- params = DEFAULT_PARAMS.merge(params)
- # TODO JRuby: need to support SSLContext#options (since Ruby 2.5)
- #self.options = params.delete(:options) # set before min_version/max_version
- params.each { |name, value| self.__send__("#{name}=", value) }
- if self.verify_mode != OpenSSL::SSL::VERIFY_NONE
- unless self.ca_file or self.ca_path or self.cert_store
- self.cert_store = DEFAULT_CERT_STORE
- end
- end
- return params
- end unless method_defined? :set_params
-
- # call-seq:
- # ctx.min_version = OpenSSL::SSL::TLS1_2_VERSION
- # ctx.min_version = :TLS1_2
- # ctx.min_version = nil
- #
- # Sets the lower bound on the supported SSL/TLS protocol version. The
- # version may be specified by an integer constant named
- # OpenSSL::SSL::*_VERSION, a Symbol, or +nil+ which means "any version".
- #
- # Be careful that you don't overwrite OpenSSL::SSL::OP_NO_{SSL,TLS}v*
- # options by #options= once you have called #min_version= or
- # #max_version=.
- #
- # === Example
- # ctx = OpenSSL::SSL::SSLContext.new
- # ctx.min_version = OpenSSL::SSL::TLS1_1_VERSION
- # ctx.max_version = OpenSSL::SSL::TLS1_2_VERSION
- #
- # sock = OpenSSL::SSL::SSLSocket.new(tcp_sock, ctx)
- # sock.connect # Initiates a connection using either TLS 1.1 or TLS 1.2
- def min_version=(version)
- set_minmax_proto_version(version, @max_proto_version ||= nil)
- @min_proto_version = version
- end
-
- # call-seq:
- # ctx.max_version = OpenSSL::SSL::TLS1_2_VERSION
- # ctx.max_version = :TLS1_2
- # ctx.max_version = nil
- #
- # Sets the upper bound of the supported SSL/TLS protocol version. See
- # #min_version= for the possible values.
- def max_version=(version)
- set_minmax_proto_version(@min_proto_version ||= nil, version)
- @max_proto_version = version
- end
-
- # call-seq:
- # ctx.ssl_version = :TLSv1
- # ctx.ssl_version = "SSLv23"
- #
- # Sets the SSL/TLS protocol version for the context. This forces
- # connections to use only the specified protocol version. This is
- # deprecated and only provided for backwards compatibility. Use
- # #min_version= and #max_version= instead.
- #
- # === History
- # As the name hints, this used to call the SSL_CTX_set_ssl_version()
- # function which sets the SSL method used for connections created from
- # the context. As of Ruby/OpenSSL 2.1, this accessor method is
- # implemented to call #min_version= and #max_version= instead.
- def ssl_version=(meth)
- meth = meth.to_s if meth.is_a?(Symbol)
- if /(?_client|_server)\z/ =~ meth
- meth = $`
- if $VERBOSE
- warn "#{caller(1, 1)[0]}: method type #{type.inspect} is ignored"
- end
- end
- version = METHODS_MAP[meth.intern] or
- raise ArgumentError, "unknown SSL method `%s'" % meth
- set_minmax_proto_version(version, version)
- @min_proto_version = @max_proto_version = version
- end unless method_defined? :ssl_version=
-
- METHODS_MAP = {
- SSLv23: 0,
- SSLv2: OpenSSL::SSL::SSL2_VERSION,
- SSLv3: OpenSSL::SSL::SSL3_VERSION,
- TLSv1: OpenSSL::SSL::TLS1_VERSION,
- TLSv1_1: OpenSSL::SSL::TLS1_1_VERSION,
- TLSv1_2: OpenSSL::SSL::TLS1_2_VERSION,
- }.freeze
- private_constant :METHODS_MAP
-
- # METHODS setup from native (JRuby)
- # deprecate_constant :METHODS
- end
-
- module SocketForwarder
- def addr
- to_io.addr
- end
-
- def peeraddr
- to_io.peeraddr
- end
-
- def setsockopt(level, optname, optval)
- to_io.setsockopt(level, optname, optval)
- end
-
- def getsockopt(level, optname)
- to_io.getsockopt(level, optname)
- end
-
- def fcntl(*args)
- to_io.fcntl(*args)
- end
-
- def closed?
- to_io.closed?
- end
-
- def do_not_reverse_lookup=(flag)
- to_io.do_not_reverse_lookup = flag
- end
- end unless const_defined? :SocketForwarder # JRuby: hooked up in "native" Java
-
- def verify_certificate_identity(cert, hostname)
- should_verify_common_name = true
- cert.extensions.each{|ext|
- next if ext.oid != "subjectAltName"
- ext.value.split(/,\s+/).each { |general_name|
- #case san.tag
- # MRI 2.2.3 (JRuby parses ASN.1 differently)
- #when 2 # dNSName in GeneralName (RFC5280)
- if /\ADNS:(.*)/ =~ general_name
- should_verify_common_name = false
- return true if verify_hostname(hostname, $1)
- # MRI 2.2.3 (JRuby parses ASN.1 differently)
- #when 7 # iPAddress in GeneralName (RFC5280)
- elsif /\AIP(?: Address)?:(.*)/ =~ general_name
- should_verify_common_name = false
- return true if $1 == hostname
- # NOTE: bellow logic makes little sense JRuby reads exts differently
- # follows GENERAL_NAME_print() in x509v3/v3_alt.c
- #if san.value.size == 4
- # return true if san.value.unpack('C*').join('.') == hostname
- #elsif san.value.size == 16
- # return true if san.value.unpack('n*').map { |e| sprintf("%X", e) }.join(':') == hostname
- #end
- end
- }
- }
- if should_verify_common_name
- cert.subject.to_a.each{|oid, value|
- if oid == "CN"
- return true if verify_hostname(hostname, value)
- end
- }
- end
- return false
- end
- module_function :verify_certificate_identity
-
- def verify_hostname(hostname, san) # :nodoc:
- # RFC 5280, IA5String is limited to the set of ASCII characters
- return false unless san.ascii_only?
- return false unless hostname.ascii_only?
-
- # See RFC 6125, section 6.4.1
- # Matching is case-insensitive.
- san_parts = san.downcase.split(".")
-
- # TODO: this behavior should probably be more strict
- return san == hostname if san_parts.size < 2
-
- # Matching is case-insensitive.
- host_parts = hostname.downcase.split(".")
-
- # RFC 6125, section 6.4.3, subitem 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).
- return false unless san_parts.size == host_parts.size
-
- # RFC 6125, section 6.4.3, subitem 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).
- return false unless verify_wildcard(host_parts.shift, san_parts.shift)
-
- san_parts.join(".") == host_parts.join(".")
- end
- module_function :verify_hostname
-
- def verify_wildcard(domain_component, san_component) # :nodoc:
- parts = san_component.split("*", -1)
-
- return false if parts.size > 2
- return san_component == domain_component if parts.size == 1
-
- # RFC 6125, section 6.4.3, subitem 3.
- # The client SHOULD NOT attempt to match a presented identifier
- # where the wildcard character is embedded within an A-label or
- # U-label of an internationalized domain name.
- return false if domain_component.start_with?("xn--") && san_component != "*"
-
- parts[0].length + parts[1].length < domain_component.length &&
- domain_component.start_with?(parts[0]) &&
- domain_component.end_with?(parts[1])
- end
- module_function :verify_wildcard
-
- class SSLSocket
- include Buffering
- include SocketForwarder
-
- # attr_reader :hostname
- #
- # # The underlying IO object.
- # attr_reader :io
- # alias :to_io :io
- #
- # # The SSLContext object used in this connection.
- # attr_reader :context
- #
- # # Whether to close the underlying socket as well, when the SSL/TLS
- # # connection is shut down. This defaults to +false+.
- # attr_accessor :sync_close
-
- # call-seq:
- # ssl.sysclose => nil
- #
- # Sends "close notify" to the peer and tries to shut down the SSL
- # connection gracefully.
- #
- # If sync_close is set to +true+, the underlying IO is also closed.
- def sysclose
- return if closed?
- stop
- io.close if sync_close
- end unless method_defined? :sysclose
-
- # call-seq:
- # ssl.post_connection_check(hostname) -> true
- #
- # Perform hostname verification following RFC 6125.
- #
- # This method MUST be called after calling #connect to ensure that the
- # hostname of a remote peer has been verified.
- def post_connection_check(hostname)
- if peer_cert.nil?
- msg = "Peer verification enabled, but no certificate received."
- if using_anon_cipher?
- msg += " Anonymous cipher suite #{cipher[0]} was negotiated. " \
- "Anonymous suites must be disabled to use peer verification."
- end
- raise SSLError, msg
- end
-
- unless OpenSSL::SSL.verify_certificate_identity(peer_cert, hostname)
- raise SSLError, "hostname \"#{hostname}\" does not match the server certificate"
- end
- return true
- end
-
- # call-seq:
- # ssl.session -> aSession
- #
- # Returns the SSLSession object currently used, or nil if the session is
- # not established.
- def session
- SSL::Session.new(self)
- rescue SSL::Session::SessionError
- nil
- end unless method_defined? :session # JRuby
-
- private
-
- def using_anon_cipher?
- ctx = OpenSSL::SSL::SSLContext.new
- ctx.ciphers = "aNULL"
- ctx.ciphers.include?(cipher)
- end
-
- def client_cert_cb
- @context.client_cert_cb
- end
-
- def tmp_dh_callback
- @context.tmp_dh_callback || OpenSSL::SSL::SSLContext::DEFAULT_TMP_DH_CALLBACK
- end
-
- def tmp_ecdh_callback
- @context.tmp_ecdh_callback
- end
-
- def session_new_cb
- @context.session_new_cb
- end
-
- def session_get_cb
- @context.session_get_cb
- end
- end
-
- ##
- # SSLServer represents a TCP/IP server socket with Secure Sockets Layer.
- class SSLServer
- include SocketForwarder
- # When true then #accept works exactly the same as TCPServer#accept
- attr_accessor :start_immediately
-
- # Creates a new instance of SSLServer.
- # * _srv_ is an instance of TCPServer.
- # * _ctx_ is an instance of OpenSSL::SSL::SSLContext.
- def initialize(svr, ctx)
- @svr = svr
- @ctx = ctx
- unless ctx.session_id_context
- # see #6137 - session id may not exceed 32 bytes
- prng = ::Random.new($0.hash)
- session_id = prng.bytes(16).unpack('H*')[0]
- @ctx.session_id_context = session_id
- end
- @start_immediately = true
- end
-
- # Returns the TCPServer passed to the SSLServer when initialized.
- def to_io
- @svr
- end
-
- # See TCPServer#listen for details.
- def listen(backlog=5)
- @svr.listen(backlog)
- end
-
- # See BasicSocket#shutdown for details.
- def shutdown(how=Socket::SHUT_RDWR)
- @svr.shutdown(how)
- end
-
- # Works similar to TCPServer#accept.
- def accept
- # Socket#accept returns [socket, addrinfo].
- # TCPServer#accept returns a socket.
- # The following comma strips addrinfo.
- sock, = @svr.accept
- begin
- ssl = OpenSSL::SSL::SSLSocket.new(sock, @ctx)
- ssl.sync_close = true
- ssl.accept if @start_immediately
- ssl
- rescue Exception => ex
- if ssl
- ssl.close
- else
- sock.close
- end
- raise ex
- end
- end
-
- # See IO#close for details.
- def close
- @svr.close
- end
- end
- end
-end
diff --git a/lib/jopenssl23/openssl/x509.rb b/lib/jopenssl23/openssl/x509.rb
deleted file mode 100644
index 682b9627..00000000
--- a/lib/jopenssl23/openssl/x509.rb
+++ /dev/null
@@ -1,208 +0,0 @@
-# frozen_string_literal: false
-#--
-# = Ruby-space definitions that completes C-space funcs for X509 and subclasses
-#
-# = Info
-# 'OpenSSL for Ruby 2' project
-# Copyright (C) 2002 Michal Rokos
-# All rights reserved.
-#
-# = Licence
-# This program is licensed under the same licence as Ruby.
-# (See the file 'LICENCE'.)
-#++
-
-module OpenSSL
- module X509
- # class ExtensionFactory
- # def create_extension(*arg)
- # if arg.size > 1
- # create_ext(*arg)
- # else
- # send("create_ext_from_"+arg[0].class.name.downcase, arg[0])
- # end
- # end
- #
- # def create_ext_from_array(ary)
- # raise ExtensionError, "unexpected array form" if ary.size > 3
- # create_ext(ary[0], ary[1], ary[2])
- # end
- #
- # def create_ext_from_string(str) # "oid = critical, value"
- # oid, value = str.split(/=/, 2)
- # oid.strip!
- # value.strip!
- # create_ext(oid, value)
- # end
- #
- # def create_ext_from_hash(hash)
- # create_ext(hash["oid"], hash["value"], hash["critical"])
- # end
- # end
- #
- # class Extension
- # def ==(other)
- # return false unless Extension === other
- # to_der == other.to_der
- # end
- #
- # def to_s # "oid = critical, value"
- # str = self.oid
- # str << " = "
- # str << "critical, " if self.critical?
- # str << self.value.gsub(/\n/, ", ")
- # end
- #
- # def to_h # {"oid"=>sn|ln, "value"=>value, "critical"=>true|false}
- # {"oid"=>self.oid,"value"=>self.value,"critical"=>self.critical?}
- # end
- #
- # def to_a
- # [ self.oid, self.value, self.critical? ]
- # end
- # end
-
- class Name
- module RFC2253DN
- Special = ',=+<>#;'
- HexChar = /[0-9a-fA-F]/
- HexPair = /#{HexChar}#{HexChar}/
- HexString = /#{HexPair}+/
- Pair = /\\(?:[#{Special}]|\\|"|#{HexPair})/
- StringChar = /[^\\"#{Special}]/
- QuoteChar = /[^\\"]/
- AttributeType = /[a-zA-Z][0-9a-zA-Z]*|[0-9]+(?:\.[0-9]+)*/
- AttributeValue = /
- (?!["#])((?:#{StringChar}|#{Pair})*)|
- \#(#{HexString})|
- "((?:#{QuoteChar}|#{Pair})*)"
- /x
- TypeAndValue = /\A(#{AttributeType})=#{AttributeValue}/
-
- module_function
-
- def expand_pair(str)
- return nil unless str
- return str.gsub(Pair){
- pair = $&
- case pair.size
- when 2 then pair[1,1]
- when 3 then Integer("0x#{pair[1,2]}").chr
- else raise OpenSSL::X509::NameError, "invalid pair: #{str}"
- end
- }
- end
-
- def expand_hexstring(str)
- return nil unless str
- der = str.gsub(HexPair){$&.to_i(16).chr }
- a1 = OpenSSL::ASN1.decode(der)
- return a1.value, a1.tag
- end
-
- def expand_value(str1, str2, str3)
- value = expand_pair(str1)
- value, tag = expand_hexstring(str2) unless value
- value = expand_pair(str3) unless value
- return value, tag
- end
-
- def scan(dn)
- str = dn
- ary = []
- while true
- if md = TypeAndValue.match(str)
- remain = md.post_match
- type = md[1]
- value, tag = expand_value(md[2], md[3], md[4]) rescue nil
- if value
- type_and_value = [type, value]
- type_and_value.push(tag) if tag
- ary.unshift(type_and_value)
- if remain.length > 2 && remain[0] == ?,
- str = remain[1..-1]
- next
- elsif remain.length > 2 && remain[0] == ?+
- raise OpenSSL::X509::NameError,
- "multi-valued RDN is not supported: #{dn}"
- elsif remain.empty?
- break
- end
- end
- end
- msg_dn = dn[0, dn.length - str.length] + " =>" + str
- raise OpenSSL::X509::NameError, "malformed RDN: #{msg_dn}"
- end
- return ary
- end
- end
-
- class << self
- def parse_rfc2253(str, template=OBJECT_TYPE_TEMPLATE)
- ary = OpenSSL::X509::Name::RFC2253DN.scan(str)
- self.new(ary, template)
- end
-
- def parse_openssl(str, template=OBJECT_TYPE_TEMPLATE)
- if str.start_with?("/")
- # /A=B/C=D format
- ary = str[1..-1].split("/").map { |i| i.split("=", 2) }
- else
- # Comma-separated
- ary = str.split(",").map { |i| i.strip.split("=", 2) }
- end
- self.new(ary, template)
- end
-
- alias parse parse_openssl
- end
-
- def pretty_print(q)
- q.object_group(self) {
- q.text ' '
- q.text to_s(OpenSSL::X509::Name::RFC2253)
- }
- end
- end
-
- # class Attribute
- # def ==(other)
- # return false unless Attribute === other
- # to_der == other.to_der
- # end
- # end
-
- class StoreContext
- def cleanup
- warn "(#{caller.first}) OpenSSL::X509::StoreContext#cleanup is deprecated with no replacement" if $VERBOSE
- end
- end
-
- class Certificate
- def pretty_print(q)
- q.object_group(self) {
- q.breakable
- q.text 'subject='; q.pp self.subject; q.text ','; q.breakable
- q.text 'issuer='; q.pp self.issuer; q.text ','; q.breakable
- q.text 'serial='; q.pp self.serial; q.text ','; q.breakable
- q.text 'not_before='; q.pp self.not_before; q.text ','; q.breakable
- q.text 'not_after='; q.pp self.not_after
- }
- end
- end
-
- # class CRL
- # def ==(other)
- # return false unless CRL === other
- # to_der == other.to_der
- # end
- # end
-
- # class Request
- # def ==(other)
- # return false unless Request === other
- # to_der == other.to_der
- # end
- # end
- end
-end
diff --git a/lib/openssl.rb b/lib/openssl.rb
index f3ce235d..e9f85bde 100644
--- a/lib/openssl.rb
+++ b/lib/openssl.rb
@@ -1 +1,3 @@
-require 'jopenssl/load'
\ No newline at end of file
+# frozen_string_literal: true
+
+require 'jopenssl/load'
diff --git a/lib/openssl/bn.rb b/lib/openssl/bn.rb
index a1b40772..0a5e11b4 100644
--- a/lib/openssl/bn.rb
+++ b/lib/openssl/bn.rb
@@ -1,9 +1,40 @@
-if RUBY_VERSION > '2.3'
- load "jopenssl23/openssl/#{File.basename(__FILE__)}"
-elsif RUBY_VERSION > '2.2'
- load "jopenssl22/openssl/#{File.basename(__FILE__)}"
-elsif RUBY_VERSION > '2.1'
- load "jopenssl21/openssl/#{File.basename(__FILE__)}"
-else
- load "jopenssl19/openssl/#{File.basename(__FILE__)}"
-end
\ No newline at end of file
+# frozen_string_literal: true
+#--
+#
+# = Ruby-space definitions that completes C-space funcs for BN
+#
+# = Info
+# 'OpenSSL for Ruby 2' project
+# Copyright (C) 2002 Michal Rokos
+# All rights reserved.
+#
+# = Licence
+# This program is licensed under the same licence as Ruby.
+# (See the file 'LICENCE'.)
+#++
+
+module OpenSSL
+ class BN
+ include Comparable
+
+ def pretty_print(q)
+ q.object_group(self) {
+ q.text ' '
+ q.text to_i.to_s
+ }
+ end
+ end # BN
+end # OpenSSL
+
+##
+#--
+# Add double dispatch to Integer
+#++
+class Integer
+ # Casts an Integer as an OpenSSL::BN
+ #
+ # See `man bn` for more info.
+ def to_bn
+ OpenSSL::BN::new(self)
+ end
+end # Integer
diff --git a/lib/openssl/buffering.rb b/lib/openssl/buffering.rb
index a1b40772..c0559abf 100644
--- a/lib/openssl/buffering.rb
+++ b/lib/openssl/buffering.rb
@@ -1,9 +1,480 @@
-if RUBY_VERSION > '2.3'
- load "jopenssl23/openssl/#{File.basename(__FILE__)}"
-elsif RUBY_VERSION > '2.2'
- load "jopenssl22/openssl/#{File.basename(__FILE__)}"
-elsif RUBY_VERSION > '2.1'
- load "jopenssl21/openssl/#{File.basename(__FILE__)}"
-else
- load "jopenssl19/openssl/#{File.basename(__FILE__)}"
-end
\ No newline at end of file
+# coding: binary
+# frozen_string_literal: true
+#--
+#= Info
+# 'OpenSSL for Ruby 2' project
+# Copyright (C) 2001 GOTOU YUUZOU
+# All rights reserved.
+#
+#= Licence
+# This program is licensed under the same licence as Ruby.
+# (See the file 'LICENCE'.)
+#++
+
+##
+# OpenSSL IO buffering mix-in module.
+#
+# This module allows an OpenSSL::SSL::SSLSocket to behave like an IO.
+#
+# You typically won't use this module directly, you can see it implemented in
+# OpenSSL::SSL::SSLSocket.
+
+module OpenSSL::Buffering
+ include Enumerable
+
+ # A buffer which will retain binary encoding.
+ class Buffer < String
+ BINARY = Encoding::BINARY
+
+ def initialize
+ super
+
+ force_encoding(BINARY)
+ end
+
+ def << string
+ if string.encoding == BINARY
+ super(string)
+ else
+ super(string.b)
+ end
+
+ return self
+ end
+
+ alias concat <<
+ end
+
+ ##
+ # The "sync mode" of the SSLSocket.
+ #
+ # See IO#sync for full details.
+
+ attr_accessor :sync
+
+ ##
+ # Default size to read from or write to the SSLSocket for buffer operations.
+
+ BLOCK_SIZE = 1024*16
+
+ ##
+ # Creates an instance of OpenSSL's buffering IO module.
+
+ def initialize(*)
+ # super
+ @eof = false
+ @rbuffer = Buffer.new
+ @sync = @io.sync
+ end
+
+ #
+ # for reading.
+ #
+ private
+
+ ##
+ # Fills the buffer from the underlying SSLSocket
+
+ def fill_rbuff
+ begin
+ @rbuffer << self.sysread(BLOCK_SIZE)
+ rescue Errno::EAGAIN
+ retry
+ rescue EOFError
+ @eof = true
+ end
+ end
+
+ ##
+ # Consumes _size_ bytes from the buffer
+
+ def consume_rbuff(size=nil)
+ if @rbuffer.empty?
+ nil
+ else
+ size = @rbuffer.size unless size
+ @rbuffer.slice!(0, size)
+ end
+ end
+
+ public
+
+ # call-seq:
+ # ssl.getbyte => 81
+ #
+ # Get the next 8bit byte from `ssl`. Returns `nil` on EOF
+ def getbyte
+ read(1)&.ord
+ end
+
+ ##
+ # Reads _size_ bytes from the stream. If _buf_ is provided it must
+ # reference a string which will receive the data.
+ #
+ # See IO#read for full details.
+
+ def read(size=nil, buf=nil)
+ if size == 0
+ if buf
+ buf.clear
+ return buf
+ else
+ return ""
+ end
+ end
+ until @eof
+ break if size && size <= @rbuffer.size
+ fill_rbuff
+ end
+ ret = consume_rbuff(size) || ""
+ if buf
+ buf.replace(ret)
+ ret = buf
+ end
+ (size && ret.empty?) ? nil : ret
+ end
+
+ ##
+ # Reads at most _maxlen_ bytes from the stream. If _buf_ is provided it
+ # must reference a string which will receive the data.
+ #
+ # See IO#readpartial for full details.
+
+ def readpartial(maxlen, buf=nil)
+ # JRuby: sysread does `maxlen == 0` short-circuit check internally
+ if @rbuffer.empty?
+ begin
+ return sysread(maxlen, buf)
+ rescue Errno::EAGAIN
+ retry
+ end
+ end
+ do_consume_rbuff(maxlen, buf)
+ end
+
+ ##
+ # Reads at most _maxlen_ bytes in the non-blocking manner.
+ #
+ # When no data can be read without blocking it raises
+ # OpenSSL::SSL::SSLError extended by IO::WaitReadable or IO::WaitWritable.
+ #
+ # IO::WaitReadable means SSL needs to read internally so read_nonblock
+ # should be called again when the underlying IO is readable.
+ #
+ # IO::WaitWritable means SSL needs to write internally so read_nonblock
+ # should be called again after the underlying IO is writable.
+ #
+ # OpenSSL::Buffering#read_nonblock needs two rescue clause as follows:
+ #
+ # # emulates blocking read (readpartial).
+ # begin
+ # result = ssl.read_nonblock(maxlen)
+ # rescue IO::WaitReadable
+ # IO.select([io])
+ # retry
+ # rescue IO::WaitWritable
+ # IO.select(nil, [io])
+ # retry
+ # end
+ #
+ # Note that one reason that read_nonblock writes to the underlying IO is
+ # when the peer requests a new TLS/SSL handshake. See openssl the FAQ for
+ # more details. http://www.openssl.org/support/faq.html
+ #
+ # By specifying a keyword argument _exception_ to +false+, you can indicate
+ # that read_nonblock should not raise an IO::Wait*able exception, but
+ # return the symbol +:wait_writable+ or +:wait_readable+ instead. At EOF,
+ # it will return +nil+ instead of raising EOFError.
+
+ def read_nonblock(maxlen, buf=nil, exception: true)
+ # JRuby: sysread does `maxlen == 0` short-circuit check internally
+ if @rbuffer.empty?
+ return sysread_nonblock(maxlen, buf, exception: exception)
+ end
+ do_consume_rbuff(maxlen, buf)
+ end
+
+ # @private
+ def do_consume_rbuff(maxlen, buf)
+ if maxlen == 0
+ if buf
+ buf.clear
+ return buf
+ else
+ return ""
+ end
+ end
+
+ ret = consume_rbuff(maxlen)
+ if buf
+ buf.replace(ret)
+ ret = buf
+ end
+ ret
+ end
+ private :do_consume_rbuff
+
+ ##
+ # Reads the next "line" from the stream. Lines are separated by _eol_. If
+ # _limit_ is provided the result will not be longer than the given number of
+ # bytes.
+ #
+ # _eol_ may be a String or Regexp.
+ #
+ # Unlike IO#gets the line read will not be assigned to +$_+.
+ #
+ # Unlike IO#gets the separator must be provided if a limit is provided.
+
+ def gets(eol=$/, limit=nil)
+ idx = @rbuffer.index(eol)
+ until @eof
+ break if idx
+ fill_rbuff
+ idx = @rbuffer.index(eol)
+ end
+ if eol.is_a?(Regexp)
+ size = idx ? idx+$&.size : nil
+ else
+ size = idx ? idx+eol.size : nil
+ end
+ if size && limit && limit >= 0
+ size = [size, limit].min
+ end
+ consume_rbuff(size)
+ end
+
+ ##
+ # Executes the block for every line in the stream where lines are separated
+ # by _eol_.
+ #
+ # See also #gets
+
+ def each(eol=$/)
+ while line = self.gets(eol)
+ yield line
+ end
+ end
+ alias each_line each
+
+ ##
+ # Reads lines from the stream which are separated by _eol_.
+ #
+ # See also #gets
+
+ def readlines(eol=$/)
+ ary = []
+ while line = self.gets(eol)
+ ary << line
+ end
+ ary
+ end
+
+ ##
+ # Reads a line from the stream which is separated by _eol_.
+ #
+ # Raises EOFError if at end of file.
+
+ def readline(eol=$/)
+ raise EOFError if eof?
+ gets(eol)
+ end
+
+ ##
+ # Reads one character from the stream. Returns nil if called at end of
+ # file.
+
+ def getc
+ read(1)
+ end
+
+ ##
+ # Calls the given block once for each byte in the stream.
+
+ def each_byte # :yields: byte
+ while c = getc
+ yield(c.ord)
+ end
+ end
+
+ ##
+ # Reads a one-character string from the stream. Raises an EOFError at end
+ # of file.
+
+ def readchar
+ raise EOFError if eof?
+ getc
+ end
+
+ ##
+ # Pushes character _c_ back onto the stream such that a subsequent buffered
+ # character read will return it.
+ #
+ # Unlike IO#getc multiple bytes may be pushed back onto the stream.
+ #
+ # Has no effect on unbuffered reads (such as #sysread).
+
+ def ungetc(c)
+ @rbuffer[0,0] = c.chr
+ end
+
+ ##
+ # Returns true if the stream is at file which means there is no more data to
+ # be read.
+
+ def eof?
+ fill_rbuff if !@eof && @rbuffer.empty?
+ @eof && @rbuffer.empty?
+ end
+ alias eof eof?
+
+ #
+ # for writing.
+ #
+ private
+
+ ##
+ # Writes _s_ to the buffer. When the buffer is full or #sync is true the
+ # buffer is flushed to the underlying socket.
+
+ def do_write(s)
+ @wbuffer ||= Buffer.new
+ @wbuffer << s
+ @sync ||= false
+ if @sync or @wbuffer.size > BLOCK_SIZE
+ until @wbuffer.empty?
+ begin
+ nwrote = syswrite(@wbuffer)
+ rescue Errno::EAGAIN
+ retry
+ end
+ @wbuffer[0, nwrote] = ""
+ end
+ end
+ end
+
+ public
+
+ ##
+ # Writes _s_ to the stream. If the argument is not a String it will be
+ # converted using +.to_s+ method. Returns the number of bytes written.
+
+ def write(*s)
+ s.inject(0) do |written, str|
+ do_write(str)
+ written + str.bytesize
+ end
+ end
+
+ ##
+ # Writes _s_ in the non-blocking manner.
+ #
+ # If there is buffered data, it is flushed first. This may block.
+ #
+ # write_nonblock returns number of bytes written to the SSL connection.
+ #
+ # When no data can be written without blocking it raises
+ # OpenSSL::SSL::SSLError extended by IO::WaitReadable or IO::WaitWritable.
+ #
+ # IO::WaitReadable means SSL needs to read internally so write_nonblock
+ # should be called again after the underlying IO is readable.
+ #
+ # IO::WaitWritable means SSL needs to write internally so write_nonblock
+ # should be called again after underlying IO is writable.
+ #
+ # So OpenSSL::Buffering#write_nonblock needs two rescue clause as follows.
+ #
+ # # emulates blocking write.
+ # begin
+ # result = ssl.write_nonblock(str)
+ # rescue IO::WaitReadable
+ # IO.select([io])
+ # retry
+ # rescue IO::WaitWritable
+ # IO.select(nil, [io])
+ # retry
+ # end
+ #
+ # Note that one reason that write_nonblock reads from the underlying IO
+ # is when the peer requests a new TLS/SSL handshake. See the openssl FAQ
+ # for more details. http://www.openssl.org/support/faq.html
+ #
+ # By specifying a keyword argument _exception_ to +false+, you can indicate
+ # that write_nonblock should not raise an IO::Wait*able exception, but
+ # return the symbol +:wait_writable+ or +:wait_readable+ instead.
+
+ def write_nonblock(s, exception: true)
+ flush
+ syswrite_nonblock(s, exception: exception)
+ end
+
+ ##
+ # Writes _s_ to the stream. _s_ will be converted to a String using
+ # +.to_s+ method.
+
+ def <<(s)
+ do_write(s)
+ self
+ end
+
+ ##
+ # Writes _args_ to the stream along with a record separator.
+ #
+ # See IO#puts for full details.
+
+ def puts(*args)
+ s = Buffer.new
+ if args.empty?
+ s << "\n"
+ else
+ args.each do |arg|
+ s << arg.to_s
+ s.sub!(/(? '2.3'
- load "jopenssl23/openssl/#{File.basename(__FILE__)}"
-elsif RUBY_VERSION > '2.2'
- load "jopenssl22/openssl/#{File.basename(__FILE__)}"
-elsif RUBY_VERSION > '2.1'
- load "jopenssl21/openssl/#{File.basename(__FILE__)}"
-else
- load "jopenssl19/openssl/#{File.basename(__FILE__)}"
-end
\ No newline at end of file
+# frozen_string_literal: true
+#--
+# = Ruby-space predefined Cipher subclasses
+#
+# = Info
+# 'OpenSSL for Ruby 2' project
+# Copyright (C) 2002 Michal Rokos
+# All rights reserved.
+#
+# = Licence
+# This program is licensed under the same licence as Ruby.
+# (See the file 'LICENCE'.)
+#++
+
+module OpenSSL
+ class Cipher
+ # %w(AES CAST5 BF DES IDEA RC2 RC4 RC5).each{|name|
+ # klass = Class.new(Cipher){
+ # define_method(:initialize){|*args|
+ # cipher_name = args.inject(name){|n, arg| "#{n}-#{arg}" }
+ # super(cipher_name.downcase)
+ # }
+ # }
+ # const_set(name, klass)
+ # }
+ #
+ # %w(128 192 256).each{|keylen|
+ # klass = Class.new(Cipher){
+ # define_method(:initialize){|mode = "CBC"|
+ # super("aes-#{keylen}-#{mode}".downcase)
+ # }
+ # }
+ # const_set("AES#{keylen}", klass)
+ # }
+
+ # call-seq:
+ # cipher.random_key -> key
+ #
+ # Generate a random key with OpenSSL::Random.random_bytes and sets it to
+ # the cipher, and returns it.
+ #
+ # You must call #encrypt or #decrypt before calling this method.
+ # def random_key
+ # str = OpenSSL::Random.random_bytes(self.key_len)
+ # self.key = str
+ # end
+
+ # call-seq:
+ # cipher.random_iv -> iv
+ #
+ # Generate a random IV with OpenSSL::Random.random_bytes and sets it to the
+ # cipher, and returns it.
+ #
+ # You must call #encrypt or #decrypt before calling this method.
+ # def random_iv
+ # str = OpenSSL::Random.random_bytes(self.iv_len)
+ # self.iv = str
+ # end
+
+ # Deprecated.
+ #
+ # This class is only provided for backwards compatibility.
+ # Use OpenSSL::Cipher.
+ class Cipher < Cipher; end
+ deprecate_constant :Cipher
+ end # Cipher
+end # OpenSSL
diff --git a/lib/openssl/config.rb b/lib/openssl/config.rb
index 20571d36..547b08f2 100644
--- a/lib/openssl/config.rb
+++ b/lib/openssl/config.rb
@@ -1,17 +1,504 @@
-if RUBY_VERSION > '2.3'
- load "jopenssl23/openssl/#{File.basename(__FILE__)}"
-elsif RUBY_VERSION > '2.2'
- load "jopenssl22/openssl/#{File.basename(__FILE__)}"
-elsif RUBY_VERSION > '2.1'
- load "jopenssl21/openssl/#{File.basename(__FILE__)}"
-else
- load "jopenssl19/openssl/#{File.basename(__FILE__)}"
-end
+# frozen_string_literal: true
+=begin
+= Ruby-space definitions that completes C-space funcs for Config
+
+= Info
+ Copyright (C) 2010 Hiroshi Nakamura
+
+= Licence
+ This program is licensed under the same licence as Ruby.
+ (See the file 'LICENCE'.)
+
+=end
+
+require 'stringio'
-# @note moved from JOpenSSL native bits.
module OpenSSL
+ class ConfigError < OpenSSLError; end
+ ##
+ # = OpenSSL::Config
+ #
+ # Configuration for the openssl library.
+ #
+ # Many system's installation of openssl library will depend on your system
+ # configuration. See the value of OpenSSL::Config::DEFAULT_CONFIG_FILE for
+ # the location of the file for your host.
+ #
+ # See also http://www.openssl.org/docs/apps/config.html
class Config
- DEFAULT_CONFIG_FILE = nil
+ include Enumerable
+
+ DEFAULT_CONFIG_FILE = nil # JRuby: compatibility (we do not read openssl.cnf)
+
+ class << self
+
+ ##
+ # Parses a given _string_ as a blob that contains configuration for
+ # OpenSSL.
+ #
+ # If the source of the IO is a file, then consider using #parse_config.
+ def parse(string)
+ c = new()
+ parse_config(StringIO.new(string)).each do |section, hash|
+ c.set_section(section, hash)
+ end
+ c
+ end
+
+ ##
+ # load is an alias to ::new
+ alias load new
+
+ ##
+ # Parses the configuration data read from _io_, see also #parse.
+ #
+ # Raises a ConfigError on invalid configuration data.
+ def parse_config(io)
+ begin
+ parse_config_lines(io)
+ rescue => error
+ raise ConfigError, "error in line #{io.lineno}: " + error.message
+ end
+ end
+
+ def get_key_string(data, section, key) # :nodoc:
+ if v = data[section] && data[section][key]
+ return v
+ elsif section == 'ENV'
+ if v = ENV[key]
+ return v
+ end
+ end
+ if v = data['default'] && data['default'][key]
+ return v
+ end
+ end
+
+ private
+
+ def parse_config_lines(io)
+ section = 'default'
+ data = {section => {}}
+ io_stack = [io]
+ while definition = get_definition(io_stack)
+ definition = clear_comments(definition)
+ next if definition.empty?
+ case definition
+ when /\A\[/
+ if /\[([^\]]*)\]/ =~ definition
+ section = $1.strip
+ data[section] ||= {}
+ else
+ raise ConfigError, "missing close square bracket"
+ end
+ when /\A\.include (\s*=\s*)?(.+)\z/
+ path = $2
+ if File.directory?(path)
+ files = Dir.glob(File.join(path, "*.{cnf,conf}"), File::FNM_EXTGLOB)
+ else
+ files = [path]
+ end
+
+ files.each do |filename|
+ begin
+ io_stack << StringIO.new(File.read(filename))
+ rescue
+ raise ConfigError, "could not include file '%s'" % filename
+ end
+ end
+ when /\A([^:\s]*)(?:::([^:\s]*))?\s*=(.*)\z/
+ if $2
+ section = $1
+ key = $2
+ else
+ key = $1
+ end
+ value = unescape_value(data, section, $3)
+ (data[section] ||= {})[key] = value.strip
+ else
+ raise ConfigError, "missing equal sign"
+ end
+ end
+ data
+ end
+
+ # escape with backslash
+ QUOTE_REGEXP_SQ = /\A([^'\\]*(?:\\.[^'\\]*)*)'/
+ # escape with backslash and doubled dq
+ QUOTE_REGEXP_DQ = /\A([^"\\]*(?:""[^"\\]*|\\.[^"\\]*)*)"/
+ # escaped char map
+ ESCAPE_MAP = {
+ "r" => "\r",
+ "n" => "\n",
+ "b" => "\b",
+ "t" => "\t",
+ }
+
+ def unescape_value(data, section, value)
+ scanned = []
+ while m = value.match(/['"\\$]/)
+ scanned << m.pre_match
+ c = m[0]
+ value = m.post_match
+ case c
+ when "'"
+ if m = value.match(QUOTE_REGEXP_SQ)
+ scanned << m[1].gsub(/\\(.)/, '\\1')
+ value = m.post_match
+ else
+ break
+ end
+ when '"'
+ if m = value.match(QUOTE_REGEXP_DQ)
+ scanned << m[1].gsub(/""/, '').gsub(/\\(.)/, '\\1')
+ value = m.post_match
+ else
+ break
+ end
+ when "\\"
+ c = value.slice!(0, 1)
+ scanned << (ESCAPE_MAP[c] || c)
+ when "$"
+ ref, value = extract_reference(value)
+ refsec = section
+ if ref.index('::')
+ refsec, ref = ref.split('::', 2)
+ end
+ if v = get_key_string(data, refsec, ref)
+ scanned << v
+ else
+ raise ConfigError, "variable has no value"
+ end
+ else
+ raise 'must not reaced'
+ end
+ end
+ scanned << value
+ scanned.join
+ end
+
+ def extract_reference(value)
+ rest = ''
+ if m = value.match(/\(([^)]*)\)|\{([^}]*)\}/)
+ value = m[1] || m[2]
+ rest = m.post_match
+ elsif [?(, ?{].include?(value[0])
+ raise ConfigError, "no close brace"
+ end
+ if m = value.match(/[a-zA-Z0-9_]*(?:::[a-zA-Z0-9_]*)?/)
+ return m[0], m.post_match + rest
+ else
+ raise
+ end
+ end
+
+ def clear_comments(line)
+ # FCOMMENT
+ if m = line.match(/\A([\t\n\f ]*);.*\z/)
+ return m[1]
+ end
+ # COMMENT
+ scanned = []
+ while m = line.match(/[#'"\\]/)
+ scanned << m.pre_match
+ c = m[0]
+ line = m.post_match
+ case c
+ when '#'
+ line = nil
+ break
+ when "'", '"'
+ regexp = (c == "'") ? QUOTE_REGEXP_SQ : QUOTE_REGEXP_DQ
+ scanned << c
+ if m = line.match(regexp)
+ scanned << m[0]
+ line = m.post_match
+ else
+ scanned << line
+ line = nil
+ break
+ end
+ when "\\"
+ scanned << c
+ scanned << line.slice!(0, 1)
+ else
+ raise 'must not reaced'
+ end
+ end
+ scanned << line
+ scanned.join
+ end
+
+ def get_definition(io_stack)
+ if line = get_line(io_stack)
+ while /[^\\]\\\z/ =~ line
+ if extra = get_line(io_stack)
+ line += extra
+ else
+ break
+ end
+ end
+ return line.strip
+ end
+ end
+
+ def get_line(io_stack)
+ while io = io_stack.last
+ if line = io.gets
+ return line.gsub(/[\r\n]*/, '')
+ end
+ io_stack.pop
+ end
+ end
+ end
+
+ ##
+ # Creates an instance of OpenSSL's configuration class.
+ #
+ # This can be used in contexts like OpenSSL::X509::ExtensionFactory.config=
+ #
+ # If the optional _filename_ parameter is provided, then it is read in and
+ # parsed via #parse_config.
+ #
+ # This can raise IO exceptions based on the access, or availability of the
+ # file. A ConfigError exception may be raised depending on the validity of
+ # the data being configured.
+ #
+ def initialize(filename = nil)
+ @data = {}
+ if filename
+ File.open(filename.to_s) do |file|
+ Config.parse_config(file).each do |section, hash|
+ set_section(section, hash)
+ end
+ end
+ end
+ end
+
+ ##
+ # Gets the value of _key_ from the given _section_
+ #
+ # Given the following configurating file being loaded:
+ #
+ # config = OpenSSL::Config.load('foo.cnf')
+ # #=> #
+ # puts config.to_s
+ # #=> [ default ]
+ # # foo=bar
+ #
+ # You can get a specific value from the config if you know the _section_
+ # and _key_ like so:
+ #
+ # config.get_value('default','foo')
+ # #=> "bar"
+ #
+ def get_value(section, key)
+ if section.nil?
+ raise TypeError.new('nil not allowed')
+ end
+ section = 'default' if section.empty?
+ get_key_string(section, key)
+ end
+
+ ##
+ #
+ # *Deprecated*
+ #
+ # Use #get_value instead
+ def value(arg1, arg2 = nil) # :nodoc:
+ warn('Config#value is deprecated; use Config#get_value')
+ if arg2.nil?
+ section, key = 'default', arg1
+ else
+ section, key = arg1, arg2
+ end
+ section ||= 'default'
+ section = 'default' if section.empty?
+ get_key_string(section, key)
+ end
+
+ ##
+ # *Deprecated in v2.2.0*. This method will be removed in a future release.
+ #
+ # Set the target _key_ with a given _value_ under a specific _section_.
+ #
+ # Given the following configurating file being loaded:
+ #
+ # config = OpenSSL::Config.load('foo.cnf')
+ # #=> #
+ # puts config.to_s
+ # #=> [ default ]
+ # # foo=bar
+ #
+ # You can set the value of _foo_ under the _default_ section to a new
+ # value:
+ #
+ # config.add_value('default', 'foo', 'buzz')
+ # #=> "buzz"
+ # puts config.to_s
+ # #=> [ default ]
+ # # foo=buzz
+ #
+ def add_value(section, key, value)
+ check_modify
+ (@data[section] ||= {})[key] = value
+ end
+
+ ##
+ # Get a specific _section_ from the current configuration
+ #
+ # Given the following configurating file being loaded:
+ #
+ # config = OpenSSL::Config.load('foo.cnf')
+ # #=> #
+ # puts config.to_s
+ # #=> [ default ]
+ # # foo=bar
+ #
+ # You can get a hash of the specific section like so:
+ #
+ # config['default']
+ # #=> {"foo"=>"bar"}
+ #
+ def [](section)
+ @data[section] || {}
+ end
+
+ ##
+ # Deprecated
+ #
+ # Use #[] instead
+ def section(name) # :nodoc:
+ warn('Config#section is deprecated; use Config#[]')
+ @data[name] || {}
+ end
+
+ ##
+ # *Deprecated in v2.2.0*. This method will be removed in a future release.
+ #
+ # Sets a specific _section_ name with a Hash _pairs_.
+ #
+ # Given the following configuration being created:
+ #
+ # config = OpenSSL::Config.new
+ # #=> #
+ # config['default'] = {"foo"=>"bar","baz"=>"buz"}
+ # #=> {"foo"=>"bar", "baz"=>"buz"}
+ # puts config.to_s
+ # #=> [ default ]
+ # # foo=bar
+ # # baz=buz
+ #
+ # It's important to note that this will essentially merge any of the keys
+ # in _pairs_ with the existing _section_. For example:
+ #
+ # config['default']
+ # #=> {"foo"=>"bar", "baz"=>"buz"}
+ # config['default'] = {"foo" => "changed"}
+ # #=> {"foo"=>"changed"}
+ # config['default']
+ # #=> {"foo"=>"changed", "baz"=>"buz"}
+ #
+ def []=(section, pairs)
+ check_modify
+ set_section(section, pairs)
+ end
+
+ def set_section(section, pairs) # :nodoc:
+ hash = @data[section] ||= {}
+ pairs.each do |key, value|
+ hash[key] = value
+ end
+ end
+
+ ##
+ # Get the names of all sections in the current configuration
+ def sections
+ @data.keys
+ end
+
+ ##
+ # Get the parsable form of the current configuration
+ #
+ # Given the following configuration being created:
+ #
+ # config = OpenSSL::Config.new
+ # #=> #
+ # config['default'] = {"foo"=>"bar","baz"=>"buz"}
+ # #=> {"foo"=>"bar", "baz"=>"buz"}
+ # puts config.to_s
+ # #=> [ default ]
+ # # foo=bar
+ # # baz=buz
+ #
+ # You can parse get the serialized configuration using #to_s and then parse
+ # it later:
+ #
+ # serialized_config = config.to_s
+ # # much later...
+ # new_config = OpenSSL::Config.parse(serialized_config)
+ # #=> #
+ # puts new_config
+ # #=> [ default ]
+ # foo=bar
+ # baz=buz
+ #
+ def to_s
+ ary = []
+ @data.keys.sort.each do |section|
+ ary << "[ #{section} ]\n"
+ @data[section].keys.each do |key|
+ ary << "#{key}=#{@data[section][key]}\n"
+ end
+ ary << "\n"
+ end
+ ary.join
+ end
+
+ ##
+ # For a block.
+ #
+ # Receive the section and its pairs for the current configuration.
+ #
+ # config.each do |section, key, value|
+ # # ...
+ # end
+ #
+ def each
+ @data.each do |section, hash|
+ hash.each do |key, value|
+ yield [section, key, value]
+ end
+ end
+ end
+
+ ##
+ # String representation of this configuration object, including the class
+ # name and its sections.
+ def inspect
+ "#<#{self.class.name} sections=#{sections.inspect}>"
+ end
+
+ protected
+
+ def data # :nodoc:
+ @data
+ end
+
+ private
+
+ def initialize_copy(other)
+ @data = other.data.dup
+ end
+
+ def check_modify
+ warn "#{caller(2, 1)[0]}: warning: do not modify OpenSSL::Config; this " \
+ "method is deprecated and will be removed in a future release."
+ raise TypeError.new("Insecure: can't modify OpenSSL config") if frozen?
+ end
+
+ def get_key_string(section, key)
+ Config.get_key_string(@data, section, key)
+ end
end
- class ConfigError < OpenSSLError; end
end
diff --git a/lib/openssl/digest.rb b/lib/openssl/digest.rb
index a1b40772..9208b496 100644
--- a/lib/openssl/digest.rb
+++ b/lib/openssl/digest.rb
@@ -1,9 +1,73 @@
-if RUBY_VERSION > '2.3'
- load "jopenssl23/openssl/#{File.basename(__FILE__)}"
-elsif RUBY_VERSION > '2.2'
- load "jopenssl22/openssl/#{File.basename(__FILE__)}"
-elsif RUBY_VERSION > '2.1'
- load "jopenssl21/openssl/#{File.basename(__FILE__)}"
-else
- load "jopenssl19/openssl/#{File.basename(__FILE__)}"
-end
\ No newline at end of file
+# frozen_string_literal: true
+#--
+# = Ruby-space predefined Digest subclasses
+#
+# = Info
+# 'OpenSSL for Ruby 2' project
+# Copyright (C) 2002 Michal Rokos
+# All rights reserved.
+#
+# = Licence
+# This program is licensed under the same licence as Ruby.
+# (See the file 'LICENCE'.)
+#++
+
+module OpenSSL
+ class Digest
+
+ # Return the hash value computed with _name_ Digest. _name_ is either the
+ # long name or short name of a supported digest algorithm.
+ #
+ # === Examples
+ #
+ # OpenSSL::Digest.digest("SHA256", "abc")
+ #
+ # which is equivalent to:
+ #
+ # OpenSSL::Digest.digest('SHA256', "abc")
+ #
+ # def self.digest(name, data)
+ # super(data, name)
+ # end
+ #
+ # %w(MD4 MD5 RIPEMD160 SHA1 SHA224 SHA256 SHA384 SHA512).each do |name|
+ # klass = Class.new(self) {
+ # define_method(:initialize, ->(data = nil) {super(name, data)})
+ # }
+ #
+ # singleton = (class << klass; self; end)
+ #
+ # singleton.class_eval{
+ # define_method(:digest) {|data| new.digest(data)}
+ # define_method(:hexdigest) {|data| new.hexdigest(data)}
+ # }
+ #
+ # const_set(name.tr('-', '_'), klass)
+ # end
+
+ # Deprecated.
+ #
+ # This class is only provided for backwards compatibility.
+ # Use OpenSSL::Digest instead.
+ class Digest < Digest; end # :nodoc:
+ deprecate_constant :Digest
+
+ end # Digest
+
+ # Returns a Digest subclass by _name_
+ #
+ # require 'openssl'
+ #
+ # OpenSSL::Digest("MD5")
+ # # => OpenSSL::Digest::MD5
+ #
+ # Digest("Foo")
+ # # => NameError: wrong constant name Foo
+
+ def Digest(name)
+ OpenSSL::Digest.const_get(name)
+ end
+
+ module_function :Digest
+
+end # OpenSSL
diff --git a/lib/openssl/hmac.rb b/lib/openssl/hmac.rb
new file mode 100644
index 00000000..f80174e9
--- /dev/null
+++ b/lib/openssl/hmac.rb
@@ -0,0 +1,78 @@
+# frozen_string_literal: true
+
+module OpenSSL
+ class HMAC
+ # Securely compare with another HMAC instance in constant time.
+ def ==(other)
+ return false unless HMAC === other
+ return false unless self.digest.bytesize == other.digest.bytesize
+
+ OpenSSL.fixed_length_secure_compare(self.digest, other.digest)
+ end
+
+ # :call-seq:
+ # hmac.base64digest -> string
+ #
+ # Returns the authentication code an a Base64-encoded string.
+ def base64digest
+ [digest].pack("m0")
+ end
+
+ class << self
+ # :call-seq:
+ # HMAC.digest(digest, key, data) -> aString
+ #
+ # Returns the authentication code as a binary string. The _digest_ parameter
+ # specifies the digest algorithm to use. This may be a String representing
+ # the algorithm name or an instance of OpenSSL::Digest.
+ #
+ # === Example
+ # key = 'key'
+ # data = 'The quick brown fox jumps over the lazy dog'
+ #
+ # hmac = OpenSSL::HMAC.digest('SHA1', key, data)
+ # #=> "\xDE|\x9B\x85\xB8\xB7\x8A\xA6\xBC\x8Az6\xF7\n\x90p\x1C\x9D\xB4\xD9"
+ def digest(digest, key, data)
+ hmac = new(key, digest)
+ hmac << data
+ hmac.digest
+ end unless method_defined?(:digest) # JRuby
+
+ # :call-seq:
+ # HMAC.hexdigest(digest, key, data) -> aString
+ #
+ # Returns the authentication code as a hex-encoded string. The _digest_
+ # parameter specifies the digest algorithm to use. This may be a String
+ # representing the algorithm name or an instance of OpenSSL::Digest.
+ #
+ # === Example
+ # key = 'key'
+ # data = 'The quick brown fox jumps over the lazy dog'
+ #
+ # hmac = OpenSSL::HMAC.hexdigest('SHA1', key, data)
+ # #=> "de7c9b85b8b78aa6bc8a7a36f70a90701c9db4d9"
+ def hexdigest(digest, key, data)
+ hmac = new(key, digest)
+ hmac << data
+ hmac.hexdigest
+ end unless method_defined?(:hexdigest) # JRuby
+
+ # :call-seq:
+ # HMAC.base64digest(digest, key, data) -> aString
+ #
+ # Returns the authentication code as a Base64-encoded string. The _digest_
+ # parameter specifies the digest algorithm to use. This may be a String
+ # representing the algorithm name or an instance of OpenSSL::Digest.
+ #
+ # === Example
+ # key = 'key'
+ # data = 'The quick brown fox jumps over the lazy dog'
+ #
+ # hmac = OpenSSL::HMAC.base64digest('SHA1', key, data)
+ # #=> "3nybhbi3iqa8ino29wqQcBydtNk="
+ def base64digest(digest, key, data)
+ [digest(digest, key, data)].pack("m0")
+ end
+ end
+ end
+end
diff --git a/lib/openssl/marshal.rb b/lib/openssl/marshal.rb
new file mode 100644
index 00000000..af564719
--- /dev/null
+++ b/lib/openssl/marshal.rb
@@ -0,0 +1,30 @@
+# frozen_string_literal: true
+#--
+# = Ruby-space definitions to add DER (de)serialization to classes
+#
+# = Info
+# 'OpenSSL for Ruby 2' project
+# Copyright (C) 2002 Michal Rokos
+# All rights reserved.
+#
+# = Licence
+# This program is licensed under the same licence as Ruby.
+# (See the file 'LICENCE'.)
+#++
+module OpenSSL
+ module Marshal
+ def self.included(base)
+ base.extend(ClassMethods)
+ end
+
+ module ClassMethods
+ def _load(string)
+ new(string)
+ end
+ end
+
+ def _dump(_level)
+ to_der
+ end
+ end
+end
diff --git a/lib/openssl/pkcs5.rb b/lib/openssl/pkcs5.rb
index cbfddea3..87c25cfb 100644
--- a/lib/openssl/pkcs5.rb
+++ b/lib/openssl/pkcs5.rb
@@ -1,10 +1,9 @@
+# frozen_string_literal: true
#--
# Ruby/OpenSSL Project
# Copyright (C) 2017 Ruby/OpenSSL Project Authors
#++
-# JOpenSSL has these - here for explicit require 'openssl/pkcs5' compatibility
-
# module OpenSSL
# module PKCS5
# module_function
@@ -12,7 +11,8 @@
# # OpenSSL::PKCS5.pbkdf2_hmac has been renamed to OpenSSL::KDF.pbkdf2_hmac.
# # This method is provided for backwards compatibility.
# def pbkdf2_hmac(pass, salt, iter, keylen, digest)
-# OpenSSL::KDF.pbkdf2_hmac(pass, salt: salt, iterations: iter, length: keylen, hash: digest)
+# OpenSSL::KDF.pbkdf2_hmac(pass, salt: salt, iterations: iter,
+# length: keylen, hash: digest)
# end
#
# def pbkdf2_hmac_sha1(pass, salt, iter, keylen)
diff --git a/lib/openssl/pkey.rb b/lib/openssl/pkey.rb
index 370bb9d6..c935b72a 100644
--- a/lib/openssl/pkey.rb
+++ b/lib/openssl/pkey.rb
@@ -1,5 +1,42 @@
-if RUBY_VERSION > '2.3'
- load "jopenssl23/openssl/#{File.basename(__FILE__)}"
-else
- raise LoadError, "no such file to load -- openssl/pkey"
-end
\ No newline at end of file
+# frozen_string_literal: true
+#--
+# Ruby/OpenSSL Project
+# Copyright (C) 2017 Ruby/OpenSSL Project Authors
+#++
+
+require_relative 'marshal'
+
+module OpenSSL::PKey
+ class DH
+ include OpenSSL::Marshal
+ end
+
+ class DSA
+ include OpenSSL::Marshal
+ end
+
+ if defined?(EC)
+ class EC
+ include OpenSSL::Marshal
+ end
+ class EC::Point
+ # :call-seq:
+ # point.to_bn([conversion_form]) -> OpenSSL::BN
+ #
+ # Returns the octet string representation of the EC point as an instance of
+ # OpenSSL::BN.
+ #
+ # If _conversion_form_ is not given, the _point_conversion_form_ attribute
+ # set to the group is used.
+ #
+ # See #to_octet_string for more information.
+ # def to_bn(conversion_form = group.point_conversion_form)
+ # OpenSSL::BN.new(to_octet_string(conversion_form), 2)
+ # end
+ end
+ end
+
+ class RSA
+ include OpenSSL::Marshal
+ end
+end
diff --git a/lib/openssl/ssl-internal.rb b/lib/openssl/ssl-internal.rb
deleted file mode 100644
index 1717760b..00000000
--- a/lib/openssl/ssl-internal.rb
+++ /dev/null
@@ -1,5 +0,0 @@
-if RUBY_VERSION > '2.1'
- raise LoadError, "no such library in #{RUBY_VERSION}: openssl/ssl-internal.rb"
-else
- load "jopenssl19/openssl/#{File.basename(__FILE__)}"
-end
\ No newline at end of file
diff --git a/lib/openssl/ssl.rb b/lib/openssl/ssl.rb
index a1b40772..991f05e3 100644
--- a/lib/openssl/ssl.rb
+++ b/lib/openssl/ssl.rb
@@ -1,9 +1,543 @@
-if RUBY_VERSION > '2.3'
- load "jopenssl23/openssl/#{File.basename(__FILE__)}"
-elsif RUBY_VERSION > '2.2'
- load "jopenssl22/openssl/#{File.basename(__FILE__)}"
-elsif RUBY_VERSION > '2.1'
- load "jopenssl21/openssl/#{File.basename(__FILE__)}"
-else
- load "jopenssl19/openssl/#{File.basename(__FILE__)}"
-end
\ No newline at end of file
+# frozen_string_literal: true
+=begin
+= Info
+ 'OpenSSL for Ruby 2' project
+ Copyright (C) 2001 GOTOU YUUZOU
+ All rights reserved.
+
+= Licence
+ This program is licensed under the same licence as Ruby.
+ (See the file 'LICENCE'.)
+=end
+
+require "openssl/buffering"
+require "io/nonblock"
+require "socket"
+
+module OpenSSL
+ module SSL
+ class SSLContext
+ DEFAULT_PARAMS = { # :nodoc:
+ :min_version => OpenSSL::SSL::TLS1_VERSION,
+ :verify_mode => OpenSSL::SSL::VERIFY_PEER,
+ :verify_hostname => true,
+ :options => OpenSSL::SSL::OP_ALL | OpenSSL::SSL::OP_NO_COMPRESSION
+ }
+
+ # JRuby does not want this non (recent) OpenSSL fallback to happen
+ # if !(OpenSSL::OPENSSL_VERSION.start_with?("OpenSSL") &&
+ # OpenSSL::OPENSSL_VERSION_NUMBER >= 0x10100000)
+ # DEFAULT_PARAMS.merge!(
+ # ciphers: %w{
+ # ECDHE-ECDSA-AES128-GCM-SHA256
+ # ECDHE-RSA-AES128-GCM-SHA256
+ # ECDHE-ECDSA-AES256-GCM-SHA384
+ # ECDHE-RSA-AES256-GCM-SHA384
+ # DHE-RSA-AES128-GCM-SHA256
+ # DHE-DSS-AES128-GCM-SHA256
+ # DHE-RSA-AES256-GCM-SHA384
+ # DHE-DSS-AES256-GCM-SHA384
+ # ECDHE-ECDSA-AES128-SHA256
+ # ECDHE-RSA-AES128-SHA256
+ # ECDHE-ECDSA-AES128-SHA
+ # ECDHE-RSA-AES128-SHA
+ # ECDHE-ECDSA-AES256-SHA384
+ # ECDHE-RSA-AES256-SHA384
+ # ECDHE-ECDSA-AES256-SHA
+ # ECDHE-RSA-AES256-SHA
+ # DHE-RSA-AES128-SHA256
+ # DHE-RSA-AES256-SHA256
+ # DHE-RSA-AES128-SHA
+ # DHE-RSA-AES256-SHA
+ # DHE-DSS-AES128-SHA256
+ # DHE-DSS-AES256-SHA256
+ # DHE-DSS-AES128-SHA
+ # DHE-DSS-AES256-SHA
+ # AES128-GCM-SHA256
+ # AES256-GCM-SHA384
+ # AES128-SHA256
+ # AES256-SHA256
+ # AES128-SHA
+ # AES256-SHA
+ # }.join(":"),
+ # )
+ # end
+
+ if defined?(OpenSSL::PKey::DH)
+ DEFAULT_2048 = OpenSSL::PKey::DH.new <<-_end_of_pem_
+-----BEGIN DH PARAMETERS-----
+MIIBCAKCAQEA7E6kBrYiyvmKAMzQ7i8WvwVk9Y/+f8S7sCTN712KkK3cqd1jhJDY
+JbrYeNV3kUIKhPxWHhObHKpD1R84UpL+s2b55+iMd6GmL7OYmNIT/FccKhTcveab
+VBmZT86BZKYyf45hUF9FOuUM9xPzuK3Vd8oJQvfYMCd7LPC0taAEljQLR4Edf8E6
+YoaOffgTf5qxiwkjnlVZQc3whgnEt9FpVMvQ9eknyeGB5KHfayAc3+hUAvI3/Cr3
+1bNveX5wInh5GDx1FGhKBZ+s1H+aedudCm7sCgRwv8lKWYGiHzObSma8A86KG+MD
+7Lo5JquQ3DlBodj3IDyPrxIv96lvRPFtAwIBAg==
+-----END DH PARAMETERS-----
+ _end_of_pem_
+ private_constant :DEFAULT_2048
+
+ DEFAULT_TMP_DH_CALLBACK = lambda { |ctx, is_export, keylen| # :nodoc:
+ warn "using default DH parameters." if $VERBOSE
+ DEFAULT_2048
+ }
+ end
+
+ DEFAULT_CERT_STORE = OpenSSL::X509::Store.new # :nodoc:
+ DEFAULT_CERT_STORE.set_default_paths
+ DEFAULT_CERT_STORE.flags = OpenSSL::X509::V_FLAG_CRL_CHECK_ALL
+
+ # A callback invoked when DH parameters are required for ephemeral DH key
+ # exchange.
+ #
+ # The callback is invoked with the SSLSocket, a
+ # flag indicating the use of an export cipher and the keylength
+ # required.
+ #
+ # The callback must return an OpenSSL::PKey::DH instance of the correct
+ # key length.
+ #
+ # Deprecated in version 3.0. Use #tmp_dh= instead.
+ attr_accessor :tmp_dh_callback
+
+ # A callback invoked at connect time to distinguish between multiple
+ # server names.
+ #
+ # The callback is invoked with an SSLSocket and a server name. The
+ # callback must return an SSLContext for the server name or nil.
+ attr_accessor :servername_cb
+
+ # call-seq:
+ # SSLContext.new -> ctx
+ # SSLContext.new(:TLSv1) -> ctx
+ # SSLContext.new("SSLv23") -> ctx
+ #
+ # Creates a new SSL context.
+ #
+ # If an argument is given, #ssl_version= is called with the value. Note
+ # that this form is deprecated. New applications should use #min_version=
+ # and #max_version= as necessary.
+ # def initialize(version = nil)
+ # self.options |= OpenSSL::SSL::OP_ALL
+ # self.ssl_version = version if version
+ # self.verify_mode = OpenSSL::SSL::VERIFY_NONE
+ # self.verify_hostname = false
+ # end
+
+ ##
+ # call-seq:
+ # ctx.set_params(params = {}) -> params
+ #
+ # Sets saner defaults optimized for the use with HTTP-like protocols.
+ #
+ # If a Hash _params_ is given, the parameters are overridden with it.
+ # The keys in _params_ must be assignment methods on SSLContext.
+ #
+ # If the verify_mode is not VERIFY_NONE and ca_file, ca_path and
+ # cert_store are not set then the system default certificate store is
+ # used.
+ def set_params(params={})
+ params = DEFAULT_PARAMS.merge(params)
+ self.options = params.delete(:options) # set before min_version/max_version
+ params.each{|name, value| self.__send__("#{name}=", value) }
+ if self.verify_mode != OpenSSL::SSL::VERIFY_NONE
+ unless self.ca_file or self.ca_path or self.cert_store
+ self.cert_store = DEFAULT_CERT_STORE
+ end
+ end
+ return params
+ end
+
+ # call-seq:
+ # ctx.min_version = OpenSSL::SSL::TLS1_2_VERSION
+ # ctx.min_version = :TLS1_2
+ # ctx.min_version = nil
+ #
+ # Sets the lower bound on the supported SSL/TLS protocol version. The
+ # version may be specified by an integer constant named
+ # OpenSSL::SSL::*_VERSION, a Symbol, or +nil+ which means "any version".
+ #
+ # Be careful that you don't overwrite OpenSSL::SSL::OP_NO_{SSL,TLS}v*
+ # options by #options= once you have called #min_version= or
+ # #max_version=.
+ #
+ # === Example
+ # ctx = OpenSSL::SSL::SSLContext.new
+ # ctx.min_version = OpenSSL::SSL::TLS1_1_VERSION
+ # ctx.max_version = OpenSSL::SSL::TLS1_2_VERSION
+ #
+ # sock = OpenSSL::SSL::SSLSocket.new(tcp_sock, ctx)
+ # sock.connect # Initiates a connection using either TLS 1.1 or TLS 1.2
+ def min_version=(version)
+ set_minmax_proto_version(version, @max_proto_version ||= nil)
+ @min_proto_version = version
+ end
+
+ # call-seq:
+ # ctx.max_version = OpenSSL::SSL::TLS1_2_VERSION
+ # ctx.max_version = :TLS1_2
+ # ctx.max_version = nil
+ #
+ # Sets the upper bound of the supported SSL/TLS protocol version. See
+ # #min_version= for the possible values.
+ def max_version=(version)
+ set_minmax_proto_version(@min_proto_version ||= nil, version)
+ @max_proto_version = version
+ end
+
+ # call-seq:
+ # ctx.ssl_version = :TLSv1
+ # ctx.ssl_version = "SSLv23"
+ #
+ # Sets the SSL/TLS protocol version for the context. This forces
+ # connections to use only the specified protocol version. This is
+ # deprecated and only provided for backwards compatibility. Use
+ # #min_version= and #max_version= instead.
+ #
+ # === History
+ # As the name hints, this used to call the SSL_CTX_set_ssl_version()
+ # function which sets the SSL method used for connections created from
+ # the context. As of Ruby/OpenSSL 2.1, this accessor method is
+ # implemented to call #min_version= and #max_version= instead.
+ # def ssl_version=(meth)
+ # meth = meth.to_s if meth.is_a?(Symbol)
+ # if /(?_client|_server)\z/ =~ meth
+ # meth = $`
+ # if $VERBOSE
+ # warn "#{caller(1, 1)[0]}: method type #{type.inspect} is ignored"
+ # end
+ # end
+ # version = METHODS_MAP[meth.intern] or
+ # raise ArgumentError, "unknown SSL method `%s'" % meth
+ # set_minmax_proto_version(version, version)
+ # @min_proto_version = @max_proto_version = version
+ # end
+ #
+ # METHODS_MAP = {
+ # SSLv23: 0,
+ # SSLv2: OpenSSL::SSL::SSL2_VERSION,
+ # SSLv3: OpenSSL::SSL::SSL3_VERSION,
+ # TLSv1: OpenSSL::SSL::TLS1_VERSION,
+ # TLSv1_1: OpenSSL::SSL::TLS1_1_VERSION,
+ # TLSv1_2: OpenSSL::SSL::TLS1_2_VERSION,
+ # }.freeze
+ # private_constant :METHODS_MAP
+
+ # METHODS setup from native (JRuby)
+ # The list of available SSL/TLS methods. This constant is only provided
+ # for backwards compatibility.
+ # METHODS = METHODS_MAP.flat_map { |name,|
+ # [name, :"#{name}_client", :"#{name}_server"]
+ # }.freeze
+ # deprecate_constant :METHODS
+ end
+
+ module SocketForwarder
+ # The file descriptor for the socket.
+ def fileno
+ to_io.fileno
+ end
+
+ def addr
+ to_io.addr
+ end
+
+ def peeraddr
+ to_io.peeraddr
+ end
+
+ def setsockopt(level, optname, optval)
+ to_io.setsockopt(level, optname, optval)
+ end
+
+ def getsockopt(level, optname)
+ to_io.getsockopt(level, optname)
+ end
+
+ def fcntl(*args)
+ to_io.fcntl(*args)
+ end
+
+ def closed?
+ to_io.closed?
+ end
+
+ def do_not_reverse_lookup=(flag)
+ to_io.do_not_reverse_lookup = flag
+ end
+ end
+
+ def verify_certificate_identity(cert, hostname)
+ should_verify_common_name = true
+ cert.extensions.each{|ext|
+ next if ext.oid != "subjectAltName"
+ ext.value.split(/,\s+/).each { |general_name|
+ #case san.tag
+ # MRI 2.2.3 (JRuby parses ASN.1 differently)
+ #when 2 # dNSName in GeneralName (RFC5280)
+ if /\ADNS:(.*)/ =~ general_name
+ should_verify_common_name = false
+ return true if verify_hostname(hostname, $1)
+ # MRI 2.2.3 (JRuby parses ASN.1 differently)
+ #when 7 # iPAddress in GeneralName (RFC5280)
+ elsif /\AIP(?: Address)?:(.*)/ =~ general_name
+ should_verify_common_name = false
+ return true if $1 == hostname
+ # NOTE: bellow logic makes little sense JRuby reads exts differently
+ # follows GENERAL_NAME_print() in x509v3/v3_alt.c
+ # if san.value.size == 4 || san.value.size == 16
+ # begin
+ # return true if $1 == IPAddr.new(hostname).to_s
+ # rescue IPAddr::InvalidAddressError
+ # end
+ # end
+ end
+ }
+ }
+ if should_verify_common_name
+ cert.subject.to_a.each{|oid, value|
+ if oid == "CN"
+ return true if verify_hostname(hostname, value)
+ end
+ }
+ end
+ return false
+ end
+ module_function :verify_certificate_identity
+
+ def verify_hostname(hostname, san) # :nodoc:
+ # RFC 5280, IA5String is limited to the set of ASCII characters
+ return false unless san.ascii_only?
+ return false unless hostname.ascii_only?
+
+ # See RFC 6125, section 6.4.1
+ # Matching is case-insensitive.
+ san_parts = san.downcase.split(".")
+
+ # TODO: this behavior should probably be more strict
+ return san == hostname if san_parts.size < 2
+
+ # Matching is case-insensitive.
+ host_parts = hostname.downcase.split(".")
+
+ # RFC 6125, section 6.4.3, subitem 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).
+ return false unless san_parts.size == host_parts.size
+
+ # RFC 6125, section 6.4.3, subitem 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).
+ return false unless verify_wildcard(host_parts.shift, san_parts.shift)
+
+ san_parts.join(".") == host_parts.join(".")
+ end
+ module_function :verify_hostname
+
+ def verify_wildcard(domain_component, san_component) # :nodoc:
+ parts = san_component.split("*", -1)
+
+ return false if parts.size > 2
+ return san_component == domain_component if parts.size == 1
+
+ # RFC 6125, section 6.4.3, subitem 3.
+ # The client SHOULD NOT attempt to match a presented identifier
+ # where the wildcard character is embedded within an A-label or
+ # U-label of an internationalized domain name.
+ return false if domain_component.start_with?("xn--") && san_component != "*"
+
+ parts[0].length + parts[1].length < domain_component.length &&
+ domain_component.start_with?(parts[0]) &&
+ domain_component.end_with?(parts[1])
+ end
+ module_function :verify_wildcard
+
+ class SSLSocket
+ include Buffering
+ include SocketForwarder
+
+ #attr_reader :hostname
+
+ # The underlying IO object.
+ #attr_reader :io
+ #alias :to_io :io
+
+ # The SSLContext object used in this connection.
+ #attr_reader :context
+
+ # Whether to close the underlying socket as well, when the SSL/TLS
+ # connection is shut down. This defaults to +false+.
+ #attr_accessor :sync_close
+
+ # call-seq:
+ # ssl.sysclose => nil
+ #
+ # Sends "close notify" to the peer and tries to shut down the SSL
+ # connection gracefully.
+ #
+ # If sync_close is set to +true+, the underlying IO is also closed.
+ def sysclose
+ return if closed?
+ stop
+ io.close if sync_close
+ end
+
+ # call-seq:
+ # ssl.post_connection_check(hostname) -> true
+ #
+ # Perform hostname verification following RFC 6125.
+ #
+ # This method MUST be called after calling #connect to ensure that the
+ # hostname of a remote peer has been verified.
+ def post_connection_check(hostname)
+ if peer_cert.nil?
+ msg = "Peer verification enabled, but no certificate received."
+ if using_anon_cipher?
+ msg += " Anonymous cipher suite #{cipher[0]} was negotiated. " \
+ "Anonymous suites must be disabled to use peer verification."
+ end
+ raise SSLError, msg
+ end
+
+ unless OpenSSL::SSL.verify_certificate_identity(peer_cert, hostname)
+ raise SSLError, "hostname \"#{hostname}\" does not match the server certificate"
+ end
+ return true
+ end
+
+ # call-seq:
+ # ssl.session -> aSession
+ #
+ # Returns the SSLSession object currently used, or nil if the session is
+ # not established.
+ def session
+ SSL::Session.new(self)
+ rescue SSL::Session::SessionError
+ nil
+ end unless method_defined? :session # JRuby
+
+ private
+
+ def using_anon_cipher?
+ ctx = OpenSSL::SSL::SSLContext.new
+ ctx.ciphers = "aNULL"
+ ctx.ciphers.include?(cipher)
+ end
+
+ def client_cert_cb
+ @context.client_cert_cb
+ end
+
+ def tmp_dh_callback
+ @context.tmp_dh_callback || OpenSSL::SSL::SSLContext::DEFAULT_TMP_DH_CALLBACK
+ end
+
+ def session_new_cb
+ @context.session_new_cb
+ end
+
+ def session_get_cb
+ @context.session_get_cb
+ end
+
+ class << self
+
+ # call-seq:
+ # open(remote_host, remote_port, local_host=nil, local_port=nil, context: nil)
+ #
+ # Creates a new instance of SSLSocket.
+ # _remote\_host_ and _remote\_port_ are used to open TCPSocket.
+ # If _local\_host_ and _local\_port_ are specified,
+ # then those parameters are used on the local end to establish the connection.
+ # If _context_ is provided,
+ # the SSL Sockets initial params will be taken from the context.
+ #
+ # === Examples
+ #
+ # sock = OpenSSL::SSL::SSLSocket.open('localhost', 443)
+ # sock.connect # Initiates a connection to localhost:443
+ #
+ # with SSLContext:
+ #
+ # ctx = OpenSSL::SSL::SSLContext.new
+ # sock = OpenSSL::SSL::SSLSocket.open('localhost', 443, context: ctx)
+ # sock.connect # Initiates a connection to localhost:443 with SSLContext
+ def open(remote_host, remote_port, local_host=nil, local_port=nil, context: nil)
+ sock = ::TCPSocket.open(remote_host, remote_port, local_host, local_port)
+ if context.nil?
+ return OpenSSL::SSL::SSLSocket.new(sock)
+ else
+ return OpenSSL::SSL::SSLSocket.new(sock, context)
+ end
+ end
+ end
+ end
+
+ ##
+ # SSLServer represents a TCP/IP server socket with Secure Sockets Layer.
+ class SSLServer
+ include SocketForwarder
+ # When true then #accept works exactly the same as TCPServer#accept
+ attr_accessor :start_immediately
+
+ # Creates a new instance of SSLServer.
+ # * _srv_ is an instance of TCPServer.
+ # * _ctx_ is an instance of OpenSSL::SSL::SSLContext.
+ def initialize(svr, ctx)
+ @svr = svr
+ @ctx = ctx
+ unless ctx.session_id_context
+ # see #6137 - session id may not exceed 32 bytes
+ prng = ::Random.new($0.hash)
+ session_id = prng.bytes(16).unpack('H*')[0]
+ @ctx.session_id_context = session_id
+ end
+ @start_immediately = true
+ end
+
+ # Returns the TCPServer passed to the SSLServer when initialized.
+ def to_io
+ @svr
+ end
+
+ # See TCPServer#listen for details.
+ def listen(backlog=Socket::SOMAXCONN)
+ @svr.listen(backlog)
+ end
+
+ # See BasicSocket#shutdown for details.
+ def shutdown(how=Socket::SHUT_RDWR)
+ @svr.shutdown(how)
+ end
+
+ # Works similar to TCPServer#accept.
+ def accept
+ # Socket#accept returns [socket, addrinfo].
+ # TCPServer#accept returns a socket.
+ # The following comma strips addrinfo.
+ sock, = @svr.accept
+ begin
+ ssl = OpenSSL::SSL::SSLSocket.new(sock, @ctx)
+ ssl.sync_close = true
+ ssl.accept if @start_immediately
+ ssl
+ rescue Exception => ex
+ if ssl
+ ssl.close
+ else
+ sock.close
+ end
+ raise ex
+ end
+ end
+
+ # See IO#close for details.
+ def close
+ @svr.close
+ end
+ end
+ end
+end
diff --git a/lib/openssl/x509-internal.rb b/lib/openssl/x509-internal.rb
deleted file mode 100644
index b444d193..00000000
--- a/lib/openssl/x509-internal.rb
+++ /dev/null
@@ -1,5 +0,0 @@
-if RUBY_VERSION > '2.1'
- raise LoadError, "no such library in #{RUBY_VERSION}: openssl/x509-internal.rb"
-else
- load "jopenssl19/openssl/#{File.basename(__FILE__)}"
-end
\ No newline at end of file
diff --git a/lib/openssl/x509.rb b/lib/openssl/x509.rb
index a1b40772..1f378b48 100644
--- a/lib/openssl/x509.rb
+++ b/lib/openssl/x509.rb
@@ -1,9 +1,391 @@
-if RUBY_VERSION > '2.3'
- load "jopenssl23/openssl/#{File.basename(__FILE__)}"
-elsif RUBY_VERSION > '2.2'
- load "jopenssl22/openssl/#{File.basename(__FILE__)}"
-elsif RUBY_VERSION > '2.1'
- load "jopenssl21/openssl/#{File.basename(__FILE__)}"
-else
- load "jopenssl19/openssl/#{File.basename(__FILE__)}"
-end
\ No newline at end of file
+# frozen_string_literal: true
+#--
+# = Ruby-space definitions that completes C-space funcs for X509 and subclasses
+#
+# = Info
+# 'OpenSSL for Ruby 2' project
+# Copyright (C) 2002 Michal Rokos
+# All rights reserved.
+#
+# = Licence
+# This program is licensed under the same licence as Ruby.
+# (See the file 'LICENCE'.)
+#++
+
+require_relative 'marshal'
+
+module OpenSSL
+ module X509
+ # class ExtensionFactory
+ # def create_extension(*arg)
+ # if arg.size > 1
+ # create_ext(*arg)
+ # else
+ # send("create_ext_from_"+arg[0].class.name.downcase, arg[0])
+ # end
+ # end
+ #
+ # def create_ext_from_array(ary)
+ # raise ExtensionError, "unexpected array form" if ary.size > 3
+ # create_ext(ary[0], ary[1], ary[2])
+ # end
+ #
+ # def create_ext_from_string(str) # "oid = critical, value"
+ # oid, value = str.split(/=/, 2)
+ # oid.strip!
+ # value.strip!
+ # create_ext(oid, value)
+ # end
+ #
+ # def create_ext_from_hash(hash)
+ # create_ext(hash["oid"], hash["value"], hash["critical"])
+ # end
+ # end
+
+ class Extension
+ include OpenSSL::Marshal
+
+ def ==(other)
+ return false unless Extension === other
+ to_der == other.to_der
+ end
+
+ def to_s # "oid = critical, value"
+ str = self.oid
+ str << " = "
+ str << "critical, " if self.critical?
+ str << self.value.gsub(/\n/, ", ")
+ end
+
+ def to_h # {"oid"=>sn|ln, "value"=>value, "critical"=>true|false}
+ {"oid"=>self.oid,"value"=>self.value,"critical"=>self.critical?}
+ end
+
+ def to_a
+ [ self.oid, self.value, self.critical? ]
+ end
+
+ module Helpers
+ def find_extension(oid)
+ extensions.find { |e| e.oid == oid }
+ end
+ end
+
+ module SubjectKeyIdentifier
+ include Helpers
+
+ # Get the subject's key identifier from the subjectKeyIdentifier
+ # exteension, as described in RFC5280 Section 4.2.1.2.
+ #
+ # Returns the binary String key identifier or nil or raises
+ # ASN1::ASN1Error.
+ def subject_key_identifier
+ ext = find_extension("subjectKeyIdentifier")
+ return nil if ext.nil?
+
+ ski_asn1 = ASN1.decode(ext.value_der)
+ if ext.critical? || ski_asn1.tag_class != :UNIVERSAL || ski_asn1.tag != ASN1::OCTET_STRING
+ raise ASN1::ASN1Error, "invalid extension"
+ end
+
+ ski_asn1.value
+ end
+ end
+
+ module AuthorityKeyIdentifier
+ include Helpers
+
+ # Get the issuing certificate's key identifier from the
+ # authorityKeyIdentifier extension, as described in RFC5280
+ # Section 4.2.1.1
+ #
+ # Returns the binary String keyIdentifier or nil or raises
+ # ASN1::ASN1Error.
+ def authority_key_identifier
+ ext = find_extension("authorityKeyIdentifier")
+ return nil if ext.nil?
+
+ aki_asn1 = ASN1.decode(ext.value_der)
+ if ext.critical? || aki_asn1.tag_class != :UNIVERSAL || aki_asn1.tag != ASN1::SEQUENCE
+ raise ASN1::ASN1Error, "invalid extension"
+ end
+
+ key_id = aki_asn1.value.find do |v|
+ v.tag_class == :CONTEXT_SPECIFIC && v.tag == 0
+ end
+
+ key_id.nil? ? nil : key_id.value
+ end
+ end
+
+ module CRLDistributionPoints
+ include Helpers
+
+ # Get the distributionPoint fullName URI from the certificate's CRL
+ # distribution points extension, as described in RFC5280 Section
+ # 4.2.1.13
+ #
+ # Returns an array of strings or nil or raises ASN1::ASN1Error.
+ def crl_uris
+ ext = find_extension("crlDistributionPoints")
+ return nil if ext.nil?
+
+ cdp_asn1 = ASN1.decode(ext.value_der)
+ if cdp_asn1.tag_class != :UNIVERSAL || cdp_asn1.tag != ASN1::SEQUENCE
+ raise ASN1::ASN1Error, "invalid extension"
+ end
+
+ crl_uris = cdp_asn1.map do |crl_distribution_point|
+ distribution_point = crl_distribution_point.value.find do |v|
+ v.tag_class == :CONTEXT_SPECIFIC && v.tag == 0
+ end
+ full_name = distribution_point&.value&.find do |v|
+ v.tag_class == :CONTEXT_SPECIFIC && v.tag == 0
+ end
+ full_name&.value&.find do |v|
+ v.tag_class == :CONTEXT_SPECIFIC && v.tag == 6 # uniformResourceIdentifier
+ end
+ end
+
+ crl_uris&.map(&:value)
+ end
+ end
+
+ module AuthorityInfoAccess
+ include Helpers
+
+ # Get the information and services for the issuer from the certificate's
+ # authority information access extension exteension, as described in RFC5280
+ # Section 4.2.2.1.
+ #
+ # Returns an array of strings or nil or raises ASN1::ASN1Error.
+ def ca_issuer_uris
+ aia_asn1 = parse_aia_asn1
+ return nil if aia_asn1.nil?
+
+ ca_issuer = aia_asn1.value.select do |authority_info_access|
+ authority_info_access.value.first.value == "caIssuers"
+ end
+
+ ca_issuer&.map(&:value)&.map(&:last)&.map(&:value)
+ end
+
+ # Get the URIs for OCSP from the certificate's authority information access
+ # extension exteension, as described in RFC5280 Section 4.2.2.1.
+ #
+ # Returns an array of strings or nil or raises ASN1::ASN1Error.
+ def ocsp_uris
+ aia_asn1 = parse_aia_asn1
+ return nil if aia_asn1.nil?
+
+ ocsp = aia_asn1.value.select do |authority_info_access|
+ authority_info_access.value.first.value == "OCSP"
+ end
+
+ ocsp&.map(&:value)&.map(&:last)&.map(&:value)
+ end
+
+ private
+
+ def parse_aia_asn1
+ ext = find_extension("authorityInfoAccess")
+ return nil if ext.nil?
+
+ aia_asn1 = ASN1.decode(ext.value_der)
+ if ext.critical? || aia_asn1.tag_class != :UNIVERSAL || aia_asn1.tag != ASN1::SEQUENCE
+ raise ASN1::ASN1Error, "invalid extension"
+ end
+
+ aia_asn1
+ end
+ end
+ end
+
+ class Name
+ include OpenSSL::Marshal
+
+ module RFC2253DN
+ Special = ',=+<>#;'
+ HexChar = /[0-9a-fA-F]/
+ HexPair = /#{HexChar}#{HexChar}/
+ HexString = /#{HexPair}+/
+ Pair = /\\(?:[#{Special}]|\\|"|#{HexPair})/
+ StringChar = /[^\\"#{Special}]/
+ QuoteChar = /[^\\"]/
+ AttributeType = /[a-zA-Z][0-9a-zA-Z]*|[0-9]+(?:\.[0-9]+)*/
+ AttributeValue = /
+ (?!["#])((?:#{StringChar}|#{Pair})*)|
+ \#(#{HexString})|
+ "((?:#{QuoteChar}|#{Pair})*)"
+ /x
+ TypeAndValue = /\A(#{AttributeType})=#{AttributeValue}/
+
+ module_function
+
+ def expand_pair(str)
+ return nil unless str
+ return str.gsub(Pair){
+ pair = $&
+ case pair.size
+ when 2 then pair[1,1]
+ when 3 then Integer("0x#{pair[1,2]}").chr
+ else raise OpenSSL::X509::NameError, "invalid pair: #{str}"
+ end
+ }
+ end
+
+ def expand_hexstring(str)
+ return nil unless str
+ der = str.gsub(HexPair){$&.to_i(16).chr }
+ a1 = OpenSSL::ASN1.decode(der)
+ return a1.value, a1.tag
+ end
+
+ def expand_value(str1, str2, str3)
+ value = expand_pair(str1)
+ value, tag = expand_hexstring(str2) unless value
+ value = expand_pair(str3) unless value
+ return value, tag
+ end
+
+ def scan(dn)
+ str = dn
+ ary = []
+ while true
+ if md = TypeAndValue.match(str)
+ remain = md.post_match
+ type = md[1]
+ value, tag = expand_value(md[2], md[3], md[4]) rescue nil
+ if value
+ type_and_value = [type, value]
+ type_and_value.push(tag) if tag
+ ary.unshift(type_and_value)
+ if remain.length > 2 && remain[0] == ?,
+ str = remain[1..-1]
+ next
+ elsif remain.length > 2 && remain[0] == ?+
+ raise OpenSSL::X509::NameError,
+ "multi-valued RDN is not supported: #{dn}"
+ elsif remain.empty?
+ break
+ end
+ end
+ end
+ msg_dn = dn[0, dn.length - str.length] + " =>" + str
+ raise OpenSSL::X509::NameError, "malformed RDN: #{msg_dn}"
+ end
+ return ary
+ end
+ end
+
+ class << self
+ # Parses the UTF-8 string representation of a distinguished name,
+ # according to RFC 2253.
+ #
+ # See also #to_utf8 for the opposite operation.
+ def parse_rfc2253(str, template=OBJECT_TYPE_TEMPLATE)
+ ary = OpenSSL::X509::Name::RFC2253DN.scan(str)
+ self.new(ary, template)
+ end
+
+ # Parses the string representation of a distinguished name. Two
+ # different forms are supported:
+ #
+ # - \OpenSSL format (X509_NAME_oneline()) used by
+ # #to_s. For example: /DC=com/DC=example/CN=nobody
+ # - \OpenSSL format (X509_NAME_print())
+ # used by #to_s(OpenSSL::X509::Name::COMPAT). For example:
+ # DC=com, DC=example, CN=nobody
+ #
+ # Neither of them is standardized and has quirks and inconsistencies
+ # in handling of escaped characters or multi-valued RDNs.
+ #
+ # Use of this method is discouraged in new applications. See
+ # Name.parse_rfc2253 and #to_utf8 for the alternative.
+ def parse_openssl(str, template=OBJECT_TYPE_TEMPLATE)
+ if str.start_with?("/")
+ # /A=B/C=D format
+ ary = str[1..-1].split("/").map { |i| i.split("=", 2) }
+ else
+ # Comma-separated
+ ary = str.split(",").map { |i| i.strip.split("=", 2) }
+ end
+ self.new(ary, template)
+ end
+
+ alias parse parse_openssl
+ end
+
+ def pretty_print(q)
+ q.object_group(self) {
+ q.text ' '
+ q.text to_s(OpenSSL::X509::Name::RFC2253)
+ }
+ end
+ end
+
+ class Attribute
+ include OpenSSL::Marshal
+
+ def ==(other)
+ return false unless Attribute === other
+ to_der == other.to_der
+ end
+ end
+
+ class StoreContext
+ def cleanup
+ warn "(#{caller.first}) OpenSSL::X509::StoreContext#cleanup is deprecated with no replacement" if $VERBOSE
+ end
+ end
+
+ class Certificate
+ include OpenSSL::Marshal
+ include Extension::SubjectKeyIdentifier
+ include Extension::AuthorityKeyIdentifier
+ include Extension::CRLDistributionPoints
+ include Extension::AuthorityInfoAccess
+
+ def pretty_print(q)
+ q.object_group(self) {
+ q.breakable
+ q.text 'subject='; q.pp self.subject; q.text ','; q.breakable
+ q.text 'issuer='; q.pp self.issuer; q.text ','; q.breakable
+ q.text 'serial='; q.pp self.serial; q.text ','; q.breakable
+ q.text 'not_before='; q.pp self.not_before; q.text ','; q.breakable
+ q.text 'not_after='; q.pp self.not_after
+ }
+ end
+
+ def self.load_file(path)
+ load(File.binread(path))
+ end
+ end
+
+ class CRL
+ include OpenSSL::Marshal
+ include Extension::AuthorityKeyIdentifier
+
+ def ==(other)
+ return false unless CRL === other
+ to_der == other.to_der
+ end
+ end
+
+ class Revoked
+ def ==(other)
+ return false unless Revoked === other
+ to_der == other.to_der
+ end
+ end
+
+ class Request
+ include OpenSSL::Marshal
+
+ def ==(other)
+ return false unless Request === other
+ to_der == other.to_der
+ end
+ end
+ end
+end
diff --git a/mvnw b/mvnw
new file mode 100755
index 00000000..8d937f4c
--- /dev/null
+++ b/mvnw
@@ -0,0 +1,308 @@
+#!/bin/sh
+# ----------------------------------------------------------------------------
+# Licensed to the Apache Software Foundation (ASF) under one
+# or more contributor license agreements. See the NOTICE file
+# distributed with this work for additional information
+# regarding copyright ownership. The ASF licenses this file
+# to you under the Apache License, Version 2.0 (the
+# "License"); you may not use this file except in compliance
+# with the License. You may obtain a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing,
+# software distributed under the License is distributed on an
+# "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+# KIND, either express or implied. See the License for the
+# specific language governing permissions and limitations
+# under the License.
+# ----------------------------------------------------------------------------
+
+# ----------------------------------------------------------------------------
+# Apache Maven Wrapper startup batch script, version 3.2.0
+#
+# Required ENV vars:
+# ------------------
+# JAVA_HOME - location of a JDK home dir
+#
+# Optional ENV vars
+# -----------------
+# MAVEN_OPTS - parameters passed to the Java VM when running Maven
+# e.g. to debug Maven itself, use
+# set MAVEN_OPTS=-Xdebug -Xrunjdwp:transport=dt_socket,server=y,suspend=y,address=8000
+# MAVEN_SKIP_RC - flag to disable loading of mavenrc files
+# ----------------------------------------------------------------------------
+
+if [ -z "$MAVEN_SKIP_RC" ] ; then
+
+ if [ -f /usr/local/etc/mavenrc ] ; then
+ . /usr/local/etc/mavenrc
+ fi
+
+ if [ -f /etc/mavenrc ] ; then
+ . /etc/mavenrc
+ fi
+
+ if [ -f "$HOME/.mavenrc" ] ; then
+ . "$HOME/.mavenrc"
+ fi
+
+fi
+
+# OS specific support. $var _must_ be set to either true or false.
+cygwin=false;
+darwin=false;
+mingw=false
+case "$(uname)" in
+ CYGWIN*) cygwin=true ;;
+ MINGW*) mingw=true;;
+ Darwin*) darwin=true
+ # Use /usr/libexec/java_home if available, otherwise fall back to /Library/Java/Home
+ # See https://developer.apple.com/library/mac/qa/qa1170/_index.html
+ if [ -z "$JAVA_HOME" ]; then
+ if [ -x "/usr/libexec/java_home" ]; then
+ JAVA_HOME="$(/usr/libexec/java_home)"; export JAVA_HOME
+ else
+ JAVA_HOME="/Library/Java/Home"; export JAVA_HOME
+ fi
+ fi
+ ;;
+esac
+
+if [ -z "$JAVA_HOME" ] ; then
+ if [ -r /etc/gentoo-release ] ; then
+ JAVA_HOME=$(java-config --jre-home)
+ fi
+fi
+
+# For Cygwin, ensure paths are in UNIX format before anything is touched
+if $cygwin ; then
+ [ -n "$JAVA_HOME" ] &&
+ JAVA_HOME=$(cygpath --unix "$JAVA_HOME")
+ [ -n "$CLASSPATH" ] &&
+ CLASSPATH=$(cygpath --path --unix "$CLASSPATH")
+fi
+
+# For Mingw, ensure paths are in UNIX format before anything is touched
+if $mingw ; then
+ [ -n "$JAVA_HOME" ] && [ -d "$JAVA_HOME" ] &&
+ JAVA_HOME="$(cd "$JAVA_HOME" || (echo "cannot cd into $JAVA_HOME."; exit 1); pwd)"
+fi
+
+if [ -z "$JAVA_HOME" ]; then
+ javaExecutable="$(which javac)"
+ if [ -n "$javaExecutable" ] && ! [ "$(expr "\"$javaExecutable\"" : '\([^ ]*\)')" = "no" ]; then
+ # readlink(1) is not available as standard on Solaris 10.
+ readLink=$(which readlink)
+ if [ ! "$(expr "$readLink" : '\([^ ]*\)')" = "no" ]; then
+ if $darwin ; then
+ javaHome="$(dirname "\"$javaExecutable\"")"
+ javaExecutable="$(cd "\"$javaHome\"" && pwd -P)/javac"
+ else
+ javaExecutable="$(readlink -f "\"$javaExecutable\"")"
+ fi
+ javaHome="$(dirname "\"$javaExecutable\"")"
+ javaHome=$(expr "$javaHome" : '\(.*\)/bin')
+ JAVA_HOME="$javaHome"
+ export JAVA_HOME
+ fi
+ fi
+fi
+
+if [ -z "$JAVACMD" ] ; then
+ if [ -n "$JAVA_HOME" ] ; then
+ if [ -x "$JAVA_HOME/jre/sh/java" ] ; then
+ # IBM's JDK on AIX uses strange locations for the executables
+ JAVACMD="$JAVA_HOME/jre/sh/java"
+ else
+ JAVACMD="$JAVA_HOME/bin/java"
+ fi
+ else
+ JAVACMD="$(\unset -f command 2>/dev/null; \command -v java)"
+ fi
+fi
+
+if [ ! -x "$JAVACMD" ] ; then
+ echo "Error: JAVA_HOME is not defined correctly." >&2
+ echo " We cannot execute $JAVACMD" >&2
+ exit 1
+fi
+
+if [ -z "$JAVA_HOME" ] ; then
+ echo "Warning: JAVA_HOME environment variable is not set."
+fi
+
+# traverses directory structure from process work directory to filesystem root
+# first directory with .mvn subdirectory is considered project base directory
+find_maven_basedir() {
+ if [ -z "$1" ]
+ then
+ echo "Path not specified to find_maven_basedir"
+ return 1
+ fi
+
+ basedir="$1"
+ wdir="$1"
+ while [ "$wdir" != '/' ] ; do
+ if [ -d "$wdir"/.mvn ] ; then
+ basedir=$wdir
+ break
+ fi
+ # workaround for JBEAP-8937 (on Solaris 10/Sparc)
+ if [ -d "${wdir}" ]; then
+ wdir=$(cd "$wdir/.." || exit 1; pwd)
+ fi
+ # end of workaround
+ done
+ printf '%s' "$(cd "$basedir" || exit 1; pwd)"
+}
+
+# concatenates all lines of a file
+concat_lines() {
+ if [ -f "$1" ]; then
+ # Remove \r in case we run on Windows within Git Bash
+ # and check out the repository with auto CRLF management
+ # enabled. Otherwise, we may read lines that are delimited with
+ # \r\n and produce $'-Xarg\r' rather than -Xarg due to word
+ # splitting rules.
+ tr -s '\r\n' ' ' < "$1"
+ fi
+}
+
+log() {
+ if [ "$MVNW_VERBOSE" = true ]; then
+ printf '%s\n' "$1"
+ fi
+}
+
+BASE_DIR=$(find_maven_basedir "$(dirname "$0")")
+if [ -z "$BASE_DIR" ]; then
+ exit 1;
+fi
+
+MAVEN_PROJECTBASEDIR=${MAVEN_BASEDIR:-"$BASE_DIR"}; export MAVEN_PROJECTBASEDIR
+log "$MAVEN_PROJECTBASEDIR"
+
+##########################################################################################
+# Extension to allow automatically downloading the maven-wrapper.jar from Maven-central
+# This allows using the maven wrapper in projects that prohibit checking in binary data.
+##########################################################################################
+wrapperJarPath="$MAVEN_PROJECTBASEDIR/.mvn/wrapper/maven-wrapper.jar"
+if [ -r "$wrapperJarPath" ]; then
+ log "Found $wrapperJarPath"
+else
+ log "Couldn't find $wrapperJarPath, downloading it ..."
+
+ if [ -n "$MVNW_REPOURL" ]; then
+ wrapperUrl="$MVNW_REPOURL/org/apache/maven/wrapper/maven-wrapper/3.2.0/maven-wrapper-3.2.0.jar"
+ else
+ wrapperUrl="https://repo.maven.apache.org/maven2/org/apache/maven/wrapper/maven-wrapper/3.2.0/maven-wrapper-3.2.0.jar"
+ fi
+ while IFS="=" read -r key value; do
+ # Remove '\r' from value to allow usage on windows as IFS does not consider '\r' as a separator ( considers space, tab, new line ('\n'), and custom '=' )
+ safeValue=$(echo "$value" | tr -d '\r')
+ case "$key" in (wrapperUrl) wrapperUrl="$safeValue"; break ;;
+ esac
+ done < "$MAVEN_PROJECTBASEDIR/.mvn/wrapper/maven-wrapper.properties"
+ log "Downloading from: $wrapperUrl"
+
+ if $cygwin; then
+ wrapperJarPath=$(cygpath --path --windows "$wrapperJarPath")
+ fi
+
+ if command -v wget > /dev/null; then
+ log "Found wget ... using wget"
+ [ "$MVNW_VERBOSE" = true ] && QUIET="" || QUIET="--quiet"
+ if [ -z "$MVNW_USERNAME" ] || [ -z "$MVNW_PASSWORD" ]; then
+ wget $QUIET "$wrapperUrl" -O "$wrapperJarPath" || rm -f "$wrapperJarPath"
+ else
+ wget $QUIET --http-user="$MVNW_USERNAME" --http-password="$MVNW_PASSWORD" "$wrapperUrl" -O "$wrapperJarPath" || rm -f "$wrapperJarPath"
+ fi
+ elif command -v curl > /dev/null; then
+ log "Found curl ... using curl"
+ [ "$MVNW_VERBOSE" = true ] && QUIET="" || QUIET="--silent"
+ if [ -z "$MVNW_USERNAME" ] || [ -z "$MVNW_PASSWORD" ]; then
+ curl $QUIET -o "$wrapperJarPath" "$wrapperUrl" -f -L || rm -f "$wrapperJarPath"
+ else
+ curl $QUIET --user "$MVNW_USERNAME:$MVNW_PASSWORD" -o "$wrapperJarPath" "$wrapperUrl" -f -L || rm -f "$wrapperJarPath"
+ fi
+ else
+ log "Falling back to using Java to download"
+ javaSource="$MAVEN_PROJECTBASEDIR/.mvn/wrapper/MavenWrapperDownloader.java"
+ javaClass="$MAVEN_PROJECTBASEDIR/.mvn/wrapper/MavenWrapperDownloader.class"
+ # For Cygwin, switch paths to Windows format before running javac
+ if $cygwin; then
+ javaSource=$(cygpath --path --windows "$javaSource")
+ javaClass=$(cygpath --path --windows "$javaClass")
+ fi
+ if [ -e "$javaSource" ]; then
+ if [ ! -e "$javaClass" ]; then
+ log " - Compiling MavenWrapperDownloader.java ..."
+ ("$JAVA_HOME/bin/javac" "$javaSource")
+ fi
+ if [ -e "$javaClass" ]; then
+ log " - Running MavenWrapperDownloader.java ..."
+ ("$JAVA_HOME/bin/java" -cp .mvn/wrapper MavenWrapperDownloader "$wrapperUrl" "$wrapperJarPath") || rm -f "$wrapperJarPath"
+ fi
+ fi
+ fi
+fi
+##########################################################################################
+# End of extension
+##########################################################################################
+
+# If specified, validate the SHA-256 sum of the Maven wrapper jar file
+wrapperSha256Sum=""
+while IFS="=" read -r key value; do
+ case "$key" in (wrapperSha256Sum) wrapperSha256Sum=$value; break ;;
+ esac
+done < "$MAVEN_PROJECTBASEDIR/.mvn/wrapper/maven-wrapper.properties"
+if [ -n "$wrapperSha256Sum" ]; then
+ wrapperSha256Result=false
+ if command -v sha256sum > /dev/null; then
+ if echo "$wrapperSha256Sum $wrapperJarPath" | sha256sum -c > /dev/null 2>&1; then
+ wrapperSha256Result=true
+ fi
+ elif command -v shasum > /dev/null; then
+ if echo "$wrapperSha256Sum $wrapperJarPath" | shasum -a 256 -c > /dev/null 2>&1; then
+ wrapperSha256Result=true
+ fi
+ else
+ echo "Checksum validation was requested but neither 'sha256sum' or 'shasum' are available."
+ echo "Please install either command, or disable validation by removing 'wrapperSha256Sum' from your maven-wrapper.properties."
+ exit 1
+ fi
+ if [ $wrapperSha256Result = false ]; then
+ echo "Error: Failed to validate Maven wrapper SHA-256, your Maven wrapper might be compromised." >&2
+ echo "Investigate or delete $wrapperJarPath to attempt a clean download." >&2
+ echo "If you updated your Maven version, you need to update the specified wrapperSha256Sum property." >&2
+ exit 1
+ fi
+fi
+
+MAVEN_OPTS="$(concat_lines "$MAVEN_PROJECTBASEDIR/.mvn/jvm.config") $MAVEN_OPTS"
+
+# For Cygwin, switch paths to Windows format before running java
+if $cygwin; then
+ [ -n "$JAVA_HOME" ] &&
+ JAVA_HOME=$(cygpath --path --windows "$JAVA_HOME")
+ [ -n "$CLASSPATH" ] &&
+ CLASSPATH=$(cygpath --path --windows "$CLASSPATH")
+ [ -n "$MAVEN_PROJECTBASEDIR" ] &&
+ MAVEN_PROJECTBASEDIR=$(cygpath --path --windows "$MAVEN_PROJECTBASEDIR")
+fi
+
+# Provide a "standardized" way to retrieve the CLI args that will
+# work with both Windows and non-Windows executions.
+MAVEN_CMD_LINE_ARGS="$MAVEN_CONFIG $*"
+export MAVEN_CMD_LINE_ARGS
+
+WRAPPER_LAUNCHER=org.apache.maven.wrapper.MavenWrapperMain
+
+# shellcheck disable=SC2086 # safe args
+exec "$JAVACMD" \
+ $MAVEN_OPTS \
+ $MAVEN_DEBUG_OPTS \
+ -classpath "$MAVEN_PROJECTBASEDIR/.mvn/wrapper/maven-wrapper.jar" \
+ "-Dmaven.multiModuleProjectDirectory=${MAVEN_PROJECTBASEDIR}" \
+ ${WRAPPER_LAUNCHER} $MAVEN_CONFIG "$@"
diff --git a/mvnw.cmd b/mvnw.cmd
new file mode 100644
index 00000000..f80fbad3
--- /dev/null
+++ b/mvnw.cmd
@@ -0,0 +1,205 @@
+@REM ----------------------------------------------------------------------------
+@REM Licensed to the Apache Software Foundation (ASF) under one
+@REM or more contributor license agreements. See the NOTICE file
+@REM distributed with this work for additional information
+@REM regarding copyright ownership. The ASF licenses this file
+@REM to you under the Apache License, Version 2.0 (the
+@REM "License"); you may not use this file except in compliance
+@REM with the License. You may obtain a copy of the License at
+@REM
+@REM http://www.apache.org/licenses/LICENSE-2.0
+@REM
+@REM Unless required by applicable law or agreed to in writing,
+@REM software distributed under the License is distributed on an
+@REM "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+@REM KIND, either express or implied. See the License for the
+@REM specific language governing permissions and limitations
+@REM under the License.
+@REM ----------------------------------------------------------------------------
+
+@REM ----------------------------------------------------------------------------
+@REM Apache Maven Wrapper startup batch script, version 3.2.0
+@REM
+@REM Required ENV vars:
+@REM JAVA_HOME - location of a JDK home dir
+@REM
+@REM Optional ENV vars
+@REM MAVEN_BATCH_ECHO - set to 'on' to enable the echoing of the batch commands
+@REM MAVEN_BATCH_PAUSE - set to 'on' to wait for a keystroke before ending
+@REM MAVEN_OPTS - parameters passed to the Java VM when running Maven
+@REM e.g. to debug Maven itself, use
+@REM set MAVEN_OPTS=-Xdebug -Xrunjdwp:transport=dt_socket,server=y,suspend=y,address=8000
+@REM MAVEN_SKIP_RC - flag to disable loading of mavenrc files
+@REM ----------------------------------------------------------------------------
+
+@REM Begin all REM lines with '@' in case MAVEN_BATCH_ECHO is 'on'
+@echo off
+@REM set title of command window
+title %0
+@REM enable echoing by setting MAVEN_BATCH_ECHO to 'on'
+@if "%MAVEN_BATCH_ECHO%" == "on" echo %MAVEN_BATCH_ECHO%
+
+@REM set %HOME% to equivalent of $HOME
+if "%HOME%" == "" (set "HOME=%HOMEDRIVE%%HOMEPATH%")
+
+@REM Execute a user defined script before this one
+if not "%MAVEN_SKIP_RC%" == "" goto skipRcPre
+@REM check for pre script, once with legacy .bat ending and once with .cmd ending
+if exist "%USERPROFILE%\mavenrc_pre.bat" call "%USERPROFILE%\mavenrc_pre.bat" %*
+if exist "%USERPROFILE%\mavenrc_pre.cmd" call "%USERPROFILE%\mavenrc_pre.cmd" %*
+:skipRcPre
+
+@setlocal
+
+set ERROR_CODE=0
+
+@REM To isolate internal variables from possible post scripts, we use another setlocal
+@setlocal
+
+@REM ==== START VALIDATION ====
+if not "%JAVA_HOME%" == "" goto OkJHome
+
+echo.
+echo Error: JAVA_HOME not found in your environment. >&2
+echo Please set the JAVA_HOME variable in your environment to match the >&2
+echo location of your Java installation. >&2
+echo.
+goto error
+
+:OkJHome
+if exist "%JAVA_HOME%\bin\java.exe" goto init
+
+echo.
+echo Error: JAVA_HOME is set to an invalid directory. >&2
+echo JAVA_HOME = "%JAVA_HOME%" >&2
+echo Please set the JAVA_HOME variable in your environment to match the >&2
+echo location of your Java installation. >&2
+echo.
+goto error
+
+@REM ==== END VALIDATION ====
+
+:init
+
+@REM Find the project base dir, i.e. the directory that contains the folder ".mvn".
+@REM Fallback to current working directory if not found.
+
+set MAVEN_PROJECTBASEDIR=%MAVEN_BASEDIR%
+IF NOT "%MAVEN_PROJECTBASEDIR%"=="" goto endDetectBaseDir
+
+set EXEC_DIR=%CD%
+set WDIR=%EXEC_DIR%
+:findBaseDir
+IF EXIST "%WDIR%"\.mvn goto baseDirFound
+cd ..
+IF "%WDIR%"=="%CD%" goto baseDirNotFound
+set WDIR=%CD%
+goto findBaseDir
+
+:baseDirFound
+set MAVEN_PROJECTBASEDIR=%WDIR%
+cd "%EXEC_DIR%"
+goto endDetectBaseDir
+
+:baseDirNotFound
+set MAVEN_PROJECTBASEDIR=%EXEC_DIR%
+cd "%EXEC_DIR%"
+
+:endDetectBaseDir
+
+IF NOT EXIST "%MAVEN_PROJECTBASEDIR%\.mvn\jvm.config" goto endReadAdditionalConfig
+
+@setlocal EnableExtensions EnableDelayedExpansion
+for /F "usebackq delims=" %%a in ("%MAVEN_PROJECTBASEDIR%\.mvn\jvm.config") do set JVM_CONFIG_MAVEN_PROPS=!JVM_CONFIG_MAVEN_PROPS! %%a
+@endlocal & set JVM_CONFIG_MAVEN_PROPS=%JVM_CONFIG_MAVEN_PROPS%
+
+:endReadAdditionalConfig
+
+SET MAVEN_JAVA_EXE="%JAVA_HOME%\bin\java.exe"
+set WRAPPER_JAR="%MAVEN_PROJECTBASEDIR%\.mvn\wrapper\maven-wrapper.jar"
+set WRAPPER_LAUNCHER=org.apache.maven.wrapper.MavenWrapperMain
+
+set WRAPPER_URL="https://repo.maven.apache.org/maven2/org/apache/maven/wrapper/maven-wrapper/3.2.0/maven-wrapper-3.2.0.jar"
+
+FOR /F "usebackq tokens=1,2 delims==" %%A IN ("%MAVEN_PROJECTBASEDIR%\.mvn\wrapper\maven-wrapper.properties") DO (
+ IF "%%A"=="wrapperUrl" SET WRAPPER_URL=%%B
+)
+
+@REM Extension to allow automatically downloading the maven-wrapper.jar from Maven-central
+@REM This allows using the maven wrapper in projects that prohibit checking in binary data.
+if exist %WRAPPER_JAR% (
+ if "%MVNW_VERBOSE%" == "true" (
+ echo Found %WRAPPER_JAR%
+ )
+) else (
+ if not "%MVNW_REPOURL%" == "" (
+ SET WRAPPER_URL="%MVNW_REPOURL%/org/apache/maven/wrapper/maven-wrapper/3.2.0/maven-wrapper-3.2.0.jar"
+ )
+ if "%MVNW_VERBOSE%" == "true" (
+ echo Couldn't find %WRAPPER_JAR%, downloading it ...
+ echo Downloading from: %WRAPPER_URL%
+ )
+
+ powershell -Command "&{"^
+ "$webclient = new-object System.Net.WebClient;"^
+ "if (-not ([string]::IsNullOrEmpty('%MVNW_USERNAME%') -and [string]::IsNullOrEmpty('%MVNW_PASSWORD%'))) {"^
+ "$webclient.Credentials = new-object System.Net.NetworkCredential('%MVNW_USERNAME%', '%MVNW_PASSWORD%');"^
+ "}"^
+ "[Net.ServicePointManager]::SecurityProtocol = [Net.SecurityProtocolType]::Tls12; $webclient.DownloadFile('%WRAPPER_URL%', '%WRAPPER_JAR%')"^
+ "}"
+ if "%MVNW_VERBOSE%" == "true" (
+ echo Finished downloading %WRAPPER_JAR%
+ )
+)
+@REM End of extension
+
+@REM If specified, validate the SHA-256 sum of the Maven wrapper jar file
+SET WRAPPER_SHA_256_SUM=""
+FOR /F "usebackq tokens=1,2 delims==" %%A IN ("%MAVEN_PROJECTBASEDIR%\.mvn\wrapper\maven-wrapper.properties") DO (
+ IF "%%A"=="wrapperSha256Sum" SET WRAPPER_SHA_256_SUM=%%B
+)
+IF NOT %WRAPPER_SHA_256_SUM%=="" (
+ powershell -Command "&{"^
+ "$hash = (Get-FileHash \"%WRAPPER_JAR%\" -Algorithm SHA256).Hash.ToLower();"^
+ "If('%WRAPPER_SHA_256_SUM%' -ne $hash){"^
+ " Write-Output 'Error: Failed to validate Maven wrapper SHA-256, your Maven wrapper might be compromised.';"^
+ " Write-Output 'Investigate or delete %WRAPPER_JAR% to attempt a clean download.';"^
+ " Write-Output 'If you updated your Maven version, you need to update the specified wrapperSha256Sum property.';"^
+ " exit 1;"^
+ "}"^
+ "}"
+ if ERRORLEVEL 1 goto error
+)
+
+@REM Provide a "standardized" way to retrieve the CLI args that will
+@REM work with both Windows and non-Windows executions.
+set MAVEN_CMD_LINE_ARGS=%*
+
+%MAVEN_JAVA_EXE% ^
+ %JVM_CONFIG_MAVEN_PROPS% ^
+ %MAVEN_OPTS% ^
+ %MAVEN_DEBUG_OPTS% ^
+ -classpath %WRAPPER_JAR% ^
+ "-Dmaven.multiModuleProjectDirectory=%MAVEN_PROJECTBASEDIR%" ^
+ %WRAPPER_LAUNCHER% %MAVEN_CONFIG% %*
+if ERRORLEVEL 1 goto error
+goto end
+
+:error
+set ERROR_CODE=1
+
+:end
+@endlocal & set ERROR_CODE=%ERROR_CODE%
+
+if not "%MAVEN_SKIP_RC%"=="" goto skipRcPost
+@REM check for post script, once with legacy .bat ending and once with .cmd ending
+if exist "%USERPROFILE%\mavenrc_post.bat" call "%USERPROFILE%\mavenrc_post.bat"
+if exist "%USERPROFILE%\mavenrc_post.cmd" call "%USERPROFILE%\mavenrc_post.cmd"
+:skipRcPost
+
+@REM pause the script if MAVEN_BATCH_PAUSE is set to 'on'
+if "%MAVEN_BATCH_PAUSE%"=="on" pause
+
+if "%MAVEN_TERMINATE_CMD%"=="on" exit %ERROR_CODE%
+
+cmd /C exit /B %ERROR_CODE%
diff --git a/pom.xml b/pom.xml
index 2cbc1650..3881bc14 100644
--- a/pom.xml
+++ b/pom.xml
@@ -2,16 +2,16 @@
-
4.0.0
rubygems
jruby-openssl
- 0.10.3.dev-SNAPSHOT
+ 0.15.6.dev-SNAPSHOT
gem
JRuby OpenSSL
JRuby-OpenSSL is an add-on gem for JRuby that emulates the Ruby OpenSSL native library.
@@ -60,65 +60,56 @@ DO NOT MODIFIY - GENERATED CODE
- 1.62
+ 1.81
${maven.test.skip}
${bc.versions}
- 1.1.6
+ 3.0.2
-W0
- 9.1.17.0
- 9.1.17.0
- 0.2.1
+ 9.2.19.0
+ 9.2.19.0
+ 2.0.2
+ 2.0.2
pom.xml
- true
+ false
src/test/ruby/**/test_*.rb
- rubygems
- jar-dependencies
- [0.1,0.99999]
- gem
- test
-
-
- rubygems
- mocha
- [1.4,2.0)
- gem
- test
-
-
- rubygems
- ruby-maven
- [3.0,3.99999]
- gem
- test
+ org.bouncycastle
+ bcprov-jdk18on
+ 1.81
org.bouncycastle
- bcprov-jdk15on
- 1.62
+ bcpkix-jdk18on
+ 1.81
org.bouncycastle
- bcpkix-jdk15on
- 1.62
+ bctls-jdk18on
+ 1.81
org.bouncycastle
- bctls-jdk15on
- 1.62
+ bcutil-jdk18on
+ 1.81
org.jruby
jruby-core
- 1.7.20
+ 9.1.11.0
provided
+
+ javax.annotation
+ javax.annotation-api
+ 1.3.1
+ compile
+
junit
junit
- 4.11
+ [4.13.1,)
test
@@ -131,12 +122,12 @@ DO NOT MODIFIY - GENERATED CODE
- org.torquebox.mojo
+ org.jruby.maven
mavengem-wagon
${mavengem.wagon.version}
- de.saumya.mojo
+ org.jruby.maven
gem-with-jar-extension
${jruby.plugins.version}
@@ -190,12 +181,12 @@ DO NOT MODIFIY - GENERATED CODE
- de.saumya.mojo
+ org.jruby.maven
gem-maven-plugin
${jruby.plugins.version}
- default-initialize
+ default-package
false
something-which-does-not-exists
@@ -229,7 +220,7 @@ DO NOT MODIFIY - GENERATED CODE
java
compile
- -Djruby.bytecode.version=1.7
+ -Djruby.bytecode.version=1.8
-classpath
org.jruby.anno.InvokerGenerator
@@ -260,7 +251,7 @@ DO NOT MODIFIY - GENERATED CODE
maven-compiler-plugin
- 3.1
+ 3.9.0
compile-populators
@@ -274,14 +265,16 @@ DO NOT MODIFIY - GENERATED CODE
true
+
-XDignore.symbol.file=true
- 1.7
- 1.7
+ 1.8
+ 1.8
+ 8
UTF-8
true
true
@@ -293,9 +286,6 @@ DO NOT MODIFIY - GENERATED CODE
org.jruby.anno.AnnotationBinder
-
- -XDignore.symbol.file=true
-
@@ -361,7 +351,7 @@ DO NOT MODIFIY - GENERATED CODE
- de.saumya.mojo
+ org.jruby.maven
runit-maven-plugin
${jruby.plugins.version}
@@ -379,28 +369,7 @@ DO NOT MODIFIY - GENERATED CODE
- module-info
-
- [9,)
-
-
-
-
- maven-compiler-plugin
- 3.1
-
- 9
- 1.7
-
- module-info.java
-
-
-
-
-
-
-
- test-1.7.20
+ test-9.1.2.0
@@ -433,279 +402,13 @@ DO NOT MODIFIY - GENERATED CODE
- 1.55,1.56,1.57,1.58,1.59,1.60,1.61
- 1.9,2.0
- 1.7.20
-
-
-
- test-1.7.22
-
-
-
- maven-invoker-plugin
- 1.8
-
-
-
- install
- run
-
-
- integration
-
- */pom.xml
-
- true
-
- ${jruby.versions}
- ${jruby.modes}
- ${project.version}
- ${bc.versions}
- ${runit.dir}
-
-
-
-
-
-
-
-
- 1.55,1.56,1.57,1.58,1.59,1.60,1.61
- 1.9,2.0
- 1.7.22
-
-
-
- test-1.7.23
-
-
-
- maven-invoker-plugin
- 1.8
-
-
-
- install
- run
-
-
- integration
-
- */pom.xml
-
- true
-
- ${jruby.versions}
- ${jruby.modes}
- ${project.version}
- ${bc.versions}
- ${runit.dir}
-
-
-
-
-
-
-
-
- 1.55,1.56,1.57,1.58,1.59,1.60,1.61
- 1.9,2.0
- 1.7.23
-
-
-
- test-1.7.24
-
-
-
- maven-invoker-plugin
- 1.8
-
-
-
- install
- run
-
-
- integration
-
- */pom.xml
-
- true
-
- ${jruby.versions}
- ${jruby.modes}
- ${project.version}
- ${bc.versions}
- ${runit.dir}
-
-
-
-
-
-
-
-
- 1.55,1.56,1.57,1.58,1.59,1.60,1.61
- 1.9,2.0
- 1.7.24
-
-
-
- test-1.7.25
-
-
-
- maven-invoker-plugin
- 1.8
-
-
-
- install
- run
-
-
- integration
-
- */pom.xml
-
- true
-
- ${jruby.versions}
- ${jruby.modes}
- ${project.version}
- ${bc.versions}
- ${runit.dir}
-
-
-
-
-
-
-
-
- 1.55,1.56,1.57,1.58,1.59,1.60,1.61
- 1.9,2.0
- 1.7.25
-
-
-
- test-1.7.26
-
-
-
- maven-invoker-plugin
- 1.8
-
-
-
- install
- run
-
-
- integration
-
- */pom.xml
-
- true
-
- ${jruby.versions}
- ${jruby.modes}
- ${project.version}
- ${bc.versions}
- ${runit.dir}
-
-
-
-
-
-
-
-
- 1.55,1.56,1.57,1.58,1.59,1.60,1.61
- 1.9,2.0
- 1.7.26
-
-
-
- test-1.7.27
-
-
-
- maven-invoker-plugin
- 1.8
-
-
-
- install
- run
-
-
- integration
-
- */pom.xml
-
- true
-
- ${jruby.versions}
- ${jruby.modes}
- ${project.version}
- ${bc.versions}
- ${runit.dir}
-
-
-
-
-
-
-
-
- 1.55,1.56,1.57,1.58,1.59,1.60,1.61
- 1.9,2.0
- 1.7.27
-
-
-
- test-9.0.1.0
-
-
-
- maven-invoker-plugin
- 1.8
-
-
-
- install
- run
-
-
- integration
-
- */pom.xml
-
- true
-
- ${jruby.versions}
- ${jruby.modes}
- ${project.version}
- ${bc.versions}
- ${runit.dir}
-
-
-
-
-
-
-
-
- 1.55,1.56,1.57,1.58,1.59,1.60,1.61
- 9.0.1.0
- 9.0.1.0
+ 1.60,1.61,1.62,1.63,1.64,1.65,1.66,1.67,1.68
+ 9.1.2.0
+ 9.1.2.0
- test-9.0.5.0
+ test-9.1.8.0
@@ -737,13 +440,13 @@ DO NOT MODIFIY - GENERATED CODE
- 1.55,1.56,1.57,1.58,1.59,1.60,1.61
- 9.0.5.0
- 9.0.5.0
+ 1.60,1.61,1.62,1.63,1.64,1.65,1.66,1.67,1.68
+ 9.1.8.0
+ 9.1.8.0
- test-9.1.2.0
+ test-9.1.12.0
@@ -775,13 +478,13 @@ DO NOT MODIFIY - GENERATED CODE
- 1.55,1.56,1.57,1.58,1.59,1.60,1.61
- 9.1.2.0
- 9.1.2.0
+ 1.60,1.61,1.62,1.63,1.64,1.65,1.66,1.67,1.68
+ 9.1.12.0
+ 9.1.12.0
- test-9.1.8.0
+ test-9.1.16.0
@@ -813,13 +516,13 @@ DO NOT MODIFIY - GENERATED CODE
- 1.55,1.56,1.57,1.58,1.59,1.60,1.61
- 9.1.8.0
- 9.1.8.0
+ 1.60,1.61,1.62,1.63,1.64,1.65,1.66,1.67,1.68
+ 9.1.16.0
+ 9.1.16.0
- test-9.1.12.0
+ test-9.1.17.0
@@ -851,13 +554,13 @@ DO NOT MODIFIY - GENERATED CODE
- 1.55,1.56,1.57,1.58,1.59,1.60,1.61
- 9.1.12.0
- 9.1.12.0
+ 1.60,1.61,1.62,1.63,1.64,1.65,1.66,1.67,1.68
+ 9.1.17.0
+ 9.1.17.0
- test-9.1.16.0
+ test-9.2.0.0
@@ -889,13 +592,13 @@ DO NOT MODIFIY - GENERATED CODE
- 1.55,1.56,1.57,1.58,1.59,1.60,1.61
- 9.1.16.0
- 9.1.16.0
+ 1.60,1.61,1.62,1.63,1.64,1.65,1.66,1.67,1.68
+ 9.2.0.0
+ 9.2.0.0
- test-9.1.17.0
+ test-9.2.5.0
@@ -927,13 +630,13 @@ DO NOT MODIFIY - GENERATED CODE
- 1.55,1.56,1.57,1.58,1.59,1.60,1.61
- 9.1.17.0
- 9.1.17.0
+ 1.60,1.61,1.62,1.63,1.64,1.65,1.66,1.67,1.68
+ 9.2.5.0
+ 9.2.5.0
- test-9.2.0.0
+ test-9.2.10.0
@@ -965,13 +668,13 @@ DO NOT MODIFIY - GENERATED CODE
- 1.55,1.56,1.57,1.58,1.59,1.60,1.61
- 9.2.0.0
- 9.2.0.0
+ 1.60,1.61,1.62,1.63,1.64,1.65,1.66,1.67,1.68
+ 9.2.10.0
+ 9.2.10.0
- test-9.2.5.0
+ test-9.2.17.0
@@ -1003,13 +706,13 @@ DO NOT MODIFIY - GENERATED CODE
- 1.55,1.56,1.57,1.58,1.59,1.60,1.61
- 9.2.5.0
- 9.2.5.0
+ 1.60,1.61,1.62,1.63,1.64,1.65,1.66,1.67,1.68
+ 9.2.17.0
+ 9.2.17.0
- test-9.2.6.0
+ test-9.2.19.0
@@ -1041,9 +744,9 @@ DO NOT MODIFIY - GENERATED CODE
- 1.55,1.56,1.57,1.58,1.59,1.60,1.61
- 9.2.6.0
- 9.2.6.0
+ 1.60,1.61,1.62,1.63,1.64,1.65,1.66,1.67,1.68
+ 9.2.19.0
+ 9.2.19.0
diff --git a/src/main/java/org/jruby/ext/openssl/ASN1.java b/src/main/java/org/jruby/ext/openssl/ASN1.java
index a35e199f..3e7417e4 100644
--- a/src/main/java/org/jruby/ext/openssl/ASN1.java
+++ b/src/main/java/org/jruby/ext/openssl/ASN1.java
@@ -31,20 +31,16 @@
import java.io.IOException;
import java.io.InputStream;
import java.io.PrintStream;
-import java.lang.reflect.InvocationTargetException;
-import java.lang.reflect.Method;
import java.math.BigInteger;
+import java.nio.charset.StandardCharsets;
import java.text.ParseException;
-import java.util.Collections;
import java.util.Date;
import java.util.Enumeration;
import java.util.HashMap;
-import java.util.List;
import java.util.Map;
import java.util.WeakHashMap;
import org.bouncycastle.asn1.*;
-
import org.jruby.Ruby;
import org.jruby.RubyArray;
import org.jruby.RubyBignum;
@@ -550,12 +546,12 @@ private static Map getSymLookup(final Ruby runtime
{ "NUMERICSTRING", org.bouncycastle.asn1.DERNumericString.class, "NumericString" },
{ "PRINTABLESTRING", org.bouncycastle.asn1.DERPrintableString.class, "PrintableString" },
{ "T61STRING", org.bouncycastle.asn1.DERT61String.class, "T61String" },
- { "VIDEOTEXSTRING", null, "VideotexString" },
+ { "VIDEOTEXSTRING", org.bouncycastle.asn1.DERVideotexString.class, "VideotexString" },
{ "IA5STRING", org.bouncycastle.asn1.DERIA5String.class, "IA5String" },
{ "UTCTIME", org.bouncycastle.asn1.DERUTCTime.class, "UTCTime" },
{ "GENERALIZEDTIME", org.bouncycastle.asn1.DERGeneralizedTime.class, "GeneralizedTime" },
- { "GRAPHICSTRING", null, "GraphicString" },
- { "ISO64STRING", null, "ISO64String" },
+ { "GRAPHICSTRING", org.bouncycastle.asn1.DERGraphicString.class, "GraphicString" },
+ { "ISO64STRING", org.bouncycastle.asn1.DERVisibleString.class, "ISO64String" },
{ "GENERALSTRING", org.bouncycastle.asn1.DERGeneralString.class, "GeneralString" },
// OpenSSL::ASN1::UNIVERSALSTRING (28) :
{ "UNIVERSALSTRING", org.bouncycastle.asn1.DERUniversalString.class, "UniversalString" },
@@ -563,6 +559,30 @@ private static Map getSymLookup(final Ruby runtime
// OpenSSL::ASN1::BMPSTRING (30) :
{ "BMPSTRING", org.bouncycastle.asn1.DERBMPString.class, "BMPString" }};
+ final static int EOC = 0; // OpenSSL::ASN1::EOC (0)
+ final static int BOOLEAN = 1; // OpenSSL::ASN1::BOOLEAN (1)
+ final static int INTEGER = 2; // OpenSSL::ASN1::INTEGER (2)
+ final static int BIT_STRING = 3; // OpenSSL::ASN1::BIT_STRING (3)
+ final static int OCTET_STRING = 4; // OpenSSL::ASN1::OCTET_STRING (4)
+ final static int NULL = 5; // OpenSSL::ASN1::NULL (5)
+ final static int OBJECT = 6; // OpenSSL::ASN1::OBJECT (6)
+ final static int ENUMERATED = 10; // OpenSSL::ASN1::ENUMERATED (10)
+ final static int UTF8STRING = 12; // OpenSSL::ASN1::UTF8STRING (12)
+ final static int SEQUENCE = 16; // OpenSSL::ASN1::SEQUENCE (16)
+ final static int SET = 17; // OpenSSL::ASN1::SET (17)
+ final static int NUMERICSTRING = 18; // OpenSSL::ASN1::NUMERICSTRING (18)
+ final static int PRINTABLESTRING = 19; // OpenSSL::ASN1::PRINTABLESTRING (19)
+ final static int T61STRING = 20; // OpenSSL::ASN1::T61STRING (20)
+ final static int VIDEOTEXSTRING = 21; // OpenSSL::ASN1::VIDEOTEXSTRING (21)
+ final static int IA5STRING = 22; // OpenSSL::ASN1::IA5STRING (22)
+ final static int UTCTIME = 23; // OpenSSL::ASN1::UTCTIME (23)
+ final static int GENERALIZEDTIME = 24; // OpenSSL::ASN1::GENERALIZEDTIME (24)
+ final static int GRAPHICSTRING = 25; // OpenSSL::ASN1::GRAPHICSTRING (25)
+ final static int ISO64STRING = 26; // OpenSSL::ASN1::ISO64STRING (26)
+ final static int GENERALSTRING = 27; // OpenSSL::ASN1::GENERALSTRING (27)
+ final static int UNIVERSALSTRING = 28; // OpenSSL::ASN1::UNIVERSALSTRING (28)
+ final static int BMPSTRING = 30; // OpenSSL::ASN1::BMPSTRING (30)
+
private final static Map, Integer> JCLASS_TO_ID = new HashMap, Integer>(24, 1);
private final static Map RCLASS_TO_ID = new HashMap(28, 1);
@@ -575,33 +595,35 @@ private static Map getSymLookup(final Ruby runtime
if ( info[2] != null ) {
RCLASS_TO_ID.put((String) info[2], Integer.valueOf(i));
}
+
+ switch (i) {
+ case EOC: assert "EOC".equals(info[0]); break;
+ case BOOLEAN: assert "BOOLEAN".equals(info[0]); break;
+ case INTEGER: assert "INTEGER".equals(info[0]); break;
+ case BIT_STRING: assert "BIT_STRING".equals(info[0]); break;
+ case OCTET_STRING: assert "OCTET_STRING".equals(info[0]); break;
+ case NULL: assert "NULL".equals(info[0]); break;
+ case OBJECT: assert "OBJECT".equals(info[0]); break;
+ case ENUMERATED: assert "ENUMERATED".equals(info[0]); break;
+ case UTF8STRING: assert "UTF8STRING".equals(info[0]); break;
+ case SEQUENCE: assert "SEQUENCE".equals(info[0]); break;
+ case SET: assert "SET".equals(info[0]); break;
+ case NUMERICSTRING: assert "NUMERICSTRING".equals(info[0]); break;
+ case PRINTABLESTRING: assert "PRINTABLESTRING".equals(info[0]); break;
+ case T61STRING: assert "T61STRING".equals(info[0]); break;
+ case VIDEOTEXSTRING: assert "VIDEOTEXSTRING".equals(info[0]); break;
+ case IA5STRING: assert "IA5STRING".equals(info[0]); break;
+ case UTCTIME: assert "UTCTIME".equals(info[0]); break;
+ case GENERALIZEDTIME: assert "GENERALIZEDTIME".equals(info[0]); break;
+ case GRAPHICSTRING: assert "GRAPHICSTRING".equals(info[0]); break;
+ case ISO64STRING: assert "ISO64STRING".equals(info[0]); break;
+ case GENERALSTRING: assert "GENERALSTRING".equals(info[0]); break;
+ case UNIVERSALSTRING: assert "UNIVERSALSTRING".equals(info[0]); break;
+ case BMPSTRING: assert "BMPSTRING".equals(info[0]); break;
+ }
}
}
- private final static int EOC = 0; // OpenSSL::ASN1::EOC (0)
- //private final static int BOOLEAN = 1; // OpenSSL::ASN1::BOOLEAN (1)
- //private final static int INTEGER = 2; // OpenSSL::ASN1::INTEGER (2)
- private final static int BIT_STRING = 3; // OpenSSL::ASN1::BIT_STRING (3)
- private final static int OCTET_STRING = 4; // OpenSSL::ASN1::OCTET_STRING (4)
- //private final static int NULL = 5; // OpenSSL::ASN1::NULL (5)
- //private final static int OBJECT = 6; // OpenSSL::ASN1::OBJECT (6)
- //private final static int ENUMARATED = 10; // OpenSSL::ASN1::ENUMERATED (10)
- //private final static int UTFSTRING = 12; // OpenSSL::ASN1::UTF8STRING (12)
- private final static int SEQUENCE = 16; // OpenSSL::ASN1::SEQUENCE (16)
- private final static int SET = 17; // OpenSSL::ASN1::SET (17)
- //private final static int NUMERICSTRING = 18; // OpenSSL::ASN1::NUMERICSTRING (18)
- // OpenSSL::ASN1::PRINTABLESTRING (19)
- // OpenSSL::ASN1::T61STRING (20)
- // OpenSSL::ASN1::VIDEOTEXSTRING (21)
- // OpenSSL::ASN1::IA5STRING (22)
- // OpenSSL::ASN1::UTCTIME (23)
- // OpenSSL::ASN1::GENERALIZEDTIME (24)
- // OpenSSL::ASN1::GRAPHICSTRING (25)
- // OpenSSL::ASN1::ISO64STRING (26)
- // OpenSSL::ASN1::GENERALSTRING (27)
- // OpenSSL::ASN1::UNIVERSALSTRING (28)
- // OpenSSL::ASN1::BMPSTRING (30)
-
private static Integer typeId(Class> type) {
Integer id = null;
while ( type != Object.class && id == null ) {
@@ -633,23 +655,9 @@ static Class extends ASN1Encodable> typeClass(final int typeId) {
return (Class extends ASN1Encodable>) ASN1_INFO[typeId][1];
}
- static ASN1Encodable typeInstance(Class extends ASN1Encodable> type, Object value)
- throws NoSuchMethodException, IllegalAccessException, InvocationTargetException {
- Method getInstance = null;
- try {
- getInstance = type.getMethod("getInstance", Object.class);
- }
- catch (NoSuchMethodException e) {
- Class superType = type.getSuperclass();
- try {
- if ( superType != Object.class ) {
- getInstance = type.getSuperclass().getMethod("getInstance", Object.class);
- }
- }
- catch (NoSuchMethodException e2) { }
- if ( getInstance == null ) throw e;
- }
- return (ASN1Encodable) getInstance.invoke(null, value);
+ static Class extends ASN1Encodable> typeClassSafe(final int typeId) {
+ if (typeId >= ASN1_INFO.length || typeId < 0) return null;
+ return typeClass(typeId);
}
public static void createASN1(final Ruby runtime, final RubyModule OpenSSL, final RubyClass OpenSSLError) {
@@ -677,21 +685,28 @@ public static void createASN1(final Ruby runtime, final RubyModule OpenSSL, fina
_ASN1Data.addReadWriteAttribute(context, "value");
_ASN1Data.addReadWriteAttribute(context, "tag");
_ASN1Data.addReadWriteAttribute(context, "tag_class");
+ _ASN1Data.addReadWriteAttribute(context, "indefinite_length");
+ _ASN1Data.defineAlias( "infinite_length", "indefinite_length");
+ _ASN1Data.defineAlias( "infinite_length=", "indefinite_length=");
_ASN1Data.defineAnnotatedMethods(ASN1Data.class);
final ObjectAllocator primitiveAllocator = Primitive.ALLOCATOR;
RubyClass Primitive = ASN1.defineClassUnder("Primitive", _ASN1Data, primitiveAllocator);
Primitive.addReadWriteAttribute(context, "tagging");
- Primitive.addReadAttribute(context, "infinite_length");
+ Primitive.undefineMethod("infinite_length=");
+ Primitive.undefineMethod("indefinite_length=");
Primitive.defineAnnotatedMethods(Primitive.class);
final ObjectAllocator constructiveAllocator = Constructive.ALLOCATOR;
RubyClass Constructive = ASN1.defineClassUnder("Constructive", _ASN1Data, constructiveAllocator);
Constructive.includeModule( runtime.getModule("Enumerable") );
Constructive.addReadWriteAttribute(context, "tagging");
- Constructive.addReadWriteAttribute(context, "infinite_length");
Constructive.defineAnnotatedMethods(Constructive.class);
+ final ObjectAllocator eocAllocator = EndOfContent.ALLOCATOR;
+ RubyClass EndOfContent = ASN1.defineClassUnder("EndOfContent", _ASN1Data, eocAllocator);
+ EndOfContent.defineAnnotatedMethods(EndOfContent.class);
+
ASN1.defineClassUnder("Boolean", Primitive, primitiveAllocator); // OpenSSL::ASN1::Boolean <=> value is a Boolean
ASN1.defineClassUnder("Integer", Primitive, primitiveAllocator); // OpenSSL::ASN1::Integer <=> value is a Number
ASN1.defineClassUnder("Null", Primitive, primitiveAllocator); // OpenSSL::ASN1::Null <=> value is always nil
@@ -716,10 +731,8 @@ public static void createASN1(final Ruby runtime, final RubyModule OpenSSL, fina
ASN1.defineClassUnder("UTCTime", Primitive, primitiveAllocator); // OpenSSL::ASN1::UTCTime <=> value is a Time
ASN1.defineClassUnder("GeneralizedTime", Primitive, primitiveAllocator); // OpenSSL::ASN1::GeneralizedTime <=> value is a Time
- ASN1.defineClassUnder("EndOfContent", Primitive, primitiveAllocator); // OpenSSL::ASN1::EndOfContent <=> value is always nil
-
- RubyClass ObjectId = ASN1.defineClassUnder("ObjectId", Primitive, primitiveAllocator);
- ObjectId.defineAnnotatedMethods(ObjectId.class);
+ ASN1.defineClassUnder("ObjectId", Primitive, primitiveAllocator).
+ defineAnnotatedMethods(ObjectId.class);
ASN1.defineClassUnder("Sequence", Constructive, Constructive.getAllocator());
ASN1.defineClassUnder("Set", Constructive, Constructive.getAllocator());
@@ -829,7 +842,7 @@ public static IRubyObject fact_BMPString(ThreadContext context, IRubyObject self
return newInstance(context, self, "BMPString", args);
}
- @JRubyMethod(name="Nul", module=true, rest=true)
+ @JRubyMethod(name={"Null", "Nul"}, module=true, rest=true) // TODO Nul name should be dropped
public static IRubyObject fact_Null(ThreadContext context, IRubyObject self, IRubyObject[] args) {
return newInstance(context, self, "Null", args);
}
@@ -849,6 +862,11 @@ public static IRubyObject fact_GeneralizedTime(ThreadContext context, IRubyObjec
return newInstance(context, self, "GeneralizedTime", args);
}
+ @JRubyMethod(name="EndOfContent", module=true, rest=true)
+ public static IRubyObject fact_EndOfContent(ThreadContext context, IRubyObject self, IRubyObject[] args) {
+ return newInstance(context, self, "EndOfContent", args);
+ }
+
@JRubyMethod(name="Sequence", module=true, rest=true)
public static IRubyObject fact_Sequence(ThreadContext context, IRubyObject self, IRubyObject[] args) {
return newInstance(context, self, "Sequence", args);
@@ -897,6 +915,14 @@ public static RubyString oid(final ThreadContext context, final IRubyObject self
return runtime.newString( getObjectID(runtime, self.callMethod(context, "value").toString()).getId() );
}
+ @JRubyMethod(name = "==")
+ public static IRubyObject eq(final ThreadContext context, final IRubyObject self, final IRubyObject other) {
+ if (!other.getMetaClass().equals(_ASN1(context.runtime).getClass("ObjectId"))) {
+ return context.runtime.getFalse();
+ }
+ return self.callMethod(context, "value").op_eqq(context, other.callMethod(context, "value"));
+ }
+
private static RubyString name(final ThreadContext context, IRubyObject value,
final boolean longName) {
final Ruby runtime = context.runtime;
@@ -925,46 +951,63 @@ static IRubyObject decodeObject(final ThreadContext context,
return ASN1.getClass("Integer").newInstance(context, val, Block.NULL_BLOCK);
}
- if ( obj instanceof DERBitString ) {
- final DERBitString derObj = (DERBitString) obj;
- RubyString str = runtime.newString( new ByteList(derObj.getBytes(), false) );
+ if ( obj instanceof ASN1BitString ) {
+ final ASN1BitString derObj = (ASN1BitString) obj;
+ RubyString str = runtime.newString(new ByteList(derObj.getBytes(), false));
IRubyObject bitString = ASN1.getClass("BitString").newInstance(context, str, Block.NULL_BLOCK);
- bitString.callMethod(context, "unused_bits=", runtime.newFixnum( derObj.getPadBits() ));
+ bitString.getInstanceVariables().setInstanceVariable("@unused_bits", runtime.newFixnum(derObj.getPadBits()));
return bitString;
}
if ( obj instanceof ASN1String ) {
final Integer typeId = typeId( obj.getClass() );
String type = typeId == null ? null : (String) ( ASN1_INFO[typeId][2] );
final ByteList bytes;
- if ( obj instanceof DERUTF8String ) {
+ if ( obj instanceof ASN1UTF8String ) {
if ( type == null ) type = "UTF8String";
- bytes = new ByteList(((DERUTF8String) obj).getString().getBytes("UTF-8"), false);
+ bytes = new ByteList(((ASN1UTF8String) obj).getString().getBytes(StandardCharsets.UTF_8), false);
+ }
+ else if ( obj instanceof ASN1UniversalString ) {
+ if ( type == null ) type = "UniversalString";
+ bytes = new ByteList(((ASN1UniversalString) obj).getOctets(), false);
+ }
+ else if ( obj instanceof ASN1BMPString ) {
+ if ( type == null ) type = "BMPString";
+ final String val = ((ASN1BMPString) obj).getString();
+ final byte[] valBytes = new byte[val.length() * 2];
+ for (int i = 0; i < val.length(); i++) {
+ char c = val.charAt(i);
+ valBytes[i * 2] = (byte) ((c >> 8) & 0xff);
+ valBytes[i * 2 + 1] = (byte) (c & 0xff);
+ }
+ bytes = new ByteList(valBytes, false);
}
else {
if ( type == null ) {
- if ( obj instanceof DERNumericString ) {
+ if ( obj instanceof ASN1NumericString ) {
type = "NumericString";
}
- else if ( obj instanceof DERPrintableString ) {
+ else if ( obj instanceof ASN1PrintableString ) {
type = "PrintableString";
}
- else if ( obj instanceof DERIA5String ) {
+ else if ( obj instanceof ASN1IA5String ) {
type = "IA5String";
}
- else if ( obj instanceof DERT61String ) {
+ else if ( obj instanceof ASN1T61String ) {
type = "T61String";
}
- else if ( obj instanceof DERGeneralString ) {
+ else if ( obj instanceof ASN1GeneralString ) {
type = "GeneralString";
}
- else if ( obj instanceof DERUniversalString ) {
- type = "UniversalString";
+ else if ( obj instanceof ASN1VideotexString ) {
+ type = "VideotexString";
+ }
+ else if ( obj instanceof ASN1VisibleString ) {
+ type = "ISO64String";
}
- else if ( obj instanceof DERBMPString ) {
- type = "BMPString";
+ else if ( obj instanceof ASN1GraphicString ) {
+ type = "GraphicString";
}
else {
- // NOTE "VideotexString", "GraphicString", "ISO64String" not-handled in BC !
throw new IllegalArgumentException("could not handle ASN1 string type: " + obj + " (" + obj.getClass().getName() + ")");
}
}
@@ -973,11 +1016,6 @@ else if ( obj instanceof DERBMPString ) {
return ASN1.getClass(type).newInstance(context, runtime.newString(bytes), Block.NULL_BLOCK);
}
- //if ( obj instanceof DEROctetString ) {
- // byte[] octets = ((ASN1OctetString) obj).getOctets();
- // if ( (octets[0] & 0xFF) == 0xD1 ) Thread.dumpStack();
- //}
-
if ( obj instanceof ASN1OctetString ) {
final ByteList octets = new ByteList(((ASN1OctetString) obj).getOctets(), false);
// NOTE: sometimes MRI does include the tag but it really should not ;( !
@@ -1000,14 +1038,6 @@ else if ( obj instanceof DERBMPString ) {
final RubyTime time = RubyTime.newTime(runtime, adjustedTime.getTime());
return ASN1.getClass("UTCTime").newInstance(context, time, Block.NULL_BLOCK);
}
- // NOTE: keep for BC versions compatibility ... extends ASN1UTCTime (since BC 1.51)
- if ( obj instanceof DERUTCTime ) {
- final Date adjustedTime;
- try { adjustedTime = ((DERUTCTime) obj).getAdjustedDate(); }
- catch (ParseException e) { throw new IOException(e); }
- final RubyTime time = RubyTime.newTime(runtime, adjustedTime.getTime());
- return ASN1.getClass("UTCTime").newInstance(context, time, Block.NULL_BLOCK);
- }
if ( obj instanceof ASN1GeneralizedTime ) {
final Date generalTime;
@@ -1016,39 +1046,40 @@ else if ( obj instanceof DERBMPString ) {
final RubyTime time = RubyTime.newTime(runtime, generalTime.getTime());
return ASN1.getClass("GeneralizedTime").newInstance(context, time, Block.NULL_BLOCK);
}
- // NOTE: keep for BC versions compatibility ... extends ASN1GeneralizedTime (since BC 1.51)
- //if ( obj instanceof DERGeneralizedTime ) {
- // final Date generalTime;
- // try {
- // generalTime = ((DERGeneralizedTime) obj).getDate();
- // }
- // catch (ParseException e) { throw new IOException(e); }
- // final RubyTime time = RubyTime.newTime(runtime, generalTime.getTime());
- // return ASN1.getClass("GeneralizedTime").newInstance(context, time, Block.NULL_BLOCK);
- //}
if ( obj instanceof ASN1ObjectIdentifier ) {
final String objId = ((ASN1ObjectIdentifier) obj).getId();
return ASN1.getClass("ObjectId").newInstance(context, runtime.newString(objId), Block.NULL_BLOCK);
}
- if ( obj instanceof ASN1TaggedObject ) {
+ if (obj instanceof ASN1TaggedObject) {
final ASN1TaggedObject taggedObj = (ASN1TaggedObject) obj;
- IRubyObject val = decodeObject(context, ASN1, taggedObj.getObject());
- IRubyObject tag = runtime.newFixnum( taggedObj.getTagNo() );
- IRubyObject tag_class = runtime.newSymbol("CONTEXT_SPECIFIC");
- final RubyArray valArr = runtime.newArray(val);
- return ASN1.getClass("ASN1Data").newInstance(context, new IRubyObject[] { valArr, tag, tag_class }, Block.NULL_BLOCK);
- }
-
- if ( obj instanceof ASN1ApplicationSpecific ) {
- final ASN1ApplicationSpecific appSpecific = (ASN1ApplicationSpecific) obj;
- IRubyObject tag = runtime.newFixnum( appSpecific.getApplicationTag() );
- IRubyObject tag_class = runtime.newSymbol("APPLICATION");
- final ASN1Sequence sequence = (ASN1Sequence) appSpecific.getObject(SEQUENCE);
- @SuppressWarnings("unchecked")
- final RubyArray valArr = decodeObjects(context, ASN1, sequence.getObjects());
- return ASN1.getClass("ASN1Data").newInstance(context, new IRubyObject[] { valArr, tag, tag_class }, Block.NULL_BLOCK);
+ final IRubyObject tag = runtime.newFixnum(taggedObj.getTagNo());
+ final IRubyObject tag_class;
+ switch (taggedObj.getTagClass()) {
+ case BERTags.PRIVATE:
+ tag_class = runtime.newSymbol("PRIVATE");
+ break;
+ case BERTags.APPLICATION:
+ tag_class = runtime.newSymbol("APPLICATION");
+ break;
+ case BERTags.CONTEXT_SPECIFIC:
+ tag_class = runtime.newSymbol("CONTEXT_SPECIFIC");
+ break;
+ default:
+ tag_class = runtime.newSymbol("UNIVERSAL");
+ break;
+ }
+
+ try {
+ final ASN1Sequence sequence = (ASN1Sequence) taggedObj.getBaseUniversal(false, SEQUENCE);
+ @SuppressWarnings("unchecked")
+ final RubyArray valArr = decodeObjects(context, ASN1, sequence.getObjects());
+ return ASN1.getClass("ASN1Data").newInstance(context, new IRubyObject[] { valArr, tag, tag_class }, Block.NULL_BLOCK);
+ } catch (IllegalStateException e) {
+ IRubyObject val = decodeObject(context, ASN1, taggedObj.getBaseObject()).callMethod(context, "value");
+ return ASN1.getClass("ASN1Data").newInstance(context, new IRubyObject[] { val, tag, tag_class }, Block.NULL_BLOCK);
+ }
}
if ( obj instanceof ASN1Sequence ) {
@@ -1087,18 +1118,17 @@ public static IRubyObject decode(final ThreadContext context,
return decodeImpl(context, (RubyModule) self, obj);
}
catch (IOException e) {
- //throw context.runtime.newIOErrorFromException(e);
- throw newASN1Error(context.runtime, e.getMessage());
+ throw newASN1Error(context.runtime, e);
}
catch (IllegalArgumentException e) {
debugStackTrace(context.runtime, e);
- throw context.runtime.newArgumentError(e.getMessage());
+ throw (RaiseException) context.runtime.newArgumentError(e.getMessage()).initCause(e);
+ }
+ catch (RuntimeException e) {
+
+ debugStackTrace(context.runtime, e);
+ throw Utils.newRuntimeError(context.runtime, e);
}
- //catch (RuntimeException e) {
- // final Ruby runtime = context.runtime;
- // debugStackTrace(runtime, e);
- // throw Utils.newRuntimeError(context.runtime, e);
- //}
}
static IRubyObject decodeImpl(final ThreadContext context, IRubyObject obj)
@@ -1134,26 +1164,64 @@ private BytesInputStream(final ByteList bytes) {
}
- private static IRubyObject decodeImpl(final ThreadContext context,
- final RubyModule ASN1, final BytesInputStream in) throws IOException, IllegalArgumentException {
+ private static IRubyObject decodeImpl(final ThreadContext context, final RubyModule ASN1, final BytesInputStream in)
+ throws IOException, IllegalArgumentException {
+ final byte[] asn1 = in.bytes();
+ int offset = in.offset();
+ final int tag = asn1[offset] & 0xFF;
+
+ if ( ( tag & BERTags.CONSTRUCTED ) == 0 ) {
+ return decodeObject(context, ASN1, readObject(in));
+ }
+
// NOTE: need to handle OpenSSL::ASN1::Constructive wrapping by hand :
- final Integer tag = getConstructiveTag(in.bytes(), in.offset());
- IRubyObject decoded = decodeObject(context, ASN1, readObject( in ));
- if ( tag != null ) { // OpenSSL::ASN1::Constructive.new( arg ) :
- final String type; List value = null;
- if ( tag.intValue() == SEQUENCE ) {
- //type = "Sequence"; // got a OpenSSL::ASN1::Sequence already :
- return Constructive.setInfiniteLength(context, decoded);
- }
- else if ( tag.intValue() == SET ) {
- //type = "Set"; // got a OpenSSL::ASN1::Set already :
- return Constructive.setInfiniteLength(context, decoded);
+ int tagNo = tag & 0x1f;
+ if (tagNo == 0x1f)
+ {
+ tagNo = 0;
+ int b = asn1[ ++offset ];
+
+ // X.690-0207 8.1.2.4.2
+ // "c) bits 7 to 1 of the first subsequent octet shall not all be zero."
+ if ((b & 0x7f) == 0) // Note: -1 will pass
+ {
+ throw new IOException("corrupted stream - invalid high tag number found");
}
- else {
- type = "Constructive";
+
+ while ((b >= 0) && ((b & 0x80) != 0))
+ {
+ tagNo |= (b & 0x7f);
+ tagNo <<= 7;
+ b = asn1[ ++offset ];
+ }
+
+ if (b < 0)
+ {
+ throw new IOException("EOF found inside tag value.");
+ }
+
+ tagNo |= (b & 0x7f);
+ }
+ final int length = asn1[ ++offset ] & 0xFF;
+ final boolean isIndefiniteLength = length == 0x80;
+ IRubyObject decoded;
+
+ decoded = decodeObject(context, ASN1, readObject(in));
+
+ final boolean isUniversal = ((ASN1Data) decoded).isUniversal(context);
+
+ if (isIndefiniteLength) {
+ if (tagNo == BERTags.SEQUENCE || tagNo == BERTags.SET) {
+ return ASN1Data.setInfiniteLength(context, decoded);
+ } else if (isUniversal) {
+ decoded = Constructive.newInfiniteLength(context, context.runtime.newArray(decoded), tagNo);
+ } else {
+ if (decoded instanceof ASN1Data) {
+ return ASN1Data.setInfiniteLength(context, decoded);
+ } else {
+ decoded = ASN1Data.newInfiniteLength(context, context.runtime.newArray(decoded), tagNo, ((ASN1Data) decoded).tagClass());
+ }
}
- if ( value == null ) value = Collections.singletonList(decoded);
- return Constructive.newInfiniteConstructive(context, type, value, tag);
}
return decoded;
}
@@ -1172,8 +1240,7 @@ public static IRubyObject decode_all(final ThreadContext context,
arr.append( decodeImpl(context, ASN1, in) );
}
catch (IOException e) {
- //throw context.runtime.newIOErrorFromException(e);
- throw newASN1Error(context.runtime, e.getMessage());
+ throw newASN1Error(context.runtime, e);
}
catch (IllegalArgumentException e) {
debugStackTrace(context.runtime, e);
@@ -1193,6 +1260,10 @@ public static RaiseException newASN1Error(Ruby runtime, String message) {
return Utils.newError(runtime, _ASN1(runtime).getClass("ASN1Error"), message, false);
}
+ static RaiseException newASN1Error(Ruby runtime, Throwable ex) {
+ return (RaiseException) newASN1Error(runtime, ex.getMessage()).initCause(ex);
+ }
+
static RubyModule _ASN1(final Ruby runtime) {
return (RubyModule) runtime.getModule("OpenSSL").getConstant("ASN1");
}
@@ -1207,89 +1278,6 @@ private static org.bouncycastle.asn1.ASN1Primitive readObject(final InputStream
return new ASN1InputStream(bytes).readObject();
}
- // NOTE: BC's ASNInputStream internals "reinvented" a bit :
- private static Integer getConstructiveTag(final byte[] asn1, int offset) {
- final int tag = asn1[ offset ] & 0xFF;
- if ( ( tag & BERTags.CONSTRUCTED ) != 0 ) { // isConstructed
- //
- // calculate tag number
- //
- // readTagNumber(asn1, ++offset, tag) :
- int tagNo = tag & 0x1f;
- //
- // with tagged object tag number is bottom 5 bits, or stored at the start of the content
- //
- if (tagNo == 0x1f)
- {
- tagNo = 0;
-
- int b = asn1[ ++offset ]; //s.read();
-
- // X.690-0207 8.1.2.4.2
- // "c) bits 7 to 1 of the first subsequent octet shall not all be zero."
- if ((b & 0x7f) == 0) // Note: -1 will pass
- {
- return null; //throw new IOException("corrupted stream - invalid high tag number found");
- }
-
- while ((b >= 0) && ((b & 0x80) != 0))
- {
- tagNo |= (b & 0x7f);
- tagNo <<= 7;
- b = asn1[ ++offset ]; //s.read();
- }
-
- if (b < 0)
- {
- return null; //throw new EOFException("EOF found inside tag value.");
- }
-
- tagNo |= (b & 0x7f);
- }
-
- //
- // calculate length
- //
- final int length = asn1[ ++offset ] & 0xFF;
-
- if ( length == 0x80 ) {
- // return -1; // indefinite-length encoding
- }
- else {
- return null;
- }
-
- if ((tag & BERTags.APPLICATION) != 0) {
- //return new BERApplicationSpecificParser(tagNo, sp).getLoadedObject();
- }
-
- if ((tag & BERTags.TAGGED) != 0) {
- //return new BERTaggedObjectParser(true, tagNo, sp).getLoadedObject();
- }
-
- //System.out.println(" tagNo = 0x" + Integer.toHexString(tagNo));
- // TODO There are other tags that may be constructed (e.g. BIT_STRING)
- switch (tagNo) {
- case BERTags.SEQUENCE :
- //return new BERSequenceParser(sp).getLoadedObject();
- return Integer.valueOf( SEQUENCE ); //return "Sequence";
- case BERTags.SET :
- //return new BERSetParser(sp).getLoadedObject();
- return Integer.valueOf( SET ); //return "Set";
- case BERTags.OCTET_STRING :
- return Integer.valueOf( OCTET_STRING );
- //return new BEROctetStringParser(sp).getLoadedObject();
- case BERTags.EXTERNAL :
- //return new DERExternalParser(sp).getLoadedObject();
- default:
- return Integer.valueOf( 0 ); //return "Constructive";
- //throw new IOException("unknown BER object encountered");
- }
- }
-
- return null;
- }
-
public static class ASN1Data extends RubyObject {
private static final long serialVersionUID = 6117598347932209839L;
@@ -1308,31 +1296,97 @@ public ASN1Data(Ruby runtime, RubyClass type) {
@JRubyMethod(visibility = Visibility.PRIVATE)
public IRubyObject initialize(final ThreadContext context,
final IRubyObject value, final IRubyObject tag, final IRubyObject tag_class) {
- checkTag(context.runtime, tag, tag_class, "UNIVERSAL");
+ checkTag(context.runtime, tag, tag_class);
this.callMethod(context, "tag=", tag);
this.callMethod(context, "value=", value);
this.callMethod(context, "tag_class=", tag_class);
+ this.setInstanceVariable("@indefinite_length", context.runtime.getFalse());
return this;
}
- private void checkTag(final Ruby runtime, final IRubyObject tag,
- final IRubyObject tagClass, final String expected) {
+ static ASN1Data newInfiniteLength(final ThreadContext context,
+ final IRubyObject value, final int defaultTag, final IRubyObject tagClass) {
+ final Ruby runtime = context.runtime;
+
+ final RubyClass klass = _ASN1(runtime).getClass("ASN1Data");
+ final ASN1Data self = new Constructive(runtime, klass);
+
+ ASN1Data.newInfiniteLengthImpl(context, self, value, defaultTag, tagClass);
+ return self;
+ }
+
+ static void newInfiniteLengthImpl(final ThreadContext context, final ASN1Data self, final IRubyObject value, final int defaultTag, final IRubyObject tagClass) {
+ self.setInstanceVariable("@tag", context.runtime.newFixnum(defaultTag));
+ self.setInstanceVariable("@value", value);
+ self.setInstanceVariable("@tag_class", tagClass);
+ self.setInstanceVariable("@tagging", context.nil);
+
+ setInfiniteLength(context, self);
+ }
+
+ static ASN1Data setInfiniteLength(final ThreadContext context, final IRubyObject constructive) {
+ final ASN1Data instance = ((ASN1Data) constructive);
+ final IRubyObject value = instance.value(context);
+ value.callMethod(context, "<<", EndOfContent.newInstance(context));
+ instance.setInstanceVariable("@indefinite_length", context.runtime.getTrue());
+ return instance;
+ }
+
+ private void checkTag(final Ruby runtime, final IRubyObject tag, final IRubyObject tagClass) {
if ( ! (tagClass instanceof RubySymbol) ) {
throw newASN1Error(runtime, "invalid tag class");
}
- if ( tagClass.toString().equals(expected) && RubyNumeric.fix2int(tag) > MAX_TAG_VALUE ) {
- throw newASN1Error(runtime, "tag number for :" + expected + " too large");
+ if ( "UNIVERSAL".equals(tagClass.toString()) && RubyNumeric.fix2int(tag) > MAX_TAG_VALUE ) {
+ throw newASN1Error(runtime, "tag number for :UNIVERSAL too large (" + tag + ")");
}
}
- boolean isEOC() { return false; }
+ private boolean isConstructive() {
+ return "Constructive".equals(getMetaClass().getRealClass().getBaseName());
+ }
+
+ boolean isInfiniteLength() {
+ return getInstanceVariable("@indefinite_length").isTrue();
+ }
+
+ boolean isEOC(final ThreadContext context) {
+ return getTag(context) == 0 && isUniversal((context));
+ }
+
+ boolean isUniversal(final ThreadContext context) {
+ return getTagClass(context) == BERTags.UNIVERSAL;
+ }
+
+ IRubyObject tagging() {
+ return getInstanceVariable("@tagging");
+ }
+
+ IRubyObject tagClass() {
+ return getInstanceVariable("@tag_class");
+ }
boolean isExplicitTagging() { return ! isImplicitTagging(); }
boolean isImplicitTagging() { return true; }
int getTag(final ThreadContext context) {
- return RubyNumeric.fix2int(callMethod(context, "tag"));
+ return RubyNumeric.fix2int(getInstanceVariable("@tag"));
+ }
+
+ int getTagClass(final ThreadContext context) {
+ IRubyObject tag_class = getInstanceVariable("@tag_class");
+ if (tag_class instanceof RubySymbol) {
+ switch (((RubySymbol) tag_class).asJavaString()) {
+ case "PRIVATE":
+ return BERTags.PRIVATE;
+ case "APPLICATION":
+ return BERTags.APPLICATION;
+ case "CONTEXT_SPECIFIC":
+ return BERTags.CONTEXT_SPECIFIC;
+ default: // fallback to BERTags.UNIVERSAL
+ }
+ }
+ return BERTags.UNIVERSAL; // 0
}
ASN1Encodable toASN1(final ThreadContext context) {
@@ -1341,27 +1395,56 @@ ASN1Encodable toASN1(final ThreadContext context) {
final ASN1TaggedObject toASN1TaggedObject(final ThreadContext context) {
final int tag = getTag(context);
+ final int tagClass = getTagClass(context);
+
+ final IRubyObject value = callMethod(context, "value");
+ if (value instanceof RubyArray) {
+ // Cruby openssl joins elements of array and casts to string
+ final RubyArray arr = (RubyArray) value;
+
+ StringBuilder values = new StringBuilder();
+ ASN1EncodableVector vec = new ASN1EncodableVector();
- final IRubyObject val = callMethod(context, "value");
- if ( val instanceof RubyArray ) {
- final RubyArray arr = (RubyArray) val;
- if ( arr.size() > 1 ) {
- ASN1EncodableVector vec = new ASN1EncodableVector();
- for ( final IRubyObject obj : arr.toJavaArray() ) {
+ for (final IRubyObject obj : arr.toJavaArray()) {
+ if (obj instanceof ASN1Data) {
ASN1Encodable data = ((ASN1Data) obj).toASN1(context);
- if ( data == null ) break; vec.add( data );
+ if (data == null) break;
+ vec.add(data);
+ } else {
+ final IRubyObject string = obj.checkStringType();
+ if (string instanceof RubyString) {
+ values.append(string.asJavaString());
+ } else {
+ throw context.runtime.newTypeError(
+ "no implicit conversion of " + obj.getMetaClass().getBaseName() + " into String");
+ }
}
- return new DERTaggedObject(isExplicitTagging(), tag, new DERSequence(vec));
}
- else if ( arr.size() == 1 ) {
- ASN1Encodable data = ((ASN1Data) arr.entry(0)).toASN1(context);
- return new DERTaggedObject(isExplicitTagging(), tag, data);
+
+ if (values.length() > 0) {
+ return new DERTaggedObject(isExplicitTagging(), tagClass, tag, new DERGeneralString(values.toString()));
+ } else {
+ // array of strings as value (default)
+ return new DERTaggedObject(isExplicitTagging(), tagClass, tag, new BERSequence(vec));
}
- else {
- throw new IllegalStateException("empty array detected");
+ } else if (value instanceof ASN1Data) {
+ return new DERTaggedObject(isExplicitTagging(), tagClass, tag, ((ASN1Data) value).toASN1(context));
+ } else if (value instanceof RubyObject) {
+ if (isEOC(context)) {
+ return null;
+ }
+ final IRubyObject string = value.checkStringType();
+ if (string instanceof RubyString) {
+ return new DERTaggedObject(isExplicitTagging(), tagClass, tag,
+ new DERGeneralString(string.asJavaString()));
+ } else {
+ throw context.runtime.newTypeError(
+ "no implicit conversion of " + value.getMetaClass().getBaseName() + " into String");
}
+ } else {
+ throw context.runtime.newTypeError(
+ "no implicit conversion of " + value.getMetaClass().getBaseName() + " into String");
}
- return new DERTaggedObject(isExplicitTagging(), tag, ((ASN1Data) val).toASN1(context));
}
@JRubyMethod
@@ -1376,7 +1459,147 @@ public IRubyObject to_der(final ThreadContext context) {
}
byte[] toDER(final ThreadContext context) throws IOException {
- return toASN1(context).toASN1Primitive().getEncoded(ASN1Encoding.DER);
+ if (
+ ("ASN1Data".equals(getClassBaseName()) && isUniversal(context))
+ ) {
+ return toDERInternal(context, isConstructive(), isInfiniteLength(), value(context));
+ }
+
+ final ASN1Primitive prim = toASN1(context).toASN1Primitive();
+
+ if (isInfiniteLength()) {
+ final java.io.ByteArrayOutputStream tagOut = new ByteArrayOutputStream();
+ final java.io.ByteArrayOutputStream contentOut = new ByteArrayOutputStream();
+ final java.io.ByteArrayOutputStream out = new ByteArrayOutputStream();
+ prim.encodeTo(contentOut, ASN1Encoding.DER);
+ writeDERIdentifier(getTag(context), getTagClass(context) | BERTags.CONSTRUCTED, tagOut);
+
+ byte[] tagOutArr = tagOut.toByteArray();
+ byte[] contentOutArr = contentOut.toByteArray();
+
+ out.write(tagOutArr);
+ out.write(0x80);
+ out.write(contentOutArr, tagOutArr.length + 1, contentOutArr.length - tagOutArr.length - 1);
+ out.write(0x00);
+ out.write(0x00);
+
+ return out.toByteArray();
+ } else {
+ return prim.getEncoded(ASN1Encoding.DER);
+ }
+ }
+
+ byte[] toDERInternal(final ThreadContext context, boolean isConstructed, boolean isIndefiniteLength, final IRubyObject value) throws IOException {
+ // handstitch conversion
+ final java.io.ByteArrayOutputStream out = new ByteArrayOutputStream();
+
+ final byte[] valueBytes;
+
+ if (value == null) {
+ valueBytes = new byte[] {};
+ } else if (value instanceof RubyArray) {
+ final IRubyObject[] arr = ((RubyArray) value).toJavaArray();
+ final java.io.ByteArrayOutputStream valueOut = new ByteArrayOutputStream();
+
+
+ for ( int i = 0; i < arr.length; i++ ) {
+ final IRubyObject obj = arr[i];
+
+ if (obj instanceof EndOfContent && i != arr.length - 1) {
+ throw newASN1Error(context.runtime, "illegal EOC octets in value");
+ }
+
+ final byte[] objBytes;
+
+ if (obj.respondsTo("to_der")) {
+ objBytes = ((RubyString) obj.callMethod(context, "to_der")).getBytes();
+ } else {
+ objBytes = ((RubyString) obj.convertToString()).getBytes();
+ }
+
+ valueOut.write(objBytes);
+ }
+
+ if (isIndefiniteLength) {
+ if (arr.length != 0 && !(arr[arr.length - 1] instanceof EndOfContent)) {
+ // indefinite length object with no EOC object in the array.
+ valueOut.write(0x00);
+ valueOut.write(0x00);
+ }
+ }
+
+ valueBytes = valueOut.toByteArray();
+ } else {
+ if (isIndefiniteLength) {
+ throw newASN1Error(
+ context.runtime,
+ "indefinite length form cannot be used with primitive encoding"
+ );
+ }
+
+ if (value instanceof RubyString) {
+ valueBytes = ((RubyString) value).getBytes();
+ } else {
+ valueBytes = value.convertToString().getBytes();
+ }
+ }
+
+ int flags = getTagClass(context);
+ if (isConstructed) {
+ flags |= BERTags.CONSTRUCTED;
+ }
+ // tag
+ writeDERIdentifier(getTag(context), flags, out);
+ if (isIndefiniteLength) {
+ out.write(0x80);
+ } else {
+ writeDERLength(valueBytes.length, out);
+ }
+ // value
+ out.write(valueBytes);
+
+ return out.toByteArray();
+ }
+
+ void writeDERIdentifier(int tag, int flags, java.io.ByteArrayOutputStream out) {
+ if (tag > 0x1f) {
+ byte[] stack = new byte[6];
+ int pos = stack.length;
+
+ stack[--pos] = (byte)(tag & 0x7F);
+ while (tag > 127)
+ {
+ tag >>>= 7;
+ stack[--pos] = (byte)(tag & 0x7F | 0x80);
+ }
+
+ stack[--pos] = (byte)(flags | 0x1F);
+
+ out.write(stack, pos, stack.length - pos);
+ } else {
+ out.write(flags | tag);
+ }
+ }
+
+ void writeDERLength(int length, java.io.ByteArrayOutputStream out) {
+ if (length < 128) {
+ out.write(length);
+ } else {
+ byte[] stack = new byte[5];
+ int pos = stack.length;
+
+ do
+ {
+ stack[--pos] = (byte)length;
+ length >>>= 8;
+ }
+ while (length != 0);
+
+ int count = stack.length - pos;
+ stack[--pos] = (byte)(0x80 | count);
+
+ out.write(stack, pos, count - pos);
+ }
}
protected IRubyObject defaultTag() {
@@ -1426,12 +1649,46 @@ static void printArray(final PrintStream out, final int indent, final RubyArray
}
}
- static RaiseException createNativeRaiseException(final ThreadContext context,
- final Throwable e) {
- Throwable cause = e.getCause(); if ( cause == null ) cause = e;
- return RaiseException.createNativeRaiseException(context.runtime, cause);
+ }
+
+ public static class EndOfContent extends ASN1Data {
+
+ static ObjectAllocator ALLOCATOR = new ObjectAllocator() {
+ public IRubyObject allocate(Ruby runtime, RubyClass klass) {
+ return new EndOfContent(runtime, klass);
+ }
+ };
+
+ public EndOfContent(Ruby runtime, RubyClass type) {
+ super(runtime,type);
}
+
+ @JRubyMethod(required = 0, optional = 0, visibility = Visibility.PRIVATE)
+ public static IRubyObject initialize(final ThreadContext context, final IRubyObject self) {
+ final Ruby runtime = context.runtime;
+ self.getInstanceVariables().setInstanceVariable("@tag", runtime.newFixnum(0));
+ self.getInstanceVariables().setInstanceVariable("@value", RubyString.newEmptyString(context.runtime));
+ self.getInstanceVariables().setInstanceVariable("@tag_class", runtime.newSymbol("UNIVERSAL"));
+ return self;
+ }
+
+ static IRubyObject newInstance(final ThreadContext context) {
+ RubyClass klass = _ASN1(context.runtime).getClass("EndOfContent");
+ return klass.newInstance(context, Block.NULL_BLOCK);
+ }
+
+ @Override
+ boolean isImplicitTagging() {
+ IRubyObject tagging = tagging();
+ if ( tagging.isNil() ) return true;
+ return "IMPLICIT".equals( tagging.toString() );
+ }
+
+ @Override
+ byte[] toDER(final ThreadContext context) throws IOException {
+ return toDERInternal(context, false, false, null);
+ }
}
public static class Primitive extends ASN1Data {
@@ -1450,7 +1707,7 @@ public Primitive(Ruby runtime, RubyClass type) {
@Override
@JRubyMethod
public IRubyObject to_der(final ThreadContext context) {
- if ( value(context).isNil() ) {
+ if ( value(context).isNil() && !isNull() ) {
// MRI compatibility but avoids Java exceptions as well e.g.
// Java::JavaLang::NumberFormatException
// java.math.BigInteger.(BigInteger.java:296)
@@ -1489,12 +1746,20 @@ static void initializeImpl(final ThreadContext context,
if ( tag.isNil() ) throw newASN1Error(runtime, "must specify tag number");
- if ( tagging.isNil() ) tagging = runtime.newSymbol("EXPLICIT");
- if ( ! (tagging instanceof RubySymbol) ) {
- throw newASN1Error(runtime, "invalid tag default");
+ if ( tagging.isNil()) {
+ if (tag_class.isNil()) {
+ tag_class = runtime.newSymbol("UNIVERSAL");
+ }
+ } else {
+ if (!(tagging instanceof RubySymbol)) {
+ throw newASN1Error(runtime, "invalid tagging method");
+ }
+
+ if (tag_class.isNil()) {
+ tag_class = runtime.newSymbol("CONTEXT_SPECIFIC");
+ }
}
- if ( tag_class.isNil() ) tag_class = runtime.newSymbol("CONTEXT_SPECIFIC");
if ( ! (tag_class instanceof RubySymbol) ) {
throw newASN1Error(runtime, "invalid tag class");
}
@@ -1510,84 +1775,86 @@ static void initializeImpl(final ThreadContext context,
// NOTE: Primitive only
final String baseName = self.getMetaClass().getRealClass().getBaseName();
- if ( "ObjectId".equals( baseName ) ) {
- final String name;
- try {
- name = oid2Sym( runtime, getObjectID(runtime, value.toString()), true );
- }
- catch (IllegalArgumentException e) {
- // e.g. in case of nil "string not an OID"
- throw runtime.newTypeError(e.getMessage());
- }
- if ( name != null ) value = runtime.newString(name);
+ switch (baseName) {
+ case "ObjectId":
+ final String name;
+ try {
+ name = oid2Sym( runtime, getObjectID(runtime, value.toString()), true );
+ }
+ catch (IllegalArgumentException e) {
+ // e.g. in case of nil "string not an OID"
+ throw runtime.newTypeError(e.getMessage());
+ }
+ if ( name != null ) value = runtime.newString(name);
+ break;
+ case "BitString":
+ self.setInstanceVariable("@unused_bits", runtime.newFixnum(0));
+ break;
}
self.setInstanceVariable("@tag", tag);
self.setInstanceVariable("@value", value);
self.setInstanceVariable("@tag_class", tag_class);
self.setInstanceVariable("@tagging", tagging);
- self.setInstanceVariable("@infinite_length", runtime.getFalse());
+ self.setInstanceVariable("@indefinite_length", runtime.getFalse());
+ }
+
+ boolean isTagged() {
+ return !tagging().isNil();
}
@Override
boolean isExplicitTagging() {
- return "EXPLICIT".equals( getInstanceVariable("@tagging").toString() );
+ return "EXPLICIT".equals( tagging().toString() );
}
@Override
boolean isImplicitTagging() {
- IRubyObject tagging = getInstanceVariable("@tagging");
+ IRubyObject tagging = tagging();
if ( tagging.isNil() ) return true;
return "IMPLICIT".equals( tagging.toString() );
}
@Override
- boolean isEOC() {
- return "EndOfContent".equals( getClassBaseName() );
+ boolean isEOC(final ThreadContext context) {
+ return false;
+ }
+
+ private boolean isNull() {
+ return "Null".equals(getMetaClass().getRealClass().getBaseName());
}
@Override
byte[] toDER(final ThreadContext context) throws IOException {
- if ( isEOC() ) return new byte[] { 0x00, 0x00 };
- return toASN1(context).toASN1Primitive().getEncoded(ASN1Encoding.DER);
- }
+ Class extends ASN1Encodable> type = typeClass( getMetaClass() );
+ final IRubyObject value = value(context);
- static Primitive newInstance(final ThreadContext context, final String type,
- final IRubyObject value) {
- RubyClass klass = _ASN1(context.runtime).getClass(type);
- final Primitive self = new Primitive(context.runtime, klass);
- if ( value != null ) self.setInstanceVariable("@value", value);
- return self;
- }
+ if ( type == null ) {
+ RubyString string;
- static Primitive newEndOfContent(final ThreadContext context) {
- return newInstance(context, "EndOfContent", null);
- }
+ if (value instanceof RubyString) {
+ string = (RubyString) value;
+ } else {
+ string = value.convertToString();
+ }
- /*
- private static final Class DERBooleanClass;
- static {
- Class klass;
- try {
- klass = Class.forName("org.bouncycastle.asn1.DERBoolean");
+ return toDERInternal(context, false, false, string);
}
- catch(ClassNotFoundException e) { klass = null; }
- DERBooleanClass = klass;
- } */
+
+ return toASN1(context).toASN1Primitive().getEncoded(ASN1Encoding.DER);
+ }
@Override
ASN1Encodable toASN1(final ThreadContext context) {
- Class extends ASN1Encodable> type = typeClass( getMetaClass() );
- if ( type == null ) {
- final int tag = getTag(context);
- if ( tag == 0 ) return null; // TODO pass EOC to BC ?
- if ( isExplicitTagging() ) type = typeClass( tag );
- if ( type == null ) {
- throw new IllegalArgumentException(
- "no type for: " + getMetaClass() + " or tag: " + getTag(context)
- );
- }
+ final ASN1Encodable primitive = toASN1Primitive(context);
+ if (isTagged()) {
+ return new DERTaggedObject(isExplicitTagging(), getTagClass(context), getTag(context), primitive);
}
+ return primitive;
+ }
+
+ private ASN1Encodable toASN1Primitive(final ThreadContext context) {
+ Class extends ASN1Encodable> type = typeClass( getMetaClass() );
final IRubyObject val = value(context);
if ( type == ASN1ObjectIdentifier.class ) {
@@ -1621,22 +1888,17 @@ ASN1Encodable toASN1(final ThreadContext context) {
return new DEROctetString( val.asString().getBytes() );
}
if ( type == DERBitString.class ) {
- final byte[] bs = val.asString().getBytes();
- int unused = 0;
- for ( int i = (bs.length - 1); i > -1; i-- ) {
- if (bs[i] == 0) unused += 8;
- else {
- byte v2 = bs[i];
- int x = 8;
- while ( v2 != 0 ) {
- v2 <<= 1;
- x--;
- }
- unused += x;
- break;
- }
+ final byte[] data = val.asString().getBytes();
+ int padBits = 0;
+ IRubyObject unused_bits = getInstanceVariable("@unused_bits");
+ if (unused_bits != null) {
+ padBits = unused_bits.convertToInteger("to_i").getIntValue();
+ }
+ try {
+ return new DERBitString(data, padBits);
+ } catch (IllegalArgumentException e) {
+ throw newASN1Error(context.runtime, e.getMessage());
}
- return new DERBitString(bs, unused);
}
if ( type == DERIA5String.class ) {
return new DERIA5String( val.asString().toString() );
@@ -1645,45 +1907,57 @@ ASN1Encodable toASN1(final ThreadContext context) {
return new DERUTF8String( val.asString().toString() );
}
if ( type == DERBMPString.class ) {
- return new DERBMPString( val.asString().toString() );
+ return new DERBMPString(new String(toBMPChars(val.asString().getByteList())));
}
if ( type == DERUniversalString.class ) {
return new DERUniversalString( val.asString().getBytes() );
}
if ( type == DERGeneralString.class ) {
- return DERGeneralString.getInstance( val.asString().getBytes() );
+ return new DERGeneralString( val.asString().toString() );
}
if ( type == DERVisibleString.class ) {
- return DERVisibleString.getInstance( val.asString().getBytes() );
+ return new DERVisibleString( val.asString().toString() );
}
if ( type == DERNumericString.class ) {
- return DERNumericString.getInstance( val.asString().getBytes() );
+ return new DERNumericString( val.asString().toString() );
+ }
+ if ( type == DERPrintableString.class ) {
+ return new DERPrintableString( val.asString().toString() );
+ }
+ if ( type == DERT61String.class ) {
+ return new DERT61String( val.asString().toString() );
+ }
+ if ( type == DERVideotexString.class ) {
+ return new DERVideotexString( val.asString().getBytes() );
+ }
+ if ( type == DERGraphicString.class ) {
+ return new DERGraphicString( val.asString().getBytes() );
}
- if ( val instanceof RubyString ) {
- try {
- return typeInstance(type, ( (RubyString) val ).getBytes());
- }
- catch (Exception e) { // TODO exception handling
- debugStackTrace(context.runtime, e);
- throw createNativeRaiseException(context, e);
- }
+ if (isDebug(context.runtime)) {
+ debug(this + " toASN1() could not handle class " + getMetaClass() + " and value: " + val.inspect() + " (" + val.getMetaClass() + ")");
}
+ throw new UnsupportedOperationException("OpenSSL::ASN1Data#toASN1 (" + type + ") not implemented"); // should not happen
+ }
- // TODO throw an exception here too?
- if ( isDebug(context.runtime) ) {
- debug(this + " toASN1() could not handle class " + getMetaClass() + " and value " + val.inspect() + " (" + val.getClass().getName() + ")");
+ private static char[] toBMPChars(final ByteList string) {
+ assert string.length() % 2 == 0;
+
+ final int len = string.length() / 2;
+ final char[] chars = new char[len];
+ for (int i = 0; i < len; i++) {
+ int si = i * 2;
+ chars[i] = (char)((string.get(si) << 8) | (string.get(si + 1) & 0xff));
}
- warn(context, "WARNING: unimplemented method called: OpenSSL::ASN1Data#toASN1 (" + type + ")");
- return null;
+ return chars;
}
private static BigInteger bigIntegerValue(final IRubyObject val) {
if ( val instanceof RubyInteger ) { // RubyBignum
return ((RubyInteger) val).getBigIntegerValue();
}
- if ( val instanceof BN ) ((BN) val).getValue();
+ if ( val instanceof BN ) return ((BN) val).getValue();
return new BigInteger( val.asString().getBytes() );
}
@@ -1717,33 +1991,15 @@ public IRubyObject initialize(final ThreadContext context, final IRubyObject[] a
return this;
}
- static Constructive newInfiniteConstructive(final ThreadContext context,
- final String type, final List value, final int defaultTag) {
+ static Constructive newInfiniteLength(final ThreadContext context,
+ final IRubyObject value, final int defaultTag) {
final Ruby runtime = context.runtime;
- final RubyClass klass = _ASN1(context.runtime).getClass(type);
+ final RubyClass klass = _ASN1(context.runtime).getClass("Constructive");
final Constructive self = new Constructive(runtime, klass);
- final RubyArray values = runtime.newArray(value.size());
- for ( final IRubyObject val : value ) values.append(val);
- // values.append( Primitive.newEndOfContent(context) );
-
- self.setInstanceVariable("@tag", runtime.newFixnum(defaultTag));
- self.setInstanceVariable("@value", values);
- self.setInstanceVariable("@tag_class", runtime.newSymbol("UNIVERSAL"));
- self.setInstanceVariable("@tagging", context.nil);
-
- return setInfiniteLength(context, self);
- }
-
- static Constructive setInfiniteLength(final ThreadContext context, final IRubyObject constructive) {
- final Constructive instance = ((Constructive) constructive);
- final IRubyObject eoc = Primitive.newEndOfContent(context);
- final IRubyObject value = instance.value(context);
- if ( value instanceof RubyArray ) ((RubyArray) value).append(eoc);
- else value.callMethod(context, "<<", eoc);
- instance.setInstanceVariable("@infinite_length", context.runtime.getTrue());
- return instance;
+ ASN1Data.newInfiniteLengthImpl(context, self, value, defaultTag, runtime.newSymbol("UNIVERSAL"));
+ return self;
}
private boolean rawConstructive() {
@@ -1758,20 +2014,18 @@ private boolean isSet() {
return "Set".equals( getClassBaseName() );
}
- private boolean isInfiniteLength() {
- return getInstanceVariable("@infinite_length").isTrue();
+ private boolean isTagged() {
+ return !tagging().isNil();
}
@Override
boolean isExplicitTagging() {
- IRubyObject tagging = getInstanceVariable("@tagging");
- if ( tagging.isNil() ) return true;
- return "EXPLICIT".equals( tagging.toString() );
+ return "EXPLICIT".equals( tagging().toString() ); // nil.toString() == ""
}
@Override
boolean isImplicitTagging() {
- return "IMPLICIT".equals( getInstanceVariable("@tagging").toString() );
+ return "IMPLICIT".equals( tagging().toString() );
}
@Override
@@ -1805,38 +2059,39 @@ ASN1Encodable toASN1(final ThreadContext context) {
@Override
@JRubyMethod
public IRubyObject to_der(final ThreadContext context) {
- if ( rawConstructive() ) { // MRI compatibility
- if ( ! isInfiniteLength() && ! super.value(context).isNil() ) {
- final Ruby runtime = context.runtime;
- throw newASN1Error(runtime, "Constructive shall only be used"
- + " with infinite length");
- }
- }
return super.to_der(context);
}
@Override
byte[] toDER(final ThreadContext context) throws IOException {
- if ( isInfiniteLength() ) {
- if ( isSequence() ) {
+ final int tagNo = getTag(context);
+ final boolean isIndefiniteLength = isInfiniteLength();
+
+ if ( isIndefiniteLength ) {
+ if ( isSequence() || tagNo == SEQUENCE ) {
return sequenceToDER(context);
}
- else if ( isSet() ) {
+ if ( isSet() || tagNo == SET) {
return setToDER(context);
}
- else { // "raw" Constructive
- switch ( getTag(context) ) {
- case OCTET_STRING:
- return octetStringToDER(context);
- case BIT_STRING:
- return bitStringToDER(context);
- case SEQUENCE:
- return sequenceToDER(context);
- case SET:
- return setToDER(context);
- }
- throw new UnsupportedOperationException( this.inspect().toString() );
+ // "raw" Constructive
+ switch ( getTag(context) ) {
+ case OCTET_STRING:
+ return octetStringToDER(context);
+ case BIT_STRING:
+ return bitStringToDER(context);
}
+ return toDERInternal(context, true, isInfiniteLength(), value(context));
+ }
+
+ if (isEOC(context)) {
+ return toDERInternal(context, true, isIndefiniteLength, null);
+ }
+
+ Class extends ASN1Encodable> type = typeClass( getMetaClass() );
+
+ if ( type == null ) {
+ return toDERInternal(context, true, isIndefiniteLength, valueAsArray(context));
}
return super.toDER(context);
@@ -1888,21 +2143,23 @@ private byte[] setToDER(final ThreadContext context) throws IOException {
private ASN1EncodableVector toASN1EncodableVector(final ThreadContext context) {
final ASN1EncodableVector vec = new ASN1EncodableVector();
final IRubyObject value = value(context);
- if ( value instanceof RubyArray ) {
- final RubyArray val = (RubyArray) value;
- for ( int i = 0; i < val.size(); i++ ) {
- if ( addEntry(context, vec, val.entry(i)) ) break;
- }
+ final RubyArray val = valueAsArray(context);
+ for ( int i = 0; i < val.size(); i++ ) {
+ if ( addEntry(context, vec, val.entry(i)) ) break;
}
- else {
- final int size = RubyInteger.num2int(value.callMethod(context, "size"));
- for ( int i = 0; i < size; i++ ) {
- final RubyInteger idx = context.runtime.newFixnum(i);
- IRubyObject entry = value.callMethod(context, "[]", idx);
- if ( addEntry(context, vec, entry) ) break;
+ return vec;
+ }
+
+ private RubyArray valueAsArray(final ThreadContext context) {
+ final IRubyObject value = value(context);
+ if (value instanceof RubyArray ) {
+ return (RubyArray) value;
+ } else {
+ if (!value.respondsTo("to_a")) {
+ throw context.runtime.newTypeError("can't convert " + value.getMetaClass().getName() + " into Array");
}
+ return (RubyArray) value.callMethod(context, "to_a");
}
- return vec;
}
public ASN1Primitive toASN1Primitive() {
@@ -1922,8 +2179,7 @@ public ASN1Primitive toASN1Primitive() {
}
- private static boolean addEntry(final ThreadContext context,
- final ASN1EncodableVector vec, final IRubyObject entry) {
+ private static boolean addEntry(final ThreadContext context, final ASN1EncodableVector vec, final IRubyObject entry) {
try {
if ( entry instanceof Constructive ) {
final Constructive constructive = (Constructive) entry;
@@ -1936,7 +2192,7 @@ private static boolean addEntry(final ThreadContext context,
}
else if ( entry instanceof ASN1Data ) {
final ASN1Data data = ( (ASN1Data) entry );
- if ( data.isEOC() ) return true;
+ if ( data.isEOC(context) ) return true;
vec.add( data.toASN1(context) );
}
else {
diff --git a/src/main/java/org/jruby/ext/openssl/BN.java b/src/main/java/org/jruby/ext/openssl/BN.java
index 375f5f0c..1e61d463 100644
--- a/src/main/java/org/jruby/ext/openssl/BN.java
+++ b/src/main/java/org/jruby/ext/openssl/BN.java
@@ -46,7 +46,6 @@
import org.jruby.anno.JRubyMethod;
import org.jruby.exceptions.RaiseException;
import org.jruby.runtime.Arity;
-import org.jruby.runtime.ObjectAllocator;
import org.jruby.runtime.ThreadContext;
import org.jruby.runtime.builtin.IRubyObject;
import org.jruby.util.ByteList;
@@ -77,10 +76,6 @@ public class BN extends RubyObject {
private static final int DEFAULT_CERTAINTY = 100;
- private static final ObjectAllocator BN_ALLOCATOR = new ObjectAllocator() {
- public BN allocate(Ruby runtime, RubyClass klass) { return new BN(runtime, klass); }
- };
-
public static BN newBN(Ruby runtime, BigInteger value) {
return newInstance(runtime, value);
}
@@ -92,7 +87,7 @@ static BN newInstance(final Ruby runtime, BigInteger value) {
static void createBN(final Ruby runtime, final RubyModule OpenSSL, final RubyClass OpenSSLError) {
OpenSSL.defineClassUnder("BNError", OpenSSLError, OpenSSLError.getAllocator());
- RubyClass BN = OpenSSL.defineClassUnder("BN", runtime.getObject(), BN_ALLOCATOR);
+ RubyClass BN = OpenSSL.defineClassUnder("BN", runtime.getObject(), (r, klass) -> new BN(r, klass));
BN.includeModule( runtime.getModule("Comparable") );
BN.defineAnnotatedMethods(BN.class);
}
@@ -176,6 +171,7 @@ private static BigInteger initBigIntegerBase(final Ruby runtime, RubyString str,
}
}
+ @JRubyMethod(visibility = Visibility.PRIVATE)
@Override
public IRubyObject initialize_copy(final IRubyObject that) {
super.initialize_copy(that);
@@ -297,8 +293,8 @@ public RubyInteger hash(final ThreadContext context) {
return context.runtime.newFixnum(hashCode());
}
- @JRubyMethod(name = "to_i")
- public RubyInteger to_i() {
+ @JRubyMethod(name = "to_int", alias = "to_i")
+ public RubyInteger to_int() {
if ( value.compareTo( MAX_LONG ) > 0 || value.compareTo( MIN_LONG ) < 0 ) {
return RubyBignum.newBignum(getRuntime(), value);
}
@@ -311,7 +307,6 @@ public BN to_bn() {
}
@JRubyMethod(name="coerce")
- // FIXME: is this right? don't see how it would be useful...
public IRubyObject coerce(IRubyObject other) {
final Ruby runtime = getRuntime();
IRubyObject self;
@@ -319,7 +314,7 @@ public IRubyObject coerce(IRubyObject other) {
self = runtime.newString(value.toString());
}
else if ( other instanceof RubyInteger ) {
- self = to_i();
+ self = to_int();
}
else if ( other instanceof BN ) {
self = this;
@@ -330,6 +325,26 @@ else if ( other instanceof BN ) {
return runtime.newArray(other, self);
}
+ @JRubyMethod(name="-@")
+ public IRubyObject op_uminus(final ThreadContext context) {
+ return newBN(context.runtime, value.negate());
+ }
+
+ @JRubyMethod(name="+@")
+ public IRubyObject op_uplus(final ThreadContext context) {
+ return newBN(context.runtime, value);
+ }
+
+ @JRubyMethod
+ public IRubyObject abs(final ThreadContext context) {
+ return newBN(context.runtime, value.abs());
+ }
+
+ @JRubyMethod(name="negative?")
+ public IRubyObject negative_p(final ThreadContext context) {
+ return context.runtime.newBoolean(value.signum() == -1);
+ }
+
@JRubyMethod(name="zero?")
public RubyBoolean zero_p(final ThreadContext context) {
return context.runtime.newBoolean( value.equals(BigInteger.ZERO) );
@@ -909,7 +924,7 @@ public static BigInteger getBigInteger(final IRubyObject arg) {
@Override
public Object toJava(Class target) {
- if ( target.isAssignableFrom(BigInteger.class) || target == Number.class ) return value;
+ if ( target.isAssignableFrom(BigInteger.class) ) return value;
if ( target == Long.class || target == Long.TYPE ) return value.longValue();
if ( target == Integer.class || target == Integer.TYPE ) return value.intValue();
if ( target == Double.class || target == Double.TYPE ) return value.doubleValue();
diff --git a/src/main/java/org/jruby/ext/openssl/Cipher.java b/src/main/java/org/jruby/ext/openssl/Cipher.java
index 92239715..af911304 100644
--- a/src/main/java/org/jruby/ext/openssl/Cipher.java
+++ b/src/main/java/org/jruby/ext/openssl/Cipher.java
@@ -42,9 +42,6 @@
import java.util.HashMap;
import java.util.LinkedHashMap;
import java.util.List;
-import java.util.Map;
-import static javax.crypto.Cipher.DECRYPT_MODE;
-import static javax.crypto.Cipher.ENCRYPT_MODE;
import javax.crypto.NoSuchPaddingException;
import javax.crypto.spec.GCMParameterSpec;
import javax.crypto.spec.IvParameterSpec;
@@ -70,6 +67,8 @@
import org.jruby.util.ByteList;
import org.jruby.util.TypeConverter;
+import static javax.crypto.Cipher.DECRYPT_MODE;
+import static javax.crypto.Cipher.ENCRYPT_MODE;
import static org.jruby.ext.openssl.OpenSSL.*;
/**
@@ -78,12 +77,8 @@
public class Cipher extends RubyObject {
private static final long serialVersionUID = -5390983669951165103L;
- private static final ObjectAllocator ALLOCATOR = new ObjectAllocator() {
- public Cipher allocate(Ruby runtime, RubyClass klass) { return new Cipher(runtime, klass); }
- };
-
static void createCipher(final Ruby runtime, final RubyModule OpenSSL, final RubyClass OpenSSLError) {
- final RubyClass Cipher = OpenSSL.defineClassUnder("Cipher", runtime.getObject(), ALLOCATOR);
+ final RubyClass Cipher = OpenSSL.defineClassUnder("Cipher", runtime.getObject(), (r, klass) -> new Cipher(r, klass));
Cipher.defineClassUnder("CipherError", OpenSSLError, OpenSSLError.getAllocator());
Cipher.defineAnnotatedMethods(Cipher.class);
@@ -142,7 +137,7 @@ static RubyClass _Cipher(final Ruby runtime) {
public static IRubyObject ciphers(final ThreadContext context, final IRubyObject self) {
final Ruby runtime = context.runtime;
- final Collection ciphers = Algorithm.allSupportedCiphers().keySet();
+ final Collection ciphers = Algorithm.AllSupportedCiphers.CIPHERS_MAP.keySet();
final RubyArray result = runtime.newArray( ciphers.size() * 2 );
for ( final String cipher : ciphers ) {
result.append( runtime.newString(cipher) ); // upper-case
@@ -156,7 +151,7 @@ public static IRubyObject ciphers(final ThreadContext context, final IRubyObject
public static boolean isSupportedCipher(final String name) {
final String osslName = name.toUpperCase();
//
- if ( Algorithm.allSupportedCiphers().get( osslName ) != null ) {
+ if ( Algorithm.AllSupportedCiphers.CIPHERS_MAP.get( osslName ) != null ) {
return true;
}
//
@@ -273,125 +268,115 @@ public static final class Algorithm {
NO_PADDING_BLOCK_MODES.add("GCM");
}
- // Ruby to Java name String (or FALSE)
- static final HashMap supportedCiphers = new LinkedHashMap(120, 1);
- // we're _marking_ unsupported keys with a Boolean.FALSE mapping
- // all cipher mappings are resolved on `OpenSSL::Cipher.ciphers`
- // ... probably a slightly _rare_ case to be going for up front
- static boolean supportedCiphersAll;
-
- static Map allSupportedCiphers() {
- if ( supportedCiphersAll ) return supportedCiphers;
- synchronized ( supportedCiphers ) {
- if ( supportedCiphersAll ) return supportedCiphers;
+ final static class AllSupportedCiphers {
+ // Ruby to Java name String
+ static final HashMap CIPHERS_MAP = new LinkedHashMap(120, 1);
+ static {
// cleanup all FALSE keys :
//for ( String key: supportedCiphers.keySet() ) {
- //if ( supportedCiphers.get(key) == Boolean.FALSE ) supportedCiphers.remove(key);
+ //if ( supportedCiphers.get(key) == Boolean.FALSE ) supportedCiphers.remove(key);
//}
// OpenSSL: all the block ciphers normally use PKCS#5 padding
String[] modes;
modes = cipherModes("AES"); // null if not supported
- if ( modes != null ) {
- for ( final String mode : modes ) {
+ if (modes != null) {
+ for (final String mode : modes) {
final String realName = "AES/" + mode; // + "/PKCS5Padding"
- supportedCiphers.put( "AES-128-" + mode, new String[] { "AES", mode, "128", realName } );
- supportedCiphers.put( "AES-192-" + mode, new String[] { "AES", mode, "192", realName } );
- supportedCiphers.put( "AES-256-" + mode, new String[] { "AES", mode, "256", realName } );
+ CIPHERS_MAP.put("AES-128-" + mode, new String[]{"AES", mode, "128", realName});
+ CIPHERS_MAP.put("AES-192-" + mode, new String[]{"AES", mode, "192", realName});
+ CIPHERS_MAP.put("AES-256-" + mode, new String[]{"AES", mode, "256", realName});
}
final String realName = "AES/CBC";
- supportedCiphers.put( "AES128", new String[] { "AES", "CBC", "128", realName } );
- supportedCiphers.put( "AES192", new String[] { "AES", "CBC", "192", realName } );
- supportedCiphers.put( "AES256", new String[] { "AES", "CBC", "256", realName } );
+ CIPHERS_MAP.put("AES128", new String[]{"AES", "CBC", "128", realName});
+ CIPHERS_MAP.put("AES192", new String[]{"AES", "CBC", "192", realName});
+ CIPHERS_MAP.put("AES256", new String[]{"AES", "CBC", "256", realName});
}
modes = cipherModes("Blowfish");
- if ( modes != null ) {
- supportedCiphers.put( "BF", new String[] { "BF", "CBC", null, "Blowfish/CBC" });
- for ( final String mode : modes ) {
- supportedCiphers.put( "BF-" + mode, new String[] { "BF", mode, null, "Blowfish/" + mode } );
+ if (modes != null) {
+ CIPHERS_MAP.put("BF", new String[]{"BF", "CBC", null, "Blowfish/CBC"});
+ for (final String mode : modes) {
+ CIPHERS_MAP.put("BF-" + mode, new String[]{"BF", mode, null, "Blowfish/" + mode});
}
}
modes = cipherModes("Camellia");
- if ( modes != null ) {
- for ( final String mode : modes ) {
+ if (modes != null) {
+ for (final String mode : modes) {
final String realName = "Camellia/" + mode;
- supportedCiphers.put( "CAMELLIA-128-" + mode, new String[] { "CAMELLIA", mode, "128", realName } );
- supportedCiphers.put( "CAMELLIA-192-" + mode, new String[] { "CAMELLIA", mode, "192", realName } );
- supportedCiphers.put( "CAMELLIA-256-" + mode, new String[] { "CAMELLIA", mode, "256", realName } );
+ CIPHERS_MAP.put("CAMELLIA-128-" + mode, new String[]{"CAMELLIA", mode, "128", realName});
+ CIPHERS_MAP.put("CAMELLIA-192-" + mode, new String[]{"CAMELLIA", mode, "192", realName});
+ CIPHERS_MAP.put("CAMELLIA-256-" + mode, new String[]{"CAMELLIA", mode, "256", realName});
}
final String realName = "Camellia/CBC";
- supportedCiphers.put( "CAMELLIA128", new String[] { "CAMELLIA", "CBC", "128", realName } );
- supportedCiphers.put( "CAMELLIA192", new String[] { "CAMELLIA", "CBC", "192", realName } );
- supportedCiphers.put( "CAMELLIA256", new String[] { "CAMELLIA", "CBC", "256", realName } );
+ CIPHERS_MAP.put("CAMELLIA128", new String[]{"CAMELLIA", "CBC", "128", realName});
+ CIPHERS_MAP.put("CAMELLIA192", new String[]{"CAMELLIA", "CBC", "192", realName});
+ CIPHERS_MAP.put("CAMELLIA256", new String[]{"CAMELLIA", "CBC", "256", realName});
}
modes = cipherModes("CAST5");
- if ( modes != null ) {
- supportedCiphers.put( "CAST", new String[] { "CAST", "CBC", null, "CAST5/CBC" } );
- supportedCiphers.put( "CAST-CBC", supportedCiphers.get("CAST") );
- for ( final String mode : modes ) {
- supportedCiphers.put( "CAST5-" + mode, new String[] { "CAST", mode, null, "CAST5/" + mode });
+ if (modes != null) {
+ CIPHERS_MAP.put("CAST", new String[]{"CAST", "CBC", null, "CAST5/CBC"});
+ CIPHERS_MAP.put("CAST-CBC", CIPHERS_MAP.get("CAST"));
+ for (final String mode : modes) {
+ CIPHERS_MAP.put("CAST5-" + mode, new String[]{"CAST", mode, null, "CAST5/" + mode});
}
}
modes = cipherModes("CAST6");
- if ( modes != null ) {
- for ( final String mode : modes ) {
- supportedCiphers.put( "CAST6-" + mode, new String[] { "CAST6", mode, null, "CAST6/" + mode });
+ if (modes != null) {
+ for (final String mode : modes) {
+ CIPHERS_MAP.put("CAST6-" + mode, new String[]{"CAST6", mode, null, "CAST6/" + mode});
}
}
modes = cipherModes("DES");
- if ( modes != null ) {
- supportedCiphers.put( "DES", new String[] { "DES", "CBC", null, "DES/CBC" } );
- for ( final String mode : modes ) {
- supportedCiphers.put( "DES-" + mode, new String[] { "DES", mode, null, "DES/" + mode });
+ if (modes != null) {
+ CIPHERS_MAP.put("DES", new String[]{"DES", "CBC", null, "DES/CBC"});
+ for (final String mode : modes) {
+ CIPHERS_MAP.put("DES-" + mode, new String[]{"DES", mode, null, "DES/" + mode});
}
}
modes = cipherModes("DESede");
- if ( modes != null ) {
- supportedCiphers.put( "DES-EDE", new String[] { "DES", "ECB", "EDE", "DESede/ECB" } );
- supportedCiphers.put( "DES-EDE-CBC", new String[] { "DES", "CBC", "EDE", "DESede/CBC" } );
- supportedCiphers.put( "DES-EDE-CFB", new String[] { "DES", "CBC", "EDE", "DESede/CFB" } );
- supportedCiphers.put( "DES-EDE-OFB", new String[] { "DES", "CBC", "EDE", "DESede/OFB" } );
- supportedCiphers.put( "DES-EDE3", new String[] { "DES", "ECB", "EDE3", "DESede/ECB" });
- for ( final String mode : modes ) {
- supportedCiphers.put( "DES-EDE3-" + mode, new String[] { "DES", mode, "EDE3", "DESede/" + mode });
+ if (modes != null) {
+ CIPHERS_MAP.put("DES-EDE", new String[]{"DES", "ECB", "EDE", "DESede/ECB"});
+ CIPHERS_MAP.put("DES-EDE-CBC", new String[]{"DES", "CBC", "EDE", "DESede/CBC"});
+ CIPHERS_MAP.put("DES-EDE-CFB", new String[]{"DES", "CBC", "EDE", "DESede/CFB"});
+ CIPHERS_MAP.put("DES-EDE-OFB", new String[]{"DES", "CBC", "EDE", "DESede/OFB"});
+ CIPHERS_MAP.put("DES-EDE3", new String[]{"DES", "ECB", "EDE3", "DESede/ECB"});
+ for (final String mode : modes) {
+ CIPHERS_MAP.put("DES-EDE3-" + mode, new String[]{"DES", mode, "EDE3", "DESede/" + mode});
}
- supportedCiphers.put( "DES3", new String[] { "DES", "CBC", "EDE3", "DESede/CBC" } );
+ CIPHERS_MAP.put("DES3", new String[]{"DES", "CBC", "EDE3", "DESede/CBC"});
}
modes = cipherModes("RC2");
- if ( modes != null ) {
- supportedCiphers.put( "RC2", new String[] { "RC2", "CBC", null, "RC2/CBC" } );
- for ( final String mode : modes ) {
- supportedCiphers.put( "RC2-" + mode, new String[] { "RC2", mode, null, "RC2/" + mode } );
+ if (modes != null) {
+ CIPHERS_MAP.put("RC2", new String[]{"RC2", "CBC", null, "RC2/CBC"});
+ for (final String mode : modes) {
+ CIPHERS_MAP.put("RC2-" + mode, new String[]{"RC2", mode, null, "RC2/" + mode});
}
- supportedCiphers.put( "RC2-40-CBC", new String[] { "RC2", "CBC", "40", "RC2/CBC" } );
- supportedCiphers.put( "RC2-64-CBC", new String[] { "RC2", "CBC", "64", "RC2/CBC" } );
+ CIPHERS_MAP.put("RC2-40-CBC", new String[]{"RC2", "CBC", "40", "RC2/CBC"});
+ CIPHERS_MAP.put("RC2-64-CBC", new String[]{"RC2", "CBC", "64", "RC2/CBC"});
}
modes = cipherModes("RC4"); // NOTE: stream cipher (BC supported)
- if ( modes != null ) {
- supportedCiphers.put( "RC4", new String[] { "RC4", null, null, "RC4" } );
- supportedCiphers.put( "RC4-40", new String[] { "RC4", null, "40", "RC4" } );
+ if (modes != null) {
+ CIPHERS_MAP.put("RC4", new String[]{"RC4", null, null, "RC4"});
+ CIPHERS_MAP.put("RC4-40", new String[]{"RC4", null, "40", "RC4"});
//supportedCiphers.put( "RC2-HMAC-MD5", new String[] { "RC4", null, null, "RC4" });
}
modes = cipherModes("SEED");
- if ( modes != null ) {
- supportedCiphers.put( "SEED", new String[] { "SEED", "CBC", null, "SEED/CBC" } );
- for ( final String mode : modes ) {
- supportedCiphers.put( "SEED-" + mode, new String[] { "SEED", mode, null, "SEED/" + mode });
+ if (modes != null) {
+ CIPHERS_MAP.put("SEED", new String[]{"SEED", "CBC", null, "SEED/CBC"});
+ for (final String mode : modes) {
+ CIPHERS_MAP.put("SEED-" + mode, new String[]{"SEED", mode, null, "SEED/" + mode});
}
}
-
- supportedCiphersAll = true;
- return supportedCiphers;
}
}
@@ -436,7 +421,7 @@ static Algorithm osslToJava(final String osslName) {
private static Algorithm osslToJava(final String osslName, final String padding) {
- final String[] algVals = supportedCiphers.get(osslName);
+ final String[] algVals = AllSupportedCiphers.CIPHERS_MAP.get(osslName);
if ( algVals != null ) {
final String cryptoMode = algVals[1];
Algorithm alg = new Algorithm(algVals[0], algVals[2], cryptoMode);
@@ -730,7 +715,6 @@ public Cipher(Ruby runtime, RubyClass type) {
private int generateKeyLength = -1;
private int ivLength = -1;
private boolean encryptMode = true;
- //private IRubyObject[] modeParams;
private boolean cipherInited = false;
private byte[] key;
private byte[] realIV;
@@ -825,6 +809,12 @@ public final RubyInteger iv_len() {
return getRuntime().newFixnum(ivLength);
}
+ @JRubyMethod(name = "iv_len=", required = 1)
+ public final IRubyObject set_iv_len(IRubyObject len) {
+ this.ivLength = RubyNumeric.fix2int(len);
+ return len;
+ }
+
@JRubyMethod(name = "key_len=", required = 1)
public final IRubyObject set_key_len(IRubyObject len) {
this.keyLength = RubyNumeric.fix2int(len);
@@ -1274,11 +1264,24 @@ public IRubyObject set_padding(IRubyObject padding) {
}
private transient ByteList auth_tag;
+ private int auth_tag_len = 16;
@JRubyMethod(name = "auth_tag")
public IRubyObject auth_tag(final ThreadContext context) {
+ return getAuthTag(context, auth_tag_len);
+ }
+
+ @JRubyMethod(name = "auth_tag")
+ public IRubyObject auth_tag(final ThreadContext context, IRubyObject tag_len) {
+ return getAuthTag(context, tag_len.convertToInteger().getIntValue());
+ }
+
+ private IRubyObject getAuthTag(final ThreadContext context, final int tag_len) {
if ( auth_tag != null ) {
- return RubyString.newString(context.runtime, auth_tag);
+ if (auth_tag.length() <= tag_len) {
+ return RubyString.newString(context.runtime, auth_tag);
+ }
+ return RubyString.newString(context.runtime, (ByteList) auth_tag.subSequence(0, tag_len));
}
if ( ! isAuthDataMode() ) {
throw newCipherError(context.runtime, "authentication tag not supported by this cipher");
@@ -1296,14 +1299,18 @@ public IRubyObject set_auth_tag(final ThreadContext context, final IRubyObject t
return auth_tag;
}
+ @JRubyMethod(name = "auth_tag_len=")
+ public IRubyObject set_auth_tag_len(IRubyObject tag_len) {
+ this.auth_tag_len = tag_len.convertToInteger().getIntValue();
+ return tag_len;
+ }
+
private boolean isAuthDataMode() { // Authenticated Encryption with Associated Data (AEAD)
return "GCM".equalsIgnoreCase(cryptoMode) || "CCM".equalsIgnoreCase(cryptoMode);
}
- private static final int MAX_AUTH_TAG_LENGTH = 16;
-
private int getAuthTagLength() {
- return Math.min(MAX_AUTH_TAG_LENGTH, this.key.length); // in bytes
+ return Math.min(auth_tag_len, this.key.length); // in bytes
}
private transient ByteList auth_data;
@@ -1355,22 +1362,10 @@ public IRubyObject random_iv(final ThreadContext context) {
this.set_iv(context, str); return str;
}
- //String getAlgorithm() {
- // return this.cipher.getAlgorithm();
- //}
-
final String getName() {
return this.name;
}
- //String getCryptoBase() {
- // return this.cryptoBase;
- //}
-
- //String getCryptoMode() {
- // return this.cryptoMode;
- //}
-
final int getKeyLength() {
return keyLength;
}
diff --git a/src/main/java/org/jruby/ext/openssl/CipherStrings.java b/src/main/java/org/jruby/ext/openssl/CipherStrings.java
index c626fd9b..160d61b6 100644
--- a/src/main/java/org/jruby/ext/openssl/CipherStrings.java
+++ b/src/main/java/org/jruby/ext/openssl/CipherStrings.java
@@ -38,6 +38,11 @@
import java.util.Map;
import java.util.Set;
+import static org.jruby.ext.openssl.SSL.SSL3_VERSION;
+import static org.jruby.ext.openssl.SSL.TLS1_VERSION;
+import static org.jruby.ext.openssl.SSL.TLS1_1_VERSION;
+import static org.jruby.ext.openssl.SSL.TLS1_2_VERSION;
+
/**
*
* @author Ola Bini
@@ -149,49 +154,108 @@ public class CipherStrings {
public final static String SSL_TXT_kRSA = "kRSA";
public final static String SSL_TXT_kDHr = "kDHr";
public final static String SSL_TXT_kDHd = "kDHd";
- public final static String SSL_TXT_kEDH = "kEDH";
+ public final static String SSL_TXT_kEDH = "kEDH"; /* alias for kDHE */
+ public final static String SSL_TXT_kDHE = "kDHE";
+ public final static String SSL_TXT_kEECDH = "kEECDH"; /* alias for kECDHE */
+ public final static String SSL_TXT_kECDHE = "kECDHE";
+
public final static String SSL_TXT_aRSA = "aRSA";
public final static String SSL_TXT_aDSS = "aDSS";
public final static String SSL_TXT_aDH = "aDH";
+ public final static String SSL_TXT_aECDSA = "aECDSA";
+
public final static String SSL_TXT_DSS = "DSS";
public final static String SSL_TXT_DH = "DH";
- public final static String SSL_TXT_EDH = "EDH";
+ public final static String SSL_TXT_DHE = "DHE"; /* same as "kDHE:-ADH" */;
+ public final static String SSL_TXT_EDH = "EDH"; /* alias for DHE */
public final static String SSL_TXT_ADH = "ADH";
public final static String SSL_TXT_RSA = "RSA";
+ public final static String SSL_TXT_ECDH = "ECDH";
+ public final static String SSL_TXT_EECDH = "EECDH"; /* alias for ECDHE" */
+ public final static String SSL_TXT_ECDHE = "ECDHE"; /* same as "kECDHE:-AECDH" */
+ public final static String SSL_TXT_AECDH = "AECDH";
+ public final static String SSL_TXT_ECDSA = "ECDSA";
+ //public final static String SSL_TXT_PSK = "PSK";
+ //public final static String SSL_TXT_SRP = "SRP";
+
public final static String SSL_TXT_DES = "DES";
public final static String SSL_TXT_3DES = "3DES";
public final static String SSL_TXT_RC4 = "RC4";
public final static String SSL_TXT_RC2 = "RC2";
public final static String SSL_TXT_IDEA = "IDEA";
+ public final static String SSL_TXT_SEED = "SEED";
+ public final static String SSL_TXT_AES128 = "AES128";
+ public final static String SSL_TXT_AES256 = "AES256";
public final static String SSL_TXT_AES = "AES";
+ public final static String SSL_TXT_AES_GCM = "AESGCM";
+ public final static String SSL_TXT_AES_CCM = "AESCCM";
+ public final static String SSL_TXT_AES_CCM_8 = "AESCCM8";
+ public final static String SSL_TXT_CAMELLIA128 = "CAMELLIA128";
+ public final static String SSL_TXT_CAMELLIA256 = "CAMELLIA256";
+ public final static String SSL_TXT_CAMELLIA = "CAMELLIA";
+ public final static String SSL_TXT_CHACHA20 = "CHACHA20";
+ public final static String SSL_TXT_GOST = "GOST89";
+ public final static String SSL_TXT_ARIA = "ARIA";
+ public final static String SSL_TXT_ARIA_GCM = "ARIAGCM";
+ public final static String SSL_TXT_ARIA128 = "ARIA128";
+ public final static String SSL_TXT_ARIA256 = "ARIA256";
+
public final static String SSL_TXT_MD5 = "MD5";
public final static String SSL_TXT_SHA1 = "SHA1";
- public final static String SSL_TXT_SHA = "SHA";
+ public final static String SSL_TXT_SHA = "SHA";/* same as "SHA1" */
+ public final static String SSL_TXT_SHA256 = "SHA256";
+ public final static String SSL_TXT_SHA384 = "SHA384";
+
public final static String SSL_TXT_EXP = "EXP";
public final static String SSL_TXT_EXPORT = "EXPORT";
public final static String SSL_TXT_EXP40 = "EXPORT40";
public final static String SSL_TXT_EXP56 = "EXPORT56";
+
public final static String SSL_TXT_SSLV2 = "SSLv2";
public final static String SSL_TXT_SSLV3 = "SSLv3";
public final static String SSL_TXT_TLSV1 = "TLSv1";
+ public final static String SSL_TXT_TLSV1_1 = "TLSv1.1";
+ public final static String SSL_TXT_TLSV1_2 = "TLSv1.2";
+
public final static String SSL_TXT_ALL = "ALL";
+
public final static String SSL_TXT_ECC = "ECCdraft";
public final static String SSL_TXT_CMPALL = "COMPLEMENTOFALL";
public final static String SSL_TXT_CMPDEF = "COMPLEMENTOFDEFAULT";
- // "ALL:!aNULL:!eNULL:!SSLv2" is for OpenSSL 1.0.0 GA
- public final static String SSL_DEFAULT_CIPHER_LIST = "AES:ALL:!aNULL:!eNULL:+RC4:@STRENGTH";
+ // NOTE: Default list of TLSv1.2 (and earlier) ciphers deprecated in 3.0.0
+ // "ALL:!EXPORT:!LOW:!aNULL:!eNULL:!SSLv2" in OpenSSL 1.0.2
+ public final static String SSL_DEFAULT_CIPHER_LIST; // = "AES:ALL:!aNULL:!eNULL:!SSLv2:+RC4:@STRENGTH";
+ /*
+ * The following cipher list is used by default. It also is substituted when
+ * an application-defined cipher list string starts with 'DEFAULT'.
+ * This applies to ciphersuites for TLSv1.2 and below.
+ */
+ //public final static String SSL_DEFAULT_CIPHER_LIST = "ALL:!COMPLEMENTOFDEFAULT:!eNULL"; // OpenSSL 1.1.1
+ /* This is the default set of TLSv1.3 ciphersuites */
+ public final static String TLS_DEFAULT_CIPHERSUITES = "TLS_AES_256_GCM_SHA384:" +
+ "TLS_CHACHA20_POLY1305_SHA256:" +
+ "TLS_AES_128_GCM_SHA256";
+ static { // TODO REVIEW 1.3 cipher matching
+ SSL_DEFAULT_CIPHER_LIST = "ALL:!EXPORT:!LOW:!aNULL:!eNULL:!SSLv2"; // + ':' + TLS_DEFAULT_CIPHERSUITES;
+ }
public final static long SSL_MKEY_MASK = 0x000000FFL;
+ /* Bits for algorithm_mkey (key exchange algorithm) */
+ /* RSA key exchange */
public final static long SSL_kRSA = 0x00000001L;
public final static long SSL_kDHr = 0x00000002L;
public final static long SSL_kDHd = 0x00000004L;
public final static long SSL_kFZA = 0x00000008L;
- public final static long SSL_kEDH = 0x00000010L;
+ /* tmp DH key no DH cert */
+ public final static long SSL_kDHE = 0x00000010L;
+ public final static long SSL_kEDH = SSL_kDHE; /* synonym */
public final static long SSL_kKRB5 = 0x00000020L;
public final static long SSL_kECDH = 0x00000040L;
+ /* ephemeral ECDH */
public final static long SSL_kECDHE = 0x00000080L;
+ public final static long SSL_kEECDH = SSL_kECDHE; /* synonym */
public final static long SSL_aNULL = 0x00000800L;
public final static long SSL_AUTH_MASK = 0x00007F00L;
public final static long SSL_EDH = (SSL_kEDH|(SSL_AUTH_MASK^SSL_aNULL));
@@ -202,9 +266,7 @@ public class CipherStrings {
public final static long SSL_aDH = 0x00001000L;
public final static long SSL_aKRB5 = 0x00002000L;
public final static long SSL_aECDSA = 0x00004000L;
- public final static long SSL_eNULL = 0x00200000L;
public final static long SSL_eFZA = 0x00100000L;
- public final static long SSL_NULL = (SSL_eNULL);
public final static long SSL_ADH = (SSL_kEDH|SSL_aNULL);
public final static long SSL_RSA = (SSL_kRSA|SSL_aRSA);
public final static long SSL_DH = (SSL_kDHr|SSL_kDHd|SSL_kEDH);
@@ -212,17 +274,55 @@ public class CipherStrings {
public final static long SSL_FZA = (SSL_aFZA|SSL_kFZA|SSL_eFZA);
public final static long SSL_KRB5 = (SSL_kKRB5|SSL_aKRB5);
public final static long SSL_ENC_MASK = 0x043F8000L;
- public final static long SSL_DES = 0x00008000L;
- public final static long SSL_3DES = 0x00010000L;
- public final static long SSL_RC4 = 0x00020000L;
- public final static long SSL_RC2 = 0x00040000L;
- public final static long SSL_IDEA = 0x00080000L;
- public final static long SSL_AES = 0x04000000L;
+
+ /* Bits for algorithm_enc (symmetric encryption) */
+ public final static long SSL_DES = 0x00000001L;
+ public final static long SSL_3DES = 0x00000002L;
+ public final static long SSL_RC4 = 0x00000004L;
+ public final static long SSL_RC2 = 0x00000008L;
+ public final static long SSL_IDEA = 0x00000010L;
+ public final static long SSL_eNULL = 0x00000020L;
+ //public final static long SSL_AES = 0x04000000L;
+ public final static long SSL_AES128 = 0x00000040L;
+ public final static long SSL_AES256 = 0x00000080L;
+ public final static long SSL_CAMELLIA128 = 0x00000100L;
+ public final static long SSL_CAMELLIA256 = 0x00000200L;
+ public final static long SSL_eGOST2814789CNT = 0x00000400L;
+ public final static long SSL_SEED = 0x00000800L;
+ public final static long SSL_AES128GCM = 0x00001000L;
+ public final static long SSL_AES256GCM = 0x00002000L;
+ public final static long SSL_AES128CCM = 0x00004000L;
+ public final static long SSL_AES256CCM = 0x00008000L;
+ public final static long SSL_AES128CCM8 = 0x00010000L;
+ public final static long SSL_AES256CCM8 = 0x00020000L;
+ public final static long SSL_eGOST2814789CNT12 = 0x00040000L;
+ public final static long SSL_CHACHA20POLY1305 = 0x00080000L;
+ public final static long SSL_ARIA128GCM = 0x00100000L;
+ public final static long SSL_ARIA256GCM = 0x00200000L;
+
+ public final static long SSL_AESGCM = (SSL_AES128GCM | SSL_AES256GCM);
+ public final static long SSL_AESCCM = (SSL_AES128CCM | SSL_AES256CCM | SSL_AES128CCM8 | SSL_AES256CCM8);
+ public final static long SSL_AES = (SSL_AES128|SSL_AES256|SSL_AESGCM|SSL_AESCCM);
+ public final static long SSL_CAMELLIA = (SSL_CAMELLIA128|SSL_CAMELLIA256);
+ public final static long SSL_CHACHA20 = (SSL_CHACHA20POLY1305);
+ public final static long SSL_ARIAGCM = (SSL_ARIA128GCM | SSL_ARIA256GCM);
+ public final static long SSL_ARIA = (SSL_ARIAGCM);
+
+ /* Bits for algorithm_mac (symmetric authentication) */
+
public final static long SSL_MAC_MASK = 0x00c00000L;
- public final static long SSL_MD5 = 0x00400000L;
- public final static long SSL_SHA1 = 0x00800000L;
+ /* Bits for algorithm_mac (symmetric authentication) */
+ public final static long SSL_MD5 = 0x00400000L; // 0x00000001U
+ public final static long SSL_SHA1 = 0x00800000L; // 0x00000002U
public final static long SSL_SHA = (SSL_SHA1);
- public final static long SSL_SSL_MASK = 0x03000000L;
+ //# define SSL_GOST94 0x00000004U
+ //# define SSL_GOST89MAC 0x00000008U
+ // NOTE: retrofitted (from 1.1.1) - expected not to used | and w SSL_MAC_MASK
+ public final static long SSL_SHA256 = 0x00000010L;
+ public final static long SSL_SHA384 = 0x00000020L;
+
+ @Deprecated
+ public final static long SSL_SSL_MASK = 0x03000000L; // legacy
public final static long SSL_SSLV2 = 0x01000000L;
public final static long SSL_SSLV3 = 0x02000000L;
public final static long SSL_TLSV1 = SSL_SSLV3;
@@ -235,11 +335,19 @@ public class CipherStrings {
public final static long SSL_MICRO = (SSL_EXP40);
public final static long SSL_EXP56 = 0x00000010L;
public final static long SSL_MINI = (SSL_EXP56);
- public final static long SSL_LOW = 0x00000020L;
- public final static long SSL_MEDIUM = 0x00000040L;
- public final static long SSL_HIGH = 0x00000080L;
- public final static long SSL_ALL = 0xffffffffL;
+
+ // NOTE: can not be adjusted until SSL_NOT_EXP is around!
+ public final static long SSL_LOW = 0x00000020L; // 0x00000002U in OSSL 1.1
+ public final static long SSL_MEDIUM = 0x00000040L; // 0x00000004U in OSSL 1.1
+ public final static long SSL_HIGH = 0x00000080L; // 0x00000008U in OSSL 1.1
+ public final static long SSL_FIPS = 0x00000100L; // 0x00000010U in OSSL 1.1
+ public final static long SSL_NOT_DEFAULT = 0x00000200L; // 0x00000020U in OSSL 1.1
+
+ @Deprecated
+ public final static long SSL_ALL = 0xffffffffL; // legacy
+ @Deprecated
public final static long SSL_ALL_CIPHERS = (SSL_MKEY_MASK|SSL_AUTH_MASK|SSL_ENC_MASK|SSL_MAC_MASK);
+ @Deprecated
public final static long SSL_ALL_STRENGTHS = (SSL_EXP_MASK|SSL_STRONG_MASK);
public final static long SSL_PKEY_RSA_ENC = 0;
public final static long SSL_PKEY_RSA_SIGN = 1;
@@ -371,25 +479,36 @@ public class CipherStrings {
public final static String TLS1_TXT_ECDHE_RSA_WITH_DES_192_CBC3_SHA = "ECDHE-RSA-DES-CBC3-SHA";
public final static String TLS1_TXT_ECDH_anon_WITH_NULL_SHA = "AECDH-NULL-SHA";
+ // struct ssl_cipher_st
static final class Def implements Comparable, Cloneable {
- //private final byte valid;
+ final boolean valid; // TODO NOT IMPLEMENTED!
final String name;
- //private final long id;
+ private final long id;
+
final long algorithms;
private final long algStrength;
//final long algorithm2;
- final int algStrengthBits;
- final int algBits;
+ final int algStrengthBits; // bits
+ final int algBits; // alg_bits
private final long mask;
private final long algStrengthMask;
+ // OpenSSL 1.1.1
+ private long algorithm_mkey;
+ private long algorithm_auth;
+ private long algorithm_enc;
+ private long algorithm_mac;
+ private int min_tls; // "new" format using SSL.TLS_ constants
+ private int max_tls; // "new" format using SSL.TLS_ constants
+
+ // JOSSL extra
private volatile String cipherSuite;
Def(int valid, String name, long id, long algorithms, long algo_strength, long algorithm2, int strength_bits, int alg_bits, long mask, long maskStrength) {
- //this.valid = (byte) valid;
+ this.valid = valid != 0;
this.name = name;
- //this.id = id;
+ this.id = id;
this.algorithms = algorithms;
this.algStrength = algo_strength;
//this.algorithm2 = algorithm2;
@@ -400,7 +519,9 @@ static final class Def implements Comparable, Cloneable {
}
Def(String name, long algorithms, long algo_strength, int strength_bits, int alg_bits, long mask, long maskStrength) {
+ this.valid = true;
this.name = name;
+ this.id = 0;
this.algorithms = algorithms;
this.algStrength = algo_strength;
this.algStrengthBits = strength_bits;
@@ -409,6 +530,40 @@ static final class Def implements Comparable, Cloneable {
this.algStrengthMask = maskStrength;
}
+ Def(int valid, String name, String stdname, /* RFC name */
+ long id, /* uint32_t id, 4 bytes, first is version */
+ /*
+ * changed in 1.0.0: these four used to be portions of a single value
+ * 'algorithms'
+ */
+ long algorithm_mkey, /* key exchange algorithm */
+ long algorithm_auth, /* server authentication */
+ long algorithm_enc, /* symmetric encryption */
+ long algorithm_mac, /* symmetric authentication */
+ int min_tls, /* minimum SSL/TLS protocol version */
+ int max_tls /* maximum SSL/TLS protocol version */) {
+
+ this.valid = valid != 0;
+ this.name = name;
+ this.id = id;
+
+ this.algorithm_mkey = algorithm_mkey;
+ this.algorithm_auth = algorithm_auth;
+ this.algorithm_enc = algorithm_enc;
+ this.algorithm_mac = algorithm_mac;
+ this.min_tls = min_tls;
+ this.max_tls = max_tls;
+
+ this.algorithms = algorithm_mkey;
+ this.algStrength = 0;
+ this.algStrengthBits = 0;
+ this.algBits = 0;
+
+ this.mask = 0;
+ this.algStrengthMask = 0;
+ }
+
+
public String getCipherSuite() {
return cipherSuite;
}
@@ -512,8 +667,10 @@ static Collection matchingCiphers(final String cipherString, final String[]
}
int index = 0;
- switch ( part.charAt(0) ) {
- case '!': case '+': case '-': index++; break;
+ if (part.length() > 0) {
+ switch ( part.charAt(0) ) {
+ case '!': case '+': case '-': index++; break;
+ }
}
final Collection matching;
@@ -561,9 +718,22 @@ static Collection matchingCiphers(final String cipherString, final String[]
private static Collection matchingExact(final String name, final String[] all,
final boolean setSuite) {
final Def pattern = Definitions.get(name);
- if ( pattern != null ) {
+ if (pattern != null) {
return matchingPattern(pattern, all, true, setSuite);
}
+
+ final Def def = CipherNames.get(name);
+ if (def != null) {
+ if (setSuite) {
+ for (final String entry : all) {
+ if (name.equals(SuiteToOSSL.get(entry))) {
+ return Collections.singleton(def.setCipherSuite(entry));
+ }
+ }
+ } else {
+ return Collections.singleton(def);
+ }
+ }
return null; // Collections.emptyList();
}
@@ -611,7 +781,7 @@ private static Collection matchingPattern(
private final static Map Definitions;
//private final static ArrayList Ciphers;
private final static Map CipherNames;
- private final static Map SuiteToOSSL;
+ final static Map SuiteToOSSL;
static {
Definitions = new HashMap( 48, 1 );
@@ -646,7 +816,7 @@ private static Collection matchingPattern(
Definitions.put(SSL_TXT_MD5,new Def(0,SSL_TXT_MD5, 0,SSL_MD5, 0,0,0,0,SSL_MAC_MASK,0));
Definitions.put(SSL_TXT_SHA1,new Def(0,SSL_TXT_SHA1,0,SSL_SHA1, 0,0,0,0,SSL_MAC_MASK,0));
Definitions.put(SSL_TXT_SHA,new Def(0,SSL_TXT_SHA, 0,SSL_SHA, 0,0,0,0,SSL_MAC_MASK,0));
- Definitions.put(SSL_TXT_NULL,new Def(0,SSL_TXT_NULL,0,SSL_NULL, 0,0,0,0,SSL_ENC_MASK,0));
+ Definitions.put(SSL_TXT_NULL,new Def(0,SSL_TXT_NULL,0,SSL_eNULL, 0,0,0,0,SSL_ENC_MASK,0));
Definitions.put(SSL_TXT_KRB5,new Def(0,SSL_TXT_KRB5,0,SSL_KRB5, 0,0,0,0,SSL_AUTH_MASK|SSL_MKEY_MASK,0));
Definitions.put(SSL_TXT_RSA,new Def(0,SSL_TXT_RSA, 0,SSL_RSA, 0,0,0,0,SSL_AUTH_MASK|SSL_MKEY_MASK,0));
Definitions.put(SSL_TXT_ADH,new Def(0,SSL_TXT_ADH, 0,SSL_ADH, 0,0,0,0,SSL_AUTH_MASK|SSL_MKEY_MASK,0));
@@ -1999,10 +2169,13 @@ private static Collection matchingPattern(
));
SuiteToOSSL.put("TLS_ECDHE_ECDSA_WITH_NULL_SHA", "ECDHE-ECDSA-NULL-SHA");
- SuiteToOSSL.put("TLS_ECDHE_RSA_WITH_NULL_SHA", "ECDHE-RSA-NULL-SHA");
- SuiteToOSSL.put("TLS_ECDH_ECDSA_WITH_NULL_SHA", "ECDH-ECDSA-NULL-SHA");
- SuiteToOSSL.put("TLS_ECDH_RSA_WITH_NULL_SHA", "ECDH-RSA-NULL-SHA");
- SuiteToOSSL.put("TLS_ECDH_anon_WITH_NULL_SHA", "AECDH-NULL-SHA");
+ SuiteToOSSL.put("TLS_ECDHE_RSA_WITH_NULL_SHA", "ECDHE-RSA-NULL-SHA");
+ SuiteToOSSL.put("TLS_ECDH_ECDSA_WITH_NULL_SHA", "ECDH-ECDSA-NULL-SHA");
+ SuiteToOSSL.put("TLS_ECDH_RSA_WITH_NULL_SHA", "ECDH-RSA-NULL-SHA");
+ SuiteToOSSL.put("TLS_ECDH_anon_WITH_NULL_SHA", "AECDH-NULL-SHA");
+
+ SuiteToOSSL.put("TLS_DH_anon_WITH_AES_128_GCM_SHA256", "ADH-AES128-GCM-SHA256");
+ SuiteToOSSL.put("TLS_DH_anon_WITH_AES_256_GCM_SHA384", "ADH-AES256-GCM-SHA384");
/* For IBM JRE: suite names start with "SSL_". On Oracle JRE, the suite names start with "TLS_" */
SuiteToOSSL.put("SSL_DH_anon_WITH_AES_128_CBC_SHA", "ADH-AES128-SHA");
@@ -2070,19 +2243,38 @@ private static Collection matchingPattern(
SuiteToOSSL.put("SSL_RSA_WITH_AES_256_GCM_SHA384", "AES256-GCM-SHA384");
SuiteToOSSL.put("SSL_RSA_WITH_NULL_SHA256", "NULL-SHA256");
- // left overs supported by Java 7's SSLv3 / TLS v1.2 :
-
- // TLS_EMPTY_RENEGOTIATION_INFO_SCSV,
- // TLS_KRB5_WITH_3DES_EDE_CBC_SHA,
- // TLS_KRB5_WITH_3DES_EDE_CBC_MD5,
- // TLS_KRB5_WITH_RC4_128_SHA,
- // TLS_KRB5_WITH_RC4_128_MD5,
- // TLS_KRB5_WITH_DES_CBC_SHA,
- // TLS_KRB5_WITH_DES_CBC_MD5,
- // TLS_KRB5_EXPORT_WITH_DES_CBC_40_SHA,
- // TLS_KRB5_EXPORT_WITH_DES_CBC_40_MD5,
- // TLS_KRB5_EXPORT_WITH_RC4_40_SHA,
- // TLS_KRB5_EXPORT_WITH_RC4_40_MD5
+ // TLS v1.3 (Java 8/11) streaming ciphers :
+ // TODO the specifics of using these on 1.3 only is not implemented
+
+ SuiteToOSSL.put("TLS_AES_128_GCM_SHA256", name = "TLS_AES_128_GCM_SHA256");
+ CipherNames.put(name, new Def(name,
+ SSL_AES|SSL_SHA|SSL_TLSV1,
+ SSL_NOT_EXP, 128, 256, SSL_ALL_CIPHERS, SSL_ALL_STRENGTHS
+ ));
+
+ SuiteToOSSL.put("TLS_AES_256_GCM_SHA384", name = "TLS_AES_256_GCM_SHA384");
+ CipherNames.put(name, new Def(name,
+ SSL_AES|SSL_SHA|SSL_TLSV1,
+ SSL_NOT_EXP, 256, 384, SSL_ALL_CIPHERS, SSL_ALL_STRENGTHS
+ ));
+
+ SuiteToOSSL.put("TLS_CHACHA20_POLY1305_SHA256", name = "TLS_CHACHA20_POLY1305_SHA256");
+ CipherNames.put(name, new Def(name,
+ SSL_CHACHA20|SSL_SHA|SSL_TLSV1,
+ SSL_NOT_EXP, 256, 256, SSL_ALL_CIPHERS, SSL_ALL_STRENGTHS
+ ));
+
+ SuiteToOSSL.put("TLS_AES_128_CCM_SHA256", name = "TLS_AES_128_CCM_SHA256");
+ CipherNames.put(name, new Def(name,
+ SSL_AES|SSL_SHA|SSL_TLSV1,
+ SSL_NOT_EXP, 128, 256, SSL_ALL_CIPHERS, SSL_ALL_STRENGTHS
+ ));
+
+ SuiteToOSSL.put("TLS_AES_128_CCM_8_SHA256", name = "TLS_AES_128_CCM_8_SHA256");
+ CipherNames.put(name, new Def(name,
+ SSL_AES|SSL_SHA|SSL_TLSV1,
+ SSL_NOT_EXP, 256, 384, SSL_ALL_CIPHERS, SSL_ALL_STRENGTHS
+ ));
for ( Def def : Ciphers ) CipherNames.put(def.name, def);
diff --git a/src/main/java/org/jruby/ext/openssl/Config.java b/src/main/java/org/jruby/ext/openssl/Config.java
deleted file mode 100644
index 58d7f663..00000000
--- a/src/main/java/org/jruby/ext/openssl/Config.java
+++ /dev/null
@@ -1,52 +0,0 @@
-/***** BEGIN LICENSE BLOCK *****
- * Version: EPL 1.0/GPL 2.0/LGPL 2.1
- *
- * The contents of this file are subject to the Eclipse Public
- * License Version 1.0 (the "License"); you may not use this file
- * except in compliance with the License. You may obtain a copy of
- * the License at http://www.eclipse.org/legal/epl-v10.html
- *
- * Software distributed under the License is distributed on an "AS
- * IS" basis, WITHOUT WARRANTY OF ANY KIND, either express or
- * implied. See the License for the specific language governing
- * rights and limitations under the License.
- *
- * Copyright (C) 2006 Ola Bini
- *
- * Alternatively, the contents of this file may be used under the terms of
- * either of the GNU General Public License Version 2 or later (the "GPL"),
- * or the GNU Lesser General Public License Version 2.1 or later (the "LGPL"),
- * in which case the provisions of the GPL or the LGPL are applicable instead
- * of those above. If you wish to allow use of your version of this file only
- * under the terms of either the GPL or the LGPL, and not to allow others to
- * use your version of this file under the terms of the EPL, indicate your
- * decision by deleting the provisions above and replace them with the notice
- * and other provisions required by the GPL or the LGPL. If you do not delete
- * the provisions above, a recipient may use your version of this file under
- * the terms of any one of the EPL, the GPL or the LGPL.
- ***** END LICENSE BLOCK *****/
-package org.jruby.ext.openssl;
-
-import org.jruby.Ruby;
-import org.jruby.RubyClass;
-import org.jruby.RubyModule;
-
-/**
- * OpenSSL::Config (native parts)
- * @author Ola Bini
- */
-@Deprecated // move to .rb (for now) as its now lazy loaded ...
-public class Config {
- // TODO: we cannot detect OS's default config file. ignore?
- // public static final String DEFAULT_CONFIG_FILE = "./openssl.cnf";
-
- public static void createConfig(Ruby runtime, RubyModule OpenSSL) {
- RubyClass Config = OpenSSL.defineClassUnder("Config", runtime.getObject(), runtime.getObject().getAllocator());
- Config.defineAnnotatedMethods(Config.class);
- RubyClass openSSLError = OpenSSL.getClass("OpenSSLError");
- OpenSSL.defineClassUnder("ConfigError", openSSLError, openSSLError.getAllocator());
- // TODO: we should define this constant with proper path. (see above)
- Config.setConstant("DEFAULT_CONFIG_FILE", runtime.getNil()); // runtime.newString(DEFAULT_CONFIG_FILE)
- }
-
-}
diff --git a/src/main/java/org/jruby/ext/openssl/Digest.java b/src/main/java/org/jruby/ext/openssl/Digest.java
index c73f2860..76f50377 100644
--- a/src/main/java/org/jruby/ext/openssl/Digest.java
+++ b/src/main/java/org/jruby/ext/openssl/Digest.java
@@ -52,16 +52,12 @@
public class Digest extends RubyObject {
private static final long serialVersionUID = 7409857414064319518L;
- private static final ObjectAllocator ALLOCATOR = new ObjectAllocator() {
- public Digest allocate(Ruby runtime, RubyClass klass) { return new Digest(runtime, klass); }
- };
-
static void createDigest(Ruby runtime, final RubyModule OpenSSL, final RubyClass OpenSSLError) {
runtime.getLoadService().require("digest");
final RubyModule coreDigest = runtime.getModule("Digest");
final RubyClass DigestClass = coreDigest.getClass("Class"); // ::Digest::Class
- RubyClass Digest = OpenSSL.defineClassUnder("Digest", DigestClass, ALLOCATOR);
+ RubyClass Digest = OpenSSL.defineClassUnder("Digest", DigestClass, (r, klass) -> new Digest(r, klass));
OpenSSL.defineClassUnder("DigestError", OpenSSLError, OpenSSLError.getAllocator());
Digest.defineAnnotatedMethods(Digest.class);
diff --git a/src/main/java/org/jruby/ext/openssl/HMAC.java b/src/main/java/org/jruby/ext/openssl/HMAC.java
index d4487148..03bc4e92 100644
--- a/src/main/java/org/jruby/ext/openssl/HMAC.java
+++ b/src/main/java/org/jruby/ext/openssl/HMAC.java
@@ -37,7 +37,6 @@
import org.jruby.RubyObject;
import org.jruby.RubyString;
import org.jruby.anno.JRubyMethod;
-import org.jruby.runtime.ObjectAllocator;
import org.jruby.runtime.builtin.IRubyObject;
import org.jruby.util.ByteList;
import org.jruby.runtime.Visibility;
@@ -50,12 +49,8 @@
public class HMAC extends RubyObject {
private static final long serialVersionUID = 7602535792884680307L;
- private static final ObjectAllocator ALLOCATOR = new ObjectAllocator() {
- public HMAC allocate(Ruby runtime, RubyClass klass) { return new HMAC(runtime, klass); }
- };
-
static void createHMAC(final Ruby runtime, final RubyModule OpenSSL, final RubyClass OpenSSLError) {
- RubyClass HMAC = OpenSSL.defineClassUnder("HMAC", runtime.getObject(), ALLOCATOR);
+ RubyClass HMAC = OpenSSL.defineClassUnder("HMAC", runtime.getObject(), (r, klass) -> new HMAC(r, klass));
OpenSSL.defineClassUnder("HMACError", OpenSSLError, OpenSSLError.getAllocator());
HMAC.defineAnnotatedMethods(HMAC.class);
}
diff --git a/src/main/java/org/jruby/ext/openssl/NetscapeSPKI.java b/src/main/java/org/jruby/ext/openssl/NetscapeSPKI.java
index 30dc3eb1..0972207a 100644
--- a/src/main/java/org/jruby/ext/openssl/NetscapeSPKI.java
+++ b/src/main/java/org/jruby/ext/openssl/NetscapeSPKI.java
@@ -45,11 +45,9 @@
import org.jruby.RubyClass;
import org.jruby.RubyModule;
import org.jruby.RubyObject;
-import org.jruby.RubyString;
import org.jruby.anno.JRubyMethod;
import org.jruby.exceptions.RaiseException;
import org.jruby.ext.openssl.impl.Base64;
-import org.jruby.runtime.ObjectAllocator;
import org.jruby.runtime.builtin.IRubyObject;
import org.jruby.runtime.ThreadContext;
import org.jruby.runtime.Visibility;
@@ -58,8 +56,6 @@
// org.bouncycastle.jce.netscape.NetscapeCertRequest emulator:
import org.jruby.ext.openssl.impl.NetscapeCertRequest;
-import static org.jruby.ext.openssl.PKeyDSA._DSA;
-import static org.jruby.ext.openssl.PKeyRSA._RSA;
import static org.jruby.ext.openssl.OpenSSL.*;
/**
@@ -68,15 +64,9 @@
public class NetscapeSPKI extends RubyObject {
private static final long serialVersionUID = 3211242351810109432L;
- private static ObjectAllocator NETSCAPESPKI_ALLOCATOR = new ObjectAllocator() {
- public IRubyObject allocate(Ruby runtime, RubyClass klass) {
- return new NetscapeSPKI(runtime, klass);
- }
- };
-
static void createNetscapeSPKI(Ruby runtime, final RubyModule OpenSSL, final RubyClass OpenSSLError) {
RubyModule Netscape = OpenSSL.defineModuleUnder("Netscape");
- RubyClass SPKI = Netscape.defineClassUnder("SPKI",runtime.getObject(),NETSCAPESPKI_ALLOCATOR);
+ RubyClass SPKI = Netscape.defineClassUnder("SPKI", runtime.getObject(), (r, klass) -> new NetscapeSPKI(r, klass));
Netscape.defineClassUnder("SPKIError", OpenSSLError, OpenSSLError.getAllocator());
SPKI.defineAnnotatedMethods(NetscapeSPKI.class);
}
@@ -109,19 +99,7 @@ public IRubyObject initialize(final ThreadContext context, final IRubyObject[] a
catch (GeneralSecurityException e) { throw newSPKIError(e); }
catch (IllegalArgumentException e) { throw newSPKIError(e); }
- final PublicKey publicKey = cert.getPublicKey();
- final String algorithm = publicKey.getAlgorithm();
- final RubyString pub_key = RubyString.newString(runtime, publicKey.getEncoded());
-
- if ( "RSA".equalsIgnoreCase(algorithm) ) {
- this.public_key = _RSA(runtime).callMethod(context, "new", pub_key);
- }
- else if ( "DSA".equalsIgnoreCase(algorithm) ) {
- this.public_key = _DSA(runtime).callMethod(context, "new", pub_key);
- }
- else {
- throw runtime.newLoadError("not implemented algo for public key: " + algorithm);
- }
+ this.public_key = PKey.newInstance(runtime, cert.getPublicKey());
}
return this;
}
diff --git a/src/main/java/org/jruby/ext/openssl/OCSPBasicResponse.java b/src/main/java/org/jruby/ext/openssl/OCSPBasicResponse.java
index 8e07b52e..b6912b1e 100644
--- a/src/main/java/org/jruby/ext/openssl/OCSPBasicResponse.java
+++ b/src/main/java/org/jruby/ext/openssl/OCSPBasicResponse.java
@@ -88,7 +88,6 @@
import org.jruby.ext.openssl.x509store.X509AuxCertificate;
import org.jruby.ext.openssl.x509store.X509Utils;
import org.jruby.runtime.Arity;
-import org.jruby.runtime.ObjectAllocator;
import org.jruby.runtime.ThreadContext;
import org.jruby.runtime.Visibility;
import org.jruby.runtime.builtin.IRubyObject;
@@ -117,14 +116,8 @@ public class OCSPBasicResponse extends RubyObject {
private static final String OCSP_RESPID_KEY = "RESPID_KEY";
private static final String OCSP_TRUSTOTHER = "TRUSTOTHER";
- private static ObjectAllocator BASICRESPONSE_ALLOCATOR = new ObjectAllocator() {
- public IRubyObject allocate(Ruby runtime, RubyClass klass) {
- return new OCSPBasicResponse(runtime, klass);
- }
- };
-
public static void createBasicResponse(final Ruby runtime, final RubyModule OCSP) {
- RubyClass BasicResponse = OCSP.defineClassUnder("BasicResponse", runtime.getObject(), BASICRESPONSE_ALLOCATOR);
+ RubyClass BasicResponse = OCSP.defineClassUnder("BasicResponse", runtime.getObject(), (r, klass) -> new OCSPBasicResponse(r, klass));
BasicResponse.defineAnnotatedMethods(OCSPBasicResponse.class);
}
@@ -357,8 +350,12 @@ public IRubyObject sign(final ThreadContext context, IRubyObject[] args) {
}
try {
- Extension[] respExtAry = new Extension[extensions.size()];
- Extensions respExtensions = new Extensions(extensions.toArray(respExtAry));
+ final Extensions respExtensions;
+ if (extensions != null && extensions.size() > 0) {
+ respExtensions = new Extensions(extensions.toArray(new Extension[extensions.size()]));
+ } else {
+ respExtensions = null;
+ }
BasicOCSPResp bcBasicOCSPResp = respBuilder.setResponseExtensions(respExtensions).build(contentSigner, chain, producedAt);
asn1BCBasicOCSPResp = BasicOCSPResponse.getInstance(bcBasicOCSPResp.getEncoded());
}
@@ -530,6 +527,9 @@ private boolean checkDelegated(X509Cert signerCA) {
catch (CertificateParsingException e) {
throw newOCSPError(getRuntime(), e);
}
+ catch (IOException e) {
+ throw newOCSPError(getRuntime(), e);
+ }
}
private boolean matchIssuerId(X509Cert signerCA, CertificateID certId, List singleResponses) throws IOException {
@@ -590,22 +590,21 @@ else if (intOrTime instanceof RubyTime) {
return new ASN1GeneralizedTime(retTime);
}
- private Extensions convertRubyExtensions(IRubyObject extensions) {
- if (extensions.isNil()) return null;
- List retExtensions = new ArrayList();
- Iterator rubyExtensions = ((RubyArray)extensions).iterator();
- while (rubyExtensions.hasNext()) {
- X509Extension rubyExt = (X509Extension)rubyExtensions.next();
- Extension ext = Extension.getInstance(((RubyString)rubyExt.to_der()).getBytes());
- retExtensions.add(ext);
- }
- Extension[] exts = new Extension[retExtensions.size()];
- retExtensions.toArray(exts);
- return new Extensions(exts);
+ private Extensions convertRubyExtensions(final IRubyObject arg) {
+ if (arg.isNil()) return null;
+ final RubyArray rubyExts = arg.convertToArray(); // Array
+ if (rubyExts.isEmpty()) return null;
+
+ final Extension[] extensions = new Extension[rubyExts.size()];
+ for (int i = 0; i convertRubyCerts(IRubyObject certificates) {
- Iterator it = ((RubyArray)certificates).iterator();
+ Iterator it = certificates.convertToArray().iterator();
List ret = new ArrayList();
while (it.hasNext()) {
ret.add(it.next());
diff --git a/src/main/java/org/jruby/ext/openssl/OCSPCertificateId.java b/src/main/java/org/jruby/ext/openssl/OCSPCertificateId.java
index 67066b0b..ec266009 100644
--- a/src/main/java/org/jruby/ext/openssl/OCSPCertificateId.java
+++ b/src/main/java/org/jruby/ext/openssl/OCSPCertificateId.java
@@ -52,7 +52,6 @@
import org.jruby.RubyObject;
import org.jruby.RubyString;
import org.jruby.anno.JRubyMethod;
-import org.jruby.runtime.ObjectAllocator;
import org.jruby.runtime.ThreadContext;
import org.jruby.runtime.Visibility;
import org.jruby.runtime.builtin.IRubyObject;
@@ -69,14 +68,8 @@
public class OCSPCertificateId extends RubyObject {
private static final long serialVersionUID = 6324454052172773918L;
- private static ObjectAllocator CERTIFICATEID_ALLOCATOR = new ObjectAllocator() {
- public IRubyObject allocate(Ruby runtime, RubyClass klass) {
- return new OCSPCertificateId(runtime, klass);
- }
- };
-
public static void createCertificateId(final Ruby runtime, final RubyModule _OCSP) {
- RubyClass _certificateId = _OCSP.defineClassUnder("CertificateId", runtime.getObject(), CERTIFICATEID_ALLOCATOR);
+ RubyClass _certificateId = _OCSP.defineClassUnder("CertificateId", runtime.getObject(), (r, klass) -> new OCSPCertificateId(r, klass));
_certificateId.defineAnnotatedMethods(OCSPCertificateId.class);
}
@@ -101,39 +94,35 @@ public IRubyObject initialize(final ThreadContext context, IRubyObject subject,
originalIssuer = (X509Cert) issuer;
BigInteger serial = subjectCert.getSerial();
- return initializeImpl(context, serial, originalIssuer, digest);
+ return initializeImpl(context.runtime, serial, originalIssuer, digest);
}
@JRubyMethod(name = "initialize", visibility = Visibility.PRIVATE)
public IRubyObject initialize(final ThreadContext context, IRubyObject subject, IRubyObject issuer) {
- Ruby runtime = context.getRuntime();
+ final Ruby runtime = context.runtime;
X509Cert subjectCert = (X509Cert) subject;
originalIssuer = (X509Cert) issuer;
BigInteger serial = subjectCert.getSerial();
- Digest digestInstance = new Digest(runtime, _Digest(runtime));
- IRubyObject digest = digestInstance.initialize(context, new IRubyObject[] { RubyString.newString(runtime, "SHA1") });
+ Digest digest = new Digest(runtime, _Digest(runtime));
+ digest.initializeImpl(runtime, RubyString.newString(runtime, "SHA1"), runtime.getNil());
- return initializeImpl(context, serial, originalIssuer, digest);
+ return initializeImpl(runtime, serial, originalIssuer, digest);
}
@JRubyMethod(name = "initialize", visibility = Visibility.PRIVATE)
public IRubyObject initialize(final ThreadContext context, IRubyObject der) {
- Ruby runtime = context.getRuntime();
-
RubyString derStr = StringHelper.readPossibleDERInput(context, der);
- try {
+ try {
return initializeImpl(derStr.getBytes());
}
- catch (IOException e) {
- throw newOCSPError(runtime, e);
+ catch (Exception e) {
+ throw newOCSPError(context.runtime, e);
}
}
- private IRubyObject initializeImpl(final ThreadContext context, BigInteger serial,
- IRubyObject issuerCert, IRubyObject digest) {
- Ruby runtime = context.getRuntime();
+ private IRubyObject initializeImpl(final Ruby runtime, BigInteger serial, X509Cert issuerCert, IRubyObject digest) {
Digest rubyDigest = (Digest) digest;
ASN1ObjectIdentifier oid = ASN1.sym2Oid(runtime, rubyDigest.getName().toLowerCase());
@@ -147,10 +136,8 @@ private IRubyObject initializeImpl(final ThreadContext context, BigInteger seria
throw newOCSPError(runtime, e);
}
- X509Cert rubyCert = (X509Cert) issuerCert;
-
try {
- this.bcCertId = new CertificateID(calc, new X509CertificateHolder(rubyCert.getAuxCert().getEncoded()), serial).toASN1Primitive();
+ this.bcCertId = new CertificateID(calc, new X509CertificateHolder(issuerCert.getAuxCert().getEncoded()), serial).toASN1Primitive();
}
catch (Exception e) {
throw newOCSPError(runtime, e);
@@ -159,7 +146,7 @@ private IRubyObject initializeImpl(final ThreadContext context, BigInteger seria
return this;
}
- private IRubyObject initializeImpl(byte[] derByteStream) throws IOException {
+ private IRubyObject initializeImpl(byte[] derByteStream) {
this.bcCertId = CertID.getInstance(derByteStream);
return this;
@@ -171,8 +158,8 @@ public IRubyObject serial() {
}
@JRubyMethod(name = "issuer_name_hash")
- public IRubyObject issuer_name_hash() {
- Ruby runtime = getRuntime();
+ public IRubyObject issuer_name_hash(ThreadContext context) {
+ Ruby runtime = context.runtime;
String oidSym = ASN1.oid2Sym(runtime, getBCCertificateID().getHashAlgOID());
RubyString digestName = RubyString.newString(runtime, oidSym);
@@ -183,17 +170,14 @@ public IRubyObject issuer_name_hash() {
// a hash of a hash if we don't have the original issuer around.
if (originalIssuer == null) {
try {
- return Digest.hexdigest(runtime.getCurrentContext(), this, digestName,
+ return Digest.hexdigest(context, this, digestName,
RubyString.newString(runtime, bcCertId.getIssuerNameHash().getEncoded("DER")));
}
catch (IOException e) {
throw newOCSPError(runtime, e);
}
}
- else {
- return Digest.hexdigest(runtime.getCurrentContext(), this, digestName,
- originalIssuer.getSubject().to_der(runtime.getCurrentContext()));
- }
+ return Digest.hexdigest(context, this, digestName, originalIssuer.getSubject().to_der(context));
}
// For whatever reason, the MRI Ruby tests appear to suggest that they compute the hexdigest hash
@@ -202,23 +186,22 @@ public IRubyObject issuer_name_hash() {
// is already computed and can't be reversed to get to the original key, so we just compute
// a hash of a hash if we don't have the original issuer around.
@JRubyMethod(name = "issuer_key_hash")
- public IRubyObject issuer_key_hash() {
- Ruby runtime = getRuntime();
+ public IRubyObject issuer_key_hash(ThreadContext context) {
+ Ruby runtime = context.runtime;
String oidSym = ASN1.oid2Sym(runtime, getBCCertificateID().getHashAlgOID());
RubyString digestName = RubyString.newString(runtime, oidSym);
- if (originalIssuer == null) {
- try {
- return Digest.hexdigest(runtime.getCurrentContext(), this, RubyString.newString(runtime, oidSym),
+ try {
+ if (originalIssuer == null) {
+ return Digest.hexdigest(context, this, digestName,
RubyString.newString(runtime, bcCertId.getIssuerKeyHash().getEncoded("DER")));
}
- catch (IOException e) {
- throw newOCSPError(runtime, e);
- }
+ PKey key = (PKey) originalIssuer.public_key(context);
+ byte[] key_der = key.toASN1PublicInfo().toASN1Primitive().getEncoded(ASN1Encoding.DER);
+ return Digest.hexdigest(context, this, digestName, RubyString.newStringNoCopy(runtime, key_der));
}
- else {
- PKey key = (PKey)originalIssuer.public_key(runtime.getCurrentContext());
- return Digest.hexdigest(runtime.getCurrentContext(), this, digestName, key.to_der());
+ catch (IOException e) {
+ throw newOCSPError(runtime, e);
}
}
@@ -226,10 +209,7 @@ public IRubyObject issuer_key_hash() {
public IRubyObject hash_algorithm() {
Ruby runtime = getRuntime();
ASN1ObjectIdentifier oid = bcCertId.getHashAlgorithm().getAlgorithm();
- Integer nid = ASN1.oid2nid(runtime, oid);
- String ln = ASN1.nid2ln(runtime, nid);
-
- return RubyString.newString(runtime, ln);
+ return RubyString.newString(runtime, ASN1.o2a(runtime, oid));
}
@JRubyMethod(name = "cmp")
diff --git a/src/main/java/org/jruby/ext/openssl/OCSPRequest.java b/src/main/java/org/jruby/ext/openssl/OCSPRequest.java
index dfaacf4d..6f748478 100644
--- a/src/main/java/org/jruby/ext/openssl/OCSPRequest.java
+++ b/src/main/java/org/jruby/ext/openssl/OCSPRequest.java
@@ -82,7 +82,6 @@
import org.jruby.anno.JRubyMethod;
import org.jruby.ext.openssl.x509store.X509AuxCertificate;
import org.jruby.runtime.Arity;
-import org.jruby.runtime.ObjectAllocator;
import org.jruby.runtime.ThreadContext;
import org.jruby.runtime.Visibility;
import org.jruby.runtime.builtin.IRubyObject;
@@ -98,19 +97,13 @@
*/
public class OCSPRequest extends RubyObject {
private static final long serialVersionUID = -4020616730425816999L;
-
- private static ObjectAllocator REQUEST_ALLOCATOR = new ObjectAllocator() {
- public IRubyObject allocate(Ruby runtime, RubyClass klass) {
- return new OCSPRequest(runtime, klass);
- }
- };
public OCSPRequest(Ruby runtime, RubyClass metaClass) {
super(runtime, metaClass);
}
public static void createRequest(final Ruby runtime, final RubyModule _OCSP) {
- RubyClass _request = _OCSP.defineClassUnder("Request", runtime.getObject(), REQUEST_ALLOCATOR);
+ RubyClass _request = _OCSP.defineClassUnder("Request", runtime.getObject(), (r, klass) -> new OCSPRequest(r, klass));
_request.defineAnnotatedMethods(OCSPRequest.class);
}
diff --git a/src/main/java/org/jruby/ext/openssl/OCSPResponse.java b/src/main/java/org/jruby/ext/openssl/OCSPResponse.java
index 0f83dc03..8d3db3da 100644
--- a/src/main/java/org/jruby/ext/openssl/OCSPResponse.java
+++ b/src/main/java/org/jruby/ext/openssl/OCSPResponse.java
@@ -47,7 +47,6 @@
import org.jruby.RubyString;
import org.jruby.anno.JRubyMethod;
import org.jruby.runtime.Arity;
-import org.jruby.runtime.ObjectAllocator;
import org.jruby.runtime.ThreadContext;
import org.jruby.runtime.Visibility;
import org.jruby.runtime.builtin.IRubyObject;
@@ -62,12 +61,6 @@
*/
public class OCSPResponse extends RubyObject {
private static final long serialVersionUID = 5763247988029815198L;
-
- private static ObjectAllocator RESPONSE_ALLOCATOR = new ObjectAllocator() {
- public IRubyObject allocate(Ruby runtime, RubyClass klass) {
- return new OCSPResponse(runtime, klass);
- }
- };
public OCSPResponse(Ruby runtime, RubyClass metaClass) {
super(runtime, metaClass);
@@ -78,7 +71,7 @@ public OCSPResponse(Ruby runtime) {
}
public static void createResponse(final Ruby runtime, final RubyModule OCSP) {
- RubyClass Response = OCSP.defineClassUnder("Response", runtime.getObject(), RESPONSE_ALLOCATOR);
+ RubyClass Response = OCSP.defineClassUnder("Response", runtime.getObject(), (r, klass) -> new OCSPResponse(r, klass));
Response.defineAnnotatedMethods(OCSPResponse.class);
}
diff --git a/src/main/java/org/jruby/ext/openssl/OCSPSingleResponse.java b/src/main/java/org/jruby/ext/openssl/OCSPSingleResponse.java
index 0ab299fa..fed7a254 100644
--- a/src/main/java/org/jruby/ext/openssl/OCSPSingleResponse.java
+++ b/src/main/java/org/jruby/ext/openssl/OCSPSingleResponse.java
@@ -56,7 +56,6 @@
import org.jruby.RubyTime;
import org.jruby.anno.JRubyMethod;
import org.jruby.runtime.Arity;
-import org.jruby.runtime.ObjectAllocator;
import org.jruby.runtime.ThreadContext;
import org.jruby.runtime.Visibility;
import org.jruby.runtime.builtin.IRubyObject;
@@ -71,15 +70,9 @@
*/
public class OCSPSingleResponse extends RubyObject {
private static final long serialVersionUID = 7947277768033100227L;
-
- private static ObjectAllocator SINGLERESPONSE_ALLOCATOR = new ObjectAllocator() {
- public IRubyObject allocate(Ruby runtime, RubyClass klass) {
- return new OCSPSingleResponse(runtime, klass);
- }
- };
public static void createSingleResponse(final Ruby runtime, final RubyModule _OCSP) {
- RubyClass _request = _OCSP.defineClassUnder("SingleResponse", runtime.getObject(), SINGLERESPONSE_ALLOCATOR);
+ RubyClass _request = _OCSP.defineClassUnder("SingleResponse", runtime.getObject(), (r, klass) -> new OCSPSingleResponse(r, klass));
_request.defineAnnotatedMethods(OCSPSingleResponse.class);
}
diff --git a/src/main/java/org/jruby/ext/openssl/ObjectSupport.java b/src/main/java/org/jruby/ext/openssl/ObjectSupport.java
index 0207fe2c..ef8df162 100644
--- a/src/main/java/org/jruby/ext/openssl/ObjectSupport.java
+++ b/src/main/java/org/jruby/ext/openssl/ObjectSupport.java
@@ -48,11 +48,21 @@ static RubyString inspect(final RubyBasicObject self, final List varia
return RubyString.newString(runtime, inspect(runtime, self, variableList));
}
+ static RubyString inspect(final RubyBasicObject self, final CharSequence content) {
+ final Ruby runtime = self.getRuntime();
+ final StringBuilder part = inspectHeader(self).append(' ').append(content).append('>');
+ return RubyString.newString(runtime, part);
+ }
+
+ private static StringBuilder inspectHeader(final RubyBasicObject self) {
+ final StringBuilder part = new StringBuilder();
+ part.append("#<").append(self.getMetaClass().getRealClass().getName());
+ return part;
+ }
+
private static StringBuilder inspect(final Ruby runtime, final RubyBasicObject self,
final List variableList) {
- final StringBuilder part = new StringBuilder();
- String cname = self.getMetaClass().getRealClass().getName();
- part.append("#<").append(cname).append(":0x");
+ final StringBuilder part = inspectHeader(self).append(":0x");
part.append(Integer.toHexString(System.identityHashCode(self)));
if (runtime.isInspecting(self)) {
diff --git a/src/main/java/org/jruby/ext/openssl/OpenSSL.java b/src/main/java/org/jruby/ext/openssl/OpenSSL.java
index d6123a4f..7330f650 100644
--- a/src/main/java/org/jruby/ext/openssl/OpenSSL.java
+++ b/src/main/java/org/jruby/ext/openssl/OpenSSL.java
@@ -23,13 +23,14 @@
*/
package org.jruby.ext.openssl;
-import java.security.NoSuchProviderException;
import java.security.SecureRandom;
import java.util.Map;
+import org.bouncycastle.util.Arrays;
import org.jruby.*;
import org.jruby.anno.JRubyMethod;
import org.jruby.anno.JRubyModule;
+import org.jruby.common.IRubyWarnings;
import org.jruby.runtime.ThreadContext;
import org.jruby.runtime.Visibility;
import org.jruby.runtime.builtin.IRubyObject;
@@ -88,18 +89,7 @@ public static void createOpenSSL(final Ruby runtime) {
runtime.getLoadService().require("jopenssl/version");
- // MRI 1.8.7 :
- // OpenSSL::VERSION: "1.0.0"
- // OpenSSL::OPENSSL_VERSION: "OpenSSL 1.0.1c 10 May 2012"
- // OpenSSL::OPENSSL_VERSION_NUMBER: 268439615
- // MRI 1.9.3 / 2.2.3 :
- // OpenSSL::VERSION: "1.1.0"
- // OpenSSL::OPENSSL_VERSION: "OpenSSL 1.0.1f 6 Jan 2014"
- // OpenSSL::OPENSSL_VERSION_NUMBER: 268439663
- // OpenSSL::OPENSSL_LIBRARY_VERSION: ""OpenSSL 1.0.2d 9 Jul 2015"
- // OpenSSL::FIPS: false
-
- final byte[] version = { '1','.','1','.','0' };
+ final byte[] version = { '2','.','2','.','3' }; // C OpenSSL gem version
_OpenSSL.setConstant("VERSION", StringHelper.newString(runtime, version));
@@ -107,13 +97,18 @@ public static void createOpenSSL(final Ruby runtime) {
final RubyString jVERSION = JOpenSSL.getConstantAt("VERSION").asString();
final byte[] JRuby_OpenSSL_ = { 'J','R','u','b','y','-','O','p','e','n','S','S','L',' ' };
- final int OPENSSL_VERSION_NUMBER = 999999999; // NOTE: smt more useful?
-
ByteList OPENSSL_VERSION = new ByteList( jVERSION.getByteList().getRealSize() + JRuby_OpenSSL_.length );
OPENSSL_VERSION.setEncoding( jVERSION.getEncoding() );
OPENSSL_VERSION.append( JRuby_OpenSSL_ );
OPENSSL_VERSION.append( jVERSION.getByteList() );
+ // < 3.0.0 until we have decent (full) compatibility
+ final byte OPENSSL_VERSION_MAJOR = 2;
+ final byte OPENSSL_VERSION_MINOR = 9;
+ final byte OPENSSL_VERSION_PATCH = 9;
+ final byte OPENSSL_VERSION_PRE_RELEASE = 0;
+ final int OPENSSL_VERSION_NUMBER = OPENSSL_VERSION_MAJOR << 28 | OPENSSL_VERSION_MINOR << 20 | OPENSSL_VERSION_PATCH << 4 | OPENSSL_VERSION_PRE_RELEASE;
+
final RubyString VERSION;
_OpenSSL.setConstant("OPENSSL_VERSION", VERSION = runtime.newString(OPENSSL_VERSION));
_OpenSSL.setConstant("OPENSSL_VERSION_NUMBER", runtime.newFixnum(OPENSSL_VERSION_NUMBER));
@@ -162,6 +157,20 @@ public static IRubyObject Digest(final IRubyObject self, final IRubyObject name)
return Digest.getConstantAt( name.asJavaString() );
}
+ @JRubyMethod(meta = true)
+ public static IRubyObject fixed_length_secure_compare(ThreadContext context, IRubyObject self, IRubyObject arg1, IRubyObject arg2) {
+ final ByteList str1 = arg1.convertToString().getByteList();
+ final ByteList str2 = arg2.convertToString().getByteList();
+ if (str1.length() != str2.length()) {
+ throw context.runtime.newArgumentError("inputs must be of equal length");
+ }
+ return context.runtime.newBoolean(
+ Arrays.constantTimeAreEqual(str1.length(),
+ str1.unsafeBytes(), str1.begin(),
+ str2.unsafeBytes(), str2.begin()
+ ));
+ }
+
// API "stubs" in JRuby-OpenSSL :
@JRubyMethod(meta = true)
@@ -174,11 +183,16 @@ public static IRubyObject check_func(final IRubyObject self, final IRubyObject[]
return self.getRuntime().getNil(); // no-op in JRuby-OpenSSL
}
- // Added in 2.0; not masked because it does nothing anyway (there's no reader in MRI)
+ @JRubyMethod(name = "fips_mode", meta = true)
+ public static IRubyObject get_fips_mode(ThreadContext context, IRubyObject self) {
+ warn(context, "FIPS mode not implemented on JRuby-OpenSSL");
+ return context.nil;
+ }
+
@JRubyMethod(name = "fips_mode=", meta = true)
public static IRubyObject set_fips_mode(ThreadContext context, IRubyObject self, IRubyObject value) {
if ( value.isTrue() ) {
- warn(context, "FIPS mode not supported on JRuby-OpenSSL");
+ warn(context, "FIPS mode not implemented on JRuby-OpenSSL");
}
return value;
}
@@ -186,18 +200,14 @@ public static IRubyObject set_fips_mode(ThreadContext context, IRubyObject self,
// internal (package-level) helpers :
/**
- * PRIMARILY MEANT FOR TESTING ONLY, USAGE IS DISCOURAGED!
- * @see org.jruby.ext.openssl.util.CryptoSecurity
+ * @deprecated
*/
@JRubyMethod(name = "_disable_security_restrictions!", visibility = Visibility.PRIVATE, meta = true)
public static IRubyObject _disable_security_restrictions(ThreadContext context, IRubyObject self) {
- Boolean unrestrict = org.jruby.ext.openssl.util.CryptoSecurity.unrestrictSecurity();
- Boolean allPerm = org.jruby.ext.openssl.util.CryptoSecurity.setAllPermissionPolicy();
- if ( unrestrict == null || allPerm == null ) return context.nil;
- return context.runtime.newBoolean( unrestrict && allPerm );
+ warnDeprecated(context, "OpenSSL._disable_security_restrictions! is deprecated for removal");
+ return context.nil;
}
-
private static boolean debug;
// on by default, warnings can be disabled using -Djruby.openssl.warn=false
@@ -235,6 +245,14 @@ public static void debug(final Ruby runtime, final CharSequence msg, final Throw
if ( isDebug(runtime) ) runtime.getOut().println(msg.toString() + ' ' + e);
}
+ public static void debugStackTrace(final Ruby runtime, final CharSequence msg, final Throwable e) {
+ if ( isDebug(runtime) ) {
+ synchronized (runtime.getOut()) {
+ runtime.getOut().print(msg.toString() + ' ');
+ e.printStackTrace(runtime.getOut());
+ }
+ }
+ }
static void warn(final ThreadContext context, final CharSequence msg) {
if ( warn ) warn(context, RubyString.newString(context.runtime, msg));
}
@@ -247,6 +265,12 @@ static void warn(final ThreadContext context, final IRubyObject msg) {
if ( warn ) context.runtime.getModule("OpenSSL").callMethod(context, "warn", msg);
}
+ public static void warnDeprecated(final ThreadContext context, final CharSequence msg) {
+ if ( warn ) {
+ context.runtime.getWarnings().warn(IRubyWarnings.ID.DEPRECATED_METHOD, msg.toString());
+ }
+ }
+
private static String javaVersion(final String def, final int len) {
String javaVersion = SafePropertyAccessor.getProperty("java.version", def);
if ( "0".equals(javaVersion) ) javaVersion = "1.7.0"; // Android
@@ -307,7 +331,6 @@ static SecureRandom getSecureRandom(final Ruby runtime) {
return getSecureRandom(runtime, false);
}
-
static SecureRandom getSecureRandom(final Ruby runtime, final boolean nullByDefault) {
if ( tryContextSecureRandom ) {
SecureRandom random = getSecureRandomFrom(runtime.getCurrentContext());
@@ -316,19 +339,21 @@ static SecureRandom getSecureRandom(final Ruby runtime, final boolean nullByDefa
return nullByDefault ? null : new SecureRandom();
}
- static SecureRandom getSecureRandomFrom(final ThreadContext context) {
+ static SecureRandom getSecureRandom(final ThreadContext context) {
if ( tryContextSecureRandom ) {
- try {
- SecureRandom random = context.secureRandom;
- if (random == null) { // public SecureRandom getSecureRandom() on 9K
- random = (SecureRandom) context.getClass().getMethod("getSecureRandom").invoke(context);
- }
- return random;
- }
- catch (Throwable ex) {
- tryContextSecureRandom = false;
- debug(context.runtime, "JRuby-OpenSSL failed to retrieve secure random from thread-context", ex);
- }
+ SecureRandom random = getSecureRandomFrom(context);
+ if ( random != null ) return random;
+ }
+ return new SecureRandom();
+ }
+
+ private static SecureRandom getSecureRandomFrom(final ThreadContext context) {
+ try {
+ return context.getSecureRandom();
+ }
+ catch (Throwable ex) {
+ tryContextSecureRandom = false;
+ debug(context.runtime, "JRuby-OpenSSL failed to retrieve secure random from thread-context", ex);
}
return null;
}
@@ -343,11 +368,7 @@ static IRubyObject to_der_if_possible(final ThreadContext context, IRubyObject o
//
- static String bcExceptionMessage(NoSuchProviderException ex) {
- return "You need to configure JVM/classpath to enable BouncyCastle Security Provider: " + ex;
- }
-
- static String bcExceptionMessage(NoClassDefFoundError ex) {
+ static String bcExceptionMessage(Throwable ex) {
return "You need to configure JVM/classpath to enable BouncyCastle Security Provider: " + ex;
}
diff --git a/src/main/java/org/jruby/ext/openssl/OpenSSLReal.java b/src/main/java/org/jruby/ext/openssl/OpenSSLReal.java
deleted file mode 100644
index bda68ebc..00000000
--- a/src/main/java/org/jruby/ext/openssl/OpenSSLReal.java
+++ /dev/null
@@ -1,108 +0,0 @@
-/***** BEGIN LICENSE BLOCK *****
- * Version: EPL 1.0/GPL 2.0/LGPL 2.1
- *
- * The contents of this file are subject to the Eclipse Public
- * License Version 1.0 (the "License"); you may not use this file
- * except in compliance with the License. You may obtain a copy of
- * the License at http://www.eclipse.org/legal/epl-v10.html
- *
- * Software distributed under the License is distributed on an "AS
- * IS" basis, WITHOUT WARRANTY OF ANY KIND, either express or
- * implied. See the License for the specific language governing
- * rights and limitations under the License.
- *
- * Copyright (C) 2006 Ola Bini
- *
- * Alternatively, the contents of this file may be used under the terms of
- * either of the GNU General Public License Version 2 or later (the "GPL"),
- * or the GNU Lesser General Public License Version 2.1 or later (the "LGPL"),
- * in which case the provisions of the GPL or the LGPL are applicable instead
- * of those above. If you wish to allow use of your version of this file only
- * under the terms of either the GPL or the LGPL, and not to allow others to
- * use your version of this file under the terms of the EPL, indicate your
- * decision by deleting the provisions above and replace them with the notice
- * and other provisions required by the GPL or the LGPL. If you do not delete
- * the provisions above, a recipient may use your version of this file under
- * the terms of any one of the EPL, the GPL or the LGPL.
- ***** END LICENSE BLOCK *****/
-package org.jruby.ext.openssl;
-
-import java.security.GeneralSecurityException;
-import java.security.NoSuchProviderException;
-import java.security.Provider;
-
-/**
- * @deprecated no longer used
- * @see OpenSSL
- * @author Ola Bini
- */
-public class OpenSSLReal {
-
- private OpenSSLReal() { /* no instances */ }
-
- @Deprecated
- public interface Runnable {
- void run() throws GeneralSecurityException;
- }
-
- public interface Callable {
- T call() throws GeneralSecurityException;
- }
-
- /**
- * Run a block of code with 'BC' provider installed.
- *
- * @deprecated No longer used within the JRuby-OpenSSL code-base, please avoid!
- *
- * @param block
- * @throws GeneralSecurityException
- */
- @Deprecated
- public static void doWithBCProvider(final Runnable block) throws GeneralSecurityException {
- getWithBCProvider(new Callable() {
- public Void call() throws GeneralSecurityException {
- block.run(); return null;
- }
- });
- }
-
- /**
- * Adds BouncyCastleProvider if it's allowed (no security exceptions thrown)
- * and runs the block of code. Once added the provider will stay registered
- * within java.security.Security API. This might lead to memory
- * leaks e.g. when the Ruby runtime that loaded BC is teared down.
- *
- * Removing the 'BC' provided (once the block run) can remove pre-installed
- * or another runtime-added BC provider thus causing unknown runtime errors.
- *
- * @deprecated No longer used within the JRuby-OpenSSL code-base, please avoid!
- *
- * @param
- * @param block
- * @return
- * @throws GeneralSecurityException
- */
- @Deprecated
- public static T getWithBCProvider(final Callable block) throws GeneralSecurityException {
- try {
- final Provider provider = SecurityHelper.getSecurityProvider(); // BC
- if (provider != null && java.security.Security.getProvider(provider.getName()) == null) {
- java.security.Security.addProvider(provider);
- }
- return block.call();
- } catch (NoSuchProviderException nspe) {
- throw new GeneralSecurityException(bcExceptionMessage(nspe), nspe);
- } catch (Exception e) {
- throw new GeneralSecurityException(e.getMessage(), e);
- }
- }
-
- public static String bcExceptionMessage(NoSuchProviderException e) {
- return OpenSSL.bcExceptionMessage(e);
- }
-
- public static String bcExceptionMessage(NoClassDefFoundError e) {
- return OpenSSL.bcExceptionMessage(e);
- }
-
-}
\ No newline at end of file
diff --git a/src/main/java/org/jruby/ext/openssl/PKCS7.java b/src/main/java/org/jruby/ext/openssl/PKCS7.java
index 09e1d66f..fb1f25be 100644
--- a/src/main/java/org/jruby/ext/openssl/PKCS7.java
+++ b/src/main/java/org/jruby/ext/openssl/PKCS7.java
@@ -29,8 +29,10 @@
import java.io.IOException;
import java.io.StringWriter;
+import java.text.ParseException;
import java.util.ArrayList;
import java.util.Collection;
+import java.util.Date;
import java.util.HashSet;
import java.util.List;
@@ -39,6 +41,7 @@
import java.security.cert.CertificateEncodingException;
import org.bouncycastle.asn1.ASN1Encodable;
+import org.bouncycastle.asn1.ASN1UTCTime;
import org.jruby.Ruby;
import org.jruby.RubyArray;
@@ -49,6 +52,7 @@
import org.jruby.RubyNumeric;
import org.jruby.RubyObject;
import org.jruby.RubyString;
+import org.jruby.RubyTime;
import org.jruby.anno.JRubyClass;
import org.jruby.anno.JRubyMethod;
import org.jruby.exceptions.RaiseException;
@@ -738,8 +742,20 @@ public IRubyObject serial() {
@JRubyMethod
public IRubyObject signed_time(final ThreadContext context) {
- warn(context, "WARNING: unimplemented method called: OpenSSL::PKCS7::SignerInfo#signed_time");
- return context.runtime.getNil();
+ ASN1Encodable asn1obj = info.getSignedAttribute(ASN1Registry.NID_pkcs9_signingTime);
+ if (asn1obj == null) {
+ throw newPKCS7Error(context.runtime, "no signing time attribute");
+ }
+ if (asn1obj instanceof ASN1UTCTime) {
+ final Date adjusted;
+ try {
+ adjusted = ((ASN1UTCTime) asn1obj).getAdjustedDate();
+ } catch (ParseException ex) {
+ throw newPKCS7Error(context.runtime, ex);
+ }
+ return RubyTime.newTime(context.runtime, adjusted.getTime());
+ }
+ return context.nil;
}
}
diff --git a/src/main/java/org/jruby/ext/openssl/PKey.java b/src/main/java/org/jruby/ext/openssl/PKey.java
index e162835f..51b99b48 100644
--- a/src/main/java/org/jruby/ext/openssl/PKey.java
+++ b/src/main/java/org/jruby/ext/openssl/PKey.java
@@ -27,20 +27,23 @@
***** END LICENSE BLOCK *****/
package org.jruby.ext.openssl;
-import java.io.ByteArrayInputStream;
+import java.io.Console;
import java.io.IOException;
-import java.io.InputStreamReader;
import java.io.StringReader;
import java.math.BigInteger;
import java.security.*;
import java.security.interfaces.DSAPrivateKey;
import java.security.interfaces.DSAPublicKey;
+import java.security.interfaces.ECPublicKey;
import java.security.interfaces.RSAPrivateCrtKey;
import java.security.interfaces.RSAPublicKey;
import java.security.spec.InvalidKeySpecException;
import java.security.spec.PKCS8EncodedKeySpec;
import java.security.spec.X509EncodedKeySpec;
+import org.bouncycastle.asn1.ASN1InputStream;
+import org.bouncycastle.asn1.ASN1Primitive;
+import org.bouncycastle.asn1.ASN1Sequence;
import org.jruby.Ruby;
import org.jruby.RubyClass;
import org.jruby.RubyModule;
@@ -48,15 +51,17 @@
import org.jruby.RubyString;
import org.jruby.anno.JRubyMethod;
import org.jruby.exceptions.RaiseException;
+import org.jruby.runtime.Block;
import org.jruby.runtime.ObjectAllocator;
import org.jruby.runtime.ThreadContext;
import org.jruby.runtime.builtin.IRubyObject;
import org.jruby.runtime.Visibility;
+import org.jruby.util.ByteList;
+import org.jruby.ext.openssl.impl.CipherSpec;
import org.jruby.ext.openssl.x509store.PEMInputOutput;
+
import static org.jruby.ext.openssl.OpenSSL.*;
-import org.jruby.ext.openssl.impl.CipherSpec;
-import org.jruby.util.ByteList;
/**
* @author Ola Bini
@@ -84,6 +89,20 @@ public static RaiseException newPKeyError(Ruby runtime, String message) {
return Utils.newError(runtime, (RubyClass) _PKey(runtime).getConstantAt("PKeyError"), message);
}
+ public static PKey newInstance(final Ruby runtime, final PublicKey publicKey) {
+ if (publicKey instanceof RSAPublicKey) {
+ return new PKeyRSA(runtime, (RSAPublicKey) publicKey);
+ }
+ if (publicKey instanceof DSAPublicKey) {
+ return new PKeyDSA(runtime, (DSAPublicKey) publicKey);
+ }
+ if (publicKey instanceof ECPublicKey) {
+ return new PKeyEC(runtime, publicKey);
+ }
+ assert publicKey != null;
+ throw runtime.newNotImplementedError("public key algorithm: " + (publicKey != null ? publicKey.getAlgorithm() : "nil"));
+ }
+
static RubyModule _PKey(final Ruby runtime) {
return (RubyModule) runtime.getModule("OpenSSL").getConstantAt("PKey");
}
@@ -105,74 +124,71 @@ public static IRubyObject read(final ThreadContext context, IRubyObject recv, IR
pass = args[1].isNil() ? null : args[1].toString().toCharArray();
}
- final byte[] input = StringHelper.readX509PEM(context, data);
- KeyPair key = null;
+ final RubyString str = readInitArg(context, data);
+ KeyPair keyPair;
// d2i_PrivateKey_bio
try {
- key =org.jruby.ext.openssl.impl.PKey.readPrivateKey(input);
- } catch (IOException ioe) {
- // ignore
- } catch (GeneralSecurityException gse) {
- // ignore
+ keyPair = readPrivateKey(str, pass);
+ } catch (IOException e) {
+ debugStackTrace(runtime, "PKey readPrivateKey", e); /* ignore */
+ keyPair = null;
}
// PEM_read_bio_PrivateKey
- if (key == null) {
- try {
- key = PEMInputOutput.readPrivateKey(new InputStreamReader(new ByteArrayInputStream(input)), pass);
- } catch (IOException ioe) {
- // ignore
- }
- }
- if (key != null) {
- final String alg = getAlgorithm(key);
+ if (keyPair != null) {
+ final String alg = getAlgorithm(keyPair);
if ( "RSA".equals(alg) ) {
- return new PKeyRSA(runtime, _PKey(runtime).getClass("RSA"),
- (RSAPrivateCrtKey) key.getPrivate(), (RSAPublicKey) key.getPublic()
- );
+ return new PKeyRSA(runtime, _PKey(runtime).getClass("RSA"), (RSAPrivateCrtKey) keyPair.getPrivate(), (RSAPublicKey) keyPair.getPublic());
}
if ( "DSA".equals(alg) ) {
- return new PKeyDSA(runtime, _PKey(runtime).getClass("DSA"),
- (DSAPrivateKey) key.getPrivate(), (DSAPublicKey) key.getPublic()
- );
+ return new PKeyDSA(runtime, _PKey(runtime).getClass("DSA"), (DSAPrivateKey) keyPair.getPrivate(), (DSAPublicKey) keyPair.getPublic());
}
- if ( "ECDSA".equals(alg) ) {
- return new PKeyEC(runtime, _PKey(runtime).getClass("EC"),
- (PrivateKey) key.getPrivate(), (PublicKey) key.getPublic()
- );
+ if ( "EC".equals(alg) || "ECDSA".equals(alg) ) { // Sun vs BC provider naming
+ return new PKeyEC(runtime, _PKey(runtime).getClass("EC"), keyPair.getPrivate(), keyPair.getPublic());
}
+ debug(runtime, "PKey readPrivateKey unexpected key pair algorithm: " + alg);
}
PublicKey pubKey = null;
+ try {
+ pubKey = PEMInputOutput.readRSAPublicKey(new StringReader(str.toString()), null);
+ if (pubKey != null) return new PKeyRSA(runtime, (RSAPublicKey) pubKey);
+ } catch (IOException e) {
+ debugStackTrace(runtime, "PKey readRSAPublicKey", e); /* ignore */
+ }
+ try {
+ pubKey = PEMInputOutput.readDSAPublicKey(new StringReader(str.toString()), null);
+ if (pubKey != null) return new PKeyDSA(runtime, (DSAPublicKey) pubKey);
+ } catch (IOException e) {
+ debugStackTrace(runtime, "PKey readDSAPublicKey", e); /* ignore */
+ }
+
+ final byte[] input = StringHelper.readX509PEM(context, str);
// d2i_PUBKEY_bio
try {
pubKey = org.jruby.ext.openssl.impl.PKey.readPublicKey(input);
- } catch (IOException ioe) {
- // ignore
- } catch (GeneralSecurityException gse) {
- // ignore
+ } catch (IOException e) {
+ debugStackTrace(runtime, "PKey readPublicKey", e); /* ignore */
}
// PEM_read_bio_PUBKEY
if (pubKey == null) {
try {
- pubKey = PEMInputOutput.readPubKey(new InputStreamReader(new ByteArrayInputStream(input)));
- } catch (IOException ioe) {
- // ignore
+ pubKey = PEMInputOutput.readPubKey(new StringReader(str.toString()));
+ } catch (IOException e) {
+ debugStackTrace(runtime, "PKey readPubKey", e); /* ignore */
}
}
- if (pubKey != null) {
- if ( "RSA".equals(pubKey.getAlgorithm()) ) {
- return new PKeyRSA(runtime, (RSAPublicKey) pubKey);
- }
- if ( "DSA".equals(pubKey.getAlgorithm()) ) {
- return new PKeyDSA(runtime, (DSAPublicKey) pubKey);
- }
- if ( "ECDSA".equals(pubKey.getAlgorithm()) ) {
- return new PKeyEC(runtime, pubKey);
- }
+ if (pubKey instanceof RSAPublicKey) {
+ return new PKeyRSA(runtime, (RSAPublicKey) pubKey);
+ }
+ if (pubKey instanceof DSAPublicKey) {
+ return new PKeyDSA(runtime, (DSAPublicKey) pubKey);
+ }
+ if (pubKey instanceof ECPublicKey) {
+ return new PKeyEC(runtime, pubKey);
}
- throw runtime.newArgumentError("Could not parse PKey");
+ throw newPKeyError(runtime, "Could not parse PKey: unsupported");
}
private static String getAlgorithm(final KeyPair key) {
@@ -180,7 +196,6 @@ private static String getAlgorithm(final KeyPair key) {
if ( key.getPublic() != null ) return key.getPublic().getAlgorithm();
return null;
}
-
}
public PKey(Ruby runtime, RubyClass type) {
@@ -199,16 +214,13 @@ public IRubyObject initialize(ThreadContext context) {
public String getAlgorithm() { return "NONE"; }
+ public String getKeyType() { return getAlgorithm(); }
+
public boolean isPrivateKey() { return getPrivateKey() != null; }
public abstract RubyString to_der() ;
- public abstract RubyString to_pem(final IRubyObject[] args) ;
-
- @Deprecated
- public RubyString export(final IRubyObject[] args) {
- return to_pem(args);
- }
+ public abstract RubyString to_pem(ThreadContext context, final IRubyObject[] args) ;
@JRubyMethod(name = "sign")
public IRubyObject sign(IRubyObject digest, IRubyObject data) {
@@ -224,6 +236,35 @@ public IRubyObject sign(IRubyObject digest, IRubyObject data) {
}
}
+ public ASN1Primitive toASN1PublicInfo() throws IOException {
+ ASN1InputStream input = new ASN1InputStream(to_der().getBytes());
+
+ ASN1Primitive data = input.readObject();
+ if (data instanceof ASN1Sequence) {
+ return ((ASN1Sequence) data).getObjectAt(1).toASN1Primitive();
+ }
+ return data;
+ }
+
+ @Override
+ public Object toJava(final Class target) {
+ if (PrivateKey.class.isAssignableFrom(target)) {
+ final PrivateKey key = getPrivateKey();
+ if (key == null) {
+ throw getRuntime().newRuntimeError("private key not available, to convert to " + target);
+ }
+ if (target.isInstance(key)) return key;
+ throw getRuntime().newTypeError("cannot convert private key of type " + key.getClass() + " to " + target);
+ }
+ if (target.isAssignableFrom(PublicKey.class) || Key.class.isAssignableFrom(target)) {
+ // default is public key, also want to_java() as well as to_java(java.lang.Object) to end up here
+ final PublicKey key = getPublicKey();
+ if (target.isInstance(key)) return key;
+ throw getRuntime().newTypeError("cannot convert public key of type " + key.getClass() + " to " + target);
+ }
+ return super.toJava(target);
+ }
+
static ByteList sign(final String signAlg, final PrivateKey privateKey, final ByteList data)
throws NoSuchAlgorithmException, InvalidKeyException, SignatureException {
Signature signature = SecurityHelper.getSignature(signAlg);
@@ -274,7 +315,7 @@ static SecureRandom getSecureRandom(final Ruby runtime) {
return OpenSSL.getSecureRandom(runtime);
}
- // shared Helpers for PKeyRSA / PKEyDSA :
+ // shared Helpers for PKeyRSA / PKeyDSA :
protected PrivateKey tryPKCS8EncodedKey(final Ruby runtime, final KeyFactory keyFactory, final byte[] encodedKey) {
try {
@@ -335,20 +376,22 @@ protected PublicKey tryX509EncodedKey(final Ruby runtime, final KeyFactory keyFa
}
protected static void addSplittedAndFormatted(StringBuilder result, BigInteger value) {
- String v = value.toString(16);
- if ((v.length() % 2) != 0) {
- v = "0" + v;
- }
- String sep = "";
+ addSplittedAndFormatted(result, value.toString(16));
+ }
+
+ static void addSplittedAndFormatted(StringBuilder result, CharSequence v) {
+ if ((v.length() % 2) != 0) v = "0" + v;
+
+ char sep = '\0';
for (int i = 0; i < v.length(); i += 2) {
result.append(sep);
if ((i % 30) == 0) {
result.append("\n ");
}
- result.append(v.substring(i, i + 2));
- sep = ":";
+ result.append(v, i, i + 2);
+ sep = ':';
}
- result.append("\n");
+ result.append('\n');
}
protected static CipherSpec cipherSpec(final IRubyObject cipher) {
@@ -359,6 +402,7 @@ protected static CipherSpec cipherSpec(final IRubyObject cipher) {
return null;
}
+ @Deprecated
protected static char[] password(final IRubyObject pass) {
if ( pass != null && ! pass.isNil() ) {
return pass.toString().toCharArray();
@@ -366,17 +410,33 @@ protected static char[] password(final IRubyObject pass) {
return null;
}
+ protected static char[] password(final ThreadContext context, IRubyObject pass, final Block block) {
+ if (pass != null && !pass.isNil()) { // argument takes precedence (instead of block)
+ return pass.toString().toCharArray();
+ }
+ if (block != null && block.isGiven()) {
+ return password(context, block.call(context), null);
+ }
+ return null;
+ }
+
protected static char[] passwordPrompt(final ThreadContext context) {
return passwordPrompt(context, "Enter PEM pass phrase:");
}
protected static char[] passwordPrompt(final ThreadContext context, final String prompt) {
+ Console console = System.console();
+ if (console != null) {
+ return console.readPassword(prompt);
+ }
+
+ // fall back on simple IO, but may be broken (jruby/jruby#5588)
final RubyModule Kernel = context.runtime.getKernel();
// NOTE: just a fast and simple print && gets - hopefully better than nothing!
Kernel.callMethod("print", context.runtime.newString(prompt));
final RubyString gets = Kernel.callMethod(context, "gets").convertToString();
gets.chomp_bang(context);
- return gets.toString().toCharArray();
+ return gets.decodeString().toCharArray();
}
protected static boolean ttySTDIN(final ThreadContext context) {
@@ -389,12 +449,12 @@ protected static boolean ttySTDIN(final ThreadContext context) {
catch (RaiseException ex) { return false; }
}
- static Object readPrivateKey(final String str, final char[] passwd)
+ static KeyPair readPrivateKey(final String str, final char[] passwd)
throws PEMInputOutput.PasswordRequiredException, IOException {
return PEMInputOutput.readPrivateKey(new StringReader(str), passwd);
}
- static Object readPrivateKey(final RubyString str, final char[] passwd)
+ static KeyPair readPrivateKey(final RubyString str, final char[] passwd)
throws PEMInputOutput.PasswordRequiredException, IOException {
return readPrivateKey(str.toString(), passwd);
}
diff --git a/src/main/java/org/jruby/ext/openssl/PKeyDH.java b/src/main/java/org/jruby/ext/openssl/PKeyDH.java
index dd5f4ec9..d05dea65 100644
--- a/src/main/java/org/jruby/ext/openssl/PKeyDH.java
+++ b/src/main/java/org/jruby/ext/openssl/PKeyDH.java
@@ -97,16 +97,11 @@ public static RaiseException newDHError(Ruby runtime, String message) {
private transient volatile BigInteger dh_y;
private transient volatile BigInteger dh_x;
- // FIXME! need to figure out what it means in MRI/OSSL code to
- // claim a DH is(/has) private if an engine is present -- doesn't really
- // map to Java implementation.
-
- //private volatile boolean haveEngine;
-
public PKeyDH(Ruby runtime, RubyClass clazz) {
super(runtime, clazz);
}
+ @JRubyMethod(visibility = Visibility.PRIVATE)
@Override
public IRubyObject initialize_copy(final IRubyObject original) {
if (this == original) return this;
@@ -120,6 +115,21 @@ public IRubyObject initialize_copy(final IRubyObject original) {
return this;
}
+ @JRubyMethod(name = "generate", meta = true, rest = true)
+ public static IRubyObject generate(final ThreadContext context, IRubyObject self, IRubyObject[] args) {
+ final Ruby runtime = context.runtime;
+ final int g;
+ if (Arity.checkArgumentCount(runtime, args, 1, 2) == 2) {
+ g = RubyNumeric.num2int(args[1]);
+ } else {
+ g = 2;
+ }
+
+ PKeyDH pkey = new PKeyDH(runtime, _PKey(runtime).getClass("DH"));
+ pkey.generate(runtime, args[0], g);
+ return pkey;
+ }
+
@JRubyMethod(name="initialize", rest=true, visibility = Visibility.PRIVATE)
public synchronized IRubyObject initialize(final ThreadContext context, final IRubyObject[] args) {
final Ruby runtime = context.runtime;
@@ -150,28 +160,29 @@ public synchronized IRubyObject initialize(final ThreadContext context, final IR
throw runtime.newIOErrorFromException(e);
}
} else {
- int bits = RubyNumeric.fix2int(arg0);
- // g defaults to 2
- int gval = argc == 2 ? RubyNumeric.fix2int(args[1]) : 2;
- BigInteger p;
- try {
- p = generateP(bits, gval);
- }
- catch(IllegalArgumentException e) {
- throw runtime.newArgumentError(e.getMessage());
- }
- BigInteger g = BigInteger.valueOf(gval);
- BigInteger x = generateX(p);
- BigInteger y = generateY(p, g, x);
- this.dh_p = p;
- this.dh_g = g;
- this.dh_x = x; // private key
- this.dh_y = y; // public key
+ generate(runtime, arg0, argc == 2 ? RubyNumeric.num2int(args[1]) : 2); // g defaults to 2
}
}
return this;
}
+ private void generate(final Ruby runtime, final IRubyObject bits, final int gval) {
+ BigInteger p;
+ try {
+ p = generateP(RubyNumeric.num2int(bits), gval);
+ }
+ catch(IllegalArgumentException e) {
+ throw runtime.newArgumentError(e.getMessage());
+ }
+ BigInteger g = BigInteger.valueOf(gval);
+ BigInteger x = generateX(p);
+ BigInteger y = generateY(p, g, x);
+ this.dh_p = p;
+ this.dh_g = g;
+ this.dh_x = x; // private key
+ this.dh_y = y; // public key
+ }
+
public static BigInteger generateP(int bits, int g) {
// FIXME? I'm following algorithms used in OpenSSL, could use JCE provider instead.
@@ -202,9 +213,6 @@ public static BigInteger generateX(BigInteger p, int limit) {
BigInteger x;
SecureRandom secureRandom = new SecureRandom();
// adapting algorithm from org.bouncycastle.crypto.generators.DHKeyGeneratorHelper,
- // which seems a little stronger (?) than OpenSSL's (OSSL just generates a random,
- // while BC generates a random potential prime [for limit > 0], though it's not
- // subject to Miller-Rabin [certainty = 0], but is subject to other constraints)
// see also [ossl]/crypto/dh/dh_key.c #generate_key
if (limit == 0) {
final BigInteger pSub2 = p.subtract(TWO);
@@ -213,8 +221,7 @@ public static BigInteger generateX(BigInteger p, int limit) {
} while (x.equals(BigInteger.ZERO));
} else {
do {
- // generate potential prime, though with 0 certainty (no Miller-Rabin tests)
- x = new BigInteger(limit, 0, secureRandom);
+ x = new BigInteger(limit, secureRandom);
} while (x.equals(BigInteger.ZERO));
}
return x;
@@ -229,10 +236,6 @@ public static BigInteger generateY(BigInteger p, BigInteger g, BigInteger x) {
return g.modPow(x, p);
}
- public static BigInteger generateY(BigInteger p, int g, BigInteger x) {
- return generateY(p, BigInteger.valueOf(g), x);
- }
-
@JRubyMethod(name = "generate_key!")
public synchronized IRubyObject generate_key() {
BigInteger p, g, x, y;
@@ -275,20 +278,17 @@ public RubyBoolean public_p() {
@Override
public boolean isPrivateKey() {
- return dh_x != null /* || haveEngine */;
+ return dh_x != null;
}
@JRubyMethod(name = "private?")
public RubyBoolean private_p() {
- // FIXME! need to figure out what it means in MRI/OSSL code to
- // claim a DH is private if an engine is present -- doesn't really
- // map to Java implementation.
return getRuntime().newBoolean(isPrivateKey());
}
@Override
@JRubyMethod(name = { "to_pem", "to_s" }, alias = "export", rest = true)
- public RubyString to_pem(final IRubyObject[] args) {
+ public RubyString to_pem(ThreadContext context, final IRubyObject[] args) {
//Arity.checkArgumentCount(getRuntime(), args, 0, 2);
//CipherSpec spec = null; char[] passwd = null;
@@ -377,6 +377,21 @@ public synchronized IRubyObject set_g(IRubyObject arg) {
return arg;
}
+ @JRubyMethod(name = "q")
+ public IRubyObject q(final ThreadContext context) {
+ return context.nil;
+ }
+
+ @JRubyMethod
+ public IRubyObject set_pqg(final ThreadContext context, IRubyObject p, IRubyObject q, IRubyObject g) {
+ set_p(p);
+ if (!q.isNil()) {
+ OpenSSL.warn(context, "JRuby-OpenSSL does not support setting q param on " + inspect());
+ }
+ set_g(g);
+ return this;
+ }
+
// don't need synchronized as value is volatile
@JRubyMethod(name = "pub_key")
public IRubyObject pub_key() {
@@ -417,6 +432,13 @@ public synchronized IRubyObject set_priv_key(IRubyObject arg) {
return arg;
}
+ @JRubyMethod
+ public IRubyObject set_key(final ThreadContext context, IRubyObject pub_key, IRubyObject priv_key) {
+ set_pub_key(pub_key);
+ set_priv_key(priv_key);
+ return this;
+ }
+
private IRubyObject newBN(BigInteger value) {
if (value == null) return getRuntime().getNil();
return BN.newBN(getRuntime(), value);
diff --git a/src/main/java/org/jruby/ext/openssl/PKeyDSA.java b/src/main/java/org/jruby/ext/openssl/PKeyDSA.java
index 7063a771..83c6d1fd 100644
--- a/src/main/java/org/jruby/ext/openssl/PKeyDSA.java
+++ b/src/main/java/org/jruby/ext/openssl/PKeyDSA.java
@@ -34,16 +34,19 @@
import java.math.BigInteger;
import java.security.*;
import java.security.interfaces.DSAKey;
+import java.security.interfaces.DSAParams;
import java.security.interfaces.DSAPrivateKey;
import java.security.interfaces.DSAPublicKey;
import java.security.spec.DSAPrivateKeySpec;
import java.security.spec.DSAPublicKeySpec;
import java.security.spec.InvalidKeySpecException;
+import org.bouncycastle.asn1.ASN1Primitive;
import org.jruby.Ruby;
import org.jruby.RubyBoolean;
import org.jruby.RubyClass;
import org.jruby.RubyFixnum;
+import org.jruby.RubyHash;
import org.jruby.RubyModule;
import org.jruby.RubyNumeric;
import org.jruby.RubyString;
@@ -52,6 +55,7 @@
import org.jruby.ext.openssl.impl.CipherSpec;
import org.jruby.ext.openssl.x509store.PEMInputOutput;
import org.jruby.runtime.Arity;
+import org.jruby.runtime.Block;
import org.jruby.runtime.ObjectAllocator;
import org.jruby.runtime.builtin.IRubyObject;
import org.jruby.runtime.ThreadContext;
@@ -61,8 +65,9 @@
import static org.jruby.ext.openssl.OpenSSL.*;
import static org.jruby.ext.openssl.impl.PKey.readDSAPrivateKey;
import static org.jruby.ext.openssl.impl.PKey.readDSAPublicKey;
+import static org.jruby.ext.openssl.impl.PKey.toASN1Primitive;
import static org.jruby.ext.openssl.impl.PKey.toDerDSAKey;
-import static org.jruby.ext.openssl.PKey._PKey;
+import static org.jruby.ext.openssl.impl.PKey.toDerDSAPublicKey;
/**
* @author Ola Bini
@@ -112,6 +117,7 @@ public PKeyDSA(Ruby runtime, RubyClass type, DSAPrivateKey privKey, DSAPublicKey
private transient volatile BigInteger dsa_q;
private transient volatile BigInteger dsa_g;
+ @JRubyMethod(visibility = Visibility.PRIVATE)
@Override
public IRubyObject initialize_copy(final IRubyObject original) {
if (this == original) return this;
@@ -165,29 +171,23 @@ private static PKeyDSA dsaGenerate(final Ruby runtime,
}
}
- static PKeyDSA newInstance(final Ruby runtime, final PublicKey publicKey) {
- //if ( publicKey instanceof DSAPublicKey ) {
- return new PKeyDSA(runtime, (DSAPublicKey) publicKey);
- //}
- }
-
@JRubyMethod(rest = true, visibility = Visibility.PRIVATE)
- public IRubyObject initialize(final ThreadContext context, final IRubyObject[] args) {
+ public IRubyObject initialize(final ThreadContext context, final IRubyObject[] args, final Block block) {
final Ruby runtime = context.runtime;
if ( Arity.checkArgumentCount(runtime, args, 0, 2) == 0 ) {
this.privateKey = null; this.publicKey = null; return this;
}
- IRubyObject arg = args[0]; IRubyObject pass = null;
- if ( args.length > 1 ) pass = args[1];
+ IRubyObject arg = args[0];
+ IRubyObject arg1 = args.length > 1 ? args[1] : null; // password (String)
if ( arg instanceof RubyFixnum ) {
int keySize = RubyNumeric.fix2int((RubyFixnum) arg);
return dsaGenerate(context.runtime, this, keySize);
}
- final char[] passwd = password(pass);
+ final char[] passwd = password(context, arg1, block);
final RubyString str = readInitArg(context, arg);
final String strJava = str.toString();
@@ -237,7 +237,7 @@ public IRubyObject initialize(final ThreadContext context, final IRubyObject[] a
}
catch (NoClassDefFoundError e) { noClassDef = true; debugStackTrace(runtime, e); }
catch (InvalidKeySpecException e) { debug(runtime, "PKeyDSA could not read private key", e); }
- catch (IOException e) { debug(runtime, "PKeyDSA could not read private key", e); }
+ catch (IOException e) { debugStackTrace(runtime, "PKeyDSA could not read private key", e); }
catch (RuntimeException e) {
if ( isKeyGenerationFailure(e) ) debug(runtime, "PKeyDSA could not read private key", e);
else debugStackTrace(runtime, e);
@@ -249,7 +249,7 @@ public IRubyObject initialize(final ThreadContext context, final IRubyObject[] a
}
catch (NoClassDefFoundError e) { noClassDef = true; debugStackTrace(runtime, e); }
catch (InvalidKeySpecException e) { debug(runtime, "PKeyDSA could not read public key", e); }
- catch (IOException e) { debug(runtime, "PKeyDSA could not read public key", e); }
+ catch (IOException e) { debugStackTrace(runtime, "PKeyDSA could not read public key", e); }
catch (RuntimeException e) {
if ( isKeyGenerationFailure(e) ) debug(runtime, "PKeyDSA could not read public key", e);
else debugStackTrace(runtime, e);
@@ -300,6 +300,21 @@ public RubyBoolean private_p() {
return privateKey != null ? getRuntime().getTrue() : getRuntime().getFalse();
}
+ @JRubyMethod(name = "public_to_der")
+ public RubyString public_to_der(ThreadContext context) {
+ final byte[] bytes;
+ try {
+ bytes = toDerDSAPublicKey(publicKey);
+ }
+ catch (NoClassDefFoundError e) {
+ throw newDSAError(getRuntime(), bcExceptionMessage(e));
+ }
+ catch (IOException e) {
+ throw newDSAError(getRuntime(), e.getMessage(), e);
+ }
+ return StringHelper.newString(context.runtime, bytes);
+ }
+
@Override
@JRubyMethod(name = "to_der")
public RubyString to_der() {
@@ -316,6 +331,11 @@ public RubyString to_der() {
return StringHelper.newString(getRuntime(), bytes);
}
+ @Override
+ public ASN1Primitive toASN1PublicInfo() {
+ return toASN1Primitive(publicKey);
+ }
+
@JRubyMethod
public RubyString to_text() {
StringBuilder result = new StringBuilder();
@@ -341,15 +361,40 @@ public PKeyDSA public_key() {
return new PKeyDSA(getRuntime(), this.publicKey);
}
+ @JRubyMethod
+ public IRubyObject params(final ThreadContext context) {
+ final Ruby runtime = context.runtime;
+ RubyHash hash = RubyHash.newHash(runtime);
+ if (publicKey != null) {
+ if (publicKey.getParams() != null) {
+ setParams(context, runtime, hash, publicKey.getParams());
+ }
+ hash.op_aset(context, runtime.newString("pub_key"), BN.newBN(runtime, publicKey.getY()));
+ }
+ if (privateKey != null) {
+ if (publicKey == null && privateKey.getParams() != null) {
+ setParams(context, runtime, hash, privateKey.getParams());
+ }
+ hash.op_aset(context, runtime.newString("priv_key"), BN.newBN(runtime, privateKey.getX()));
+ }
+ return hash;
+ }
+
+ private static void setParams(ThreadContext context, Ruby runtime, RubyHash hash, DSAParams params) {
+ hash.op_aset(context, runtime.newString("p"), BN.newBN(runtime, params.getP()));
+ hash.op_aset(context, runtime.newString("q"), BN.newBN(runtime, params.getQ()));
+ hash.op_aset(context, runtime.newString("g"), BN.newBN(runtime, params.getG()));
+ }
+
@Override
@JRubyMethod(name = { "to_pem", "to_s" }, alias = "export", rest = true)
- public RubyString to_pem(final IRubyObject[] args) {
- Arity.checkArgumentCount(getRuntime(), args, 0, 2);
+ public RubyString to_pem(final ThreadContext context, final IRubyObject[] args) {
+ Arity.checkArgumentCount(context.runtime, args, 0, 2);
CipherSpec spec = null; char[] passwd = null;
if ( args.length > 0 ) {
spec = cipherSpec( args[0] );
- if ( args.length > 1 ) passwd = password(args[1]);
+ if ( args.length > 1 ) passwd = password(context, args[1], null);
}
try {
@@ -360,13 +405,28 @@ public RubyString to_pem(final IRubyObject[] args) {
else {
PEMInputOutput.writeDSAPublicKey(writer, publicKey);
}
- return RubyString.newString(getRuntime(), writer.getBuffer());
+ return RubyString.newString(context.runtime, writer.getBuffer());
}
catch (NoClassDefFoundError ncdfe) {
- throw newDSAError(getRuntime(), bcExceptionMessage(ncdfe));
+ throw newDSAError(context.runtime, bcExceptionMessage(ncdfe));
}
catch (IOException e) {
- throw newDSAError(getRuntime(), e.getMessage(), e);
+ throw newDSAError(context.runtime, e.getMessage(), e);
+ }
+ }
+
+ @JRubyMethod
+ public RubyString public_to_pem(ThreadContext context) {
+ try {
+ final StringWriter writer = new StringWriter();
+ PEMInputOutput.writeDSAPublicKey(writer, publicKey);
+ return RubyString.newString(context.runtime, writer.getBuffer());
+ }
+ catch (NoClassDefFoundError ncdfe) {
+ throw newDSAError(context.runtime, bcExceptionMessage(ncdfe));
+ }
+ catch (IOException e) {
+ throw newDSAError(context.runtime, e.getMessage(), e);
}
}
@@ -407,6 +467,11 @@ public IRubyObject sysverify(IRubyObject data, IRubyObject sign) {
}
}
+ @JRubyMethod
+ public IRubyObject oid() {
+ return getRuntime().newString("DSA");
+ }
+
private DSAKey getDsaKey() {
DSAKey result;
return (result = publicKey) != null ? result : privateKey;
@@ -417,11 +482,11 @@ private IRubyObject toBN(BigInteger value) {
}
private synchronized BigInteger getP() {
+ if (dsa_p != null) return dsa_p;
+
DSAKey key = getDsaKey();
- if (key != null) {
- return key.getParams().getP();
- }
- return dsa_p;
+ if (key != null) return key.getParams().getP();
+ return null;
}
@JRubyMethod(name = "p")
@@ -435,11 +500,11 @@ public synchronized IRubyObject set_p(IRubyObject p) {
}
private synchronized BigInteger getQ() {
+ if (dsa_q != null) return dsa_q;
+
DSAKey key = getDsaKey();
- if (key != null) {
- return key.getParams().getQ();
- }
- return dsa_q;
+ if (key != null) return key.getParams().getQ();
+ return null;
}
@JRubyMethod(name = "q")
@@ -453,11 +518,11 @@ public synchronized IRubyObject set_q(IRubyObject q) {
}
private synchronized BigInteger getG() {
+ if (dsa_g != null) return dsa_g;
+
DSAKey key = getDsaKey();
- if (key != null) {
- return key.getParams().getG();
- }
- return dsa_g;
+ if (key != null) return key.getParams().getG();
+ return null;
}
@JRubyMethod(name = "g")
@@ -470,6 +535,23 @@ public synchronized IRubyObject set_g(IRubyObject g) {
return setKeySpecComponent(SPEC_G, g);
}
+ @JRubyMethod
+ public IRubyObject set_pqg(IRubyObject p, IRubyObject q, IRubyObject g) {
+ this.dsa_p = BN.getBigInteger(p);
+ this.dsa_q = BN.getBigInteger(q);
+ this.dsa_g = BN.getBigInteger(g);
+ generateKeyInternal();
+ return this;
+ }
+
+ @JRubyMethod
+ public IRubyObject set_key(final ThreadContext context, IRubyObject pub_key, IRubyObject priv_key) {
+ this.dsa_y = BN.getBigInteger(pub_key);
+ this.dsa_x = BN.getBigInteger(priv_key);
+ generateKeyInternal();
+ return this;
+ }
+
@JRubyMethod(name = "priv_key")
public synchronized IRubyObject get_priv_key() {
DSAPrivateKey key;
@@ -500,7 +582,6 @@ public synchronized IRubyObject set_pub_key(IRubyObject pub_key) {
private IRubyObject setKeySpecComponent(final int index, final IRubyObject value) {
final BigInteger val = BN.getBigInteger(value);
-
switch (index) {
case SPEC_X: this.dsa_x = val; break;
case SPEC_Y: this.dsa_y = val; break;
@@ -509,19 +590,49 @@ private IRubyObject setKeySpecComponent(final int index, final IRubyObject value
case SPEC_G: this.dsa_g = val; break;
}
+ generateKeyInternal();
+ return value;
+ }
+
+ private BigInteger getX() {
+ if (dsa_x != null) return dsa_x;
+
+ DSAPrivateKey key;
+ if ((key = this.privateKey) != null) {
+ return key.getX();
+ }
+ return null;
+ }
+
+ private BigInteger getY() {
+ if (dsa_y != null) return dsa_y;
+
+ DSAPublicKey key;
+ if ((key = this.publicKey) != null) {
+ return key.getY();
+ }
+ return null;
+ }
+
+ private void generateKeyInternal() {
// Don't access the dsa_p, dsa_q and dsa_g fields directly. They may
// have already been consumed and cleared.
- BigInteger _dsa_p = getP();
- BigInteger _dsa_q = getQ();
- BigInteger _dsa_g = getG();
+ final BigInteger dsa_p = getP();
+ final BigInteger dsa_q = getQ();
+ final BigInteger dsa_g = getG();
- if ( dsa_x != null && _dsa_p != null && _dsa_q != null && _dsa_g != null ) {
+ final BigInteger dsa_x = getX();
+ final BigInteger dsa_y = getY();
+
+ if ( dsa_x != null && dsa_p != null && dsa_q != null && dsa_g != null ) {
// we now have all private key components. create the key :
- DSAPrivateKeySpec spec = new DSAPrivateKeySpec(dsa_x, _dsa_p, _dsa_q, _dsa_g);
+ DSAPrivateKeySpec spec = new DSAPrivateKeySpec(dsa_x, dsa_p, dsa_q, dsa_g);
try {
this.privateKey = (DSAPrivateKey) SecurityHelper.getKeyFactory("DSA").generatePrivate(spec);
}
catch (InvalidKeySpecException e) {
+ e.printStackTrace();
+
throw newDSAError(getRuntime(), "invalid keyspec", e);
}
catch (NoSuchAlgorithmException e) {
@@ -531,9 +642,9 @@ private IRubyObject setKeySpecComponent(final int index, final IRubyObject value
this.dsa_x = this.dsa_p = this.dsa_q = this.dsa_g = null;
}
- if ( dsa_y != null && _dsa_p != null && _dsa_q != null && _dsa_g != null ) {
+ if ( dsa_y != null && dsa_p != null && dsa_q != null && dsa_g != null ) {
// we now have all public key components. create the key :
- DSAPublicKeySpec spec = new DSAPublicKeySpec(dsa_y, _dsa_p, _dsa_q, _dsa_g);
+ DSAPublicKeySpec spec = new DSAPublicKeySpec(dsa_y, dsa_p, dsa_q, dsa_g);
try {
this.publicKey = (DSAPublicKey) SecurityHelper.getKeyFactory("DSA").generatePublic(spec);
}
@@ -546,8 +657,6 @@ private IRubyObject setKeySpecComponent(final int index, final IRubyObject value
// clear out the specValues
this.dsa_y = this.dsa_p = this.dsa_q = this.dsa_g = null;
}
-
- return value;
}
private static final int SPEC_X = 0;
diff --git a/src/main/java/org/jruby/ext/openssl/PKeyEC.java b/src/main/java/org/jruby/ext/openssl/PKeyEC.java
index d0369cb9..e698e425 100644
--- a/src/main/java/org/jruby/ext/openssl/PKeyEC.java
+++ b/src/main/java/org/jruby/ext/openssl/PKeyEC.java
@@ -11,6 +11,7 @@
import java.io.StringReader;
import java.io.StringWriter;
import java.math.BigInteger;
+import java.security.AlgorithmParameters;
import java.security.GeneralSecurityException;
import java.security.InvalidKeyException;
import java.security.KeyFactory;
@@ -20,7 +21,6 @@
import java.security.PrivateKey;
import java.security.PublicKey;
-import java.security.SecureRandom;
import java.security.interfaces.ECPrivateKey;
import java.security.interfaces.ECPublicKey;
import java.security.spec.ECGenParameterSpec;
@@ -30,35 +30,60 @@
import java.security.spec.ECPublicKeySpec;
import java.security.spec.EllipticCurve;
import java.security.spec.InvalidKeySpecException;
+import java.security.spec.InvalidParameterSpecException;
import java.util.Collections;
import java.util.Enumeration;
import java.util.List;
+import java.util.Locale;
+import java.util.Optional;
import javax.crypto.KeyAgreement;
+
import org.bouncycastle.asn1.ASN1EncodableVector;
+import org.bouncycastle.asn1.ASN1Encoding;
+import org.bouncycastle.asn1.ASN1InputStream;
import org.bouncycastle.asn1.ASN1Integer;
import org.bouncycastle.asn1.ASN1ObjectIdentifier;
import org.bouncycastle.asn1.ASN1OutputStream;
-import org.bouncycastle.asn1.DLSequence;
-
-import org.bouncycastle.crypto.params.ECNamedDomainParameters;
+import org.bouncycastle.asn1.ASN1Primitive;
+import org.bouncycastle.asn1.ASN1Sequence;
+import org.bouncycastle.asn1.DERNull;
+import org.bouncycastle.asn1.DERSequence;
+import org.bouncycastle.asn1.pkcs.PrivateKeyInfo;
+import org.bouncycastle.asn1.x509.AlgorithmIdentifier;
+import org.bouncycastle.asn1.x509.SubjectPublicKeyInfo;
+import org.bouncycastle.asn1.x9.X962Parameters;
+import org.bouncycastle.asn1.x9.X9ECParameters;
+import org.bouncycastle.asn1.x9.X9ECPoint;
+import org.bouncycastle.asn1.x9.X9ObjectIdentifiers;
+import org.bouncycastle.crypto.params.ECDomainParameters;
import org.bouncycastle.crypto.params.ECPrivateKeyParameters;
+import org.bouncycastle.crypto.params.ECPublicKeyParameters;
import org.bouncycastle.crypto.signers.ECDSASigner;
+import org.bouncycastle.jcajce.provider.asymmetric.util.EC5Util;
+import org.bouncycastle.jcajce.provider.asymmetric.util.ECUtil;
+import org.bouncycastle.jcajce.provider.config.ProviderConfiguration;
import org.bouncycastle.jce.ECNamedCurveTable;
import org.bouncycastle.jce.ECPointUtil;
+import org.bouncycastle.jce.provider.BouncyCastleProvider;
import org.bouncycastle.jce.spec.ECNamedCurveParameterSpec;
import org.bouncycastle.jce.spec.ECNamedCurveSpec;
-
+import org.bouncycastle.math.ec.ECAlgorithms;
+import org.bouncycastle.math.ec.ECCurve;
import org.jruby.Ruby;
import org.jruby.RubyArray;
+import org.jruby.RubyBignum;
import org.jruby.RubyBoolean;
import org.jruby.RubyClass;
+import org.jruby.RubyFixnum;
import org.jruby.RubyModule;
import org.jruby.RubyObject;
import org.jruby.RubyString;
+import org.jruby.RubySymbol;
import org.jruby.anno.JRubyClass;
import org.jruby.anno.JRubyMethod;
import org.jruby.exceptions.RaiseException;
import org.jruby.runtime.Arity;
+import org.jruby.runtime.Block;
import org.jruby.runtime.ObjectAllocator;
import org.jruby.runtime.ThreadContext;
import org.jruby.runtime.Visibility;
@@ -66,14 +91,15 @@
import org.jruby.runtime.component.VariableEntry;
import org.jruby.ext.openssl.impl.CipherSpec;
-import static org.jruby.ext.openssl.OpenSSL.debug;
-import static org.jruby.ext.openssl.OpenSSL.debugStackTrace;
-import static org.jruby.ext.openssl.PKey._PKey;
import org.jruby.ext.openssl.impl.ECPrivateKeyWithName;
-import static org.jruby.ext.openssl.impl.PKey.readECPrivateKey;
+
import org.jruby.ext.openssl.util.ByteArrayOutputStream;
import org.jruby.ext.openssl.x509store.PEMInputOutput;
+import static org.jruby.ext.openssl.OpenSSL.debug;
+import static org.jruby.ext.openssl.OpenSSL.debugStackTrace;
+import static org.jruby.ext.openssl.impl.PKey.readECPrivateKey;
+
/**
* OpenSSL::PKey::EC implementation.
*
@@ -104,10 +130,14 @@ static RubyClass _EC(final Ruby runtime) {
return _PKey(runtime).getClass("EC");
}
- public static RaiseException newECError(Ruby runtime, String message) {
+ private static RaiseException newECError(Ruby runtime, String message) {
return Utils.newError(runtime, _PKey(runtime).getClass("ECError"), message);
}
+ private static RaiseException newECError(Ruby runtime, String message, Exception cause) {
+ return Utils.newError(runtime, _PKey(runtime).getClass("ECError"), message, cause);
+ }
+
@JRubyMethod(meta = true)
public static RubyArray builtin_curves(ThreadContext context, IRubyObject self) {
final Ruby runtime = context.runtime;
@@ -152,37 +182,23 @@ public static RubyArray builtin_curves(ThreadContext context, IRubyObject self)
return curves;
}
- private static ASN1ObjectIdentifier getCurveOID(final String curveName) {
- ASN1ObjectIdentifier id;
- id = org.bouncycastle.asn1.sec.SECNamedCurves.getOID(curveName);
- if ( id != null ) return id;
- id = org.bouncycastle.asn1.x9.X962NamedCurves.getOID(curveName);
- if ( id != null ) return id;
- id = org.bouncycastle.asn1.nist.NISTNamedCurves.getOID(curveName);
- if ( id != null ) return id;
- id = org.bouncycastle.asn1.teletrust.TeleTrusTNamedCurves.getOID(curveName);
- if ( id != null ) return id;
- throw new IllegalStateException("could not identify curve name: " + curveName);
+ private static Optional getCurveOID(String curveName) {
+ if (curveName == null) return Optional.empty();
+ // work-around getNamedCurveOid not being able to handle "... " (assuming spacePos + 1 is valid index)
+ if (curveName.indexOf(' ') == curveName.length() - 1) return Optional.empty();
+ return Optional.ofNullable(ECUtil.getNamedCurveOid(curveName));
}
private static boolean isCurveName(final String curveName) {
- try {
- return getCurveOID(curveName) != null;
- }
- catch (IllegalStateException ex) { return false; }
+ return getCurveOID(curveName).isPresent();
}
private static String getCurveName(final ASN1ObjectIdentifier oid) {
- String name;
- name = org.bouncycastle.asn1.sec.SECNamedCurves.getName(oid);
- if ( name != null ) return name;
- name = org.bouncycastle.asn1.x9.X962NamedCurves.getName(oid);
- if ( name != null ) return name;
- name = org.bouncycastle.asn1.nist.NISTNamedCurves.getName(oid);
- if ( name != null ) return name;
- name = org.bouncycastle.asn1.teletrust.TeleTrusTNamedCurves.getName(oid);
- if ( name != null ) return name;
- throw new IllegalStateException("could not identify curve name from: " + oid);
+ final String name = ECUtil.getCurveName(oid);
+ if (name == null) {
+ throw new IllegalStateException("could not identify curve name from: " + oid);
+ }
+ return name;
}
public PKeyEC(Ruby runtime, RubyClass type) {
@@ -195,8 +211,13 @@ public PKeyEC(Ruby runtime, RubyClass type) {
PKeyEC(Ruby runtime, RubyClass type, PrivateKey privKey, PublicKey pubKey) {
super(runtime, type);
- this.privateKey = privKey;
this.publicKey = (ECPublicKey) pubKey;
+ if (privKey instanceof ECPrivateKey) {
+ setPrivateKey((ECPrivateKey) privKey);
+ } else {
+ this.privateKey = privKey;
+ setCurveNameFromPublicKeyIfNeeded();
+ }
}
private transient Group group;
@@ -206,11 +227,16 @@ public PKeyEC(Ruby runtime, RubyClass type) {
private String curveName;
- private String getCurveName() { return curveName; }
+ private String getCurveName() {
+ if (curveName == null && group != null) {
+ curveName = group.getCurveName();
+ }
+ return curveName;
+ }
-// private ECNamedCurveParameterSpec getParameterSpec() {
-// return ECNamedCurveTable.getParameterSpec( getCurveName() );
-// }
+ private ECNamedCurveParameterSpec getParameterSpec() {
+ return ECNamedCurveTable.getParameterSpec(getCurveName());
+ }
@Override
public PublicKey getPublicKey() { return publicKey; }
@@ -219,10 +245,13 @@ public PKeyEC(Ruby runtime, RubyClass type) {
public PrivateKey getPrivateKey() { return privateKey; }
@Override
- public String getAlgorithm() { return "EC"; }
+ public String getAlgorithm() { return "ECDSA"; }
+
+ @Override
+ public String getKeyType() { return "EC"; }
@JRubyMethod(rest = true, visibility = Visibility.PRIVATE)
- public IRubyObject initialize(final ThreadContext context, final IRubyObject[] args) {
+ public IRubyObject initialize(final ThreadContext context, final IRubyObject[] args, Block block) {
final Ruby runtime = context.runtime;
privateKey = null; publicKey = null;
@@ -234,18 +263,17 @@ public IRubyObject initialize(final ThreadContext context, final IRubyObject[] a
IRubyObject arg = args[0];
if ( arg instanceof Group ) {
- this.group = (Group) arg;
- this.curveName = this.group.getCurveName();
+ setGroup((Group) arg);
return this;
}
IRubyObject pass = null;
if ( args.length > 1 ) pass = args[1];
- final char[] passwd = password(pass);
+ final char[] passwd = password(context, pass, block);
final RubyString str = readInitArg(context, arg);
final String strJava = str.toString();
- if ( isCurveName(strJava) ) {
+ if (isCurveName(strJava)) {
this.curveName = strJava;
return this;
}
@@ -253,17 +281,17 @@ public IRubyObject initialize(final ThreadContext context, final IRubyObject[] a
Object key = null;
final KeyFactory ecdsaFactory;
try {
- ecdsaFactory = SecurityHelper.getKeyFactory("ECDSA");
+ ecdsaFactory = SecurityHelper.getKeyFactory("EC");
}
catch (NoSuchAlgorithmException e) {
- throw runtime.newRuntimeError("unsupported key algorithm (ECDSA)");
+ throw runtime.newRuntimeError("unsupported key algorithm (EC)");
}
catch (RuntimeException e) {
- throw runtime.newRuntimeError("unsupported key algorithm (ECDSA) " + e);
+ throw runtime.newRuntimeError("unsupported key algorithm (EC) " + e);
}
// TODO: ugly NoClassDefFoundError catching for no BC env. How can we remove this?
boolean noClassDef = false;
- if ( key == null && ! noClassDef ) { // PEM_read_bio_DSAPrivateKey
+ if ( key == null && ! noClassDef ) {
try {
key = readPrivateKey(strJava, passwd);
}
@@ -295,35 +323,16 @@ public IRubyObject initialize(final ThreadContext context, final IRubyObject[] a
key = readECPrivateKey(ecdsaFactory, str.getBytes());
}
catch (NoClassDefFoundError e) { noClassDef = true; debugStackTrace(runtime, e); }
- catch (InvalidKeySpecException e) { debug(runtime, "PKeyEC could not read private key", e); }
- catch (IOException e) { debug(runtime, "PKeyEC could not read private key", e); }
+ catch (InvalidKeySpecException|IOException e) { debug(runtime, "PKeyEC could not read private key", e); }
catch (RuntimeException e) {
if ( isKeyGenerationFailure(e) ) debug(runtime, "PKeyEC could not read private key", e);
else debugStackTrace(runtime, e);
}
}
-// if ( key == null && ! noClassDef ) {
-// try { // readECParameters
-// ASN1ObjectIdentifier oid = ASN1ObjectIdentifier.getInstance(str.getBytes());
-// ECNamedCurveParameterSpec paramSpec = ECNamedCurveTable.getParameterSpec(oid.getId());
-//
-// // ecdsaFactory.generatePublic(keySpec)
-//
-// }
-// catch (NoClassDefFoundError e) { noClassDef = true; debugStackTrace(runtime, e); }
-// catch (InvalidKeySpecException e) { debug(runtime, "PKeyEC could not read public key", e); }
-// catch (IOException e) { debug(runtime, "PKeyEC could not read public key", e); }
-// catch (RuntimeException e) {
-// if ( isKeyGenerationFailure(e) ) debug(runtime, "PKeyEC could not read public key", e);
-// else debugStackTrace(runtime, e);
-// }
-// }
if ( key == null ) key = tryPKCS8EncodedKey(runtime, ecdsaFactory, str.getBytes());
if ( key == null ) key = tryX509EncodedKey(runtime, ecdsaFactory, str.getBytes());
- if ( key == null ) throw newECError(runtime, "Neither PUB key nor PRIV key:");
-
if ( key instanceof KeyPair ) {
final PublicKey pubKey = ((KeyPair) key).getPublic();
final PrivateKey privKey = ((KeyPair) key).getPrivate();
@@ -334,28 +343,39 @@ public IRubyObject initialize(final ThreadContext context, final IRubyObject[] a
throw newECError(runtime, "Neither PUB key nor PRIV key: (invalid key type " + privKey.getClass().getName() + ")");
}
this.publicKey = (ECPublicKey) pubKey;
- this.privateKey = (ECPrivateKey) privKey;
- unwrapPrivateKeyWithName();
+ setPrivateKey((ECPrivateKey) privKey);
}
else if ( key instanceof ECPrivateKey ) {
- this.privateKey = (ECPrivateKey) key;
- unwrapPrivateKeyWithName();
+ setPrivateKey((ECPrivateKey) key);
}
else if ( key instanceof ECPublicKey ) {
- this.publicKey = (ECPublicKey) key; this.privateKey = null;
+ this.publicKey = (ECPublicKey) key;
+ this.privateKey = null;
}
else {
- throw newECError(runtime, "Neither PUB key nor PRIV key: " + key.getClass().getName());
+ throw newECError(runtime, "Neither PUB key nor PRIV key: ");
}
- if ( publicKey != null ) {
- publicKey.getParams().getCurve();
- }
- // TODO set curveName ?!?!?!?!?!?!?!
+ setCurveNameFromPublicKeyIfNeeded();
return this;
}
+ private void setCurveNameFromPublicKeyIfNeeded() {
+ if (curveName == null && publicKey != null) {
+ final String oid = getCurveNameObjectIdFromKey(getRuntime(), publicKey);
+ final Optional curveId = getCurveOID(oid);
+ if (curveId.isPresent()) {
+ this.curveName = getCurveName(curveId.get());
+ }
+ }
+ }
+
+ void setPrivateKey(final ECPrivateKey key) {
+ this.privateKey = key;
+ unwrapPrivateKeyWithName();
+ }
+
private void unwrapPrivateKeyWithName() {
final ECPrivateKey privKey = (ECPrivateKey) this.privateKey;
if ( privKey instanceof ECPrivateKeyWithName ) {
@@ -364,6 +384,40 @@ private void unwrapPrivateKeyWithName() {
}
}
+ private static String getCurveNameObjectIdFromKey(final Ruby runtime, final ECPublicKey key) {
+ try {
+ AlgorithmParameters algParams = AlgorithmParameters.getInstance("EC");
+ algParams.init(key.getParams());
+ return algParams.getParameterSpec(ECGenParameterSpec.class).getName();
+ }
+ catch (NoSuchAlgorithmException|InvalidParameterSpecException ex) {
+ throw newECError(runtime, ex.getMessage());
+ }
+ catch (Exception ex) {
+ throw (RaiseException) newECError(runtime, ex.toString()).initCause(ex);
+ }
+ }
+
+ private void setGroup(final Group group) {
+ this.group = group;
+ this.curveName = this.group.getCurveName();
+ }
+
+ @JRubyMethod(visibility = Visibility.PRIVATE)
+ @Override
+ public IRubyObject initialize_copy(final IRubyObject original) {
+ if (this == original) return this;
+ checkFrozen();
+
+ final PKeyEC that = (PKeyEC) original;
+ this.publicKey = that.publicKey;
+ this.privateKey = that.privateKey;
+ this.curveName = that.curveName;
+ this.group = that.group;
+
+ return this;
+ }
+
//private static ECNamedCurveParameterSpec readECParameters(final byte[] input) throws IOException {
// ASN1ObjectIdentifier oid = ASN1ObjectIdentifier.getInstance(input);
// return ECNamedCurveTable.getParameterSpec(oid.getId());
@@ -374,13 +428,12 @@ public IRubyObject check_key(final ThreadContext context) {
return context.runtime.getTrue(); // TODO not implemented stub
}
- @JRubyMethod(name = "generate_key")
+ @JRubyMethod(name = "generate_key!", alias = "generate_key")
public PKeyEC generate_key(final ThreadContext context) {
- // final ECDomainParameters params = getDomainParameters();
try {
ECGenParameterSpec genSpec = new ECGenParameterSpec(getCurveName());
- KeyPairGenerator gen = SecurityHelper.getKeyPairGenerator("ECDSA"); // "BC"
- gen.initialize(genSpec, new SecureRandom());
+ KeyPairGenerator gen = SecurityHelper.getKeyPairGenerator("EC"); // "BC"
+ gen.initialize(genSpec, OpenSSL.getSecureRandom(context));
KeyPair pair = gen.generateKeyPair();
this.publicKey = (ECPublicKey) pair.getPublic();
this.privateKey = pair.getPrivate();
@@ -391,56 +444,84 @@ public PKeyEC generate_key(final ThreadContext context) {
return this;
}
+ @JRubyMethod(meta = true)
+ public static IRubyObject generate(final ThreadContext context, final IRubyObject self, final IRubyObject group) {
+ PKeyEC randomKey = new PKeyEC(context.runtime, (RubyClass) self);
+
+ if (group instanceof Group) {
+ randomKey.setGroup((Group) group);
+ } else {
+ randomKey.curveName = group.convertToString().toString();
+ }
+
+ return randomKey.generate_key(context);
+ }
+
@JRubyMethod(name = "dsa_sign_asn1")
public IRubyObject dsa_sign_asn1(final ThreadContext context, final IRubyObject data) {
+ if (privateKey == null) {
+ throw newECError(context.runtime, "Private EC key needed!");
+ }
try {
- ECNamedCurveParameterSpec params = ECNamedCurveTable.getParameterSpec(getCurveName());
- ASN1ObjectIdentifier oid = getCurveOID(getCurveName());
- ECNamedDomainParameters domainParams = new ECNamedDomainParameters(oid,
- params.getCurve(), params.getG(), params.getN(), params.getH(), params.getSeed()
- );
+ final ECNamedCurveParameterSpec params = getParameterSpec();
final ECDSASigner signer = new ECDSASigner();
- final ECPrivateKey privKey = (ECPrivateKey) this.privateKey;
- signer.init(true, new ECPrivateKeyParameters(privKey.getS(), domainParams));
-
- final byte[] message = data.convertToString().getBytes();
- BigInteger[] signature = signer.generateSignature(message); // [r, s]
-
-// final byte[] r = signature[0].toByteArray();
-// final byte[] s = signature[1].toByteArray();
-// // ASN.1 encode as: 0x30 len 0x02 rlen (r) 0x02 slen (s)
-// final int len = 1 + (1 + r.length) + 1 + (1 + s.length);
-//
-// final byte[] encoded = new byte[1 + 1 + len]; int i;
-// encoded[0] = 0x30;
-// encoded[1] = (byte) len;
-// encoded[2] = 0x20;
-// encoded[3] = (byte) r.length;
-// System.arraycopy(r, 0, encoded, i = 4, r.length); i += r.length;
-// encoded[i++] = 0x20;
-// encoded[i++] = (byte) s.length;
-// System.arraycopy(s, 0, encoded, i, s.length);
+ signer.init(true, new ECPrivateKeyParameters(
+ ((ECPrivateKey) this.privateKey).getS(),
+ new ECDomainParameters(params.getCurve(), params.getG(), params.getN(), params.getH())
+ ));
+
+ BigInteger[] signature = signer.generateSignature(data.convertToString().getBytes()); // [r, s]
ByteArrayOutputStream bytes = new ByteArrayOutputStream();
- ASN1OutputStream asn1 = new ASN1OutputStream(bytes);
+ ASN1OutputStream asn1 = ASN1OutputStream.create(bytes, ASN1Encoding.DER);
- ASN1EncodableVector v = new ASN1EncodableVector();
+ ASN1EncodableVector v = new ASN1EncodableVector(2);
v.add(new ASN1Integer(signature[0])); // r
v.add(new ASN1Integer(signature[1])); // s
- asn1.writeObject(new DLSequence(v));
+ asn1.writeObject(new DERSequence(v));
+ asn1.close();
return StringHelper.newString(context.runtime, bytes.buffer(), bytes.size());
}
catch (IOException ex) {
- throw newECError(context.runtime, ex.toString());
+ throw newECError(context.runtime, ex.getMessage());
}
- catch (RuntimeException ex) {
- throw newECError(context.runtime, ex.toString());
+ catch (Exception ex) {
+ throw newECError(context.runtime, ex.toString(), ex);
}
}
+ @JRubyMethod(name = "dsa_verify_asn1")
+ public IRubyObject dsa_verify_asn1(final ThreadContext context, final IRubyObject data, final IRubyObject sign) {
+ final Ruby runtime = context.runtime;
+ try {
+ final ECNamedCurveParameterSpec params = getParameterSpec();
+
+ final ECDSASigner signer = new ECDSASigner();
+ signer.init(false, new ECPublicKeyParameters(
+ EC5Util.convertPoint(publicKey.getParams(), publicKey.getW()),
+ new ECDomainParameters(params.getCurve(), params.getG(), params.getN(), params.getH())
+ ));
+
+ ASN1Primitive vec = new ASN1InputStream(sign.convertToString().getBytes()).readObject();
+
+ if (!(vec instanceof ASN1Sequence)) {
+ throw newECError(runtime, "invalid signature (not a sequence)");
+ }
+
+ ASN1Sequence seq = (ASN1Sequence) vec;
+ ASN1Integer r = ASN1Integer.getInstance(seq.getObjectAt(0));
+ ASN1Integer s = ASN1Integer.getInstance(seq.getObjectAt(1));
+
+ boolean verify = signer.verifySignature(data.convertToString().getBytes(), r.getPositiveValue(), s.getPositiveValue());
+ return runtime.newBoolean(verify);
+ }
+ catch (IOException|IllegalArgumentException|IllegalStateException ex) {
+ throw newECError(runtime, "invalid signature: " + ex.getMessage(), ex);
+ }
+ }
@JRubyMethod(name = "dh_compute_key")
public IRubyObject dh_compute_key(final ThreadContext context, final IRubyObject point) {
@@ -462,23 +543,22 @@ public IRubyObject dh_compute_key(final ThreadContext context, final IRubyObject
final byte[] secret = agreement.generateSecret();
return StringHelper.newString(context.runtime, secret);
}
- catch (NoSuchAlgorithmException ex) {
- throw newECError(context.runtime, ex.toString());
- }
catch (InvalidKeyException ex) {
- throw newECError(context.runtime, ex.toString());
+ throw newECError(context.runtime, "invalid key: " + ex.getMessage());
}
catch (GeneralSecurityException ex) {
throw newECError(context.runtime, ex.toString());
}
}
+ @JRubyMethod
+ public IRubyObject oid() {
+ return getRuntime().newString("id-ecPublicKey");
+ }
+
private Group getGroup(boolean required) {
if (group == null) {
- if (publicKey != null) {
- return group = new Group(getRuntime(), this);
- }
- if (required) throw new IllegalStateException("no group (without public key)");
+ return group = new Group(getRuntime(), this);
}
return group;
}
@@ -516,7 +596,7 @@ public IRubyObject set_public_key(final ThreadContext context, final IRubyObject
final Point point = (Point) arg;
ECPublicKeySpec keySpec = new ECPublicKeySpec(point.asECPoint(), getParamSpec());
try {
- this.publicKey = (ECPublicKey) SecurityHelper.getKeyFactory("ECDSA").generatePublic(keySpec);
+ this.publicKey = (ECPublicKey) SecurityHelper.getKeyFactory("EC").generatePublic(keySpec);
return arg;
}
catch (GeneralSecurityException ex) {
@@ -524,9 +604,13 @@ public IRubyObject set_public_key(final ThreadContext context, final IRubyObject
}
}
+ /**
+ * @see ECNamedCurveSpec
+ */
private static ECParameterSpec getParamSpec(final String curveName) {
- ECNamedCurveParameterSpec spec = ECNamedCurveTable.getParameterSpec(curveName);
- return new ECNamedCurveSpec(spec.getName(), spec.getCurve(), spec.getG(), spec.getN(), spec.getH(), spec.getSeed());
+ final ECNamedCurveParameterSpec ecCurveParamSpec = ECNamedCurveTable.getParameterSpec(curveName);
+ final EllipticCurve curve = EC5Util.convertCurve(ecCurveParamSpec.getCurve(), ecCurveParamSpec.getSeed());
+ return EC5Util.convertSpec(curve, ecCurveParamSpec);
}
private ECParameterSpec getParamSpec() {
@@ -554,7 +638,7 @@ public IRubyObject set_private_key(final ThreadContext context, final IRubyObjec
}
ECPrivateKeySpec keySpec = new ECPrivateKeySpec(s, getParamSpec());
try {
- this.privateKey = SecurityHelper.getKeyFactory("ECDSA").generatePrivate(keySpec);
+ this.privateKey = SecurityHelper.getKeyFactory("EC").generatePrivate(keySpec);
return arg;
}
catch (GeneralSecurityException ex) {
@@ -562,74 +646,194 @@ public IRubyObject set_private_key(final ThreadContext context, final IRubyObjec
}
}
- @JRubyMethod(name = "public_key?")
+ @JRubyMethod(name = "public?", alias = "public_key?")
public RubyBoolean public_p() {
return publicKey != null ? getRuntime().getTrue() : getRuntime().getFalse();
}
- @JRubyMethod(name = "private_key?")
+ @JRubyMethod(name = "private?", alias = "private_key?")
public RubyBoolean private_p() {
return privateKey != null ? getRuntime().getTrue() : getRuntime().getFalse();
}
+ @JRubyMethod
+ public RubyString public_to_der(ThreadContext context) {
+ return public_to_der(context.runtime);
+ }
+
+ private RubyString public_to_der(final Ruby runtime) {
+ final byte[] bytes;
+ try {
+ bytes = publicKey.getEncoded();
+ } catch (Exception e) {
+ throw newECError(runtime, e.getMessage(), e);
+ }
+ return StringHelper.newString(runtime, bytes);
+ }
+
@Override
@JRubyMethod(name = "to_der")
public RubyString to_der() {
- final byte[] bytes;
+ final Ruby runtime = getRuntime();
+ if (publicKey != null && privateKey == null) {
+ return public_to_der(runtime);
+ }
+ if (privateKey == null) {
+ throw new IllegalStateException("private key as well as public key are null");
+ }
+
try {
- bytes = toDER();
+ byte[] encoded = toPrivateKeyStructure((ECPrivateKey) privateKey, publicKey, false).getEncoded(ASN1Encoding.DER);
+ return StringHelper.newString(runtime, encoded);
+ } catch (Exception e) {
+ throw newECError(runtime, e.getMessage(), e);
+ }
+ }
+
+ @JRubyMethod
+ public RubyString private_to_der(ThreadContext context) {
+ return private_to_der(context.runtime);
+ }
+
+ private RubyString private_to_der(final Ruby runtime) {
+ final byte[] encoded;
+ if (privateKey instanceof ECPrivateKey) {
+ try {
+ encoded = toPrivateKeyInfo((ECPrivateKey) privateKey, publicKey).getEncoded(ASN1Encoding.DER);
+ } catch (IOException e) {
+ throw newECError(runtime, e.getMessage(), e);
+ }
+ } else {
+ try {
+ encoded = privateKey.getEncoded();
+ } catch (Exception e) {
+ throw newECError(runtime, e.getMessage(), e);
+ }
}
- catch (IOException e) {
- throw newECError(getRuntime(), e.getMessage());
+ return StringHelper.newString(runtime, encoded);
+ }
+
+ private static org.bouncycastle.asn1.sec.ECPrivateKey toPrivateKeyStructure(final ECPrivateKey privateKey,
+ final ECPublicKey publicKey,
+ final boolean compressed) throws IOException {
+ final ProviderConfiguration configuration = BouncyCastleProvider.CONFIGURATION;
+ final ECParameterSpec ecSpec = privateKey.getParams();
+ final X962Parameters params = getDomainParametersFromName(ecSpec, compressed);
+
+ int orderBitLength = ECUtil.getOrderBitLength(configuration, ecSpec == null ? null : ecSpec.getOrder(), privateKey.getS());
+
+ if (publicKey == null) {
+ return new org.bouncycastle.asn1.sec.ECPrivateKey(orderBitLength, privateKey.getS(), params);
}
- return StringHelper.newString(getRuntime(), bytes);
+
+ SubjectPublicKeyInfo info = SubjectPublicKeyInfo.getInstance(ASN1Primitive.fromByteArray(publicKey.getEncoded()));
+ return new org.bouncycastle.asn1.sec.ECPrivateKey(orderBitLength, privateKey.getS(), info.getPublicKeyData(), params);
}
- private byte[] toDER() throws IOException {
- if ( publicKey != null && privateKey == null ) {
- return publicKey.getEncoded();
+ private static PrivateKeyInfo toPrivateKeyInfo(final ECPrivateKey privateKey,
+ final ECPublicKey publicKey) throws IOException {
+ final ECParameterSpec ecSpec = privateKey.getParams();
+ final X962Parameters params = getDomainParametersFromName(ecSpec, false);
+
+ org.bouncycastle.asn1.sec.ECPrivateKey keyStructure = toPrivateKeyStructure(privateKey, publicKey, false);
+ return new PrivateKeyInfo(new AlgorithmIdentifier(X9ObjectIdentifiers.id_ecPublicKey, params), keyStructure);
+ }
+
+ private static X962Parameters getDomainParametersFromName(ECParameterSpec ecSpec, boolean compressed) {
+ if (ecSpec instanceof ECNamedCurveSpec) {
+ ASN1ObjectIdentifier curveOid = ECUtil.getNamedCurveOid(((ECNamedCurveSpec)ecSpec).getName());
+ if (curveOid == null)
+ {
+ curveOid = new ASN1ObjectIdentifier(((ECNamedCurveSpec)ecSpec).getName());
+ }
+ return new X962Parameters(curveOid);
}
- if ( privateKey == null ) {
- throw new IllegalStateException("private key as well as public key are null");
+ if (ecSpec == null) {
+ return new X962Parameters(DERNull.INSTANCE);
}
- return privateKey.getEncoded();
+ ECCurve curve = EC5Util.convertCurve(ecSpec.getCurve());
+
+ X9ECParameters ecParameters = new X9ECParameters(
+ curve,
+ new X9ECPoint(EC5Util.convertPoint(curve, ecSpec.getGenerator()), compressed),
+ ecSpec.getOrder(),
+ BigInteger.valueOf(ecSpec.getCofactor()),
+ ecSpec.getCurve().getSeed());
+
+ return new X962Parameters(ecParameters);
}
@Override
@JRubyMethod(name = "to_pem", alias = "export", rest = true)
- public RubyString to_pem(final IRubyObject[] args) {
- Arity.checkArgumentCount(getRuntime(), args, 0, 2);
+ public RubyString to_pem(ThreadContext context, final IRubyObject[] args) {
+ Arity.checkArgumentCount(context.runtime, args, 0, 2);
CipherSpec spec = null; char[] passwd = null;
if ( args.length > 0 ) {
spec = cipherSpec( args[0] );
- if ( args.length > 1 ) passwd = password( args[1] );
+ if ( args.length > 1 ) passwd = password(context, args[1], null);
+ }
+
+ if (privateKey == null) {
+ return public_to_pem(context);
}
try {
final StringWriter writer = new StringWriter();
- if ( privateKey != null ) {
- PEMInputOutput.writeECPrivateKey(writer, (ECPrivateKey) privateKey, spec, passwd);
- }
- else {
- PEMInputOutput.writeECPublicKey(writer, publicKey);
- }
- return RubyString.newString(getRuntime(), writer.getBuffer());
+ PEMInputOutput.writeECPrivateKey(writer, (ECPrivateKey) privateKey, spec, passwd);
+ return RubyString.newString(context.runtime, writer.getBuffer());
+ } catch (IOException ex) {
+ throw newECError(context.runtime, ex.getMessage());
}
- catch (IOException ex) {
- throw newECError(getRuntime(), ex.getMessage());
+ }
+
+ @JRubyMethod
+ public RubyString public_to_pem(ThreadContext context) {
+ try {
+ final StringWriter writer = new StringWriter();
+ PEMInputOutput.writeECPublicKey(writer, publicKey);
+ return RubyString.newString(context.runtime, writer.getBuffer());
+ } catch (IOException ex) {
+ throw newECError(context.runtime, ex.getMessage());
+ }
+ }
+
+ @JRubyMethod
+ public RubyString to_text() {
+ StringBuilder result = new StringBuilder();
+ final ECParameterSpec spec = getParamSpec();
+ result.append("Private-Key: (").append(spec.getOrder().bitLength()).append(" bit)").append('\n');
+
+ if (privateKey instanceof ECPrivateKey) {
+ result.append("priv:");
+ addSplittedAndFormatted(result, ((ECPrivateKey) privateKey).getS());
+ }
+
+ if (publicKey != null) {
+ result.append("pub:");
+ final byte[] pubBytes = encodeCompressed(publicKey.getW());
+ final StringBuilder hexBytes = new StringBuilder(pubBytes.length * 2);
+ for (byte b: pubBytes) hexBytes.append(Integer.toHexString(Byte.toUnsignedInt(b)));
+ addSplittedAndFormatted(result, hexBytes);
+ }
+ result.append("ASN1 OID: ").append(getCurveName()).append('\n');
+
+ return RubyString.newString(getRuntime(), result);
+ }
+
+ private enum PointConversion {
+ COMPRESSED, UNCOMPRESSED, HYBRID;
+
+ String toRubyString() {
+ return super.toString().toLowerCase(Locale.ROOT);
}
}
@JRubyClass(name = "OpenSSL::PKey::EC::Group")
public static final class Group extends RubyObject {
- private static final ObjectAllocator ALLOCATOR = new ObjectAllocator() {
- public Group allocate(Ruby runtime, RubyClass klass) { return new Group(runtime, klass); }
- };
-
static void createGroup(final Ruby runtime, final RubyClass EC, final RubyClass OpenSSLError) {
- RubyClass Group = EC.defineClassUnder("Group", runtime.getObject(), ALLOCATOR);
+ RubyClass Group = EC.defineClassUnder("Group", runtime.getObject(), Group::new);
// OpenSSL::PKey::EC::Group::Error
Group.defineClassUnder("Error", OpenSSLError, OpenSSLError.getAllocator());
@@ -637,9 +841,12 @@ static void createGroup(final Ruby runtime, final RubyClass EC, final RubyClass
Group.defineAnnotatedMethods(Group.class);
}
- private transient PKeyEC key;
- private ECParameterSpec paramSpec;
- private RubyString curve_name;
+ private transient ECParameterSpec paramSpec;
+
+ private PointConversion conversionForm = PointConversion.UNCOMPRESSED;
+
+ private String curveName;
+ private RubyString impl_curve_name;
public Group(Ruby runtime, RubyClass type) {
super(runtime, type);
@@ -647,13 +854,7 @@ public Group(Ruby runtime, RubyClass type) {
Group(Ruby runtime, PKeyEC key) {
this(runtime, _EC(runtime).getClass("Group"));
- this.key = key;
- this.paramSpec = key.publicKey.getParams();
- }
-
- private String getCurveName() {
- if (key != null) return key.getCurveName();
- return curve_name.toString();
+ setCurveName(runtime, key.getCurveName());
}
@JRubyMethod(rest = true, visibility = Visibility.PRIVATE)
@@ -664,74 +865,82 @@ public IRubyObject initialize(final ThreadContext context, final IRubyObject[] a
IRubyObject arg = args[0];
if ( arg instanceof Group ) {
- IRubyObject curve_name = ((Group) arg).curve_name(context);
- this.curve_name = curve_name.isNil() ? null : (RubyString) curve_name;
+ this.curveName = ((Group) arg).curveName;
return this;
}
- this.curve_name = ((RubyString) arg);
-
- // TODO PEM/DER parsing not implemented
+ this.impl_curve_name = arg.convertToString();
}
return this;
}
+ private String getCurveName() {
+ if (curveName == null) {
+ assert impl_curve_name != null;
+ return impl_curve_name.asJavaString();
+ }
+ return curveName;
+ }
+
@Override
@JRubyMethod(name = { "==", "eql?" })
public IRubyObject op_equal(final ThreadContext context, final IRubyObject obj) {
- if ( paramSpec == null ) return context.nil;
+ final Ruby runtime = context.runtime;
if ( obj instanceof Group ) {
final Group that = (Group) obj;
- boolean equals = this.paramSpec.equals(that.paramSpec);
- return context.runtime.newBoolean(equals);
+ return context.runtime.newBoolean(this.implCurveName(runtime).equals(that.implCurveName(runtime)));
}
return context.runtime.getFalse();
}
@JRubyMethod
public IRubyObject curve_name(final ThreadContext context) {
- if (curve_name == null) {
- String prefix, curveName = key.getCurveName();
- // BC 1.54: "brainpoolP512t1" 1.55: "brainpoolp512t1"
- if (curveName.startsWith(prefix = "brainpoolp")) {
- curveName = "brainpoolP" + curveName.substring(prefix.length());
- }
- curve_name = RubyString.newString(context.runtime, curveName);
+ return implCurveName(context.runtime).dup();
+ }
+
+ private RubyString implCurveName(final Ruby runtime) {
+ if (impl_curve_name == null && curveName != null) {
+ setCurveName(runtime, curveName);
+ }
+ return impl_curve_name;
+ }
+
+ private void setCurveName(final Ruby runtime, String curveName) {
+ assert curveName != null;
+ this.curveName = curveName;
+ String prefix;
+ // BC 1.54: "brainpoolP512t1" 1.55: "brainpoolp512t1"
+ if (curveName.startsWith(prefix = "brainpoolp")) {
+ curveName = "brainpoolP" + curveName.substring(prefix.length());
}
- return curve_name.dup();
+ impl_curve_name = RubyString.newString(runtime, curveName);
}
@JRubyMethod
public IRubyObject order(final ThreadContext context) {
- if ( paramSpec == null ) return context.nil;
- return BN.newBN(context.runtime, paramSpec.getOrder());
+ return BN.newBN(context.runtime, getParamSpec().getOrder());
}
@JRubyMethod
public IRubyObject cofactor(final ThreadContext context) {
- if ( paramSpec == null ) return context.nil;
- return context.runtime.newFixnum(paramSpec.getCofactor());
+ return context.runtime.newFixnum(getParamSpec().getCofactor());
}
@JRubyMethod
public IRubyObject seed(final ThreadContext context) {
- if ( paramSpec == null ) return context.nil;
- final byte[] seed = paramSpec.getCurve().getSeed();
+ final byte[] seed = getCurve().getSeed();
return seed == null ? context.nil : StringHelper.newString(context.runtime, seed);
}
@JRubyMethod
public IRubyObject degree(final ThreadContext context) {
- if ( paramSpec == null ) return context.nil;
- final int fieldSize = paramSpec.getCurve().getField().getFieldSize();
+ final int fieldSize = getCurve().getField().getFieldSize();
return context.runtime.newFixnum(fieldSize);
}
@JRubyMethod
public IRubyObject generator(final ThreadContext context) {
- if ( paramSpec == null ) return context.nil;
- final ECPoint generator = paramSpec.getGenerator();
- //final int bitLength = paramSpec.getOrder().bitLength();
+ final ECPoint generator = getParamSpec().getGenerator();
return new Point(context.runtime, generator, this);
}
@@ -742,12 +951,14 @@ public RubyString to_pem(final ThreadContext context, final IRubyObject[] args)
CipherSpec spec = null; char[] passwd = null;
if ( args.length > 0 ) {
spec = cipherSpec( args[0] );
- if ( args.length > 1 ) passwd = password(args[1]);
+ if ( args.length > 1 ) passwd = password(context, args[1], null);
}
try {
final StringWriter writer = new StringWriter();
- PEMInputOutput.writeECParameters(writer, getCurveOID(getCurveName()), spec, passwd);
+ final ASN1ObjectIdentifier oid = getCurveOID(getCurveName())
+ .orElseThrow(() -> newECError(context.runtime, "invalid curve name: " + getCurveName()));
+ PEMInputOutput.writeECParameters(writer, oid, spec, passwd);
return RubyString.newString(context.runtime, writer.getBuffer());
}
catch (IOException ex) {
@@ -755,13 +966,43 @@ public RubyString to_pem(final ThreadContext context, final IRubyObject[] args)
}
}
- final EllipticCurve getCurve() {
+ private ECParameterSpec getParamSpec() {
if (paramSpec == null) {
- paramSpec = getParamSpec(getCurveName());
+ paramSpec = PKeyEC.getParamSpec(getCurveName());
+ }
+ return paramSpec;
+ }
+
+ EllipticCurve getCurve() {
+ return getParamSpec().getCurve();
+ }
+
+ int getBitLength() {
+ return getParamSpec().getOrder().bitLength();
+ }
+
+ @JRubyMethod
+ public RubySymbol point_conversion_form(final ThreadContext context) {
+ return context.runtime.newSymbol(this.conversionForm.toRubyString());
+ }
+
+ @JRubyMethod(name = "point_conversion_form=")
+ public IRubyObject set_point_conversion_form(final ThreadContext context, final IRubyObject form) {
+ this.conversionForm = parse_point_conversion_form(context.runtime, form);
+ return form;
+ }
+
+ static PointConversion parse_point_conversion_form(final Ruby runtime, final IRubyObject form) {
+ if (form instanceof RubySymbol) {
+ final String pointConversionForm = ((RubySymbol) form).asJavaString();
+ if ("uncompressed".equals(pointConversionForm)) return PointConversion.UNCOMPRESSED;
+ if ("compressed".equals(pointConversionForm)) return PointConversion.COMPRESSED;
+ if ("hybrid".equals(pointConversionForm)) return PointConversion.HYBRID;
}
- return paramSpec.getCurve();
+ throw runtime.newArgumentError("unsupported point conversion form: " + form.inspect());
}
+
// @Override
// @JRubyMethod
// @SuppressWarnings("unchecked")
@@ -781,12 +1022,8 @@ final EllipticCurve getCurve() {
@JRubyClass(name = "OpenSSL::PKey::EC::Point")
public static final class Point extends RubyObject {
- private static final ObjectAllocator ALLOCATOR = new ObjectAllocator() {
- public Point allocate(Ruby runtime, RubyClass klass) { return new Point(runtime, klass); }
- };
-
static void createPoint(final Ruby runtime, final RubyClass EC, final RubyClass OpenSSLError) {
- RubyClass Point = EC.defineClassUnder("Point", runtime.getObject(), ALLOCATOR);
+ RubyClass Point = EC.defineClassUnder("Point", runtime.getObject(), Point::new);
// OpenSSL::PKey::EC::Point::Error
Point.defineClassUnder("Error", OpenSSLError, OpenSSLError.getAllocator());
@@ -798,14 +1035,11 @@ public Point(Ruby runtime, RubyClass type) {
super(runtime, type);
}
- // private transient ECPublicKey publicKey;
private ECPoint point;
- //private int bitLength;
private Group group;
Point(Ruby runtime, ECPublicKey publicKey, Group group) {
this(runtime, _EC(runtime).getClass("Point"));
- //this.publicKey = publicKey;
this.point = publicKey.getW();
this.group = group;
}
@@ -821,36 +1055,54 @@ private static RaiseException newError(final Ruby runtime, final String message)
return Utils.newError(runtime, Error, message);
}
- @JRubyMethod(rest = true, visibility = Visibility.PRIVATE)
- public IRubyObject initialize(final ThreadContext context, final IRubyObject[] args) {
- final Ruby runtime = context.runtime;
+ @JRubyMethod(visibility = Visibility.PRIVATE)
+ public IRubyObject initialize(final ThreadContext context, final IRubyObject groupOrPoint) {
+ getPointAndGroup(context, groupOrPoint);
- final int argc = Arity.checkArgumentCount(runtime, args, 1, 2);
- final IRubyObject arg = args[0];
+ return this;
+ }
- if ( arg instanceof Point ) {
- this.group = ((Point) arg).group;
- this.point = ((Point) arg).point;
+ @JRubyMethod(visibility = Visibility.PRIVATE)
+ public IRubyObject initialize(final ThreadContext context, final IRubyObject groupOrPoint, final IRubyObject bn) {
+ if (getPointAndGroup(context, groupOrPoint)) {
return this;
}
- if ( arg instanceof Group ) {
- this.group = (Group) arg;
+ final byte[] encoded;
+ if (bn instanceof BN) {
+ encoded = ((BN) bn).getValue().abs().toByteArray();
+ } else {
+ encoded = bn.convertToString().getBytes();
}
- if ( argc == 2 ) { // (group, bn)
- final byte[] encoded = ((BN) args[1]).getValue().abs().toByteArray();
- try {
- this.point = ECPointUtil.decodePoint(group.getCurve(), encoded);
- }
- catch (IllegalArgumentException ex) {
- // MRI: OpenSSL::PKey::EC::Point::Error: invalid encoding
- throw newError(context.runtime, ex.getMessage());
- }
+ try {
+ this.point = ECPointUtil.decodePoint(group.getCurve(), encoded);
+ }
+ catch (IllegalArgumentException ex) {
+ // MRI: OpenSSL::PKey::EC::Point::Error: invalid encoding
+ throw newError(context.runtime, ex.getMessage());
}
return this;
}
+ private boolean getPointAndGroup(ThreadContext context, IRubyObject groupOrPoint) {
+ final Ruby runtime = context.runtime;
+
+ if ( groupOrPoint instanceof Point) {
+ this.group = ((Point) groupOrPoint).group;
+ this.point = ((Point) groupOrPoint).point;
+ return true;
+ }
+
+ if ( groupOrPoint instanceof Group) {
+ this.group = (Group) groupOrPoint;
+ this.point = this.group.getParamSpec().getGenerator();
+ } else {
+ throw runtime.newTypeError(groupOrPoint, _EC(runtime).getClass("Group"));
+ }
+ return false;
+ }
+
@Override
@JRubyMethod(name = { "==", "eql?" })
public IRubyObject op_equal(final ThreadContext context, final IRubyObject obj) {
@@ -874,16 +1126,50 @@ private ECPoint asECPoint() {
return point; // return publicKey.getW();
}
- private int bitLength() {
- return group.paramSpec.getOrder().bitLength();
+ private PointConversion getPointConversionForm() {
+ if (group == null) return null;
+ return group.conversionForm;
}
@JRubyMethod
public BN to_bn(final ThreadContext context) {
- final byte[] encoded = encode(bitLength(), point);
+ return toBN(context, getPointConversionForm()); // group.point_conversion_form
+ }
+
+ @JRubyMethod
+ public BN to_bn(final ThreadContext context, final IRubyObject conversion_form) {
+ return toBN(context, Group.parse_point_conversion_form(context.runtime, conversion_form));
+ }
+
+ private BN toBN(final ThreadContext context, final PointConversion conversionForm) {
+ final byte[] encoded = encodePoint(conversionForm);
return BN.newBN(context.runtime, new BigInteger(1, encoded));
}
+ private byte[] encodePoint(final PointConversion conversionForm) {
+ final byte[] encoded;
+ switch (conversionForm) {
+ case UNCOMPRESSED:
+ assert group != null;
+ encoded = encodeUncompressed(group.getBitLength(), point);
+ break;
+ case COMPRESSED:
+ encoded = encodeCompressed(point);
+ break;
+ case HYBRID:
+ throw getRuntime().newNotImplementedError(":hybrid compression not implemented");
+ default:
+ throw new AssertionError("unexpected conversion form: " + conversionForm);
+ }
+ return encoded;
+ }
+
+ @JRubyMethod
+ public IRubyObject to_octet_string(final ThreadContext context, final IRubyObject conversion_form) {
+ final PointConversion conversionForm = Group.parse_point_conversion_form(context.runtime, conversion_form);
+ return StringHelper.newString(context.runtime, encodePoint(conversionForm));
+ }
+
private boolean isInfinity() {
return point == ECPoint.POINT_INFINITY;
}
@@ -907,38 +1193,168 @@ public IRubyObject inspect() {
return ObjectSupport.inspect(this, (List) Collections.singletonList(entry));
}
+ @JRubyMethod(name = "add")
+ public IRubyObject add(final ThreadContext context, final IRubyObject other) {
+ Ruby runtime = context.runtime;
+
+ org.bouncycastle.math.ec.ECPoint pointSelf, pointOther, pointResult;
+
+ Group groupV = this.group;
+ Point result;
+
+ ECCurve selfCurve = EC5Util.convertCurve(groupV.getCurve());
+ pointSelf = EC5Util.convertPoint(selfCurve, asECPoint());
+
+ Point otherPoint = (Point) other;
+ ECCurve otherCurve = EC5Util.convertCurve(otherPoint.group.getCurve());
+ pointOther = EC5Util.convertPoint(otherCurve, otherPoint.asECPoint());
+
+ pointResult = pointSelf.add(pointOther);
+ if (pointResult == null) {
+ newECError(runtime, "EC_POINT_add");
+ }
+
+ result = new Point(runtime, EC5Util.convertPoint(pointResult), group);
+
+ return result;
+ }
+
+ @JRubyMethod(name = "mul")
+ public IRubyObject mul(final ThreadContext context, final IRubyObject bn1) {
+ Ruby runtime = context.runtime;
+
+ if (bn1 instanceof RubyArray) {
+ throw runtime.newNotImplementedError("calling #mul with arrays is not supported by this OpenSSL version");
+ }
+
+ org.bouncycastle.math.ec.ECPoint pointSelf;
+
+ Group groupV = this.group;
+
+ ECCurve selfCurve = EC5Util.convertCurve(groupV.getCurve());
+ pointSelf = EC5Util.convertPoint(selfCurve, asECPoint());
+
+ BigInteger bn = getBigInteger(context, bn1);
+
+ org.bouncycastle.math.ec.ECPoint mulPoint = ECAlgorithms.referenceMultiply(pointSelf, bn);
+ if (mulPoint == null) {
+ throw newECError(runtime, "bad multiply result");
+ }
+
+ return new Point(runtime, EC5Util.convertPoint(mulPoint), groupV);
+ }
+
+ @JRubyMethod(name = "mul")
+ public IRubyObject mul(final ThreadContext context, final IRubyObject bn1, final IRubyObject bn2) {
+ Ruby runtime = context.runtime;
+
+ if (bn1 instanceof RubyArray) {
+ throw runtime.newNotImplementedError("calling #mul with arrays is not supported by this OpenSSL version");
+ }
+
+ org.bouncycastle.math.ec.ECPoint pointSelf, pointResult;
+
+ Group groupV = this.group;
+
+ ECCurve selfCurve = EC5Util.convertCurve(groupV.getCurve());
+ pointSelf = EC5Util.convertPoint(selfCurve, asECPoint());
+
+ ECCurve resultCurve = EC5Util.convertCurve(groupV.getCurve());
+ pointResult = EC5Util.convertPoint(resultCurve, ((Point) groupV.generator(context)).asECPoint());
+
+ BigInteger bn = getBigInteger(context, bn1);
+ BigInteger bn_g = getBigInteger(context, bn2);
+
+ org.bouncycastle.math.ec.ECPoint mulPoint = ECAlgorithms.sumOfTwoMultiplies(pointResult, bn_g, pointSelf, bn);
+
+ if (mulPoint == null) {
+ throw newECError(runtime, "bad multiply result");
+ }
+
+ return new Point(runtime, EC5Util.convertPoint(mulPoint), groupV);
+ }
+
+ @JRubyMethod(name = "mul")
+ public IRubyObject mul(final ThreadContext context, final IRubyObject bns, final IRubyObject points, final IRubyObject bn2) {
+ throw context.runtime.newNotImplementedError("calling #mul with arrays is not supported by this OpenSSL version");
+ }
+
+ @Deprecated
+ public IRubyObject initialize(final ThreadContext context, final IRubyObject[] args) {
+ final int argc = Arity.checkArgumentCount(context.runtime, args, 1, 2);
+
+ switch (argc) {
+ case 1:
+ return initialize(context, args[0]);
+ case 2:
+ return initialize(context, args[0], args[1]);
+ default:
+ throw context.runtime.newArgumentError(args.length, 1);
+ }
+ }
+
+ }
+
+ private static BigInteger getBigInteger(ThreadContext context, IRubyObject arg1) {
+ BigInteger bn;
+ if (arg1 instanceof RubyFixnum) {
+ bn = BigInteger.valueOf(arg1.convertToInteger().getLongValue());
+ } else if (arg1 instanceof RubyBignum) {
+ bn = ((RubyBignum) arg1).getValue();
+ } else if (arg1 instanceof BN) {
+ bn = ((BN) arg1).getValue();
+ } else {
+ Ruby runtime = context.runtime;
+ throw runtime.newTypeError(arg1, runtime.getInteger());
+ }
+ return bn;
}
static byte[] encode(final ECPublicKey pubKey) {
- return encode(pubKey.getParams().getOrder().bitLength(), pubKey.getW());
+ return encodeUncompressed(pubKey.getParams().getOrder().bitLength(), pubKey.getW());
}
- private static byte[] encode(final int bitLength, final ECPoint point) {
- if ( point == ECPoint.POINT_INFINITY ) return new byte[1];
+ private static byte[] encodeUncompressed(final int fieldSize, final ECPoint point) {
+ if (point == ECPoint.POINT_INFINITY) return new byte[1];
- final int bytesLength = (bitLength + 7) / 8;
- byte[] encoded = new byte[1 + bytesLength + bytesLength];
+ final int expLength = (fieldSize + 7) / 8;
+
+ byte[] encoded = new byte[1 + expLength + expLength];
encoded[0] = 0x04;
+ addIntBytes(point.getAffineX(), expLength, encoded, 1);
+ addIntBytes(point.getAffineY(), expLength, encoded, 1 + expLength);
+
+ return encoded;
+ }
+
+ private static byte[] encodeCompressed(final ECPoint point) {
+ if (point == ECPoint.POINT_INFINITY) return new byte[1];
+
+ final int bytesLength = point.getAffineX().bitLength() / 8 + 1;
+
+ byte[] encoded = new byte[1 + bytesLength];
+
+ encoded[0] = (byte) (point.getAffineY().testBit(0) ? 0x03 : 0x02);
+
addIntBytes(point.getAffineX(), bytesLength, encoded, 1);
- addIntBytes(point.getAffineY(), bytesLength, encoded, 1 + bytesLength);
return encoded;
}
- private static void addIntBytes(BigInteger i, final int length, final byte[] dest, final int destOffset) {
- final byte[] bytes = i.toByteArray();
+ private static void addIntBytes(final BigInteger value, final int length, final byte[] dest, final int destOffset) {
+ final byte[] in = value.toByteArray();
- if (length < bytes.length) {
- System.arraycopy(bytes, bytes.length - length, dest, destOffset, length);
+ if (length < in.length) {
+ System.arraycopy(in, in.length - length, dest, destOffset, length);
}
- else if (length > bytes.length) {
- System.arraycopy(bytes, 0, dest, destOffset + (length - bytes.length), bytes.length);
+ else if (length > in.length) {
+ System.arraycopy(in, 0, dest, destOffset + (length - in.length), in.length);
}
else {
- System.arraycopy(bytes, 0, dest, destOffset, length);
+ System.arraycopy(in, 0, dest, destOffset, length);
}
}
-}
\ No newline at end of file
+}
diff --git a/src/main/java/org/jruby/ext/openssl/PKeyRSA.java b/src/main/java/org/jruby/ext/openssl/PKeyRSA.java
index 6e96f7f1..7e0e28bb 100644
--- a/src/main/java/org/jruby/ext/openssl/PKeyRSA.java
+++ b/src/main/java/org/jruby/ext/openssl/PKeyRSA.java
@@ -40,16 +40,18 @@
import java.security.NoSuchAlgorithmException;
import java.security.PrivateKey;
import java.security.PublicKey;
-import java.security.SecureRandom;
import java.security.interfaces.RSAPrivateCrtKey;
+import java.security.interfaces.RSAPrivateKey;
import java.security.interfaces.RSAPublicKey;
import java.security.spec.InvalidKeySpecException;
import java.security.spec.RSAKeyGenParameterSpec;
import java.security.spec.RSAPrivateCrtKeySpec;
+import java.security.spec.RSAPrivateKeySpec;
import java.security.spec.RSAPublicKeySpec;
import static javax.crypto.Cipher.*;
+import org.bouncycastle.asn1.ASN1Primitive;
import org.jruby.Ruby;
import org.jruby.RubyClass;
import org.jruby.RubyBignum;
@@ -62,6 +64,7 @@
import org.jruby.anno.JRubyMethod;
import org.jruby.exceptions.RaiseException;
import org.jruby.runtime.Arity;
+import org.jruby.runtime.Block;
import org.jruby.runtime.ObjectAllocator;
import org.jruby.runtime.ThreadContext;
import org.jruby.runtime.builtin.IRubyObject;
@@ -70,10 +73,11 @@
import org.jruby.ext.openssl.impl.CipherSpec;
import org.jruby.ext.openssl.x509store.PEMInputOutput;
import static org.jruby.ext.openssl.OpenSSL.*;
-import static org.jruby.ext.openssl.PKey._PKey;
import static org.jruby.ext.openssl.impl.PKey.readRSAPrivateKey;
import static org.jruby.ext.openssl.impl.PKey.readRSAPublicKey;
+import static org.jruby.ext.openssl.impl.PKey.toASN1Primitive;
import static org.jruby.ext.openssl.impl.PKey.toDerRSAKey;
+import static org.jruby.ext.openssl.impl.PKey.toDerRSAPublicKey;
/**
* @author Ola Bini
@@ -106,7 +110,11 @@ public static RaiseException newRSAError(Ruby runtime, String message) {
}
static RaiseException newRSAError(Ruby runtime, Throwable cause) {
- return Utils.newError(runtime, _PKey(runtime).getClass("RSAError"), cause.getMessage(), cause);
+ return newRSAError(runtime, cause.getMessage(), cause);
+ }
+
+ static RaiseException newRSAError(Ruby runtime, String message, Throwable cause) {
+ return Utils.newError(runtime, _PKey(runtime).getClass("RSAError"), message, cause);
}
public PKeyRSA(Ruby runtime, RubyClass type) {
@@ -124,7 +132,7 @@ public PKeyRSA(Ruby runtime, RubyClass type, RSAPrivateCrtKey privKey, RSAPublic
}
private volatile RSAPublicKey publicKey;
- private volatile transient RSAPrivateCrtKey privateKey;
+ private volatile transient RSAPrivateKey privateKey;
// fields to hold individual RSAPublicKeySpec components. this allows
// a public key to be constructed incrementally, as required by the
@@ -140,6 +148,7 @@ public PKeyRSA(Ruby runtime, RubyClass type, RSAPrivateCrtKey privKey, RSAPublic
private transient volatile BigInteger rsa_dmq1;
private transient volatile BigInteger rsa_iqmp;
+ @JRubyMethod(visibility = Visibility.PRIVATE)
@Override
public IRubyObject initialize_copy(final IRubyObject original) {
if (this == original) return this;
@@ -211,33 +220,27 @@ private static PKeyRSA rsaGenerate(final Ruby runtime,
return rsa;
}
- static PKeyRSA newInstance(final Ruby runtime, final PublicKey publicKey) {
- //if ( publicKey instanceof RSAPublicKey ) {
- return new PKeyRSA(runtime, (RSAPublicKey) publicKey);
- //}
- }
-
@JRubyMethod(rest = true, visibility = Visibility.PRIVATE)
- public IRubyObject initialize(final ThreadContext context, final IRubyObject[] args) {
+ public IRubyObject initialize(final ThreadContext context, final IRubyObject[] args, final Block block) {
final Ruby runtime = context.runtime;
if ( Arity.checkArgumentCount(runtime, args, 0, 2) == 0 ) {
privateKey = null; publicKey = null; return this;
}
- IRubyObject arg = args[0]; IRubyObject pass = null;
- if ( args.length > 1 ) pass = args[1];
+ IRubyObject arg = args[0];
+ IRubyObject arg1 = args.length > 1 ? args[1] : null; // exponent (Fixnum) or password (String)
if ( arg instanceof RubyFixnum ) {
int keySize = RubyNumeric.fix2int((RubyFixnum) arg);
BigInteger exp = RSAKeyGenParameterSpec.F4;
- if ( pass != null && ! pass.isNil() ) {
- exp = BigInteger.valueOf(RubyNumeric.num2long(pass));
+ if (arg1 != null && !arg1.isNil()) {
+ exp = BigInteger.valueOf(RubyNumeric.num2long(arg1));
}
return rsaGenerate(runtime, this, keySize, exp);
}
- final char[] passwd = password(pass);
+ final char[] passwd = password(context, arg1, block);
final RubyString str = readInitArg(context, arg);
final String strJava = str.toString();
@@ -285,7 +288,7 @@ public IRubyObject initialize(final ThreadContext context, final IRubyObject[] a
try { key = readRSAPrivateKey(rsaFactory, str.getBytes()); }
catch (NoClassDefFoundError e) { noClassDef = true; debugStackTrace(runtime, e); }
catch (InvalidKeySpecException e) { debug(runtime, "PKeyRSA could not read private key", e); }
- catch (IOException e) { debug(runtime, "PKeyRSA could not read private key", e); }
+ catch (IOException e) { debugStackTrace(runtime, "PKeyRSA could not read private key", e); }
catch (RuntimeException e) {
if ( isKeyGenerationFailure(e) ) debug(runtime, "PKeyRSA could not read private key", e);
else debugStackTrace(runtime, e);
@@ -295,7 +298,7 @@ public IRubyObject initialize(final ThreadContext context, final IRubyObject[] a
try { key = readRSAPublicKey(rsaFactory, str.getBytes()); }
catch (NoClassDefFoundError e) { noClassDef = true; debugStackTrace(runtime, e); }
catch (InvalidKeySpecException e) { debug(runtime, "PKeyRSA could not read public key", e); }
- catch (IOException e) { debug(runtime, "PKeyRSA could not read public key", e); }
+ catch (IOException e) { debugStackTrace(runtime, "PKeyRSA could not read public key", e); }
catch (RuntimeException e) {
if ( isKeyGenerationFailure(e) ) debug(runtime, "PKeyRSA could not read public key", e);
else debugStackTrace(runtime, e);
@@ -321,8 +324,9 @@ public IRubyObject initialize(final ThreadContext context, final IRubyObject[] a
}
else if ( key instanceof RSAPrivateCrtKey ) {
this.privateKey = (RSAPrivateCrtKey) key;
+ BigInteger exponent = ((RSAPrivateCrtKey) key).getPublicExponent();
try {
- this.publicKey = (RSAPublicKey) rsaFactory.generatePublic(new RSAPublicKeySpec(privateKey.getModulus(), privateKey.getPublicExponent()));
+ this.publicKey = (RSAPublicKey) rsaFactory.generatePublic(new RSAPublicKeySpec(privateKey.getModulus(), exponent));
} catch (GeneralSecurityException e) {
throw newRSAError(runtime, e.getMessage());
} catch (RuntimeException e) {
@@ -354,22 +358,42 @@ public RubyBoolean private_p() {
return getRuntime().newBoolean(isPrivateKey());
}
+ @JRubyMethod(name = "public_to_der")
+ public RubyString public_to_der(ThreadContext context) {
+ final byte[] bytes;
+ try {
+ bytes = toDerRSAPublicKey(publicKey);
+ }
+ catch (NoClassDefFoundError e) {
+ throw newRSAError(context.runtime, bcExceptionMessage(e));
+ }
+ catch (Exception e) {
+ throw newRSAError(getRuntime(), e.getMessage(), e);
+ }
+ return StringHelper.newString(context.runtime, bytes);
+ }
+
@Override
@JRubyMethod(name = "to_der")
public RubyString to_der() {
final byte[] bytes;
try {
- bytes = toDerRSAKey(publicKey, privateKey);
+ bytes = toDerRSAKey(publicKey, privateKey instanceof RSAPrivateCrtKey ? (RSAPrivateCrtKey) privateKey : null);
}
catch (NoClassDefFoundError e) {
throw newRSAError(getRuntime(), bcExceptionMessage(e));
}
- catch (IOException e) {
- throw newRSAError(getRuntime(), e.getMessage());
+ catch (Exception e) {
+ throw newRSAError(getRuntime(), e.getMessage(), e);
}
return StringHelper.newString(getRuntime(), bytes);
}
+ @Override
+ public ASN1Primitive toASN1PublicInfo() {
+ return toASN1Primitive(publicKey);
+ }
+
@JRubyMethod
public PKeyRSA public_key() {
return new PKeyRSA(getRuntime(), this.publicKey);
@@ -379,7 +403,8 @@ public PKeyRSA public_key() {
public IRubyObject params(final ThreadContext context) {
final Ruby runtime = context.runtime;
RubyHash hash = RubyHash.newHash(runtime);
- if ( privateKey != null ) {
+ if (privateKey instanceof RSAPrivateCrtKey) {
+ RSAPrivateCrtKey privateKey = (RSAPrivateCrtKey) this.privateKey;
hash.op_aset(context, runtime.newString("iqmp"), BN.newBN(runtime, privateKey.getCrtCoefficient()));
hash.op_aset(context, runtime.newString("n"), BN.newBN(runtime, privateKey.getModulus()));
hash.op_aset(context, runtime.newString("d"), BN.newBN(runtime, privateKey.getPrivateExponent()));
@@ -405,7 +430,8 @@ public IRubyObject params(final ThreadContext context) {
@JRubyMethod
public RubyString to_text() {
StringBuilder result = new StringBuilder();
- if (privateKey != null) {
+ if (privateKey instanceof RSAPrivateCrtKey) {
+ RSAPrivateCrtKey privateKey = (RSAPrivateCrtKey) this.privateKey;
int len = privateKey.getModulus().bitLength();
result.append("Private-Key: (").append(len).append(" bit)").append('\n');
result.append("modulus:");
@@ -434,30 +460,45 @@ public RubyString to_text() {
@Override
@JRubyMethod(name = { "to_pem", "to_s" }, alias = "export", rest = true)
- public RubyString to_pem(final IRubyObject[] args) {
- Arity.checkArgumentCount(getRuntime(), args, 0, 2);
+ public RubyString to_pem(ThreadContext context, final IRubyObject[] args) {
+ Arity.checkArgumentCount(context.runtime, args, 0, 2);
CipherSpec spec = null; char[] passwd = null;
if ( args.length > 0 ) {
spec = cipherSpec( args[0] );
- if ( args.length > 1 ) passwd = password(args[1]);
+ if ( args.length > 1 ) passwd = password(context, args[1], null);
}
try {
final StringWriter writer = new StringWriter();
- if ( privateKey != null ) {
- PEMInputOutput.writeRSAPrivateKey(writer, privateKey, spec, passwd);
+ if (privateKey instanceof RSAPrivateCrtKey) {
+ PEMInputOutput.writeRSAPrivateKey(writer, (RSAPrivateCrtKey) privateKey, spec, passwd);
}
else {
PEMInputOutput.writeRSAPublicKey(writer, publicKey);
}
- return RubyString.newString(getRuntime(), writer.getBuffer());
+ return RubyString.newString(context.runtime, writer.getBuffer());
}
catch (NoClassDefFoundError ncdfe) {
- throw newRSAError(getRuntime(), bcExceptionMessage(ncdfe));
+ throw newRSAError(context.runtime, bcExceptionMessage(ncdfe));
}
catch (IOException ioe) {
- throw newRSAError(getRuntime(), ioe.getMessage());
+ throw newRSAError(context.runtime, ioe.getMessage());
+ }
+ }
+
+ @JRubyMethod
+ public RubyString public_to_pem(ThreadContext context) {
+ try {
+ final StringWriter writer = new StringWriter();
+ PEMInputOutput.writeRSAPublicKey(writer, publicKey);
+ return RubyString.newString(context.runtime, writer.getBuffer());
+ }
+ catch (NoClassDefFoundError ncdfe) {
+ throw newRSAError(context.runtime, bcExceptionMessage(ncdfe));
+ }
+ catch (IOException ioe) {
+ throw newRSAError(context.runtime, ioe.getMessage());
}
}
@@ -534,6 +575,11 @@ private RubyString doCipherRSA(final Ruby runtime,
}
}
+ @JRubyMethod
+ public IRubyObject oid() {
+ return getRuntime().newString("rsaEncryption");
+ }
+
@JRubyMethod(name="d=")
public synchronized IRubyObject set_d(final ThreadContext context, IRubyObject value) {
if ( privateKey != null ) {
@@ -596,96 +642,80 @@ public synchronized IRubyObject set_iqmp(final ThreadContext context, IRubyObjec
@JRubyMethod(name="iqmp")
public synchronized IRubyObject get_iqmp() {
- BigInteger iqmp;
- if (privateKey != null) {
- iqmp = privateKey.getCrtCoefficient();
- } else {
- iqmp = rsa_iqmp;
- }
- if (iqmp != null) {
- return BN.newBN(getRuntime(), iqmp);
+ BigInteger iqmp = this.rsa_iqmp;
+ if (iqmp == null && privateKey instanceof RSAPrivateCrtKey) {
+ iqmp = ((RSAPrivateCrtKey) privateKey).getCrtCoefficient();
}
+
+ if (iqmp != null) return BN.newBN(getRuntime(), iqmp);
return getRuntime().getNil();
}
@JRubyMethod(name="dmp1")
public synchronized IRubyObject get_dmp1() {
- BigInteger dmp1;
- if (privateKey != null) {
- dmp1 = privateKey.getPrimeExponentP();
- } else {
- dmp1 = rsa_dmp1;
- }
- if (dmp1 != null) {
- return BN.newBN(getRuntime(), dmp1);
+ BigInteger dmp1 = this.rsa_dmp1;
+ if (dmp1 == null && privateKey instanceof RSAPrivateCrtKey) {
+ dmp1 = ((RSAPrivateCrtKey) privateKey).getPrimeExponentP();
}
+
+ if (dmp1 != null) return BN.newBN(getRuntime(), dmp1);
return getRuntime().getNil();
}
@JRubyMethod(name="dmq1")
public synchronized IRubyObject get_dmq1() {
- BigInteger dmq1;
- if (privateKey != null) {
- dmq1 = privateKey.getPrimeExponentQ();
- } else {
- dmq1 = rsa_dmq1;
- }
- if (dmq1 != null) {
- return BN.newBN(getRuntime(), dmq1);
+ BigInteger dmq1 = this.rsa_dmq1;
+ if (dmq1 == null && privateKey instanceof RSAPrivateCrtKey) {
+ dmq1 = ((RSAPrivateCrtKey) privateKey).getPrimeExponentQ();
}
+
+ if (dmq1 != null) return BN.newBN(getRuntime(), dmq1);
return getRuntime().getNil();
}
@JRubyMethod(name="d")
public synchronized IRubyObject get_d() {
- BigInteger d;
- if (privateKey != null) {
+ final BigInteger d = getPrivateExponent();
+ if (d != null) return BN.newBN(getRuntime(), d);
+ return getRuntime().getNil();
+ }
+
+ private BigInteger getPrivateExponent() {
+ BigInteger d = rsa_d;
+ if (d == null && privateKey != null) {
d = privateKey.getPrivateExponent();
- } else {
- d = rsa_d;
- }
- if (d != null) {
- return BN.newBN(getRuntime(), d);
}
- return getRuntime().getNil();
+ return d;
}
@JRubyMethod(name="p")
public synchronized IRubyObject get_p() {
- BigInteger p;
- if (privateKey != null) {
- p = privateKey.getPrimeP();
- } else {
- p = rsa_p;
- }
- if (p != null) {
- return BN.newBN(getRuntime(), p);
+ BigInteger p = rsa_p;
+ if (p == null && privateKey instanceof RSAPrivateCrtKey) {
+ p = ((RSAPrivateCrtKey) privateKey).getPrimeP();
}
+
+ if (p != null) return BN.newBN(getRuntime(), p);
return getRuntime().getNil();
}
@JRubyMethod(name="q")
public synchronized IRubyObject get_q() {
- BigInteger q;
- if (privateKey != null) {
- q = privateKey.getPrimeQ();
- } else {
- q = rsa_q;
- }
- if (q != null) {
- return BN.newBN(getRuntime(), q);
+ BigInteger q = rsa_q;
+ if (q == null && privateKey instanceof RSAPrivateCrtKey) {
+ q = ((RSAPrivateCrtKey) privateKey).getPrimeQ();
}
+
+ if (q != null) return BN.newBN(getRuntime(), q);
return getRuntime().getNil();
}
private BigInteger getPublicExponent() {
- if (publicKey != null) {
- return publicKey.getPublicExponent();
- } else if (privateKey != null) {
- return privateKey.getPublicExponent();
- } else {
- return rsa_e;
- }
+ if (rsa_e != null) return rsa_e;
+
+ if (publicKey != null) return publicKey.getPublicExponent();
+ if (privateKey instanceof RSAPrivateCrtKey) return ((RSAPrivateCrtKey) privateKey).getPublicExponent();
+ return null;
}
@JRubyMethod(name="e")
@@ -712,13 +742,11 @@ public synchronized IRubyObject set_e(final ThreadContext context, IRubyObject v
}
private BigInteger getModulus() {
- if (publicKey != null) {
- return publicKey.getModulus();
- } else if (privateKey != null) {
- return privateKey.getModulus();
- } else {
- return rsa_n;
- }
+ if (rsa_n != null) return rsa_n;
+
+ if (publicKey != null) return publicKey.getModulus();
+ if (privateKey != null) return privateKey.getModulus();
+ return null;
}
@JRubyMethod(name="n")
@@ -744,11 +772,36 @@ public synchronized IRubyObject set_n(final ThreadContext context, IRubyObject v
return value;
}
+ @JRubyMethod
+ public IRubyObject set_key(final ThreadContext context, IRubyObject n, IRubyObject e, IRubyObject d) {
+ this.rsa_n = BN.getBigInteger(n);
+ this.rsa_e = BN.getBigInteger(e);
+ this.rsa_d = BN.getBigInteger(d);
+ generatePublicKeyIfParams(context);
+ generatePrivateKeyIfParams(context);
+ return this;
+ }
+
+ @JRubyMethod
+ public IRubyObject set_factors(final ThreadContext context, IRubyObject p, IRubyObject q) {
+ this.rsa_p = BN.getBigInteger(p);
+ this.rsa_q = BN.getBigInteger(q);
+ generatePrivateKeyIfParams(context);
+ return this;
+ }
+
+ @JRubyMethod
+ public IRubyObject set_crt_params(final ThreadContext context, IRubyObject dmp1, IRubyObject dmq1, IRubyObject iqmp) {
+ this.rsa_dmp1 = BN.asBigInteger(dmp1);
+ this.rsa_dmq1 = BN.asBigInteger(dmq1);
+ this.rsa_iqmp = BN.asBigInteger(iqmp);
+ generatePrivateKeyIfParams(context);
+ return this;
+ }
+
private void generatePublicKeyIfParams(final ThreadContext context) {
final Ruby runtime = context.runtime;
- if ( publicKey != null ) throw newRSAError(runtime, "illegal modification");
-
// Don't access the rsa_n and rsa_e fields directly. They may have
// already been consumed and cleared by generatePrivateKeyIfParams.
BigInteger _rsa_n = getModulus();
@@ -777,14 +830,13 @@ private void generatePublicKeyIfParams(final ThreadContext context) {
private void generatePrivateKeyIfParams(final ThreadContext context) {
final Ruby runtime = context.runtime;
- if ( privateKey != null ) throw newRSAError(runtime, "illegal modification");
-
// Don't access the rsa_n and rsa_e fields directly. They may have
// already been consumed and cleared by generatePublicKeyIfParams.
- BigInteger _rsa_n = getModulus();
- BigInteger _rsa_e = getPublicExponent();
+ final BigInteger rsa_n = getModulus();
+ final BigInteger rsa_e = getPublicExponent();
+ final BigInteger rsa_d = getPrivateExponent();
- if (_rsa_n != null && _rsa_e != null && rsa_p != null && rsa_q != null && rsa_d != null && rsa_dmp1 != null && rsa_dmq1 != null && rsa_iqmp != null) {
+ if (rsa_n != null && rsa_e != null && rsa_d != null) {
final KeyFactory rsaFactory;
try {
rsaFactory = SecurityHelper.getKeyFactory("RSA");
@@ -793,17 +845,24 @@ private void generatePrivateKeyIfParams(final ThreadContext context) {
throw runtime.newLoadError("unsupported key algorithm (RSA)");
}
- try {
- privateKey = (RSAPrivateCrtKey) rsaFactory.generatePrivate(
- new RSAPrivateCrtKeySpec(_rsa_n, _rsa_e, rsa_d, rsa_p, rsa_q, rsa_dmp1, rsa_dmq1, rsa_iqmp)
- );
- }
- catch (InvalidKeySpecException e) {
- throw newRSAError(runtime, "invalid parameters");
+ if (rsa_p != null && rsa_q != null && rsa_dmp1 != null && rsa_dmq1 != null && rsa_iqmp != null) {
+ try {
+ privateKey = (RSAPrivateCrtKey) rsaFactory.generatePrivate(
+ new RSAPrivateCrtKeySpec(rsa_n, rsa_e, rsa_d, rsa_p, rsa_q, rsa_dmp1, rsa_dmq1, rsa_iqmp)
+ );
+ } catch (InvalidKeySpecException e) {
+ throw newRSAError(runtime, "invalid parameters", e);
+ }
+ this.rsa_n = this.rsa_e = this.rsa_d = null;
+ this.rsa_p = this.rsa_q = null;
+ this.rsa_dmp1 = this.rsa_dmq1 = this.rsa_iqmp = null;
+ } else {
+ try {
+ privateKey = (RSAPrivateKey) rsaFactory.generatePrivate(new RSAPrivateKeySpec(rsa_n, rsa_d));
+ } catch (InvalidKeySpecException e) {
+ throw newRSAError(runtime, "invalid parameters", e);
+ }
}
- rsa_n = null; rsa_e = null;
- rsa_d = null; rsa_p = null; rsa_q = null;
- rsa_dmp1 = null; rsa_dmq1 = null; rsa_iqmp = null;
}
}
diff --git a/src/main/java/org/jruby/ext/openssl/SSL.java b/src/main/java/org/jruby/ext/openssl/SSL.java
index c84d3d47..d58ca9c2 100644
--- a/src/main/java/org/jruby/ext/openssl/SSL.java
+++ b/src/main/java/org/jruby/ext/openssl/SSL.java
@@ -53,24 +53,40 @@ public class SSL {
public static final long OP_ALL = 0x00000FFFL;
public static final long OP_NO_TICKET = 0x00004000L;
public static final long OP_NO_SESSION_RESUMPTION_ON_RENEGOTIATION = 0x00010000L;
+ public static final long OP_NO_COMPRESSION = 0x00020000L;
public static final long OP_SINGLE_ECDH_USE = 0x00080000L;
public static final long OP_SINGLE_DH_USE = 0x00100000L;
public static final long OP_EPHEMERAL_RSA = 0x00200000L;
public static final long OP_CIPHER_SERVER_PREFERENCE = 0x00400000L;
public static final long OP_TLS_ROLLBACK_BUG = 0x00800000L;
- public static final long OP_NO_SSLv2 = 0x01000000L; // supported
- public static final long OP_NO_SSLv3 = 0x02000000L; // supported
- public static final long OP_NO_TLSv1 = 0x04000000L; // supported
- public static final long OP_PKCS1_CHECK_1 = 0x08000000L;
- public static final long OP_PKCS1_CHECK_2 = 0x10000000L;
- public static final long OP_NETSCAPE_CA_DN_BUG = 0x20000000L;
- public static final long OP_NETSCAPE_DEMO_CIPHER_CHANGE_BUG = 0x40000000L;
-
- public static final int SSL2_VERSION = 1;
- public static final int SSL3_VERSION = 768;
- public static final int TLS1_VERSION = 769;
- public static final int TLS1_1_VERSION = 770;
- public static final int TLS1_2_VERSION = 771;
+
+ public static final long OP_NO_SSLv2 = 0x01000000L;
+ public static final long OP_NO_SSLv3 = 0x02000000L;
+ public static final long OP_NO_TLSv1 = 0x04000000L;
+ public static final long OP_NO_TLSv1_2 = 0x08000000L;
+ public static final long OP_NO_TLSv1_1 = 0x10000000L;
+ public static final long OP_NO_TLSv1_3 = 0x20000000L;
+
+ // define SSL_OP_NO_SSL_MASK (SSL_OP_NO_SSLv3|SSL_OP_NO_TLSv1|SSL_OP_NO_TLSv1_1|SSL_OP_NO_TLSv1_2|SSL_OP_NO_TLSv1_3)
+
+ /* Deprecated in OpenSSL 1.0.1. */
+ static final long OP_PKCS1_CHECK_1 = 0x08000000L;
+ /* Deprecated in OpenSSL 1.0.1. */
+ static final long OP_PKCS1_CHECK_2 = 0x10000000L;
+ /* Deprecated in OpenSSL 1.1.0. */
+ static final long OP_NETSCAPE_CA_DN_BUG = 0x20000000L;
+ /* Deprecated in OpenSSL 1.1.0. */
+ static final long OP_NETSCAPE_DEMO_CIPHER_CHANGE_BUG = 0x40000000L;
+
+ public static final int SSL2_VERSION = 0x0002;
+ public static final int SSL3_VERSION = 0x0300;
+ public static final int TLS1_VERSION = 0x0301;
+ public static final int TLS1_1_VERSION = 0x0302;
+ public static final int TLS1_2_VERSION = 0x0303;
+ /* OpenSSL 1.1.1 */
+ public static final int TLS1_3_VERSION = 0x0304;
+
+ // define TLS_MAX_VERSION TLS1_3_VERSION
private static final String JSSE_TLS_ephemeralDHKeySize = "jdk.tls.ephemeralDHKeySize" ;
private static final String JSSE_TLS_ephemeralDHKeySize_default = "matched" ;
@@ -142,6 +158,7 @@ static void createSSL(final Ruby runtime, final RubyModule OpenSSL, final RubyCl
SSL.setConstant("OP_ALL", runtime.newFixnum(OP_ALL));
SSL.setConstant("OP_NO_TICKET", runtime.newFixnum(OP_NO_TICKET));
SSL.setConstant("OP_NO_SESSION_RESUMPTION_ON_RENEGOTIATION", runtime.newFixnum(OP_NO_SESSION_RESUMPTION_ON_RENEGOTIATION));
+ SSL.setConstant("OP_NO_COMPRESSION", runtime.newFixnum(OP_NO_COMPRESSION));
SSL.setConstant("OP_SINGLE_ECDH_USE", runtime.newFixnum(OP_SINGLE_ECDH_USE));
SSL.setConstant("OP_SINGLE_DH_USE", runtime.newFixnum(OP_SINGLE_DH_USE));
SSL.setConstant("OP_EPHEMERAL_RSA", runtime.newFixnum(OP_EPHEMERAL_RSA));
@@ -150,6 +167,9 @@ static void createSSL(final Ruby runtime, final RubyModule OpenSSL, final RubyCl
SSL.setConstant("OP_NO_SSLv2", runtime.newFixnum(OP_NO_SSLv2));
SSL.setConstant("OP_NO_SSLv3", runtime.newFixnum(OP_NO_SSLv3));
SSL.setConstant("OP_NO_TLSv1", runtime.newFixnum(OP_NO_TLSv1));
+ SSL.setConstant("OP_NO_TLSv1_1", runtime.newFixnum(OP_NO_TLSv1_1));
+ SSL.setConstant("OP_NO_TLSv1_2", runtime.newFixnum(OP_NO_TLSv1_2));
+ SSL.setConstant("OP_NO_TLSv1_3", runtime.newFixnum(OP_NO_TLSv1_3));
SSL.setConstant("OP_PKCS1_CHECK_1", runtime.newFixnum(OP_PKCS1_CHECK_1));
SSL.setConstant("OP_PKCS1_CHECK_2", runtime.newFixnum(OP_PKCS1_CHECK_2));
SSL.setConstant("OP_NETSCAPE_CA_DN_BUG", runtime.newFixnum(OP_NETSCAPE_CA_DN_BUG));
@@ -160,6 +180,7 @@ static void createSSL(final Ruby runtime, final RubyModule OpenSSL, final RubyCl
SSL.setConstant("TLS1_VERSION", runtime.newFixnum(TLS1_VERSION));
SSL.setConstant("TLS1_1_VERSION", runtime.newFixnum(TLS1_1_VERSION));
SSL.setConstant("TLS1_2_VERSION", runtime.newFixnum(TLS1_2_VERSION));
+ SSL.setConstant("TLS1_3_VERSION", runtime.newFixnum(TLS1_3_VERSION));
SSLContext.createSSLContext(runtime, SSL);
SSLSocket.createSSLSocket(runtime, SSL);
@@ -210,7 +231,7 @@ private static RaiseException newWaitSSLError(final Ruby runtime, final String n
if ( waitErrorBacktrace ) {
return Utils.newError(runtime, errorClass, message, false);
}
- return Utils.newErrorWithoutTrace(runtime, errorClass, message, false);
+ return Utils.newErrorWithoutTrace(runtime, errorClass, message);
}
static RubyModule _SSL(final Ruby runtime) {
diff --git a/src/main/java/org/jruby/ext/openssl/SSLContext.java b/src/main/java/org/jruby/ext/openssl/SSLContext.java
index d6ed0e92..3fc0ef5a 100644
--- a/src/main/java/org/jruby/ext/openssl/SSLContext.java
+++ b/src/main/java/org/jruby/ext/openssl/SSLContext.java
@@ -45,6 +45,7 @@
import javax.net.ssl.KeyManager;
import javax.net.ssl.SSLEngine;
+import javax.net.ssl.SSLParameters;
import javax.net.ssl.SSLSessionContext;
import javax.net.ssl.TrustManager;
import javax.net.ssl.X509ExtendedKeyManager;
@@ -53,12 +54,15 @@
import org.jruby.Ruby;
import org.jruby.RubyArray;
import org.jruby.RubyClass;
+import org.jruby.RubyString;
+import org.jruby.RubyFixnum;
import org.jruby.RubyHash;
import org.jruby.RubyInteger;
import org.jruby.RubyModule;
import org.jruby.RubyNumeric;
import org.jruby.RubyObject;
import org.jruby.RubySymbol;
+import org.jruby.RubyProc;
import org.jruby.anno.JRubyMethod;
import org.jruby.common.IRubyWarnings.ID;
import org.jruby.runtime.Arity;
@@ -79,14 +83,14 @@
import org.jruby.ext.openssl.x509store.X509Object;
import org.jruby.ext.openssl.x509store.X509Utils;
+import static org.jruby.ext.openssl.CipherStrings.SuiteToOSSL;
import static org.jruby.ext.openssl.StringHelper.*;
import static org.jruby.ext.openssl.SSL.*;
-import static org.jruby.ext.openssl.X509._X509;
import static org.jruby.ext.openssl.X509Cert._Certificate;
+import static org.jruby.ext.openssl.x509store.StoreContext.ossl_ssl_ex_vcb_idx;
import static org.jruby.ext.openssl.OpenSSL.debug;
import static org.jruby.ext.openssl.OpenSSL.debugStackTrace;
import static org.jruby.ext.openssl.OpenSSL.warn;
-import static org.jruby.ext.openssl.Utils.hasNonNilInstanceVariable;
/**
* @author Ola Bini
@@ -99,10 +103,14 @@ public class SSLContext extends RubyObject {
private static final HashMap SSL_VERSION_OSSL2JSSE;
// Mapping table for JSEE's enabled protocols for the algorithm.
private static final Map ENABLED_PROTOCOLS;
+ // Mapping table from CRuby parse_proto_version(VALUE str)
+ private static final Map PROTO_VERSION_MAP;
+
+ private static final Map JSSE_TO_VERSION;
static {
- SSL_VERSION_OSSL2JSSE = new LinkedHashMap(20, 1);
- ENABLED_PROTOCOLS = new HashMap(8, 1);
+ SSL_VERSION_OSSL2JSSE = new LinkedHashMap<>(32, 1);
+ ENABLED_PROTOCOLS = new HashMap<>(16, 1);
SSL_VERSION_OSSL2JSSE.put("TLSv1", "TLSv1");
SSL_VERSION_OSSL2JSSE.put("TLSv1_server", "TLSv1");
@@ -123,13 +131,13 @@ public class SSLContext extends RubyObject {
SSL_VERSION_OSSL2JSSE.put("SSLv23_server", "SSL");
SSL_VERSION_OSSL2JSSE.put("SSLv23_client", "SSL");
- ENABLED_PROTOCOLS.put("SSL", new String[] { "SSLv2", "SSLv3", "TLSv1", "TLSv1.1", "TLSv1.2" });
+ ENABLED_PROTOCOLS.put("SSL", new String[] { "SSLv3", "TLSv1", "TLSv1.1", "TLSv1.2", "TLSv1.3" });
// Historically we were ahead of MRI to support TLS
// ... thus the non-standard names version names :
SSL_VERSION_OSSL2JSSE.put("TLS", "TLS");
- ENABLED_PROTOCOLS.put("TLS", new String[] { "TLSv1", "TLSv1.1", "TLSv1.2" });
+ ENABLED_PROTOCOLS.put("TLS", new String[] { "TLSv1", "TLSv1.1", "TLSv1.2", "TLSv1.3" });
SSL_VERSION_OSSL2JSSE.put("TLSv1.1", "TLSv1.1");
SSL_VERSION_OSSL2JSSE.put("TLSv1_1_server", "TLSv1.1");
@@ -140,11 +148,35 @@ public class SSLContext extends RubyObject {
SSL_VERSION_OSSL2JSSE.put("TLSv1_2", "TLSv1.2"); // supported on MRI 2.x
ENABLED_PROTOCOLS.put("TLSv1.2", new String[] { "TLSv1.2" });
- SSL_VERSION_OSSL2JSSE.put("TLSv1.2", "TLSv1.2"); // just for completeness
+ SSL_VERSION_OSSL2JSSE.put("TLSv1.2", "TLSv1.2");
SSL_VERSION_OSSL2JSSE.put("TLSv1_2_server", "TLSv1.2");
SSL_VERSION_OSSL2JSSE.put("TLSv1_2_client", "TLSv1.2");
+
+ SSL_VERSION_OSSL2JSSE.put("TLSv1.3", "TLSv1.3");
+ SSL_VERSION_OSSL2JSSE.put("TLSv1_3_server", "TLSv1.3");
+ SSL_VERSION_OSSL2JSSE.put("TLSv1_3_client", "TLSv1.3");
+ SSL_VERSION_OSSL2JSSE.put("TLSv1_3", "TLSv1.3");
+ ENABLED_PROTOCOLS.put("TLSv1.3", new String[] { "TLSv1.3" });
+
+ PROTO_VERSION_MAP = new HashMap<>(8, 1);
+ PROTO_VERSION_MAP.put("SSL2", SSL.SSL2_VERSION);
+ PROTO_VERSION_MAP.put("SSL3", SSL.SSL3_VERSION);
+ PROTO_VERSION_MAP.put("TLS1", SSL.TLS1_VERSION);
+ PROTO_VERSION_MAP.put("TLS1_1", SSL.TLS1_1_VERSION);
+ PROTO_VERSION_MAP.put("TLS1_2", SSL.TLS1_2_VERSION);
+ PROTO_VERSION_MAP.put("TLS1_3", SSL.TLS1_3_VERSION);
+
+ JSSE_TO_VERSION = new HashMap<>(8, 1);
+ JSSE_TO_VERSION.put("SSLv2", SSL.SSL2_VERSION);
+ JSSE_TO_VERSION.put("SSLv3", SSL.SSL3_VERSION);
+ JSSE_TO_VERSION.put("TLSv1", SSL.TLS1_VERSION);
+ JSSE_TO_VERSION.put("TLSv1.1", SSL.TLS1_1_VERSION);
+ JSSE_TO_VERSION.put("TLSv1.2", SSL.TLS1_2_VERSION);
+ JSSE_TO_VERSION.put("TLSv1.3", SSL.TLS1_3_VERSION);
}
+ private static final String[] EMPTY_STRING_ARRAY = new String[0];
+
private static ObjectAllocator SSLCONTEXT_ALLOCATOR = new ObjectAllocator() {
public IRubyObject allocate(Ruby runtime, RubyClass klass) {
return new SSLContext(runtime, klass);
@@ -157,21 +189,65 @@ public static void createSSLContext(final Ruby runtime, final RubyModule SSL) {
final ThreadContext context = runtime.getCurrentContext();
SSLContext.addReadWriteAttribute(context, "cert");
SSLContext.addReadWriteAttribute(context, "key");
+ /*
+ * A certificate or Array of certificates that will be sent to the client.
+ */
SSLContext.addReadWriteAttribute(context, "client_ca");
+ /*
+ * The path to a file containing a PEM-format CA certificate
+ */
SSLContext.addReadWriteAttribute(context, "ca_file");
+ /*
+ * The path to a directory containing CA certificates in PEM format.
+ *
+ * Files are looked up by subject's X509 name's hash value.
+ */
SSLContext.addReadWriteAttribute(context, "ca_path");
SSLContext.addReadWriteAttribute(context, "timeout");
SSLContext.addReadWriteAttribute(context, "verify_mode");
+ /*
+ * Number of CA certificates to walk when verifying a certificate chain.
+ */
SSLContext.addReadWriteAttribute(context, "verify_depth");
+ /*
+ * A callback for additional certificate verification. The callback is
+ * invoked for each certificate in the chain.
+ *
+ * The callback is invoked with two values. _preverify_ok_ indicates
+ * indicates if the verification was passed (+true+) or not (+false+).
+ * _store_context_ is an OpenSSL::X509::StoreContext containing the
+ * context used for certificate verification.
+ *
+ * If the callback returns +false+, the chain verification is immediately
+ * stopped and a bad_certificate alert is then sent.
+ */
SSLContext.addReadWriteAttribute(context, "verify_callback");
- SSLContext.addReadWriteAttribute(context, "options");
+ /*
+ * Whether to check the server certificate is valid for the hostname.
+ *
+ * In order to make this work, verify_mode must be set to VERIFY_PEER and
+ * the server hostname must be given by OpenSSL::SSL::SSLSocket#hostname=.
+ */
+ SSLContext.addReadWriteAttribute(context, "verify_hostname");
+ /*
+ * An OpenSSL::X509::Store used for certificate verification.
+ */
SSLContext.addReadWriteAttribute(context, "cert_store");
+ /*
+ * An Array of extra X509 certificates to be added to the certificate
+ * chain.
+ *
+ * The _cert_, _key_, and _extra_chain_cert_ attributes are deprecated.
+ * It is recommended to use #add_certificate instead.
+ */
SSLContext.addReadWriteAttribute(context, "extra_chain_cert");
SSLContext.addReadWriteAttribute(context, "client_cert_cb");
SSLContext.addReadWriteAttribute(context, "session_id_context");
SSLContext.addReadWriteAttribute(context, "tmp_dh_callback");
SSLContext.addReadWriteAttribute(context, "servername_cb");
SSLContext.addReadWriteAttribute(context, "renegotiation_cb");
+ SSLContext.addReadWriteAttribute(context, "alpn_protocols");
+ SSLContext.addReadWriteAttribute(context, "alpn_select_cb");
SSLContext.defineAlias("ssl_timeout", "timeout");
SSLContext.defineAlias("ssl_timeout=", "timeout=");
@@ -181,20 +257,13 @@ public static void createSSLContext(final Ruby runtime, final RubyModule SSL) {
final Set methodKeys = SSL_VERSION_OSSL2JSSE.keySet();
final RubyArray methods = runtime.newArray( methodKeys.size() );
for ( final String method : methodKeys ) {
- if ( method.equals("SSLv2") || method.startsWith("SSLv2_") ) {
- continue; // do not report SSLv2, SSLv2_server, SSLv2_client
- }
if ( method.indexOf('.') == -1 ) {
// do not "officially" report TLSv1.1 and TLSv1.2
methods.append( runtime.newSymbol(method) );
}
}
SSLContext.defineConstant("METHODS", methods);
- // in 1.8.7 as well as 1.9.3 :
- // [:TLSv1, :TLSv1_server, :TLSv1_client, :SSLv3, :SSLv3_server, :SSLv3_client, :SSLv23, :SSLv23_server, :SSLv23_client]
- // in 2.0.0 :
- // [:TLSv1, :TLSv1_server, :TLSv1_client, :TLSv1_2, :TLSv1_2_server, :TLSv1_2_client, :TLSv1_1, :TLSv1_1_server,
- // :TLSv1_1_client, :SSLv3, :SSLv3_server, :SSLv3_client, :SSLv23, :SSLv23_server, :SSLv23_client]
+ SSLContext.deprecateConstant(runtime, "METHODS");
SSLContext.setConstant("SESSION_CACHE_OFF", runtime.newFixnum(SESSION_CACHE_OFF));
SSLContext.setConstant("SESSION_CACHE_CLIENT", runtime.newFixnum(SESSION_CACHE_CLIENT));
@@ -204,49 +273,6 @@ public static void createSSLContext(final Ruby runtime, final RubyModule SSL) {
SSLContext.setConstant("SESSION_CACHE_NO_INTERNAL_LOOKUP", runtime.newFixnum(SESSION_CACHE_NO_INTERNAL_LOOKUP));
SSLContext.setConstant("SESSION_CACHE_NO_INTERNAL_STORE", runtime.newFixnum(SESSION_CACHE_NO_INTERNAL_STORE));
SSLContext.setConstant("SESSION_CACHE_NO_INTERNAL", runtime.newFixnum(SESSION_CACHE_NO_INTERNAL));
-
- // DEFAULT_CERT_STORE = OpenSSL::X509::Store.new
- // DEFAULT_CERT_STORE.set_default_paths
- // if defined?(OpenSSL::X509::V_FLAG_CRL_CHECK_ALL)
- // DEFAULT_CERT_STORE.flags = OpenSSL::X509::V_FLAG_CRL_CHECK_ALL
- // end
- final X509Store DEFAULT_CERT_STORE = X509Store.newStore(runtime);
- DEFAULT_CERT_STORE.set_default_paths(context);
- final IRubyObject V_FLAG_CRL_CHECK_ALL = _X509(runtime).getConstantAt("V_FLAG_CRL_CHECK_ALL");
- if ( V_FLAG_CRL_CHECK_ALL != null ) DEFAULT_CERT_STORE.set_flags(V_FLAG_CRL_CHECK_ALL);
-
- SSLContext.setConstant("DEFAULT_CERT_STORE", DEFAULT_CERT_STORE);
-
- // DEFAULT_PARAMS = {
- // :ssl_version => "SSLv23",
- // :verify_mode => OpenSSL::SSL::VERIFY_PEER,
- // :ciphers => "ALL:!ADH:!EXPORT:!SSLv2:RC4+RSA:+HIGH:+MEDIUM:+LOW",
- // :options => OpenSSL::SSL::OP_ALL,
- // }
- // on MRI 2.1 (should not matter for us) :
- // :options => defined?(OpenSSL::SSL::OP_DONT_INSERT_EMPTY_FRAGMENTS) ?
- // OpenSSL::SSL::OP_ALL & ~OpenSSL::SSL::OP_DONT_INSERT_EMPTY_FRAGMENTS :
- // OpenSSL::SSL::OP_ALL
- final RubyHash DEFAULT_PARAMS = new RubyHash(runtime);
- IRubyObject ssl_version = StringHelper.newString(runtime, new byte[] { 'S','S','L','v','2','3' });
- DEFAULT_PARAMS.op_aset(context, runtime.newSymbol("ssl_version"), ssl_version);
- IRubyObject verify_mode = runtime.newFixnum(VERIFY_PEER);
- DEFAULT_PARAMS.op_aset(context, runtime.newSymbol("verify_mode"), verify_mode);
- IRubyObject ciphers = StringHelper.newString(runtime, new byte[] {
- 'A','L','L',':',
- '!','A','D','H',':',
- '!','E','X','P','O','R','T',':',
- '!','S','S','L','v','2',':',
- 'R','C','4','+','R','S','A',':',
- '+','H','I','G','H',':',
- '+','M','E','D','I','U','M',':',
- '+','L','O','W'
- });
- DEFAULT_PARAMS.op_aset(context, runtime.newSymbol("ciphers"), ciphers);
- IRubyObject options = runtime.newFixnum(OP_ALL);
- DEFAULT_PARAMS.op_aset(context, runtime.newSymbol("options"), options);
-
- SSLContext.setConstant("DEFAULT_PARAMS", DEFAULT_PARAMS);
}
static final int SESSION_CACHE_OFF = 0;
@@ -267,10 +293,20 @@ public SSLContext(Ruby runtime, RubyClass type) {
super(runtime, _SSLContext(runtime));
}
+ private long options = OP_ALL;
+
+ //private transient CipherStrings.Def cipher_list;
+ /* same as above but sorted for lookup */
+ //private transient CipherStrings.Def cipher_list_by_id;
+ /* TLSv1.3 specific ciphersuites */
+ //private transient CipherStrings.Def tls13_ciphersuites;
+
private String ciphers = CipherStrings.SSL_DEFAULT_CIPHER_LIST;
- private String protocol = "SSL"; // SSLv23 in OpenSSL by default
+ private String protocol = "SSL"; // ctx->method
private boolean protocolForServer = true;
private boolean protocolForClient = true;
+ private int minProtocolVersion = 0;
+ private int maxProtocolVersion = 0;
private PKey t_key;
private X509Cert t_cert;
@@ -279,35 +315,35 @@ public SSLContext(Ruby runtime, RubyClass type) {
//private int sessionCacheMode; // 2 default on MRI
private int sessionCacheSize; // 20480
- private InternalContext internalContext;
+ private volatile InternalContext internalContext;
@JRubyMethod(required = 0, optional = 1, visibility = Visibility.PRIVATE)
public IRubyObject initialize(IRubyObject[] args) {
- if ( args.length > 0 ) set_ssl_version(args[0]);
+ assert this.options == OP_ALL; // self.options |= OpenSSL::SSL::OP_ALL
+ if ( args.length > 0 ) set_ssl_version(args[0]); // self.ssl_version = version if version
return initializeImpl();
}
- @Override
+ @JRubyMethod(visibility = Visibility.PRIVATE)
+ @Override // NOTE: instance variables (no internal state) on #dup
public IRubyObject initialize_copy(IRubyObject original) {
- return super.initialize_copy(original);
- // NOTE: only instance variables (no internal state) on #dup
- // final SSLContext that = (SSLContext) original;
- // this.ciphers = that.ciphers;
- // return this;
+ SSLContext copy = (SSLContext) super.initialize_copy(original);
+ copy.options = ((SSLContext) original).options;
+ return copy;
}
final SSLContext initializeImpl() { return this; }
@JRubyMethod
public IRubyObject setup(final ThreadContext context) {
- final Ruby runtime = context.runtime;
+ if (isFrozen()) return context.nil;
+ return doSetup(context);
+ }
- if ( isFrozen() ) return runtime.getNil();
+ private synchronized IRubyObject doSetup(final ThreadContext context) {
+ if (isFrozen()) return context.nil;
- synchronized(this) {
- if ( isFrozen() ) return runtime.getNil();
- this.freeze(context);
- }
+ final Ruby runtime = context.runtime;
final X509Store certStore = getCertStore();
@@ -405,9 +441,9 @@ public IRubyObject setup(final ThreadContext context) {
value = getInstanceVariable("@verify_callback");
if ( value != null && ! value.isNil() ) {
- store.setExtraData(1, value);
+ store.setExtraData(ossl_ssl_ex_vcb_idx, value);
} else {
- store.setExtraData(1, null);
+ store.setExtraData(ossl_ssl_ex_vcb_idx, null);
}
value = getInstanceVariable("@verify_depth");
@@ -422,6 +458,29 @@ public IRubyObject setup(final ThreadContext context) {
// SSL_CTX_set_tlsext_servername_callback(ctx, ssl_servername_cb);
}
+ final String[] alpnProtocols;
+
+ value = getInstanceVariable("@alpn_protocols");
+ if ( value != null && ! value.isNil() ) {
+ IRubyObject[] alpn_protocols = ((RubyArray) value).toJavaArrayMaybeUnsafe();
+ String[] protocols = new String[alpn_protocols.length];
+ for(int i = 0; i < protocols.length; i++) {
+ protocols[i] = alpn_protocols[i].convertToString().asJavaString();
+ }
+ alpnProtocols = protocols;
+ } else {
+ alpnProtocols = null;
+ }
+
+ final RubyProc alpnSelectCb;
+ value = getInstanceVariable("@alpn_select_cb");
+ if ( value != null && ! value.isNil() ) {
+ alpnSelectCb = (RubyProc) value;
+ } else {
+ alpnSelectCb = null;
+ }
+
+
// NOTE: no API under javax.net to support session get/new/remove callbacks
/*
val = ossl_sslctx_get_sess_id_ctx(self);
@@ -448,36 +507,50 @@ public IRubyObject setup(final ThreadContext context) {
*/
try {
- internalContext = createInternalContext(context, cert, key, store, clientCert, extraChainCert, verifyMode, timeout);
+ internalContext = createInternalContext(context, cert, key, store, clientCert, extraChainCert,
+ verifyMode, timeout, alpnProtocols, alpnSelectCb);
}
catch (GeneralSecurityException e) {
throw newSSLError(runtime, e);
}
+ this.freeze(context);
+
return runtime.getTrue();
}
@JRubyMethod
- public RubyArray ciphers(final ThreadContext context) {
- return matchedCiphers(context);
+ public RubyArray ciphers(final ThreadContext context) { // SSL_CTX_get_ciphers
+ return matchedCiphersWithCache(context, this.ciphers);
}
- private RubyArray matchedCiphers(final ThreadContext context) {
+ private RubyArray matchedCiphersWithCache(final ThreadContext context, final String ciphers) {
+ final CipherListCache cache = cipherListCache;
+ if ( protocol.equals(cache.protocol) && ciphers.equals(cache.ciphers) ) {
+ return newSharedArray(cache.cipherList);
+ }
+
+ final RubyArray match = matchedCiphers(context, ciphers);
+ cipherListCache = new CipherListCache(protocol, ciphers, match);
+ return newSharedArray(match);
+ }
+
+ private RubyArray matchedCiphers(final ThreadContext context, final String ciphers) {
final Ruby runtime = context.runtime;
try {
- final String[] supported = getSupportedCipherSuites(this.protocol);
+ final String[] supported = getSupportedCipherSuites(context, protocol);
final Collection cipherDefs =
- CipherStrings.matchingCiphers(this.ciphers, supported, false);
+ CipherStrings.matchingCiphers(ciphers, supported, false);
final IRubyObject[] cipherList = new IRubyObject[ cipherDefs.size() ];
int i = 0; for ( CipherStrings.Def def : cipherDefs ) {
cipherList[i++] = runtime.newArrayNoCopy(
- newUTF8String(runtime, def.name), // 0
- newUTF8String(runtime, sslVersionString(def.algorithms)), // 1
+ newUTF8String(runtime, def.name).freeze(context), // 0
+ newUTF8String(runtime, sslVersionString(def.algorithms)).freeze(context), // 1
runtime.newFixnum(def.algStrengthBits), // 2
runtime.newFixnum(def.algBits) // 3
- );
+ ).freeze(context);
}
return runtime.newArrayNoCopy(cipherList);
}
@@ -486,46 +559,91 @@ private RubyArray matchedCiphers(final ThreadContext context) {
}
}
+ private static RubyArray newSharedArray(RubyArray array) {
+ return (RubyArray) array.dup(); // shares underlying IRubyObject[] values
+ }
+
@JRubyMethod(name = "ciphers=")
public IRubyObject set_ciphers(final ThreadContext context, final IRubyObject ciphers) {
+ String cipherString;
if ( ciphers.isNil() ) {
- this.ciphers = CipherStrings.SSL_DEFAULT_CIPHER_LIST;
+ cipherString = CipherStrings.SSL_DEFAULT_CIPHER_LIST;
}
else if ( ciphers instanceof RubyArray ) {
final RubyArray ciphs = (RubyArray) ciphers;
StringBuilder cipherStr = new StringBuilder();
String sep = "";
for ( int i = 0; i < ciphs.size(); i++ ) {
- cipherStr.append(sep).append( ciphs.eltInternal(i).toString() );
+ Object elem = ciphs.eltInternal(i);
+ if (elem instanceof RubyArray) {
+ elem = ((RubyArray) elem).eltInternal(0);
+ } else if (elem instanceof RubyString) {
+ // NOTE: JOSSL allows to pass in Java cipher names (in an array)
+ String osslName = SuiteToOSSL.get(((RubyString) elem).asJavaString());
+ if (osslName != null) elem = osslName;
+ }
+ cipherStr.append(sep).append( elem.toString() );
sep = ":";
}
- this.ciphers = cipherStr.toString();
+ cipherString = cipherStr.toString();
}
else {
- this.ciphers = ciphers.asString().toString();
+ cipherString = ciphers.asString().toString();
}
- if ( matchedCiphers(context).isEmpty() ) {
+
+ if (cipherString.equals(CipherStrings.SSL_DEFAULT_CIPHER_LIST)) {
+ cipherString = CipherStrings.SSL_DEFAULT_CIPHER_LIST; // due caching
+ }
+
+ if ( matchedCiphersWithCache(context, cipherString).isEmpty() ) {
throw newSSLError(context.runtime, "no cipher match");
}
+
+ this.ciphers = cipherString;
return ciphers;
}
@JRubyMethod(name = "ssl_version=")
- public IRubyObject set_ssl_version(IRubyObject version) {
- final String versionStr;
- if ( version instanceof RubySymbol ) {
- versionStr = version.toString();
+ public IRubyObject set_ssl_version(IRubyObject method) {
+ final String version;
+ if ( method instanceof RubySymbol ) {
+ version = method.toString();
} else {
- versionStr = version.convertToString().toString();
+ version = method.convertToString().toString();
}
- final String protocol = SSL_VERSION_OSSL2JSSE.get(versionStr);
+ final String protocol = SSL_VERSION_OSSL2JSSE.get(version);
if ( protocol == null ) {
- throw getRuntime().newArgumentError("unknown SSL method `"+ versionStr +"'");
+ throw getRuntime().newArgumentError("unknown SSL method `"+ version +"'");
}
this.protocol = protocol;
- protocolForServer = ! versionStr.endsWith("_client");
- protocolForClient = ! versionStr.endsWith("_server");
- return version;
+ protocolForServer = ! version.endsWith("_client");
+ protocolForClient = ! version.endsWith("_server");
+ return method;
+ }
+
+ @JRubyMethod(name = "set_minmax_proto_version", visibility = Visibility.PRIVATE)
+ public IRubyObject set_minmax_proto_version(ThreadContext context, IRubyObject minVersion, IRubyObject maxVersion) {
+ minProtocolVersion = parseProtoVersion(minVersion);
+ maxProtocolVersion = parseProtoVersion(maxVersion);
+
+ return context.nil;
+ }
+
+ private int parseProtoVersion(IRubyObject version) {
+ if (version.isNil())
+ return 0;
+ if (version instanceof RubyFixnum) {
+ return RubyFixnum.fix2int(version);
+ }
+
+ String string = version.asString().asJavaString();
+ Integer sslVersion = PROTO_VERSION_MAP.get(string);
+
+ if (sslVersion == null) {
+ throw getRuntime().newArgumentError("unrecognized version \"" + string + "\"");
+ }
+
+ return sslVersion;
}
final String getProtocol() { return this.protocol; }
@@ -594,35 +712,67 @@ void setLastVerifyResult(int verifyResult) {
this.verifyResult = verifyResult;
}
- private static String cachedProtocol = null;
- private static String[] cachedSupportedCipherSuites;
+ private static CipherListCache cipherListCache = new CipherListCache(null, null, null);
- private static String[] getSupportedCipherSuites(final String protocol)
- throws GeneralSecurityException {
- if ( cachedProtocol == null ) {
- synchronized(SSLContext.class) {
- if ( cachedProtocol == null ) {
- cachedSupportedCipherSuites = dummySSLEngine(protocol).getSupportedCipherSuites();
- cachedProtocol = protocol;
- return cachedSupportedCipherSuites;
+ private static class CipherListCache {
+ final String protocol;
+ final String ciphers;
+
+ final RubyArray cipherList;
+
+ CipherListCache(String protocol, String ciphers, RubyArray cipherList) {
+ this.protocol = protocol;
+ this.ciphers = ciphers;
+ this.cipherList = cipherList;
+ }
+ }
+
+ void setApplicationProtocolsOrSelector(final SSLEngine engine) {
+ setApplicationProtocolSelector(engine);
+ setApplicationProtocols(engine);
+ }
+
+ private void setApplicationProtocolSelector(final SSLEngine engine) {
+ final RubyProc alpn_select_cb = internalContext.alpnSelectCallback;
+ if (alpn_select_cb != null) {
+ engine.setHandshakeApplicationProtocolSelector((_engine, protocols) -> {
+ final Ruby runtime = getRuntime();
+ IRubyObject[] rubyProtocols = new IRubyObject[protocols.size()];
+ int i = 0; for (String protocol : protocols) {
+ rubyProtocols[i++] = runtime.newString(protocol);
}
- }
+
+ IRubyObject[] args = new IRubyObject[] { RubyArray.newArray(runtime, rubyProtocols) };
+ IRubyObject selected_protocol = alpn_select_cb.call(runtime.getCurrentContext(), args);
+ if (selected_protocol != null && !selected_protocol.isNil()) {
+ return ((RubyString) selected_protocol).asJavaString();
+ }
+ return null; // callback returned nil - none of the advertised names are acceptable
+ });
}
+ }
- if ( protocol.equals(cachedProtocol) ) return cachedSupportedCipherSuites;
+ private void setApplicationProtocols(final SSLEngine engine) {
+ final String[] alpn_protocols = internalContext.alpnProtocols;
+ if (alpn_protocols != null) {
+ SSLParameters params = engine.getSSLParameters();
+ params.setApplicationProtocols(alpn_protocols);
+ engine.setSSLParameters(params);
+ }
+ }
- return dummySSLEngine(protocol).getSupportedCipherSuites();
+ private static String[] getSupportedCipherSuites(ThreadContext context, final String protocol)
+ throws GeneralSecurityException {
+ return dummySSLEngine(context, protocol).getSupportedCipherSuites();
}
- private static SSLEngine dummySSLEngine(final String protocol) throws GeneralSecurityException {
+ private static SSLEngine dummySSLEngine(ThreadContext context, final String protocol) throws GeneralSecurityException {
javax.net.ssl.SSLContext sslContext = SecurityHelper.getSSLContext(protocol);
- sslContext.init(null, null, null);
+ sslContext.init(null, null, OpenSSL.getSecureRandom(context));
return sslContext.createSSLEngine();
}
- // should keep SSLContext as a member for introducin SSLSession. later...
- final SSLEngine createSSLEngine(String peerHost, int peerPort)
- throws NoSuchAlgorithmException, KeyManagementException {
+ final SSLEngine createSSLEngine(String peerHost, int peerPort) {
final SSLEngine engine;
// an empty peerHost implies no SNI (RFC 3546) support requested
if ( peerHost == null || peerHost.length() == 0 ) {
@@ -634,18 +784,18 @@ final SSLEngine createSSLEngine(String peerHost, int peerPort)
else {
engine = internalContext.getSSLContext().createSSLEngine(peerHost, peerPort);
}
- engine.setEnabledCipherSuites( getCipherSuites(engine.getSupportedCipherSuites()) );
- engine.setEnabledProtocols( getEnabledProtocols(engine) );
+ final String[] protocols = getEnabledProtocols(engine);
+ engine.setEnabledProtocols(protocols);
+ engine.setEnabledCipherSuites( getEnabledCipherSuites(engine, protocols) );
+
return engine;
}
- private String[] getCipherSuites(final String[] supported) {
- Collection cipherDefs =
- CipherStrings.matchingCiphers(this.ciphers, supported, true);
+ private String[] getEnabledCipherSuites(final SSLEngine engine, final String[] protocols) {
+ final String[] supported = engine.getSupportedCipherSuites();
+ Collection cipherDefs = CipherStrings.matchingCiphers(this.ciphers, supported, true);
final String[] result = new String[ cipherDefs.size() ]; int i = 0;
- for ( CipherStrings.Def def : cipherDefs ) {
- result[ i++ ] = def.getCipherSuite();
- }
+ for ( CipherStrings.Def def : cipherDefs ) result[ i++ ] = def.getCipherSuite();
return result;
}
@@ -654,24 +804,32 @@ private String[] getEnabledProtocols(final SSLEngine engine) {
if ( enabledProtocols != null ) {
final long options = getOptions();
final String[] engineProtocols = engine.getEnabledProtocols();
- final List protocols = new ArrayList(enabledProtocols.length);
+ final List protocols = new ArrayList<>(enabledProtocols.length);
for ( final String enabled : enabledProtocols ) {
- if (((options & SSL.OP_NO_SSLv2) != 0) && enabled.equals("SSLv2")) {
- continue;
- }
- if (((options & SSL.OP_NO_SSLv3) != 0) && enabled.equals("SSLv3")) {
- continue;
- }
- if (((options & SSL.OP_NO_TLSv1) != 0) && enabled.equals("TLSv1")) {
- continue;
- }
- for ( final String allowed : engineProtocols ) {
- if ( allowed.equals(enabled) ) protocols.add(allowed);
- }
+ int protocolVersion = JSSE_TO_VERSION.get(enabled);
+ if (minProtocolVersion != 0 && protocolVersion < minProtocolVersion) continue;
+ if (maxProtocolVersion != 0 && protocolVersion > maxProtocolVersion) continue;
+
+ if (((options & OP_NO_SSLv2) != 0) && enabled.equals("SSLv2")) continue;
+ if (((options & OP_NO_SSLv3) != 0) && enabled.equals("SSLv3")) continue;
+ if (((options & OP_NO_TLSv1) != 0) && enabled.equals("TLSv1")) continue;
+ if (((options & OP_NO_TLSv1_1) != 0) && enabled.equals("TLSv1.1")) continue;
+ if (((options & OP_NO_TLSv1_2) != 0) && enabled.equals("TLSv1.2")) continue;
+ if (((options & OP_NO_TLSv1_3) != 0) && enabled.equals("TLSv1.3")) continue;
+
+ if (arrayContains(engineProtocols, enabled)) protocols.add(enabled);
}
- return protocols.toArray( new String[ protocols.size() ] );
+
+ return protocols.toArray(EMPTY_STRING_ARRAY);
}
- return new String[0];
+ return EMPTY_STRING_ARRAY;
+ }
+
+ private static boolean arrayContains(final String[] array, final String value) {
+ for (final String elem : array) {
+ if (elem.equals(value)) return true;
+ }
+ return false;
}
private static final byte[] TLSv1 = { 'T','L','S','v','1' };
@@ -749,11 +907,23 @@ private String getCaPath() {
}
private long getOptions() {
- IRubyObject options = getInstanceVariable("@options");
- if ( options != null && ! options.isNil() ) {
- return RubyNumeric.fix2long(options);
+ return options;
+ }
+
+ @JRubyMethod
+ public RubyInteger options(ThreadContext context) {
+ return context.runtime.newFixnum(getOptions());
+ }
+
+ @JRubyMethod(name = "options=")
+ public IRubyObject options_set(final IRubyObject options) {
+ if (options.isNil()) {
+ this.options = OP_ALL;
+ } else {
+ this.options = RubyNumeric.num2long(options);
}
- return 0;
+
+ return this;
}
private static List convertToAuxCerts(final ThreadContext context, IRubyObject value) {
@@ -802,8 +972,9 @@ static RubyClass _SSLContext(final Ruby runtime) {
private InternalContext createInternalContext(ThreadContext context,
final X509Cert xCert, final PKey pKey, final Store store,
final List clientCert, final List extraChainCert,
- final int verifyMode, final int timeout) throws NoSuchAlgorithmException, KeyManagementException {
- InternalContext internalContext = new InternalContext(xCert, pKey, store, clientCert, extraChainCert, verifyMode, timeout);
+ final int verifyMode, final int timeout,
+ final String[] alpnProtocols, final RubyProc alpnSelectCb) throws NoSuchAlgorithmException, KeyManagementException {
+ InternalContext internalContext = new InternalContext(xCert, pKey, store, clientCert, extraChainCert, verifyMode, timeout, alpnProtocols, alpnSelectCb);
internalContext.initSSLContext(context);
return internalContext;
}
@@ -820,16 +991,18 @@ private class InternalContext {
final List clientCert,
final List extraChainCert,
final int verifyMode,
- final int timeout) throws NoSuchAlgorithmException {
+ final int timeout,
+ final String[] alpnProtocols,
+ final RubyProc alpnSelectCallback) throws NoSuchAlgorithmException {
if ( pKey != null && xCert != null ) {
this.privateKey = pKey.getPrivateKey();
- this.keyAlgorithm = pKey.getAlgorithm();
+ this.keyType = pKey.getKeyType();
this.cert = xCert.getAuxCert();
}
else {
this.privateKey = null;
- this.keyAlgorithm = null;
+ this.keyType = null;
this.cert = null;
}
@@ -838,6 +1011,8 @@ private class InternalContext {
this.extraChainCert = extraChainCert;
this.verifyMode = verifyMode;
this.timeout = timeout;
+ this.alpnProtocols = alpnProtocols;
+ this.alpnSelectCallback = alpnSelectCallback;
// initialize SSL context :
@@ -852,7 +1027,7 @@ void initSSLContext(final ThreadContext context) throws KeyManagementException {
// SSLContext (internals) on Sun JDK :
// private final java.security.Provider provider; "SunJSSE"
// private final javax.net.ssl.SSLContextSpi; sun.security.ssl.SSLContextImpl
- sslContext.init(keyManager, trustManager, OpenSSL.getSecureRandomFrom(context));
+ sslContext.init(keyManager, trustManager, OpenSSL.getSecureRandom(context));
// if secureRandom == null JSSE will try :
// - new SecureRandom();
// - SecureRandom.getInstance("PKCS11", cryptoProvider);
@@ -875,7 +1050,7 @@ void initSSLContext(final ThreadContext context) throws KeyManagementException {
final Store store;
final X509AuxCertificate cert;
- final String keyAlgorithm;
+ final String keyType;
final PrivateKey privateKey;
final int verifyMode;
@@ -885,6 +1060,9 @@ void initSSLContext(final ThreadContext context) throws KeyManagementException {
private final int timeout;
+ private final String[] alpnProtocols;
+ private final RubyProc alpnSelectCallback;
+
private final javax.net.ssl.SSLContext sslContext;
// part of ssl_verify_cert_chain
@@ -895,9 +1073,9 @@ StoreContext createStoreContext(final String purpose) {
if ( storeContext.init(null, null) == 0 ) return null;
// for verify_cb
- storeContext.setExtraData(1, store.getExtraData(1));
+ storeContext.setExtraData(ossl_ssl_ex_vcb_idx, store.getExtraData(ossl_ssl_ex_vcb_idx));
if ( purpose != null ) storeContext.setDefault(purpose);
- storeContext.verifyParameter.inherit(store.verifyParameter);
+ storeContext.getParam().inherit(store.getParam());
return storeContext;
}
@@ -923,7 +1101,7 @@ public String chooseEngineClientAlias(String[] keyType, java.security.Principal[
if (internalContext.privateKey == null) return null;
for (int i = 0; i < keyType.length; i++) {
- if (keyType[i].equalsIgnoreCase(internalContext.keyAlgorithm)) {
+ if (keyType[i].equalsIgnoreCase(internalContext.keyType)) {
return keyType[i];
}
}
@@ -934,7 +1112,7 @@ public String chooseEngineClientAlias(String[] keyType, java.security.Principal[
public String chooseEngineServerAlias(String keyType, java.security.Principal[] issuers, javax.net.ssl.SSLEngine engine) {
if (internalContext.privateKey == null) return null;
- if (keyType.equalsIgnoreCase(internalContext.keyAlgorithm)) {
+ if (keyType.equalsIgnoreCase(internalContext.keyType)) {
return keyType;
}
return null;
@@ -958,7 +1136,7 @@ public java.security.cert.X509Certificate[] getCertificateChain(String alias) {
chain = (List) internalContext.extraChainCert;
}
else if ( internalContext.cert != null ) {
- chain = new ArrayList(8);
+ chain = new ArrayList<>(8);
StoreContext storeCtx = internalContext.createStoreContext(null);
X509AuxCertificate x = internalContext.cert;
@@ -974,7 +1152,7 @@ else if ( internalContext.cert != null ) {
if (storeCtx.getBySubject(X509Utils.X509_LU_X509, name, s_obj) <= 0) {
break;
}
- x = ((Certificate) s_obj[0]).x509;
+ x = ((Certificate) s_obj[0]).cert;
}
catch (RuntimeException e) {
debugStackTrace(e);
@@ -1061,14 +1239,14 @@ private void verifyChain(final StoreContext storeContext) throws CertificateExce
ok = storeContext.verifyCertificate();
}
catch (Exception e) {
- internalContext.setLastVerifyResult(storeContext.error);
- if ( storeContext.error == X509Utils.V_OK ) {
+ internalContext.setLastVerifyResult(storeContext.getError());
+ if ( storeContext.getError() == X509Utils.V_OK ) {
internalContext.setLastVerifyResult(X509Utils.V_ERR_CERT_REJECTED);
}
throw new CertificateException("certificate verify failed", e);
}
- internalContext.setLastVerifyResult(storeContext.error);
+ internalContext.setLastVerifyResult(storeContext.getError());
if ( ok == 0 ) {
throw new CertificateException("certificate verify failed");
}
diff --git a/src/main/java/org/jruby/ext/openssl/SSLSession.java b/src/main/java/org/jruby/ext/openssl/SSLSession.java
index 356fb5a4..5c2a832a 100644
--- a/src/main/java/org/jruby/ext/openssl/SSLSession.java
+++ b/src/main/java/org/jruby/ext/openssl/SSLSession.java
@@ -35,7 +35,6 @@
import org.jruby.RubyString;
import org.jruby.RubyTime;
import org.jruby.anno.JRubyMethod;
-import org.jruby.runtime.ObjectAllocator;
import org.jruby.runtime.ThreadContext;
import org.jruby.runtime.Visibility;
import org.jruby.runtime.builtin.IRubyObject;
@@ -51,14 +50,8 @@
*/
public class SSLSession extends RubyObject {
- private static final ObjectAllocator SESSION_ALLOCATOR = new ObjectAllocator() {
- public IRubyObject allocate(Ruby runtime, RubyClass klass) {
- return new SSLSession(runtime, klass);
- }
- };
-
static void createSession(final Ruby runtime, final RubyModule SSL, final RubyClass OpenSSLError) { // OpenSSL::SSL
- RubyClass Session = SSL.defineClassUnder("Session", runtime.getObject(), SESSION_ALLOCATOR);
+ RubyClass Session = SSL.defineClassUnder("Session", runtime.getObject(), (r, klass) -> new SSLSession(r, klass));
// OpenSSL::SSL::Session::SessionError
Session.defineClassUnder("SessionError", OpenSSLError, OpenSSLError.getAllocator());
Session.defineAnnotatedMethods(SSLSession.class);
@@ -149,8 +142,10 @@ public IRubyObject set_time(final ThreadContext context, IRubyObject time) {
@JRubyMethod(name = "timeout")
public IRubyObject timeout(final ThreadContext context) {
final SSLSessionContext sessionContext = sslSession().getSessionContext();
- // default in OpenSSL is 300
- if ( sessionContext == null ) return context.runtime.newFixnum(300);
+ if (sessionContext == null) {
+ // JDK's default is 24h (default in OpenSSL is 300)
+ return context.runtime.newFixnum(86400);
+ }
return context.runtime.newFixnum(sessionContext.getSessionTimeout());
}
@@ -161,7 +156,11 @@ public IRubyObject set_timeout(final ThreadContext context, IRubyObject timeout)
warn(context, "WARNING: can not set OpenSSL::SSL::Session#timeout=("+ timeout +") no session context");
return context.nil;
}
- sessionContext.setSessionTimeout(RubyNumeric.fix2int(timeout)); // in seconds as well
+ try {
+ sessionContext.setSessionTimeout(RubyNumeric.fix2int(timeout)); // in seconds as well
+ } catch (IllegalArgumentException e) {
+ throw context.runtime.newArgumentError(e.getMessage());
+ }
return timeout;
}
diff --git a/src/main/java/org/jruby/ext/openssl/SSLSocket.java b/src/main/java/org/jruby/ext/openssl/SSLSocket.java
index 871be46c..2bcd5658 100644
--- a/src/main/java/org/jruby/ext/openssl/SSLSocket.java
+++ b/src/main/java/org/jruby/ext/openssl/SSLSocket.java
@@ -28,17 +28,14 @@
package org.jruby.ext.openssl;
import java.io.IOException;
-import java.net.Socket;
import java.nio.ByteBuffer;
import java.nio.channels.Channel;
import java.nio.channels.ClosedChannelException;
+import java.nio.channels.ClosedSelectorException;
import java.nio.channels.NotYetConnectedException;
-import java.nio.channels.SelectableChannel;
import java.nio.channels.SelectionKey;
import java.nio.channels.Selector;
import java.nio.channels.SocketChannel;
-import java.security.KeyManagementException;
-import java.security.NoSuchAlgorithmException;
import java.security.cert.Certificate;
import java.security.cert.CertificateEncodingException;
@@ -69,12 +66,6 @@ public class SSLSocket extends RubyObject {
private static final long serialVersionUID = -2084816623554406237L;
- private static final ObjectAllocator ALLOCATOR = new ObjectAllocator() {
- public IRubyObject allocate(Ruby runtime, RubyClass klass) {
- return new SSLSocket(runtime, klass);
- }
- };
-
private enum CallSiteIndex {
// self
@@ -110,7 +101,8 @@ public static void createSSLSocket(final Ruby runtime, final RubyModule SSL) { /
}
}
- RubyClass SSLSocket = runtime.defineClassUnder("SSLSocket", runtime.getObject(), ALLOCATOR, SSL, extraCallSites);
+ RubyClass SSLSocket = runtime.defineClassUnder("SSLSocket", runtime.getObject(),
+ (r, klass) -> new SSLSocket(r, klass), SSL, extraCallSites);
final ThreadContext context = runtime.getCurrentContext();
@@ -153,14 +145,15 @@ private static CallSite callSite(final CallSite[] sites, final CallSiteIndex ind
private SSLEngine engine;
private RubyIO io;
- private ByteBuffer peerAppData;
- private ByteBuffer peerNetData;
- private ByteBuffer netData;
- private ByteBuffer dummy;
+ private ByteBuffer appReadData;
+ private ByteBuffer netReadData;
+ private ByteBuffer netWriteData;
+ private final ByteBuffer dummy = ByteBuffer.allocate(0); // could be static
private boolean initialHandshake = false;
+ private transient long initializeTime;
- private SSLEngineResult.HandshakeStatus handshakeStatus;
+ private SSLEngineResult.HandshakeStatus handshakeStatus; // != null after hand-shake starts
private SSLEngineResult.Status status;
int verifyResult = X509Utils.V_OK;
@@ -169,17 +162,19 @@ private static CallSite callSite(final CallSite[] sites, final CallSiteIndex ind
public IRubyObject initialize(final ThreadContext context, final IRubyObject[] args) {
final Ruby runtime = context.runtime;
- if ( Arity.checkArgumentCount(runtime, args, 1, 2) == 1 ) {
- sslContext = new SSLContext(runtime).initializeImpl();
+ if (Arity.checkArgumentCount(runtime, args, 1, 2) == 1) {
+ this.sslContext = new SSLContext(runtime).initializeImpl();
} else {
- sslContext = (SSLContext) args[1];
+ if (!(args[1] instanceof SSLContext)) {
+ throw runtime.newTypeError(args[1], "OpenSSL::SSL::SSLContext");
+ }
+ this.sslContext = (SSLContext) args[1];
}
- if ( ! ( args[0] instanceof RubyIO ) ) {
+ if (!(args[0] instanceof RubyIO)) {
throw runtime.newTypeError("IO expected but got " + args[0].getMetaClass().getName());
}
- setInstanceVariable("@context", this.sslContext); // only compat (we do not use @context)
- setInstanceVariable("@io", this.io = (RubyIO) args[0]);
+ setInstanceVariable("@io", this.io = (RubyIO) args[0]); // RubyBasicSocket extends RubyIO
set_io_nonblock_checked(context, runtime.getTrue());
// This is a bit of a hack: SSLSocket should share code with
// RubyBasicSocket, which always sets sync to true.
@@ -187,6 +182,10 @@ public IRubyObject initialize(final ThreadContext context, final IRubyObject[] a
set_sync(context, runtime.getTrue()); // io.sync = true
setInstanceVariable("@sync_close", runtime.getFalse()); // self.sync_close = false
sslContext.setup(context);
+ setInstanceVariable("@context", sslContext); // only compat (we do not use @context)
+
+ this.initializeTime = System.currentTimeMillis();
+
return Utils.invokeSuper(context, this, args, Block.NULL_BLOCK); // super()
}
@@ -208,8 +207,9 @@ private IRubyObject fallback_set_io_nonblock_checked(ThreadContext context, Ruby
return context.nil;
}
- private SSLEngine ossl_ssl_setup(final ThreadContext context)
- throws NoSuchAlgorithmException, KeyManagementException {
+ private static final String SESSION_SOCKET_ID = "socket_id";
+
+ private SSLEngine ossl_ssl_setup(final ThreadContext context, final boolean server) {
SSLEngine engine = this.engine;
if ( engine != null ) return engine;
@@ -221,15 +221,18 @@ private SSLEngine ossl_ssl_setup(final ThreadContext context)
engine = sslContext.createSSLEngine(peerHost, peerPort);
final javax.net.ssl.SSLSession session = engine.getSession();
- peerNetData = ByteBuffer.allocate(session.getPacketBufferSize());
- peerAppData = ByteBuffer.allocate(session.getApplicationBufferSize());
- netData = ByteBuffer.allocate(session.getPacketBufferSize());
- peerNetData.limit(0);
- peerAppData.limit(0);
- netData.limit(0);
- dummy = ByteBuffer.allocate(0);
+ netReadData = ByteBuffer.allocate(session.getPacketBufferSize());
+ appReadData = ByteBuffer.allocate(session.getApplicationBufferSize());
+ netWriteData = ByteBuffer.allocate(session.getPacketBufferSize());
+ netReadData.limit(0);
+ appReadData.limit(0);
+ netWriteData.limit(0);
+
this.engine = engine;
copySessionSetupIfSet(context);
+
+ sslContext.setApplicationProtocolsOrSelector(engine);
+
return engine;
}
@@ -239,6 +242,15 @@ private SSLEngine ossl_ssl_setup(final ThreadContext context)
@JRubyMethod(name = "context")
public final SSLContext context() { return this.sslContext; }
+ @JRubyMethod(name = "alpn_protocol")
+ public IRubyObject alpn_protocol(final ThreadContext context) {
+ final String protocol = engine.getApplicationProtocol();
+ // null if it has not yet been determined if alpn might be used for this connection,
+ // an empty String if application protocols values will not be used,
+ if (protocol == null || protocol.isEmpty()) return context.nil;
+ return RubyString.newString(context.runtime, protocol);
+ }
+
@JRubyMethod(name = "sync")
public IRubyObject sync(final ThreadContext context) {
final CallSite[] sites = getMetaClass().getExtraCallSites();
@@ -284,7 +296,7 @@ private IRubyObject connectImpl(final ThreadContext context, final boolean block
try {
if ( ! initialHandshake ) {
- SSLEngine engine = ossl_ssl_setup(context);
+ SSLEngine engine = ossl_ssl_setup(context, false);
engine.setUseClientMode(true);
engine.beginHandshake();
handshakeStatus = engine.getHandshakeStatus();
@@ -301,16 +313,6 @@ private IRubyObject connectImpl(final ThreadContext context, final boolean block
forceClose();
throw newSSLErrorFromHandshake(context.runtime, e);
}
- catch (NoSuchAlgorithmException e) {
- debugStackTrace(context.runtime, e);
- forceClose();
- throw newSSLError(context.runtime, e);
- }
- catch (KeyManagementException e) {
- debugStackTrace(context.runtime, e);
- forceClose();
- throw newSSLError(context.runtime, e);
- }
catch (IOException e) {
//debugStackTrace(context.runtime, e);
forceClose();
@@ -354,7 +356,7 @@ private IRubyObject acceptImpl(final ThreadContext context, final boolean blocki
try {
if ( ! initialHandshake ) {
- final SSLEngine engine = ossl_ssl_setup(context);
+ final SSLEngine engine = ossl_ssl_setup(context, true);
engine.setUseClientMode(false);
final IRubyObject verify_mode = verify_mode(context);
if ( verify_mode != context.nil ) {
@@ -391,14 +393,6 @@ private IRubyObject acceptImpl(final ThreadContext context, final boolean blocki
}
throw newSSLErrorFromHandshake(context.runtime, e);
}
- catch (NoSuchAlgorithmException e) {
- debugStackTrace(context.runtime, e);
- throw newSSLError(context.runtime, e);
- }
- catch (KeyManagementException e) {
- debugStackTrace(context.runtime, e);
- throw newSSLError(context.runtime, e);
- }
catch (IOException e) {
debugStackTrace(context.runtime, e);
throw newSSLError(context.runtime, e);
@@ -459,9 +453,9 @@ private Object waitSelect(final int operations, final boolean blocking, final bo
try {
result[0] = selector.selectNow();
- if ( result[0] == 0 ) {
+ if (result[0] == 0) {
if ((operations & SelectionKey.OP_READ) != 0 && (operations & SelectionKey.OP_WRITE) != 0) {
- if ( key.isReadable() ) {
+ if (key.isReadable()) {
writeWouldBlock(runtime, exception, result);
}
//else if ( key.isWritable() ) {
@@ -470,27 +464,27 @@ private Object waitSelect(final int operations, final boolean blocking, final bo
else { //neither, pick one
readWouldBlock(runtime, exception, result);
}
- }
- else if ((operations & SelectionKey.OP_READ) != 0) {
+ } else if ((operations & SelectionKey.OP_READ) != 0) {
readWouldBlock(runtime, exception, result);
- }
- else if ((operations & SelectionKey.OP_WRITE) != 0) {
+ } else if ((operations & SelectionKey.OP_WRITE) != 0) {
writeWouldBlock(runtime, exception, result);
}
}
- }
- catch (IOException ioe) {
- throw runtime.newRuntimeError("Error with selector: " + ioe.getMessage());
+ } catch (ClosedSelectorException ex) {
+ throw Utils.newRuntimeError(runtime, "selector closed", ex);
+ } catch (IOException ex) {
+ throw Utils.newIOError(runtime, ex);
}
} else {
io.addBlockingThread(thread);
thread.executeBlockingTask(new RubyThread.BlockingTask() {
- public void run() throws InterruptedException {
+ public void run() {
try {
result[0] = selector.select();
- }
- catch (IOException ioe) {
- throw runtime.newRuntimeError("Error with selector: " + ioe.getMessage());
+ } catch (ClosedSelectorException ex) {
+ throw Utils.newRuntimeError(runtime, "selector closed", ex);
+ } catch (IOException ex) {
+ throw Utils.newIOError(runtime, ex);
}
}
@@ -512,32 +506,27 @@ public void wakeup() {
//JRuby <= 9.1.2.0 that makes this not always the case, so we have to check
return selector.selectedKeys().contains(key) ? Boolean.TRUE : Boolean.FALSE;
}
- }
- catch (InterruptedException interrupt) { return Boolean.FALSE; }
- finally {
- // Note: I don't like ignoring these exceptions, but it's
- // unclear how likely they are to happen or what damage we
- // might do by ignoring them. Note that the pieces are separate
- // so that we can ensure one failing does not affect the others
- // running.
+ } catch (InterruptedException interrupt) {
+ debug(runtime, "SSLSocket.waitSelect", interrupt);
+ return Boolean.FALSE;
+ } finally {
+ // Note: I don't like ignoring these exceptions, but it's unclear how likely they are to happen or what
+ // damage we might do by ignoring them. Note that the pieces are separate so that we can ensure one failing
+ // does not affect the others running.
// clean up the key in the selector
try {
if ( key != null ) key.cancel();
if ( selector != null ) selector.selectNow();
- }
- catch (Exception e) { // ignore
- debugStackTrace(runtime, e);
+ } catch (Exception e) { // ignore
+ debugStackTrace(runtime, "SSLSocket.waitSelect (ignored)", e);
}
// shut down and null out the selector
try {
- if ( selector != null ) {
- runtime.getSelectorPool().put(selector);
- }
- }
- catch (Exception e) { // ignore
- debugStackTrace(runtime, e);
+ if ( selector != null ) runtime.getSelectorPool().put(selector);
+ } catch (Exception e) { // ignore
+ debugStackTrace(runtime, "SSLSocket.waitSelect (ignored)", e);
}
if (blocking) {
@@ -579,7 +568,6 @@ private IRubyObject doHandshake(final boolean blocking, final boolean exception)
}
// otherwise, proceed as before
-
switch (handshakeStatus) {
case FINISHED:
case NOT_HANDSHAKING:
@@ -592,23 +580,30 @@ private IRubyObject doHandshake(final boolean blocking, final boolean exception)
if (readAndUnwrap(blocking) == -1 && handshakeStatus != SSLEngineResult.HandshakeStatus.FINISHED) {
throw new SSLHandshakeException("Socket closed");
}
- // during initialHandshake, calling readAndUnwrap that results UNDERFLOW
- // does not mean writable. we explicitly wait for readable channel to avoid
- // busy loop.
+ // during initialHandshake, calling readAndUnwrap that results UNDERFLOW does not mean writable.
+ // we explicitly wait for readable channel to avoid busy loop.
if (initialHandshake && status == SSLEngineResult.Status.BUFFER_UNDERFLOW) {
sel = waitSelect(SelectionKey.OP_READ, blocking, exception);
if ( sel instanceof IRubyObject ) return (IRubyObject) sel; // :wait_readable
}
break;
case NEED_WRAP:
- if ( netData.hasRemaining() ) {
+ if ( netWriteData.hasRemaining() ) {
while ( flushData(blocking) ) { /* loop */ }
}
- netData.clear();
- SSLEngineResult result = engine.wrap(dummy, netData);
- handshakeStatus = result.getHandshakeStatus();
- netData.flip();
+ assert !netWriteData.hasRemaining();
+ doWrap(blocking);
flushData(blocking);
+ assert status != SSLEngineResult.Status.BUFFER_UNDERFLOW;
+ if (status == SSLEngineResult.Status.BUFFER_OVERFLOW) {
+ netWriteData.compact();
+ netWriteData = Utils.ensureCapacity(netWriteData, engine.getSession().getPacketBufferSize());
+ netWriteData.flip();
+ if (handshakeStatus != SSLEngineResult.HandshakeStatus.NEED_UNWRAP) {
+ sel = waitSelect(SelectionKey.OP_WRITE, blocking, exception);
+ if ( sel instanceof IRubyObject ) return (IRubyObject) sel; // :wait_writeable
+ }
+ }
break;
default:
throw new IllegalStateException("Unknown handshaking status: " + handshakeStatus);
@@ -616,6 +611,18 @@ private IRubyObject doHandshake(final boolean blocking, final boolean exception)
}
}
+ private void doWrap(final boolean blocking) throws IOException {
+ netWriteData.clear();
+ SSLEngineResult result = engine.wrap(dummy, netWriteData);
+ netWriteData.flip();
+ handshakeStatus = result.getHandshakeStatus();
+ status = result.getStatus();
+ if (handshakeStatus == SSLEngineResult.HandshakeStatus.NEED_TASK
+ && status == SSLEngineResult.Status.OK) {
+ doTasks();
+ }
+ }
+
private void doTasks() {
Runnable task;
while ((task = engine.getDelegatedTask()) != null) {
@@ -625,18 +632,20 @@ private void doTasks() {
verifyResult = sslContext.getLastVerifyResult();
}
+ /**
+ * @param blocking
+ * @return whether buffer has remaining data
+ * @throws IOException
+ */
private boolean flushData(boolean blocking) throws IOException {
try {
- writeToChannel(netData, blocking);
+ writeToChannel(netWriteData, blocking);
}
catch (IOException ioe) {
- netData.position(netData.limit());
+ netWriteData.position(netWriteData.limit());
throw ioe;
}
- if ( netData.hasRemaining() ) {
- return true;
- }
- return false;
+ return netWriteData.hasRemaining();
}
private int writeToChannel(ByteBuffer buffer, boolean blocking) throws IOException {
@@ -650,18 +659,20 @@ private int writeToChannel(ByteBuffer buffer, boolean blocking) throws IOExcepti
private void finishInitialHandshake() {
initialHandshake = false;
+
+ final javax.net.ssl.SSLSession session = engine.getSession();
+ if (session.getValue(SESSION_SOCKET_ID) != null) {
+ session.putValue(SESSION_SOCKET_ID, getObjectId());
+ }
}
-
+
+ // NOTE: gets called on negotiation connect/accept - not really on RE-negotiation as intended?!
private void callRenegotiationCallback(final ThreadContext context) throws RaiseException {
IRubyObject renegotiationCallback = sslContext.getInstanceVariable("@renegotiation_cb");
- if(renegotiationCallback == null || renegotiationCallback.isNil()) {
- return;
- }
- else {
- // the return of the Proc is not important
- // Can throw ruby exception to "disallow" renegotiations
- renegotiationCallback.callMethod(context, "call", this);
- }
+ if (renegotiationCallback == null || renegotiationCallback.isNil()) return;
+ // the return of the Proc is not important
+ // Can throw ruby exception to "disallow" re-negotiations
+ renegotiationCallback.callMethod(context, "call", this);
}
public int write(ByteBuffer src, boolean blocking) throws SSLException, IOException {
@@ -674,15 +685,15 @@ public int write(ByteBuffer src, boolean blocking) throws SSLException, IOExcept
if ( ! blocking ) channel.configureBlocking(false);
try {
- if ( netData.hasRemaining() ) {
+ if ( netWriteData.hasRemaining() ) {
flushData(blocking);
}
- netData.clear();
- final SSLEngineResult result = engine.wrap(src, netData);
+ netWriteData.clear();
+ final SSLEngineResult result = engine.wrap(src, netWriteData);
if ( result.getStatus() == SSLEngineResult.Status.CLOSED ) {
throw getRuntime().newIOError("closed SSL engine");
}
- netData.flip();
+ netWriteData.flip();
flushData(blocking);
return result.bytesConsumed();
}
@@ -695,22 +706,22 @@ public int read(final ByteBuffer dst, final boolean blocking) throws IOException
if ( initialHandshake ) return 0;
if ( engine.isInboundDone() ) return -1;
- if ( ! peerAppData.hasRemaining() ) {
+ if ( ! appReadData.hasRemaining() ) {
int appBytesProduced = readAndUnwrap(blocking);
if (appBytesProduced == -1 || appBytesProduced == 0) {
return appBytesProduced;
}
}
- int limit = Math.min(peerAppData.remaining(), dst.remaining());
- peerAppData.get(dst.array(), dst.arrayOffset(), limit);
+ int limit = Math.min(appReadData.remaining(), dst.remaining());
+ appReadData.get(dst.array(), dst.arrayOffset(), limit);
dst.position(dst.arrayOffset() + limit);
return limit;
}
private int readAndUnwrap(final boolean blocking) throws IOException {
- final int bytesRead = socketChannelImpl().read(peerNetData);
+ final int bytesRead = socketChannelImpl().read(netReadData);
if ( bytesRead == -1 ) {
- if ( ! peerNetData.hasRemaining() ||
+ if ( ! netReadData.hasRemaining() ||
( status == SSLEngineResult.Status.BUFFER_UNDERFLOW ) ) {
closeInbound();
return -1;
@@ -719,12 +730,12 @@ private int readAndUnwrap(final boolean blocking) throws IOException {
// be defered till the last engine.unwrap() call.
// peerNetData could not be empty.
}
- peerAppData.clear();
- peerNetData.flip();
+ appReadData.clear();
+ netReadData.flip();
SSLEngineResult result;
do {
- result = engine.unwrap(peerNetData, peerAppData);
+ result = engine.unwrap(netReadData, appReadData);
}
while ( result.getStatus() == SSLEngineResult.Status.OK &&
result.getHandshakeStatus() == SSLEngineResult.HandshakeStatus.NEED_UNWRAP &&
@@ -733,15 +744,15 @@ private int readAndUnwrap(final boolean blocking) throws IOException {
if ( result.getHandshakeStatus() == SSLEngineResult.HandshakeStatus.FINISHED ) {
finishInitialHandshake();
}
- if ( peerAppData.position() == 0 &&
+ if ( appReadData.position() == 0 &&
result.getStatus() == SSLEngineResult.Status.OK &&
- peerNetData.hasRemaining() ) {
- result = engine.unwrap(peerNetData, peerAppData);
+ netReadData.hasRemaining() ) {
+ result = engine.unwrap(netReadData, appReadData);
}
status = result.getStatus();
handshakeStatus = result.getHandshakeStatus();
- if ( bytesRead == -1 && ! peerNetData.hasRemaining() ) {
+ if ( bytesRead == -1 && ! netReadData.hasRemaining() ) {
// now it's safe to call closeInbound().
closeInbound();
}
@@ -750,15 +761,15 @@ private int readAndUnwrap(final boolean blocking) throws IOException {
return -1;
}
- peerNetData.compact();
- peerAppData.flip();
+ netReadData.compact();
+ appReadData.flip();
if ( ! initialHandshake && (
handshakeStatus == SSLEngineResult.HandshakeStatus.NEED_TASK ||
handshakeStatus == SSLEngineResult.HandshakeStatus.NEED_WRAP ||
handshakeStatus == SSLEngineResult.HandshakeStatus.FINISHED ) ) {
doHandshake(blocking);
}
- return peerAppData.remaining();
+ return appReadData.remaining();
}
private void closeInbound() {
@@ -773,35 +784,42 @@ private void closeInbound() {
}
private void doShutdown() throws IOException {
- if ( engine.isOutboundDone() ) return;
+ if (engine.isOutboundDone()) return;
- netData.clear();
+ if (flushData(false)) {
+ debug(getRuntime(), "SSLSocket.doShutdown data in the data buffer - can't send close");
+ return;
+ }
+ netWriteData.clear();
try {
- engine.wrap(dummy, netData);
+ engine.wrap(dummy, netWriteData); // send close (after sslEngine.closeOutbound)
}
catch (SSLException e) {
debug(getRuntime(), "SSLSocket.doShutdown", e);
return;
}
catch (RuntimeException e) {
- debugStackTrace(getRuntime(), e);
+ debugStackTrace(getRuntime(), "SSLSocket.doShutdown", e);
return;
}
- netData.flip();
+ netWriteData.flip();
flushData(true);
}
- private IRubyObject sysreadImpl(final ThreadContext context,
- IRubyObject len, IRubyObject buff, final boolean blocking, final boolean exception) {
+ /**
+ * @return the (@link RubyString} buffer or :wait_readable / :wait_writeable {@link RubySymbol}
+ */
+ private IRubyObject sysreadImpl(final ThreadContext context, final IRubyObject len, final IRubyObject buff,
+ final boolean blocking, final boolean exception) {
final Ruby runtime = context.runtime;
final int length = RubyNumeric.fix2int(len);
final RubyString buffStr;
- if ( buff != null && ! buff.isNil() ) {
- buffStr = buff.asString();
+ if ( !buff.isNil() ) {
+ buffStr = buff.convertToString();
} else {
- buffStr = RubyString.newEmptyString(runtime); // fine since we're setValue
+ buffStr = RubyString.newEmptyString(runtime); // fine since we'll setValue
}
if ( length == 0 ) {
buffStr.clear();
@@ -813,7 +831,7 @@ private IRubyObject sysreadImpl(final ThreadContext context,
try {
// So we need to make sure to only block when there is no data left to process
- if ( engine == null || ! ( peerAppData.hasRemaining() || peerNetData.position() > 0 ) ) {
+ if ( engine == null || ! ( appReadData.hasRemaining() || netReadData.position() > 0 ) ) {
final Object ex = waitSelect(SelectionKey.OP_READ, blocking, exception);
if ( ex instanceof IRubyObject ) return (IRubyObject) ex; // :wait_readable
}
@@ -830,7 +848,7 @@ private IRubyObject sysreadImpl(final ThreadContext context,
if ( read == -1 ) {
if ( exception ) throw runtime.newEOFError();
- return runtime.getNil();
+ return context.nil;
}
if ( read == 0 && status == SSLEngineResult.Status.BUFFER_UNDERFLOW ) {
@@ -847,14 +865,15 @@ private IRubyObject sysreadImpl(final ThreadContext context,
buffStr.setValue(new ByteList(bytesRead, offset, read, false));
return buffStr;
}
- catch (IOException ioe) {
- throw runtime.newIOError(ioe.getMessage());
+ catch (IOException ex) {
+ debugStackTrace(runtime, "SSLSocket.sysreadImpl", ex);
+ throw Utils.newError(runtime::newIOErrorFromException, ex);
}
}
@JRubyMethod
public IRubyObject sysread(ThreadContext context, IRubyObject len) {
- return sysreadImpl(context, len, null, true, true);
+ return sysreadImpl(context, len, context.nil, true, true);
}
@JRubyMethod
@@ -876,14 +895,14 @@ public IRubyObject sysread(ThreadContext context, IRubyObject[] args) {
@JRubyMethod
public IRubyObject sysread_nonblock(ThreadContext context, IRubyObject len) {
- return sysreadImpl(context, len, null, false, true);
+ return sysreadImpl(context, len, context.nil, false, true);
}
@JRubyMethod
public IRubyObject sysread_nonblock(ThreadContext context, IRubyObject len, IRubyObject arg) {
if ( arg instanceof RubyHash ) { // exception: false
// NOTE: on Ruby 2.3 this is expected to raise a TypeError (but not on 2.2)
- return sysreadImpl(context, len, null, false, getExceptionOpt(context, arg));
+ return sysreadImpl(context, len, context.nil, false, getExceptionOpt(context, arg));
}
return sysreadImpl(context, len, arg, false, true); // buffer arg
}
@@ -930,8 +949,9 @@ private IRubyObject syswriteImpl(final ThreadContext context,
return runtime.newFixnum(written);
}
- catch (IOException ioe) {
- throw runtime.newIOError(ioe.getMessage());
+ catch (IOException ex) {
+ debugStackTrace(runtime, "SSLSocket.syswriteImpl", ex);
+ throw Utils.newError(runtime::newIOErrorFromException, ex);
}
}
@@ -989,7 +1009,7 @@ private void close(boolean force) {
engine.closeOutbound();
- if ( ! force && netData.hasRemaining() ) return;
+ if ( ! force && netWriteData.hasRemaining() ) return;
try {
doShutdown();
@@ -1062,15 +1082,15 @@ public IRubyObject peer_cert_chain(final ThreadContext context) {
if ( engine == null ) return runtime.getNil();
try {
- javax.security.cert.Certificate[] certs = engine.getSession().getPeerCertificateChain();
+ Certificate[] certs = engine.getSession().getPeerCertificates();
IRubyObject[] cert_chain = new IRubyObject[ certs.length ];
for ( int i = 0; i < certs.length; i++ ) {
cert_chain[i] = X509Cert.wrap(context, certs[i]);
}
return runtime.newArrayNoCopy(cert_chain);
}
- catch (javax.security.cert.CertificateEncodingException e) {
- throw X509Cert.newCertificateError(getRuntime(), e);
+ catch (CertificateEncodingException e) {
+ throw X509Cert.newCertificateError(runtime, e);
}
catch (SSLPeerUnverifiedException e) {
if (runtime.isVerbose() || OpenSSL.isDebug(runtime)) {
@@ -1115,7 +1135,7 @@ private boolean reusableSSLEngine() {
if ( engine != null ) {
final String peerHost = engine.getPeerHost();
if ( peerHost != null && peerHost.length() > 0 ) {
- // NOT getSSLContext().createSSLEngine() - no hints for session reuse
+ // getSSLContext().createSSLEngine() - no hints for session reuse
return true;
}
}
@@ -1124,14 +1144,23 @@ private boolean reusableSSLEngine() {
@JRubyMethod(name = "session_reused?")
public IRubyObject session_reused_p() {
- if ( reusableSSLEngine() ) {
- if ( ! engine.getEnableSessionCreation() ) {
+ if (reusableSSLEngine()) {
+ if (!engine.getEnableSessionCreation()) {
// if session creation is disabled we can be sure its to be re-used
return getRuntime().getTrue();
}
- //return getRuntime().getFalse(); // NOTE: likely incorrect (we can not decide)
+ // return getRuntime().getFalse(); // incorrect (we can not decide)
+ }
+ javax.net.ssl.SSLSession session = sslSession();
+ if (!isNullSession(session)) {
+ if (session.getCreationTime() < this.initializeTime) {
+ return getRuntime().getTrue();
+ }
+ Object socketId = session.getValue(SESSION_SOCKET_ID);
+ if (socketId != null && ((Long) socketId).longValue() != getObjectId()) {
+ return getRuntime().getTrue();
+ }
}
- //warn(getRuntime().getCurrentContext(), "WARNING: SSLSocket#session_reused? is not supported");
return getRuntime().getNil(); // can not decide - probably not
}
@@ -1141,6 +1170,10 @@ final javax.net.ssl.SSLSession sslSession() {
return engine == null ? null : engine.getSession();
}
+ static boolean isNullSession(final javax.net.ssl.SSLSession session) {
+ return session == null || "SSL_NULL_WITH_NULL_NULL".equals(session.getCipherSuite());
+ }
+
private transient SSLSession session;
@JRubyMethod(name = "session")
diff --git a/src/main/java/org/jruby/ext/openssl/SecurityHelper.java b/src/main/java/org/jruby/ext/openssl/SecurityHelper.java
index 0bc3f8e2..e2b91e3a 100644
--- a/src/main/java/org/jruby/ext/openssl/SecurityHelper.java
+++ b/src/main/java/org/jruby/ext/openssl/SecurityHelper.java
@@ -57,6 +57,7 @@
import java.security.cert.X509CRL;
import java.security.interfaces.DSAParams;
import java.security.interfaces.DSAPublicKey;
+import java.security.interfaces.ECPublicKey;
import java.security.interfaces.RSAPublicKey;
import java.util.Locale;
import java.util.Map;
@@ -85,11 +86,13 @@
import org.bouncycastle.crypto.params.DSAPublicKeyParameters;
import org.bouncycastle.crypto.params.RSAKeyParameters;
import org.bouncycastle.jce.provider.X509CRLObject;
+import org.bouncycastle.jcajce.provider.asymmetric.util.ECUtil;
import org.bouncycastle.operator.ContentVerifierProvider;
import org.bouncycastle.operator.DefaultDigestAlgorithmIdentifierFinder;
import org.bouncycastle.operator.DigestAlgorithmIdentifierFinder;
import org.bouncycastle.operator.OperatorException;
import org.bouncycastle.operator.bc.BcDSAContentVerifierProviderBuilder;
+import org.bouncycastle.operator.bc.BcECContentVerifierProviderBuilder;
import org.bouncycastle.operator.bc.BcRSAContentVerifierProviderBuilder;
import org.jruby.util.SafePropertyAccessor;
@@ -110,28 +113,6 @@ public abstract class SecurityHelper {
static boolean setJsseProvider = true;
static volatile Provider jsseProvider;
- /**
- * inject under a given name a cipher. also ensures that the registered
- * classes are getting used.
- *
- * @param name the name under which the class gets registered
- * @param clazz the CipherSpi class
- */
- public static void addCipher(String name, Class extends CipherSpi> clazz) {
- implEngines.put("Cipher:" + name, clazz);
- tryCipherInternal = true;
- }
-
- /**
- * inject under a given name a signature
- *
- * @param name the name under which the class gets registered
- * @param clazz the SignaturSpi class
- */
- public static void addSignature(String name, Class extends SignatureSpi> clazz) {
- implEngines.put("Signature:" + name, clazz);
- }
-
public static Provider getSecurityProvider() {
Provider provider = securityProvider;
if ( setBouncyCastleProvider && provider == null ) {
@@ -266,10 +247,7 @@ static CertificateFactory getCertificateFactory(final String type, final Provide
throws CertificateException {
final CertificateFactorySpi spi = (CertificateFactorySpi) getImplEngine("CertificateFactory", type);
if ( spi == null ) throw new CertificateException(type + " not found");
- return newInstance(CertificateFactory.class,
- new Class[]{ CertificateFactorySpi.class, Provider.class, String.class },
- new Object[]{ spi, provider, type }
- );
+ return CertificateFactory.getInstance(type, provider);
}
/**
@@ -287,12 +265,7 @@ public static KeyFactory getKeyFactory(final String algorithm)
static KeyFactory getKeyFactory(final String algorithm, final Provider provider)
throws NoSuchAlgorithmException {
- KeyFactorySpi spi = (KeyFactorySpi) getImplEngine("KeyFactory", algorithm);
- if ( spi == null ) throw new NoSuchAlgorithmException(algorithm + " not found");
- return newInstance(KeyFactory.class,
- new Class[] { KeyFactorySpi.class, Provider.class, String.class },
- new Object[] { spi, provider, algorithm }
- );
+ return KeyFactory.getInstance(algorithm, provider);
}
/**
@@ -311,28 +284,7 @@ public static KeyPairGenerator getKeyPairGenerator(final String algorithm)
@SuppressWarnings("unchecked")
static KeyPairGenerator getKeyPairGenerator(final String algorithm, final Provider provider)
throws NoSuchAlgorithmException {
- final Object spi = getImplEngine("KeyPairGenerator", algorithm);
- if ( spi == null ) {
- throw new NoSuchAlgorithmException(algorithm + " KeyPairGenerator not available");
- }
-
- final KeyPairGenerator keyPairGenerator;
- if ( spi instanceof KeyPairGenerator ) {
- keyPairGenerator = (KeyPairGenerator) spi;
- }
- else {
- final Class extends KeyPairGenerator> delegate;
- try {
- delegate = (Class extends KeyPairGenerator>)
- Class.forName(KeyPairGenerator.class.getName() + "$Delegate");
- } catch (ClassNotFoundException e) { throw new RuntimeException(e); }
-
- keyPairGenerator = newInstance(delegate,
- new Class[] { KeyPairGeneratorSpi.class, String.class }, spi, algorithm
- );
- }
- setField(keyPairGenerator, KeyPairGenerator.class, "provider", provider);
- return keyPairGenerator;
+ return KeyPairGenerator.getInstance(algorithm, provider);
}
/**
@@ -358,36 +310,20 @@ static KeyStore getKeyStore(final String type, final Provider provider)
*/
public static MessageDigest getMessageDigest(final String algorithm) throws NoSuchAlgorithmException {
try {
+ return MessageDigest.getInstance(algorithm);
+ } catch (NoSuchAlgorithmException nsae) {
+ // try reflective logic
final Provider provider = getSecurityProviderIfAccessible();
if ( provider != null ) return getMessageDigest(algorithm, provider);
+
+ throw nsae; // give up
}
- catch (NoSuchAlgorithmException e) { }
- return MessageDigest.getInstance(algorithm);
}
@SuppressWarnings("unchecked")
static MessageDigest getMessageDigest(final String algorithm, final Provider provider)
throws NoSuchAlgorithmException {
- final Object spi = getImplEngine("MessageDigest", algorithm);
- if ( spi == null ) throw new NoSuchAlgorithmException(algorithm + " not found");
-
- final MessageDigest messageDigest;
- if ( spi instanceof MessageDigest ) {
- messageDigest = (MessageDigest) spi;
- }
- else {
- final Class extends MessageDigest> delegate;
- try {
- delegate = (Class extends MessageDigest>)
- Class.forName(MessageDigest.class.getName() + "$Delegate");
- } catch (ClassNotFoundException e) { throw new RuntimeException(e); }
-
- messageDigest = newInstance(delegate,
- new Class[] { MessageDigestSpi.class, String.class }, spi, algorithm
- );
- }
- setField(messageDigest, MessageDigest.class, "provider", provider);
- return messageDigest;
+ return MessageDigest.getInstance(algorithm, provider);
}
public static SecureRandom getSecureRandom() {
@@ -401,18 +337,12 @@ public static SecureRandom getSecureRandom() {
}
}
catch (NoSuchAlgorithmException e) { }
- return new SecureRandom(); // likely "SHA1PRNG" from SPI sun.security.provider.SecureRandom
+ return new SecureRandom();
}
private static SecureRandom getSecureRandom(final String algorithm, final Provider provider)
throws NoSuchAlgorithmException {
- final SecureRandomSpi spi = (SecureRandomSpi) getImplEngine("SecureRandom", algorithm);
- if ( spi == null ) throw new NoSuchAlgorithmException(algorithm + " not found");
-
- return newInstance(SecureRandom.class,
- new Class[] { SecureRandomSpi.class, Provider.class, String.class },
- new Object[] { spi, provider, algorithm }
- );
+ return SecureRandom.getInstance(algorithm, provider);
}
// NOTE: none (at least for BC 1.47)
@@ -484,7 +414,6 @@ private static Cipher getCipherInternal(String transformation, final Provider pr
spi = (CipherSpi) getImplEngine("Cipher", algorithm);
if ( spi == null ) {
- // if ( silent ) return null;
throw new NoSuchAlgorithmException(transformation + " not found");
}
@@ -503,14 +432,9 @@ private static Cipher getCipherInternal(String transformation, final Provider pr
}
try {
// this constructor does not verify the provider
- Cipher cipher = newInstance(Cipher.class,
- new Class[] { CipherSpi.class, String.class },
- new Object[] { spi, transformation }
- );
- setField(cipher, Cipher.class, "provider", provider);
- return cipher;
+ return Cipher.getInstance(transformation, provider);
}
- catch( Exception e ) {
+ catch (Exception e) { // TODO now seems like a redundant left over
// this constructor does verify the provider which might fail
return newInstance(Cipher.class,
new Class[] { CipherSpi.class, Provider.class, String.class },
@@ -534,25 +458,7 @@ public static Signature getSignature(final String algorithm) throws NoSuchAlgori
@SuppressWarnings("unchecked")
static Signature getSignature(final String algorithm, final Provider provider)
throws NoSuchAlgorithmException {
- final Object spi = getImplEngine("Signature", algorithm);
- if ( spi == null ) throw new NoSuchAlgorithmException(algorithm + " Signature not available");
-
- final Signature signature;
- if ( spi instanceof Signature ) {
- signature = (Signature) spi;
- } else {
- final Class extends Signature> delegate;
- try {
- delegate = (Class extends Signature>)
- Class.forName(Signature.class.getName() + "$Delegate");
- } catch (ClassNotFoundException e) { throw new RuntimeException(e); }
-
- signature = newInstance(delegate,
- new Class[] { SignatureSpi.class, String.class }, spi, algorithm
- );
- }
- setField(signature, Signature.class, "provider", provider);
- return signature;
+ return Signature.getInstance(algorithm, provider);
}
/**
@@ -575,15 +481,13 @@ static Mac getMac(final String algorithm, final Provider provider)
private static Mac getMac(final String algorithm, final Provider provider, boolean silent)
throws NoSuchAlgorithmException {
- MacSpi spi = (MacSpi) getImplEngine("Mac", algorithm);
- if ( spi == null ) {
+ try {
+ return Mac.getInstance(algorithm, provider);
+ }
+ catch (NoSuchAlgorithmException e) {
if ( silent ) return null;
- throw new NoSuchAlgorithmException(algorithm + " not found");
+ throw e;
}
- return newInstance(Mac.class,
- new Class[] { MacSpi.class, Provider.class, String.class },
- new Object[] { spi, provider, algorithm }
- );
}
/**
@@ -601,13 +505,7 @@ public static KeyGenerator getKeyGenerator(final String algorithm) throws NoSuch
static KeyGenerator getKeyGenerator(final String algorithm, final Provider provider)
throws NoSuchAlgorithmException {
- final KeyGeneratorSpi spi = (KeyGeneratorSpi) getImplEngine("KeyGenerator", algorithm);
- if ( spi == null ) throw new NoSuchAlgorithmException(algorithm + " not found");
-
- return newInstance(KeyGenerator.class,
- new Class[] { KeyGeneratorSpi.class, Provider.class, String.class },
- new Object[] { spi, provider, algorithm }
- );
+ return KeyGenerator.getInstance(algorithm, provider);
}
/**
@@ -625,13 +523,7 @@ public static KeyAgreement getKeyAgreement(final String algorithm) throws NoSuch
static KeyAgreement getKeyAgreement(final String algorithm, final Provider provider)
throws NoSuchAlgorithmException {
- final KeyAgreementSpi spi = (KeyAgreementSpi) getImplEngine("KeyAgreement", algorithm);
- if ( spi == null ) throw new NoSuchAlgorithmException(algorithm + " not found");
-
- return newInstance(KeyAgreement.class,
- new Class[] { KeyAgreementSpi.class, Provider.class, String.class },
- new Object[] { spi, provider, algorithm }
- );
+ return KeyAgreement.getInstance(algorithm, provider);
}
/**
@@ -649,13 +541,7 @@ public static SecretKeyFactory getSecretKeyFactory(final String algorithm) throw
static SecretKeyFactory getSecretKeyFactory(final String algorithm, final Provider provider)
throws NoSuchAlgorithmException {
- final SecretKeyFactorySpi spi = (SecretKeyFactorySpi) getImplEngine("SecretKeyFactory", algorithm);
- if ( spi == null ) throw new NoSuchAlgorithmException(algorithm + " not found");
-
- return newInstance(SecretKeyFactory.class,
- new Class[] { SecretKeyFactorySpi.class, Provider.class, String.class },
- new Object[] { spi, provider, algorithm }
- );
+ return SecretKeyFactory.getInstance(algorithm, provider);
}
private static final String providerSSLContext; // NOTE: experimental support for using BCJSSE
@@ -720,19 +606,26 @@ static boolean verify(final X509CRL crl, final PublicKey publicKey, final boolea
try {
final DigestAlgorithmIdentifierFinder digestAlgFinder = new DefaultDigestAlgorithmIdentifierFinder();
final ContentVerifierProvider verifierProvider;
- if ( "DSA".equalsIgnoreCase( publicKey.getAlgorithm() )) {
+ if (publicKey instanceof DSAPublicKey) {
BigInteger y = ((DSAPublicKey) publicKey).getY();
DSAParams params = ((DSAPublicKey) publicKey).getParams();
DSAParameters parameters = new DSAParameters(params.getP(), params.getQ(), params.getG());
AsymmetricKeyParameter dsaKey = new DSAPublicKeyParameters(y, parameters);
verifierProvider = new BcDSAContentVerifierProviderBuilder(digestAlgFinder).build(dsaKey);
}
- else {
+ else if (publicKey instanceof ECPublicKey) {
+ AsymmetricKeyParameter ecKey = ECUtil.generatePublicKeyParameter(publicKey);
+ verifierProvider = new BcECContentVerifierProviderBuilder(digestAlgFinder).build(ecKey);
+ }
+ else if (publicKey instanceof RSAPublicKey) {
BigInteger mod = ((RSAPublicKey) publicKey).getModulus();
BigInteger exp = ((RSAPublicKey) publicKey).getPublicExponent();
AsymmetricKeyParameter rsaKey = new RSAKeyParameters(false, mod, exp);
verifierProvider = new BcRSAContentVerifierProviderBuilder(digestAlgFinder).build(rsaKey);
}
+ else {
+ throw new IllegalStateException("unsupported public key type: " + (publicKey != null ? publicKey.getClass() : null));
+ }
return new X509CRLHolder(crl.getEncoded()).isSignatureValid( verifierProvider );
}
catch (OperatorException e) {
@@ -741,7 +634,7 @@ static boolean verify(final X509CRL crl, final PublicKey publicKey, final boolea
catch (CertException e) {
throw new SignatureException(e);
}
- // can happen if the input is DER but does not match expected strucure
+ // can happen if the input is DER but does not match expected structure
catch (ClassCastException e) {
throw new SignatureException(e);
}
@@ -815,8 +708,6 @@ private static Object findImplEngine(final String baseName, String algorithm) {
}
}
- // the obligratory "reflection crap" :
-
private static T newInstance(Class klass, Class>[] paramTypes, Object... params) {
final Constructor constructor;
try {
diff --git a/src/main/java/org/jruby/ext/openssl/StringHelper.java b/src/main/java/org/jruby/ext/openssl/StringHelper.java
index 18f620e3..39800eee 100644
--- a/src/main/java/org/jruby/ext/openssl/StringHelper.java
+++ b/src/main/java/org/jruby/ext/openssl/StringHelper.java
@@ -29,6 +29,7 @@
import java.util.ArrayList;
import java.util.Locale;
+import org.jcodings.specific.ASCIIEncoding;
import org.jcodings.specific.UTF8Encoding;
import org.joda.time.DateTime;
import org.joda.time.DateTimeZone;
@@ -61,6 +62,10 @@ static RubyString newString(final Ruby runtime, final byte[] bytes, final int co
return RubyString.newString(runtime, byteList);
}
+ static RubyString newString(final Ruby runtime, final CharSequence chars) {
+ return new RubyString(runtime, runtime.getString(), chars, ASCIIEncoding.INSTANCE);
+ }
+
static ByteList setByteListShared(final RubyString str) {
str.setByteListShared();
return str.getByteList();
@@ -129,15 +134,6 @@ static RubyString readInput(final ThreadContext context, final IRubyObject arg)
static final ByteList NEW_LINE = new ByteList(new byte[] { '\n' }, false);
static final ByteList COMMA_SPACE = new ByteList(new byte[] { ',',' ' }, false);
- static void gsub(final Ruby runtime, final ByteList str, final byte match, final byte replace) {
- final int begin = str.getBegin();
- final int slen = str.getRealSize();
- final byte[] bytes = str.getUnsafeBytes();
- for ( int i = begin; i < begin + slen; i++ ) {
- if ( bytes[i] == match ) bytes[i] = replace;
- }
- }
-
static final char[] S20 = new char[] {
' ',' ',' ',' ', ' ',' ',' ',' ',
' ',' ',' ',' ', ' ',' ',' ',' ',
diff --git a/src/main/java/org/jruby/ext/openssl/Utils.java b/src/main/java/org/jruby/ext/openssl/Utils.java
index 2e443765..1afc2ffd 100644
--- a/src/main/java/org/jruby/ext/openssl/Utils.java
+++ b/src/main/java/org/jruby/ext/openssl/Utils.java
@@ -28,7 +28,9 @@
package org.jruby.ext.openssl;
import java.io.IOException;
+import java.nio.ByteBuffer;
import java.util.HashSet;
+import java.util.function.Function;
import org.jruby.*;
import org.jruby.exceptions.RaiseException;
@@ -47,17 +49,25 @@ final class Utils {
private Utils() {}
static RaiseException newIOError(Ruby runtime, IOException e) {
- RaiseException ex = newIOError(runtime, e.getMessage());
- ex.initCause(e);
- return ex;
+ return newIOError(runtime, e.getMessage(), e);
}
static RaiseException newIOError(Ruby runtime, String msg) {
return new RaiseException(runtime, runtime.getIOError(), msg, true);
}
+ static RaiseException newIOError(Ruby runtime, String msg, Exception e) {
+ RaiseException ex = newIOError(runtime, msg);
+ ex.initCause(e);
+ return ex;
+ }
+
static RaiseException newRuntimeError(Ruby runtime, Exception e) {
- RaiseException ex = newRuntimeError(runtime, e.getMessage());
+ return newRuntimeError(runtime, e.getMessage(), e);
+ }
+
+ static RaiseException newRuntimeError(Ruby runtime, String msg, Exception e) {
+ RaiseException ex = newRuntimeError(runtime, msg);
ex.initCause(e);
return ex;
}
@@ -70,9 +80,9 @@ static RaiseException newRuntimeError(Ruby runtime, String msg) {
return new RaiseException(runtime, runtime.getRuntimeError(), msg, true);
}
- static RaiseException newErrorWithoutTrace(Ruby runtime, RubyClass errorClass, String message, boolean nativeException) {
+ static RaiseException newErrorWithoutTrace(Ruby runtime, RubyClass errorClass, String message) {
final IRubyObject backtrace = runtime.newEmptyArray(); // runtime.getNil();
- return new RaiseException(runtime, errorClass, message, backtrace, nativeException);
+ return new RaiseException(runtime, errorClass, message, backtrace, false);
}
static RaiseException newError(Ruby runtime, RubyClass errorClass, String message, boolean nativeException) {
@@ -93,9 +103,10 @@ static RaiseException newError(Ruby runtime, RubyClass errorClass, String msg, T
return ex;
}
- static boolean hasNonNilInstanceVariable(final IRubyObject self, final String var) {
- final IRubyObject val = self.getInstanceVariables().getInstanceVariable(var);
- return val != null && ! val.isNil();
+ static RaiseException newError(Function errorFunction, T e) {
+ RaiseException ex = errorFunction.apply(e);
+ ex.initCause(e);
+ return ex;
}
// reinvented parts of org.jruby.runtime.Helpers for compatibility with "older" JRuby :
@@ -181,8 +192,12 @@ public void visit(IRubyObject key, IRubyObject value) {
return ret;
}
- static IRubyObject extractKeywordArg(ThreadContext context, String keyword, RubyHash opts) {
- return opts.op_aref(context, context.runtime.newSymbol(keyword));
+ static ByteBuffer ensureCapacity(final ByteBuffer buffer, final int size) {
+ if (size <= buffer.capacity()) return buffer;
+ buffer.flip();
+ ByteBuffer newBuffer = ByteBuffer.allocate(size);
+ newBuffer.put(buffer);
+ return newBuffer;
}
}// Utils
diff --git a/src/main/java/org/jruby/ext/openssl/X509Attribute.java b/src/main/java/org/jruby/ext/openssl/X509Attribute.java
index 51e0fc48..abff135b 100644
--- a/src/main/java/org/jruby/ext/openssl/X509Attribute.java
+++ b/src/main/java/org/jruby/ext/openssl/X509Attribute.java
@@ -44,7 +44,6 @@
import org.jruby.RubyObject;
import org.jruby.anno.JRubyMethod;
import org.jruby.runtime.Arity;
-import org.jruby.runtime.ObjectAllocator;
import org.jruby.runtime.ThreadContext;
import org.jruby.runtime.builtin.IRubyObject;
import org.jruby.runtime.Visibility;
@@ -59,14 +58,8 @@
public class X509Attribute extends RubyObject {
private static final long serialVersionUID = 5569940260019783275L;
- private static ObjectAllocator ATTRIBUTE_ALLOCATOR = new ObjectAllocator() {
- public IRubyObject allocate(Ruby runtime, RubyClass klass) {
- return new X509Attribute(runtime, klass);
- }
- };
-
static void createAttribute(Ruby runtime, final RubyModule X509, final RubyClass OpenSSLError) {
- RubyClass Attribute = X509.defineClassUnder("Attribute", runtime.getObject(), ATTRIBUTE_ALLOCATOR);
+ RubyClass Attribute = X509.defineClassUnder("Attribute", runtime.getObject(), (r, klass) -> new X509Attribute(r, klass));
X509.defineClassUnder("AttributeError", OpenSSLError, OpenSSLError.getAllocator());
@@ -127,6 +120,7 @@ public IRubyObject initialize(final ThreadContext context, final IRubyObject[] a
return this;
}
+ @JRubyMethod(visibility = Visibility.PRIVATE)
@Override
public IRubyObject initialize_copy(final IRubyObject original) {
if (this == original) return this;
diff --git a/src/main/java/org/jruby/ext/openssl/X509CRL.java b/src/main/java/org/jruby/ext/openssl/X509CRL.java
index a1bec72d..b5baa2b8 100644
--- a/src/main/java/org/jruby/ext/openssl/X509CRL.java
+++ b/src/main/java/org/jruby/ext/openssl/X509CRL.java
@@ -59,6 +59,8 @@
import org.bouncycastle.cert.X509v2CRLBuilder;
import org.bouncycastle.operator.ContentSigner;
import org.bouncycastle.operator.jcajce.JcaContentSignerBuilder;
+import org.bouncycastle.util.Strings;
+
import org.joda.time.DateTime;
import org.jruby.Ruby;
import org.jruby.RubyArray;
@@ -74,7 +76,6 @@
import org.jruby.ext.openssl.x509store.PEMInputOutput;
import org.jruby.runtime.Arity;
import org.jruby.runtime.Block;
-import org.jruby.runtime.ObjectAllocator;
import org.jruby.runtime.ThreadContext;
import org.jruby.runtime.Visibility;
import org.jruby.runtime.builtin.Variable;
@@ -93,14 +94,8 @@
public class X509CRL extends RubyObject {
private static final long serialVersionUID = -2463300006179688577L;
- private static ObjectAllocator X509CRL_ALLOCATOR = new ObjectAllocator() {
- public IRubyObject allocate(Ruby runtime, RubyClass klass) {
- return new X509CRL(runtime, klass);
- }
- };
-
static void createX509CRL(final Ruby runtime, final RubyModule X509, final RubyClass OpenSSLError) {
- RubyClass CRL = X509.defineClassUnder("CRL", runtime.getObject(), X509CRL_ALLOCATOR);
+ RubyClass CRL = X509.defineClassUnder("CRL", runtime.getObject(), (r, klass) -> new X509CRL(r, klass));
X509.defineClassUnder("CRLError", OpenSSLError, OpenSSLError.getAllocator());
CRL.defineAnnotatedMethods(X509CRL.class);
}
@@ -309,7 +304,7 @@ public IRubyObject initialize_copy(final IRubyObject obj) {
public IRubyObject to_pem(final ThreadContext context) {
StringWriter writer = new StringWriter();
try {
- PEMInputOutput.writeX509CRL(writer, crl);
+ PEMInputOutput.writeX509CRL(writer, getCRL());
return RubyString.newString(context.runtime, writer.getBuffer());
}
catch (IOException e) {
@@ -544,7 +539,7 @@ public IRubyObject add_extension(final IRubyObject extension) {
@JRubyMethod
public IRubyObject sign(final ThreadContext context, final IRubyObject key, IRubyObject digest) {
final Ruby runtime = context.runtime;
- final String signatureAlgorithm = getSignatureAlgorithm(runtime, (PKey) key, (Digest) digest);
+ final String signatureAlgorithm = getSignatureAlgorithm(runtime, (PKey) key, digest);
final X500Name issuerName = ((X509Name) issuer).getX500Name();
final java.util.Date thisUpdate = getLastUpdate().toDate();
@@ -645,19 +640,23 @@ public IRubyObject sign(final ThreadContext context, final IRubyObject key, IRub
return this;
}
- private String getSignatureAlgorithm(final Ruby runtime, final PKey key, final Digest digest) {
+ private static String getSignatureAlgorithm(final Ruby runtime, final PKey key, final IRubyObject digest) {
// Have to obey some artificial constraints of the OpenSSL implementation. Stupid.
final String keyAlg = key.getAlgorithm();
- final String digAlg = digest.getShortAlgorithm();
+ final String digAlg;
+ if (digest instanceof Digest) {
+ digAlg = ((Digest) digest).getShortAlgorithm();
+ } else {
+ digAlg = Strings.toUpperCase(digest.convertToString().toString());
+ }
if ( "DSA".equalsIgnoreCase(keyAlg) ) {
- if ( ( "MD5".equalsIgnoreCase( digAlg ) ) ) { // ||
- // ( "SHA1".equals( digest.name().toString() ) ) ) {
+ if ( ( "MD5".equalsIgnoreCase( digAlg ) ) ) {
throw newCRLError(runtime, "unsupported key / digest algorithm ("+ key +" / "+ digAlg +")");
}
}
else if ( "RSA".equalsIgnoreCase(keyAlg) ) {
- if ( "DSS1".equals( digest.name().toString() ) ) {
+ if ( "DSS1".equals(digAlg) || (digest instanceof Digest && "DSS1".equals(((Digest) digest).name().toString())) ) {
throw newCRLError(runtime, "unsupported key / digest algorithm ("+ key +" / "+ digAlg +")");
}
}
@@ -709,6 +708,14 @@ public IRubyObject op_equal(ThreadContext context, IRubyObject obj) {
return context.runtime.getFalse();
}
+ @Override
+ public Object toJava(Class target) {
+ if (target.isAssignableFrom(java.security.cert.X509CRL.class)) {
+ return getCRL();
+ }
+ return super.toJava(target);
+ }
+
private static RubyClass _CRLError(final Ruby runtime) {
return _X509(runtime).getClass("CRLError");
}
diff --git a/src/main/java/org/jruby/ext/openssl/X509Cert.java b/src/main/java/org/jruby/ext/openssl/X509Cert.java
index 991873db..5d4fe48c 100644
--- a/src/main/java/org/jruby/ext/openssl/X509Cert.java
+++ b/src/main/java/org/jruby/ext/openssl/X509Cert.java
@@ -32,7 +32,6 @@
import java.io.StringWriter;
import java.math.BigInteger;
-import java.security.GeneralSecurityException;
import java.security.InvalidKeyException;
import java.security.NoSuchAlgorithmException;
import java.security.NoSuchProviderException;
@@ -42,9 +41,9 @@
import java.security.cert.CertificateEncodingException;
import java.security.cert.CertificateException;
import java.security.cert.X509Certificate;
+
import java.security.interfaces.DSAPublicKey;
import java.security.interfaces.RSAPublicKey;
-
import java.util.ArrayList;
import java.util.Collection;
import java.util.LinkedHashMap;
@@ -60,6 +59,12 @@
import org.bouncycastle.asn1.x509.GeneralName;
import org.bouncycastle.asn1.x509.GeneralNames;
+import org.bouncycastle.asn1.x509.SubjectPublicKeyInfo;
+import org.bouncycastle.cert.X509CertificateHolder;
+import org.bouncycastle.cert.X509v3CertificateBuilder;
+import org.bouncycastle.operator.ContentSigner;
+import org.bouncycastle.operator.OperatorCreationException;
+import org.bouncycastle.operator.jcajce.JcaContentSignerBuilder;
import org.joda.time.DateTime;
import org.jruby.Ruby;
import org.jruby.RubyArray;
@@ -77,7 +82,6 @@
import org.jruby.ext.openssl.x509store.PEMInputOutput;
import org.jruby.ext.openssl.x509store.X509AuxCertificate;
import org.jruby.runtime.Block;
-import org.jruby.runtime.ObjectAllocator;
import org.jruby.runtime.ThreadContext;
import org.jruby.runtime.Visibility;
import org.jruby.runtime.builtin.IRubyObject;
@@ -100,14 +104,8 @@
public class X509Cert extends RubyObject {
private static final long serialVersionUID = -6524431607032364369L;
- private static ObjectAllocator X509CERT_ALLOCATOR = new ObjectAllocator() {
- public IRubyObject allocate(Ruby runtime, RubyClass klass) {
- return new X509Cert(runtime, klass);
- }
- };
-
static void createX509Cert(final Ruby runtime, final RubyModule X509, final RubyClass OpenSSLError) {
- RubyClass Certificate = X509.defineClassUnder("Certificate", runtime.getObject(), X509CERT_ALLOCATOR);
+ RubyClass Certificate = X509.defineClassUnder("Certificate", runtime.getObject(), (r, klass) -> new X509Cert(r, klass));
X509.defineClassUnder("CertificateError", OpenSSLError, OpenSSLError.getAllocator());
Certificate.defineAnnotatedMethods(X509Cert.class);
}
@@ -133,7 +131,7 @@ private X509Cert(Ruby runtime) {
private transient PKey public_key; // lazy initialized
- private final List extensions = new ArrayList();
+ private final List extensions = new ArrayList<>(4);
private boolean changed = true;
@@ -147,11 +145,16 @@ final X509AuxCertificate getAuxCert() {
public static IRubyObject wrap(Ruby runtime, Certificate cert)
throws CertificateEncodingException {
- return wrap(runtime.getCurrentContext(), cert.getEncoded());
+ return wrap(runtime.getCurrentContext(), cert);
}
static X509Cert wrap(ThreadContext context, Certificate cert)
throws CertificateEncodingException {
+ if (cert instanceof X509Certificate) {
+ final X509Cert c = new X509Cert(context.runtime);
+ c.initialize(context, (X509Certificate) cert);
+ return c;
+ }
return wrap(context, cert.getEncoded());
}
@@ -161,23 +164,14 @@ public static IRubyObject wrap(Ruby runtime, javax.security.cert.Certificate cer
return wrap(runtime.getCurrentContext(), cert.getEncoded());
}
- static X509Cert wrap(ThreadContext context, javax.security.cert.Certificate cert)
- throws javax.security.cert.CertificateEncodingException {
- return wrap(context, cert.getEncoded());
- }
-
static X509Cert wrap(final ThreadContext context, final byte[] encoded) {
- //final Ruby runtime = context.runtime;
- //final RubyString enc = StringHelper.newString(runtime, encoded);
- //return _Certificate(runtime).callMethod(context, "new", enc);
final X509Cert cert = new X509Cert(context.runtime);
cert.initialize(context, encoded);
return cert;
}
@JRubyMethod(name="initialize", optional = 1, visibility = Visibility.PRIVATE)
- public IRubyObject initialize(final ThreadContext context,
- final IRubyObject[] args, final Block unusedBlock) {
+ public IRubyObject initialize(final ThreadContext context, final IRubyObject[] args, final Block unusedBlock) {
if ( args.length == 0 ) {
this.subject = X509Name.newName(context.runtime);
@@ -197,22 +191,28 @@ private void initialize(final ThreadContext context, final byte[] encoded) {
}
private void initialize(final ThreadContext context, final byte[] encoded, final int offset, final int length) {
- final Ruby runtime = context.runtime;
-
byte[] bytes = StringHelper.readX509PEM(encoded, offset, length);
-
+ final X509Certificate cert;
try {
final ByteArrayInputStream bis = new ByteArrayInputStream(bytes);
cert = (X509Certificate) SecurityHelper.getCertificateFactory("X.509").generateCertificate(bis);
}
catch (CertificateException e) {
- throw newCertificateError(runtime, e);
+ throw newCertificateError(context.runtime, e);
}
+ initialize(context, cert);
+ }
+
+ private void initialize(final ThreadContext context, final X509Certificate cert) {
+ final Ruby runtime = context.runtime;
+
if ( cert == null ) {
throw newCertificateError(runtime, (String) null);
}
+ this.cert = cert;
+
set_serial( RubyNumeric.str2inum(runtime, runtime.newString(cert.getSerialNumber().toString()), 10) );
set_not_before( context, RubyTime.newTime( runtime, cert.getNotBefore().getTime() ) );
set_not_after( context, RubyTime.newTime( runtime, cert.getNotAfter().getTime() ) );
@@ -280,6 +280,10 @@ static RaiseException newCertificateError(final Ruby runtime, String msg) {
return Utils.newError(runtime, _CertificateError(runtime), msg);
}
+ static RaiseException newCertificateError(final Ruby runtime, String msg, Exception e) {
+ return Utils.newError(runtime, _CertificateError(runtime), msg, e);
+ }
+
@Override
@JRubyMethod(visibility = Visibility.PRIVATE)
public IRubyObject initialize_copy(IRubyObject obj) {
@@ -354,7 +358,7 @@ public IRubyObject to_text(final ThreadContext context) {
final PublicKey publicKey = getPublicKey();
text.append(S20,0,12).append("Public Key Algorithm: ").append(publicKey.getAlgorithm()).append('\n');
- if ( "RSA".equals( publicKey.getAlgorithm() ) ) {
+ if (publicKey instanceof RSAPublicKey) {
final RSAPublicKey rsaKey = ((RSAPublicKey) publicKey);
text.append(S20,0,16).append("Public-Key: (").append( rsaKey.getModulus().bitLength() ).append(" bit)\n");
@@ -365,13 +369,13 @@ public IRubyObject to_text(final ThreadContext context) {
text.append(S20,0,16).append("Exponent: ").append(exponent).
append(" (0x").append( exponent.toString(16) ).append(")\n");
}
- else if ( "DSA".equals( publicKey.getAlgorithm() ) ) {
+ else if (publicKey instanceof DSAPublicKey) {
final DSAPublicKey dsaKey = ((DSAPublicKey) publicKey);
text.append(S20,0,16).append("Public-Key: (").append( dsaKey.getY().bitLength() ).append(" bit)\n");
text.append(S20,0,16).append("TODO: not-implemented (PR HOME-WORK)").append('\n'); // left-TODO
}
- else {
+ else { // "EC" or "ECDSA"
text.append(S20,0,16).append("TODO: not-implemented (PRs WELCOME!)").append('\n'); // left-TODO
}
@@ -507,7 +511,7 @@ DateTime getNotAfter() {
@JRubyMethod
public IRubyObject public_key(final ThreadContext context) {
if ( public_key == null ) initializePublicKey();
- return public_key.callMethod(context, "public_key");
+ return public_key;
}
@JRubyMethod(name = "public_key=")
@@ -535,27 +539,7 @@ private void initializePublicKey() throws RaiseException {
throw newCertificateError(runtime, "no certificate");
}
- final PublicKey publicKey = cert.getPublicKey();
-
- final String algorithm = publicKey.getAlgorithm();
-
- if ( "RSA".equalsIgnoreCase(algorithm) ) {
- //if ( public_key == null ) {
- // throw new IllegalStateException("no public key encoded data");
- //}
- set_public_key( PKeyRSA.newInstance(runtime, publicKey) );
- }
- else if ( "DSA".equalsIgnoreCase(algorithm) ) {
- //if ( public_key == null ) {
- // throw new IllegalStateException("no public key encoded data");
- //}
- set_public_key( PKeyDSA.newInstance(runtime, publicKey) );
- }
- else {
- String message = "unsupported algorithm";
- if ( algorithm != null ) message += " '" + algorithm + "'";
- throw newCertificateError(runtime, message);
- }
+ set_public_key(PKey.newInstance(runtime, cert.getPublicKey()));
this.changed = changed;
}
@@ -564,69 +548,93 @@ else if ( "DSA".equalsIgnoreCase(algorithm) ) {
public IRubyObject sign(final ThreadContext context, final IRubyObject key, final IRubyObject digest) {
final Ruby runtime = context.runtime;
+ if (!(key instanceof PKey)) { // MRI: NoMethodError: undefined method `private?' for nil:NilClass
+ throw runtime.newTypeError(key, PKey._PKey(runtime).getClass("PKey"));
+ }
+
// Have to obey some artificial constraints of the OpenSSL implementation. Stupid.
final String keyAlg = ((PKey) key).getAlgorithm();
final String digAlg; final String digName;
if (digest instanceof Digest) {
digAlg = ((Digest) digest).getShortAlgorithm();
- digName = ((Digest) digest).name().toString();
+ digName = ((Digest) digest).getName();
}
- else {
+ else if (digest instanceof RubyString) {
digAlg = digest.asJavaString(); digName = null;
}
+ else { // MRI: TypeError: wrong argument type nil (expected OpenSSL/Digest)
+ throw runtime.newTypeError(digest, Digest._Digest(runtime));
+ }
if( ( "DSA".equalsIgnoreCase(keyAlg) && "MD5".equalsIgnoreCase(digAlg) ) ||
( "RSA".equalsIgnoreCase(keyAlg) && "DSS1".equals(digName) ) ) {
throw newCertificateError(runtime, "signature_algorithm not supported");
}
- org.bouncycastle.x509.X509V3CertificateGenerator builder = getCertificateBuilder();
-
+ final X509v3CertificateBuilder builder = newCertificateBuilder();
for ( X509Extension ext : uniqueExtensions() ) {
try {
final byte[] bytes = ext.getRealValueEncoded();
- builder.addExtension(ext.getRealObjectID().getId(), ext.isRealCritical(), bytes);
+ builder.addExtension(ext.getRealObjectID(), ext.isRealCritical(), bytes);
}
- catch (IOException ioe) {
- throw runtime.newIOErrorFromException(ioe);
+ catch (IOException e) {
+ throw newCertificateError(runtime, "invalid extension (" + e.getMessage() + ")", e);
}
}
- builder.setSignatureAlgorithm(digAlg + "WITH" + keyAlg); // "SHA1WITHRSA"
-
+ final X509CertificateHolder certHolder;
try {
- cert = builder.generate( ((PKey) key).getPrivateKey() );
- }
- catch (GeneralSecurityException e) {
+ ContentSigner signer =
+ new JcaContentSignerBuilder(digAlg + "WITH" + keyAlg).
+ build(((PKey) key).getPrivateKey());
+ certHolder = builder.build(signer);
+ } catch (OperatorCreationException e) {
+ Exception cause = (Exception) e.getCause(); // GeneralSecurityException
+ if (cause == null) cause = e;
+ throw newCertificateError(runtime, "cannot create signer: " + cause.getMessage(), cause);
+ } catch (IllegalStateException e) {
+ // e.g. "not all mandatory fields set in V3 TBScertificate generator"
+ throw newCertificateError(runtime, "could not generate certificate", e);
+ } catch (RuntimeException e) {
throw newCertificateError(runtime, e);
}
- if (cert == null) throw newCertificateError(runtime, (String) null);
+ try {
+ this.cert = (X509Certificate)
+ SecurityHelper.getCertificateFactory("X.509").
+ generateCertificate(new ByteArrayInputStream(certHolder.getEncoded()));
+ } catch (IOException|CertificateException e) {
+ throw newCertificateError(runtime, "could not re-generate certificate", e);
+ }
- String name = ASN1Registry.o2a(cert.getSigAlgOID());
+ String name = ASN1Registry.o2a(certHolder.getSignatureAlgorithm().getAlgorithm());
if ( name == null ) name = cert.getSigAlgOID();
this.sig_alg = runtime.newString(name);
this.changed = false;
return this;
}
- private org.bouncycastle.x509.X509V3CertificateGenerator getCertificateBuilder() {
- org.bouncycastle.x509.X509V3CertificateGenerator generator =
- new org.bouncycastle.x509.X509V3CertificateGenerator();
- generator.setSerialNumber( serial.abs() );
-
- if ( subject != null ) generator.setSubjectDN( ((X509Name) subject).getRealName() );
- if ( issuer != null ) generator.setIssuerDN( ((X509Name) issuer).getRealName() );
+ private X509v3CertificateBuilder newCertificateBuilder() {
+ //if ( serial.equals(BigInteger.ZERO) ) { // NOTE: diversion from MRI (OpenSSL allows not setting serial)
+ // throw newCertificateError(getRuntime(), "Certificate#serial needs to be set (to > 0)");
+ //}
- generator.setNotBefore( not_before.getJavaDate() );
- generator.setNotAfter( not_after.getJavaDate() );
- generator.setPublicKey( getPublicKey() );
+ SubjectPublicKeyInfo publicKeyInfo;
+ try {
+ publicKeyInfo = SubjectPublicKeyInfo.getInstance(public_key.getPublicKey().getEncoded());
+ } catch (Exception e) {
+ throw newCertificateError(getRuntime(), "invalid public key data", e);
+ }
- return generator;
+ return new X509v3CertificateBuilder(
+ issuer == null ? null : ((X509Name) issuer).getX500Name(),
+ serial.abs(),
+ not_before.getJavaDate(), not_after.getJavaDate(),
+ subject == null ? null : ((X509Name) subject).getX500Name(),
+ publicKeyInfo
+ );
}
- //private transient org.bouncycastle.x509.X509V3CertificateGenerator generator;
-
@JRubyMethod
public RubyBoolean verify(final IRubyObject key) {
final Ruby runtime = getRuntime();
@@ -638,23 +646,15 @@ public RubyBoolean verify(final IRubyObject key) {
return runtime.getTrue();
}
catch (CertificateException e) {
- debug(runtime, "Certificate#verify failed: ", e);
+ debugStackTrace(runtime, "X509Cert#verify", e);
throw newCertificateError(runtime, e);
}
- catch (NoSuchAlgorithmException e) {
+ catch (NoSuchProviderException|NoSuchAlgorithmException e) {
debugStackTrace(runtime, e);
throw newCertificateError(runtime, e);
}
- catch (NoSuchProviderException e) {
- debugStackTrace(runtime, e);
- throw newCertificateError(runtime, e);
- }
- catch (SignatureException e) {
- debug(runtime, "Certificate#verify failed: ", e);
- return runtime.getFalse();
- }
- catch (InvalidKeyException e) {
- debug(runtime, "Certificate#verify failed: ", e);
+ catch (SignatureException|InvalidKeyException e) {
+ debug(runtime, "X509Cert#verify failed", e);
return runtime.getFalse();
}
}
diff --git a/src/main/java/org/jruby/ext/openssl/X509Extension.java b/src/main/java/org/jruby/ext/openssl/X509Extension.java
index ca3a698a..48aafbd6 100644
--- a/src/main/java/org/jruby/ext/openssl/X509Extension.java
+++ b/src/main/java/org/jruby/ext/openssl/X509Extension.java
@@ -38,6 +38,7 @@
import org.bouncycastle.asn1.ASN1EncodableVector;
import org.bouncycastle.asn1.ASN1Encoding;
import org.bouncycastle.asn1.ASN1Integer;
+import org.bouncycastle.asn1.ASN1IA5String;
import org.bouncycastle.asn1.ASN1ObjectIdentifier;
import org.bouncycastle.asn1.ASN1OctetString;
import org.bouncycastle.asn1.ASN1Primitive;
@@ -45,7 +46,6 @@
import org.bouncycastle.asn1.ASN1String;
import org.bouncycastle.asn1.ASN1TaggedObject;
import org.bouncycastle.asn1.BERTags;
-import org.bouncycastle.asn1.DERIA5String;
import org.bouncycastle.asn1.DEROctetString;
import org.bouncycastle.asn1.DERUniversalString;
import org.bouncycastle.asn1.DLSequence;
@@ -61,14 +61,12 @@
import org.jruby.Ruby;
import org.jruby.RubyArray;
import org.jruby.RubyClass;
-import org.jruby.RubyHash;
import org.jruby.RubyModule;
import org.jruby.RubyNumeric;
import org.jruby.RubyObject;
import org.jruby.RubyString;
import org.jruby.anno.JRubyMethod;
import org.jruby.exceptions.RaiseException;
-import org.jruby.runtime.ObjectAllocator;
import org.jruby.runtime.ThreadContext;
import org.jruby.runtime.Visibility;
import org.jruby.runtime.builtin.IRubyObject;
@@ -78,7 +76,6 @@
import static org.jruby.ext.openssl.ASN1._ASN1;
import static org.jruby.ext.openssl.X509._X509;
import static org.jruby.ext.openssl.OpenSSL.*;
-import static org.jruby.ext.openssl.StringHelper.*;
/**
* OpenSSL::X509::Extension
@@ -87,16 +84,9 @@
public class X509Extension extends RubyObject {
private static final long serialVersionUID = 6463713017143658305L;
- private static ObjectAllocator ALLOCATOR = new ObjectAllocator() {
- public IRubyObject allocate(Ruby runtime, RubyClass klass) {
- return new X509Extension(runtime, klass);
- }
- };
-
static void createX509Extension(final Ruby runtime, final RubyModule X509, final RubyClass OpenSSLError) { // OpenSSL::X509
X509.defineClassUnder("ExtensionError", OpenSSLError, OpenSSLError.getAllocator());
-
- RubyClass _Extension = X509.defineClassUnder("Extension", runtime.getObject(), X509Extension.ALLOCATOR);
+ RubyClass _Extension = X509.defineClassUnder("Extension", runtime.getObject(), (r, klass) -> new X509Extension(r, klass));
_Extension.defineAnnotatedMethods(X509Extension.class);
X509ExtensionFactory.createX509ExtensionFactory(runtime, X509);
@@ -260,6 +250,7 @@ else if ( args.length > 1 ) {
return this;
}
+ @JRubyMethod(visibility = Visibility.PRIVATE)
@Override
public IRubyObject initialize_copy(final IRubyObject original) {
if (this == original) return this;
@@ -420,30 +411,36 @@ public RubyString value(final ThreadContext context) {
if ( oid.equals("2.5.29.35") ) { // authorityKeyIdentifier
ASN1Encodable value = getRealValue();
- if ( value instanceof ASN1OctetString ) {
+ if (value instanceof ASN1OctetString) {
value = ASN1.readObject( ((ASN1OctetString) value).getOctets() );
}
- final ByteList val = new ByteList(72); val.append(keyid_);
+ final ByteList val = new ByteList(72);
- if ( value instanceof ASN1Sequence ) {
+ if (value instanceof ASN1Sequence) {
final ASN1Sequence seq = (ASN1Sequence) value;
final int size = seq.size();
if ( size == 0 ) return RubyString.newEmptyString(runtime);
- ASN1Primitive keyid = seq.getObjectAt(0).toASN1Primitive();
- hexBytes( keyidBytes(keyid), val ).append('\n');
-
- for ( int i = 1; i < size; i++ ) {
- final ASN1Encodable issuer = seq.getObjectAt(i);
- // NOTE: blindly got OpenSSL tests passing (likely in-complete) :
- if ( issuer instanceof ASN1TaggedObject ) {
- ASN1Primitive obj = ((ASN1TaggedObject) issuer).getObject();
- switch( ((ASN1TaggedObject) issuer).getTagNo() ) {
+ for ( int i = 0; i < size; i++ ) {
+ final ASN1Encodable enc = seq.getObjectAt(i);
+ if (enc instanceof ASN1TaggedObject) {
+ ASN1Primitive obj = ((ASN1TaggedObject) enc).getBaseObject().toASN1Primitive();
+ switch( ((ASN1TaggedObject) enc).getTagNo() ) {
+ case 0 :
+ ASN1Primitive keyid = obj.toASN1Primitive();
+ val.append(keyid_);
+ hexBytes( keyidBytes(keyid), val );
+ break;
case 1 :
- if ( obj instanceof ASN1TaggedObject ) {
- formatGeneralName(GeneralName.getInstance(obj), val, true);
+ GeneralName name;
+ if (obj instanceof ASN1Sequence) { // GeneralNames -> toASN1Primitive()
+ GeneralName[] names = GeneralNames.getInstance(obj).getNames();
+ name = names.length > 0 ? names[0] : null;
+ } else {
+ name = GeneralName.getInstance(obj);
}
+ if (name != null) formatGeneralName(name, val, true);
break;
case 2 : // serial
val.append(new byte[] { 's','e','r','i','a','l',':' });
@@ -451,10 +448,13 @@ public RubyString value(final ThreadContext context) {
hexBytes( ((ASN1Integer) obj).getValue().toByteArray(), val);
}
else {
- hexBytes( ((ASN1OctetString) obj ).getOctets(), val );
+ hexBytes( ((ASN1OctetString) obj ).getOctets(), val );
}
break;
}
+ } else if (size == 1) {
+ ASN1Primitive keyid = enc.toASN1Primitive();
+ hexBytes( keyidBytes(keyid), val );
}
val.append('\n');
}
@@ -594,7 +594,7 @@ private RubyString rawValueAsString(final ThreadContext context) throws IOExcept
private static byte[] keyidBytes(ASN1Primitive keyid) throws IOException {
if ( keyid instanceof ASN1TaggedObject ) {
- keyid = ((ASN1TaggedObject) keyid).getObject();
+ keyid = ((ASN1TaggedObject) keyid).getBaseObject().toASN1Primitive();
}
if ( keyid instanceof ASN1OctetString ) {
return ((ASN1OctetString) keyid).getOctets();
@@ -618,7 +618,7 @@ private static boolean formatGeneralName(final GeneralName name, final ByteList
case GeneralName.uniformResourceIdentifier:
if ( ! tagged ) out.append('U').append('R').append('I').
append(':');
- val = DERIA5String.getInstance(obj).getString();
+ val = ASN1IA5String.getInstance(obj).getString();
out.append( ByteList.plain(val) );
break;
case GeneralName.directoryName:
@@ -797,7 +797,7 @@ public IRubyObject set_critical(final ThreadContext context, IRubyObject arg) {
}
@JRubyMethod
- public IRubyObject to_der() {
+ public RubyString to_der() {
try {
return StringHelper.newString(getRuntime(), toDER());
}
@@ -844,44 +844,6 @@ public IRubyObject eql_p(IRubyObject obj) {
return equalImpl(getRuntime(), obj);
}
- // [ self.oid, self.value, self.critical? ]
- @JRubyMethod
- public RubyArray to_a(final ThreadContext context) {
- RubyArray array = RubyArray.newArray(context.runtime, 3);
- array.append(oid(context));
- array.append(value(context));
- array.append(critical_p(context));
- return array;
- }
-
- // {"oid"=>self.oid,"value"=>self.value,"critical"=>self.critical?
- @JRubyMethod
- public RubyHash to_h(final ThreadContext context) {
- final Ruby runtime = context.runtime;
- RubyHash hash = RubyHash.newHash(runtime);
- hash.op_aset(context, newStringFrozen(runtime, "oid"), oid(context));
- hash.op_aset(context, newStringFrozen(runtime, "value"), value(context));
- hash.op_aset(context, newStringFrozen(runtime, "critical"), critical_p(context));
- return hash;
- }
-
- // "oid = critical, value"
- @JRubyMethod
- public RubyString to_s(final ThreadContext context) {
- final Ruby runtime = context.runtime;
- final RubyString str = RubyString.newString(runtime, oidSym(runtime));
- str.getByteList().append(' ').append('=').append(' ');
- if ( isRealCritical() ) str.getByteList().append(critical__);
- // self.value.gsub(/\n/, ", ")
- final RubyString value = value(context);
- value.callMethod(context, "gsub!", new IRubyObject[] {
- RubyString.newStringShared(runtime, StringHelper.NEW_LINE),
- RubyString.newStringShared(runtime, StringHelper.COMMA_SPACE)
- });
- str.getByteList().append(value.getByteList());
- return str;
- }
-
@Override
public X509Extension clone() {
try {
diff --git a/src/main/java/org/jruby/ext/openssl/X509ExtensionFactory.java b/src/main/java/org/jruby/ext/openssl/X509ExtensionFactory.java
index 26b0ccdd..2912e575 100644
--- a/src/main/java/org/jruby/ext/openssl/X509ExtensionFactory.java
+++ b/src/main/java/org/jruby/ext/openssl/X509ExtensionFactory.java
@@ -31,6 +31,7 @@
import java.math.BigInteger;
import java.security.GeneralSecurityException;
+import java.security.MessageDigest;
import org.bouncycastle.asn1.*;
import org.bouncycastle.asn1.x500.X500Name;
@@ -46,9 +47,9 @@
import org.jruby.RubyObject;
import org.jruby.RubyString;
import org.jruby.anno.JRubyMethod;
+import org.jruby.exceptions.RaiseException;
import org.jruby.runtime.Arity;
import org.jruby.runtime.Block;
-import org.jruby.runtime.ObjectAllocator;
import org.jruby.runtime.ThreadContext;
import org.jruby.runtime.Visibility;
import org.jruby.runtime.builtin.IRubyObject;
@@ -65,15 +66,9 @@
public class X509ExtensionFactory extends RubyObject {
private static final long serialVersionUID = 3180447029639456500L;
- private static ObjectAllocator ALLOCATOR = new ObjectAllocator() {
- public IRubyObject allocate(Ruby runtime, RubyClass klass) {
- return new X509ExtensionFactory(runtime, klass);
- }
- };
-
static void createX509ExtensionFactory(final Ruby runtime, final RubyModule _X509) { // OpenSSL::X509
final RubyClass _ExtensionFactory = _X509.defineClassUnder("ExtensionFactory",
- runtime.getObject(), X509ExtensionFactory.ALLOCATOR);
+ runtime.getObject(), (r, klass) -> new X509ExtensionFactory(r, klass));
_ExtensionFactory.defineAnnotatedMethods(X509ExtensionFactory.class);
}
@@ -401,12 +396,16 @@ private static DLSequence parseBasicConstrains(final String valuex) {
private ASN1Sequence parseAuthorityKeyIdentifier(final ThreadContext context, final String valuex) {
final ASN1EncodableVector vec = new ASN1EncodableVector();
- for ( String value : valuex.split(",") ) { // e.g. "keyid:always,issuer:always"
- if ( value.startsWith("keyid:") ) { // keyid:always
- ASN1Encodable publicKeyIdentifier = new DEROctetString(publicKeyIdentifier(context));
+ final String[] values = valuex.split(","); // e.g. "keyid:always,issuer:always"
+ for ( int i = 0; i new X509Name(r, klass));
X509.defineClassUnder("NameError", OpenSSLError, OpenSSLError.getAllocator());
_Name.defineAnnotatedMethods(X509Name.class);
@@ -166,16 +172,17 @@ static RubyClass _Name(final Ruby runtime) {
public X509Name(Ruby runtime, RubyClass type) {
super(runtime,type);
- oids = new ArrayList();
- values = new ArrayList();
- types = new ArrayList();
+ oids = new ArrayList<>(4);
+ values = new ArrayList<>(4);
+ types = new ArrayList<>(4);
}
private final List oids;
private final List values; //
- private final List types;
+ private final List types;
private transient X500Name name;
+ private transient X500Name canonicalName;
private void fromASN1Sequence(final byte[] encoded) {
try {
@@ -242,30 +249,70 @@ private void addValue(final ASN1Encodable value) {
@SuppressWarnings("unchecked")
private void addType(final Ruby runtime, final ASN1Encodable value) {
this.name = null; // NOTE: each fromX factory calls this ...
+ this.canonicalName = null;
final Integer type = ASN1.typeId(value);
- if ( type == null ) {
+ if (type == null) {
warn(runtime.getCurrentContext(), this + " addType() could not resolve type for: " +
value + " (" + (value == null ? "" : value.getClass().getName()) + ")");
- ((List) this.types).add( runtime.getNil() );
- }
- else {
- this.types.add( runtime.newFixnum( type.intValue() ) );
}
+ this.types.add(type);
}
- private void addEntry(ASN1ObjectIdentifier oid, RubyString value, RubyInteger type)
- throws IOException {
+ /**
+ * @param oid
+ * @param value
+ * @param type expected to be legit at this point
+ * @throws RuntimeException
+ */
+ private void addEntry(ASN1ObjectIdentifier oid, RubyString value, final int type) throws RuntimeException {
this.name = null;
+ this.canonicalName = null;
+ this.values.add(NAME_ENTRY_CONVERTER.convertValueFor(oid, value, type));
this.oids.add(oid);
- final ASN1Encodable convertedValue = getNameEntryConverted().
- getConvertedValue(oid, value.toString()
- );
- this.values.add( convertedValue );
this.types.add(type);
}
- private static X509NameEntryConverter getNameEntryConverted() {
- return new X509DefaultEntryConverter();
+ private static final X509NameEntryConverterImpl NAME_ENTRY_CONVERTER = new X509NameEntryConverterImpl();
+
+ private static class X509NameEntryConverterImpl extends X509DefaultEntryConverter {
+
+ ASN1Primitive convertValueFor(final ASN1ObjectIdentifier oid, final RubyString value, final int type) {
+ switch (type) {
+ case ASN1.BIT_STRING:
+ return new DERBitString(value.getBytes());
+ case ASN1.OCTET_STRING:
+ return new DEROctetString(value.getBytes());
+ case ASN1.UTF8STRING:
+ return new DERUTF8String(value.asJavaString());
+ case ASN1.NUMERICSTRING:
+ return new DERNumericString(value.asJavaString()); // validate?
+ case ASN1.PRINTABLESTRING:
+ return new DERPrintableString(value.asJavaString());
+ case ASN1.T61STRING:
+ return new DERT61String(value.asJavaString());
+ case ASN1.VIDEOTEXSTRING:
+ return new DERVideotexString(value.getBytes());
+ case ASN1.IA5STRING:
+ return new DERIA5String(value.asJavaString());
+ case ASN1.GENERALIZEDTIME:
+ return new DERGeneralizedTime(value.asJavaString());
+ case ASN1.UTCTIME:
+ return new DERUTCTime(value.asJavaString());
+ case ASN1.GRAPHICSTRING:
+ return new DERGraphicString(value.getBytes());
+ //case ASN1.ISO64STRING:
+ //return new DERVisibleString(value.asJavaString());
+ case ASN1.GENERALSTRING:
+ return new DERGeneralString(value.asJavaString());
+ case ASN1.UNIVERSALSTRING:
+ return new DERUniversalString(value.getBytes());
+ case ASN1.BMPSTRING:
+ return new DERBMPString(value.asJavaString());
+ }
+
+ return super.getConvertedValue(oid, value.toString());
+ }
+
}
@Override
@@ -286,9 +333,7 @@ public IRubyObject initialize(final ThreadContext context, IRubyObject dn, IRuby
if ( dn instanceof RubyArray ) {
RubyArray ary = (RubyArray) dn;
- final RubyClass _Name = _Name(runtime);
-
- if ( template.isNil() ) template = _Name.getConstant("OBJECT_TYPE_TEMPLATE");
+ if (template.isNil()) template = _Name(runtime).getConstant("OBJECT_TYPE_TEMPLATE");
for (int i = 0; i < ary.size(); i++) {
IRubyObject obj = ary.eltOk(i);
@@ -299,15 +344,14 @@ public IRubyObject initialize(final ThreadContext context, IRubyObject dn, IRuby
RubyArray arr = (RubyArray)obj;
- IRubyObject entry0, entry1, entry2;
- entry0 = arr.size() > 0 ? arr.eltOk(0) : context.nil;
- entry1 = arr.size() > 1 ? arr.eltOk(1) : context.nil;
- entry2 = arr.size() > 2 ? arr.eltOk(2) : context.nil;
+ IRubyObject name, value, type;
+ name = arr.size() > 0 ? arr.eltOk(0) : context.nil;
+ value = arr.size() > 1 ? arr.eltOk(1) : context.nil;
+ type = arr.size() > 2 ? arr.eltOk(2) : context.nil;
- if (entry2.isNil()) entry2 = template.callMethod(context, "[]", entry0);
- if (entry2.isNil()) entry2 = _Name.getConstant("DEFAULT_OBJECT_TYPE");
+ if (type.isNil()) type = getDefaultType(context, name, template);
- add_entry(context, entry0, entry1, entry2);
+ add_entry(context, name, value, type);
}
}
else {
@@ -350,7 +394,7 @@ else if ( obj instanceof ASN1Set ) {
@JRubyMethod
public IRubyObject add_entry(ThreadContext context, IRubyObject oid, IRubyObject value) {
- return add_entry(context, oid, value, null);
+ return add_entry(context, oid, value, context.nil);
}
@JRubyMethod
@@ -360,7 +404,7 @@ public IRubyObject add_entry(final ThreadContext context,
final RubyString oidStr = oid.asString();
- if ( type == null || type.isNil() ) type = getDefaultType(context, oidStr);
+ if ( type.isNil() ) type = getDefaultType(context, oidStr);
final ASN1ObjectIdentifier objectId;
try {
@@ -372,52 +416,63 @@ public IRubyObject add_entry(final ThreadContext context,
// NOTE: won't reach here :
if ( objectId == null ) throw newNameError(runtime, "invalid field name");
+ final int typeInt = type.convertToInteger().getIntValue();
+ if (ASN1.typeClassSafe(typeInt) == null) {
+ throw newNameError(runtime, "invalid type: " + typeInt);
+ }
+
try {
- addEntry(objectId, value.asString(), (RubyInteger) type);
+ addEntry(objectId, value.asString(), typeInt);
}
- catch (IOException e) {
- throw newNameError(runtime, "invalid value", e);
+ catch (RuntimeException e) {
+ debugStackTrace(runtime, e);
+ String msg = e.getMessage(); // X509DefaultEntryConverted: "can't recode value for oid " + oid.getId()
+ throw newNameError(runtime, msg == null ? "invalid value" : msg, e);
}
return this;
}
- private static IRubyObject getDefaultType(final ThreadContext context, final RubyString oid) {
- IRubyObject template = _Name(context.runtime).getConstant("OBJECT_TYPE_TEMPLATE");
- if ( template instanceof RubyHash ) {
- return ((RubyHash) template).op_aref(context, oid);
- }
- return template.callMethod(context, "[]", oid);
+ private IRubyObject getDefaultType(final ThreadContext context, final RubyString oid) {
+ return getDefaultType(context, oid, _Name(context.runtime).getConstant("OBJECT_TYPE_TEMPLATE"));
+ }
+
+ private IRubyObject getDefaultType(final ThreadContext context, final IRubyObject oid, final IRubyObject template) {
+ final IRubyObject type = template instanceof RubyHash ?
+ ((RubyHash) template).op_aref(context, oid) : template.callMethod(context, "[]", oid);
+ return type.isNil() ? _Name(context.runtime).getConstant("DEFAULT_OBJECT_TYPE") : type;
}
@SuppressWarnings("unchecked")
@JRubyMethod(name = "to_s", rest = true)
public IRubyObject to_s(IRubyObject[] args) {
- final Ruby runtime = getRuntime();
-
int flag = 0;
if ( args.length > 0 && ! args[0].isNil() ) {
flag = RubyNumeric.fix2int( args[0] );
}
-
- /* Should follow parameters like this:
- if 0 (COMPAT):
- irb(main):025:0> x.to_s(OpenSSL::X509::Name::COMPAT)
- => "CN=ola.bini, O=sweden/streetAddress=sweden, O=sweden/2.5.4.43343=sweden"
- irb(main):026:0> x.to_s(OpenSSL::X509::Name::ONELINE)
- => "CN = ola.bini, O = sweden, streetAddress = sweden, O = sweden, 2.5.4.43343 = sweden"
- irb(main):027:0> x.to_s(OpenSSL::X509::Name::MULTILINE)
- => "commonName = ola.bini\norganizationName = sweden\nstreetAddress = sweden\norganizationName = sweden\n2.5.4.43343 = sweden"
- irb(main):028:0> x.to_s(OpenSSL::X509::Name::RFC2253)
- => "2.5.4.43343=#0C0673776564656E,O=sweden,streetAddress=sweden,O=sweden,CN=ola.bini"
- else
- => /CN=ola.bini/O=sweden/streetAddress=sweden/O=sweden/2.5.4.43343=sweden
- */
-
+ final Ruby runtime = getRuntime();
+ // NOTE: historically we haven't screwed this up as ASCII-8BIT as C-OpenSSL does
+ return RubyString.newString(runtime, toFormat(runtime, flag));
+ }
+
+ /* Should follow parameters like this:
+ if 0 (COMPAT):
+ irb(main):025:0> x.to_s(OpenSSL::X509::Name::COMPAT)
+ => "CN=ola.bini, O=sweden/streetAddress=sweden, O=sweden/2.5.4.43343=sweden"
+ irb(main):026:0> x.to_s(OpenSSL::X509::Name::ONELINE)
+ => "CN = ola.bini, O = sweden, streetAddress = sweden, O = sweden, 2.5.4.43343 = sweden"
+ irb(main):027:0> x.to_s(OpenSSL::X509::Name::MULTILINE)
+ => "commonName = ola.bini\norganizationName = sweden\nstreetAddress = sweden\norganizationName = sweden\n2.5.4.43343 = sweden"
+ irb(main):028:0> x.to_s(OpenSSL::X509::Name::RFC2253)
+ => "2.5.4.43343=#0C0673776564656E,O=sweden,streetAddress=sweden,O=sweden,CN=ola.bini"
+ else
+ => /CN=ola.bini/O=sweden/streetAddress=sweden/O=sweden/2.5.4.43343=sweden
+ */
+ private StringBuilder toFormat(final Ruby runtime, final int format) {
final Iterator oidsIter;
final Iterator