diff --git a/build.xml b/build.xml
index 361739f2571..b5ba2627a69 100644
--- a/build.xml
+++ b/build.xml
@@ -378,6 +378,12 @@
+
+
+
+
+
+
@@ -484,7 +490,13 @@
-
+
+
+
+
+
+
+
@@ -643,7 +655,13 @@
-
+
+
+
+
+
+
+
diff --git a/build_lib/bcmail-jdk15-146.jar b/build_lib/bcmail-jdk15-146.jar
new file mode 100644
index 00000000000..de560b5157b
Binary files /dev/null and b/build_lib/bcmail-jdk15-146.jar differ
diff --git a/build_lib/bcprov-jdk15-146.jar b/build_lib/bcprov-jdk15-146.jar
new file mode 100644
index 00000000000..daa0b54cc0f
Binary files /dev/null and b/build_lib/bcprov-jdk15-146.jar differ
diff --git a/lib/ruby/1.8/openssl.rb b/lib/ruby/1.8/openssl.rb
new file mode 100644
index 00000000000..7e8aa20e4da
--- /dev/null
+++ b/lib/ruby/1.8/openssl.rb
@@ -0,0 +1,25 @@
+=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: openssl.rb 12496 2007-06-08 15:02:04Z technorama $
+=end
+
+require 'jopenssl'
+require 'openssl/bn'
+require 'openssl/cipher'
+require 'openssl/config'
+require 'openssl/digest'
+require 'openssl/pkcs7'
+require 'openssl/ssl'
+require 'openssl/x509'
+
diff --git a/lib/ruby/1.8/openssl/bn.rb b/lib/ruby/1.8/openssl/bn.rb
new file mode 100644
index 00000000000..cf44a0943c4
--- /dev/null
+++ b/lib/ruby/1.8/openssl/bn.rb
@@ -0,0 +1,35 @@
+=begin
+= $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: bn.rb 11708 2007-02-12 23:01:19Z shyouhei $
+=end
+
+##
+# Should we care what if somebody require this file directly?
+#require 'openssl'
+
+module OpenSSL
+ class BN
+ include Comparable
+ 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/ruby/1.8/openssl/buffering.rb b/lib/ruby/1.8/openssl/buffering.rb
new file mode 100644
index 00000000000..42c047c7312
--- /dev/null
+++ b/lib/ruby/1.8/openssl/buffering.rb
@@ -0,0 +1,239 @@
+=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: buffering.rb 13706 2007-10-15 08:29:08Z usa $
+=end
+
+module Buffering
+ include Enumerable
+ attr_accessor :sync
+ BLOCK_SIZE = 1024*16
+
+ def initialize(*args)
+ @eof = false
+ @rbuffer = ""
+ @sync = @io.sync
+ end
+
+ #
+ # for reading.
+ #
+ private
+
+ def fill_rbuff
+ begin
+ @rbuffer << self.sysread(BLOCK_SIZE)
+ rescue Errno::EAGAIN
+ retry
+ rescue EOFError
+ @eof = true
+ end
+ end
+
+ 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
+
+ def read(size=nil, buf=nil)
+ if size == 0
+ if buf
+ buf.clear
+ else
+ buf = ""
+ end
+ return @eof ? nil : buf
+ 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
+
+ def readpartial(maxlen, buf=nil)
+ if maxlen == 0
+ if buf
+ buf.clear
+ else
+ buf = ""
+ end
+ return @eof ? nil : buf
+ 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
+
+ def gets(eol=$/)
+ 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
+ consume_rbuff(size)
+ end
+
+ def each(eol=$/)
+ while line = self.gets(eol)
+ yield line
+ end
+ end
+ alias each_line each
+
+ def readlines(eol=$/)
+ ary = []
+ while line = self.gets(eol)
+ ary << line
+ end
+ ary
+ end
+
+ def readline(eol=$/)
+ raise EOFError if eof?
+ gets(eol)
+ end
+
+ def getc
+ c = read(1)
+ c ? c[0] : nil
+ end
+
+ def each_byte
+ while c = getc
+ yield(c)
+ end
+ end
+
+ def readchar
+ raise EOFError if eof?
+ getc
+ end
+
+ def ungetc(c)
+ @rbuffer[0,0] = c.chr
+ end
+
+ def eof?
+ fill_rbuff if !@eof && @rbuffer.empty?
+ @eof && @rbuffer.empty?
+ end
+ alias eof eof?
+
+ #
+ # for writing.
+ #
+ private
+
+ def do_write(s)
+ @wbuffer = "" unless defined? @wbuffer
+ @wbuffer << s
+ @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
+
+ def write(s)
+ do_write(s)
+ s.length
+ end
+
+ def << (s)
+ do_write(s)
+ self
+ end
+
+ 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
+
+ def print(*args)
+ s = ""
+ args.each{ |arg| s << arg.to_s }
+ do_write(s)
+ nil
+ end
+
+ def printf(s, *args)
+ do_write(s % args)
+ nil
+ end
+
+ def flush
+ osync = @sync
+ @sync = true
+ do_write ""
+ @sync = osync
+ end
+
+ def close
+ flush rescue nil
+ sysclose
+ end
+end
diff --git a/lib/ruby/1.8/openssl/cipher.rb b/lib/ruby/1.8/openssl/cipher.rb
new file mode 100644
index 00000000000..5fbfcd4005f
--- /dev/null
+++ b/lib/ruby/1.8/openssl/cipher.rb
@@ -0,0 +1,65 @@
+=begin
+= $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: cipher.rb 12496 2007-06-08 15:02:04Z technorama $
+=end
+
+##
+# Should we care what if somebody require this file directly?
+#require 'openssl'
+
+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)
+ }
+ }
+ const_set(name, klass)
+ }
+
+ %w(128 192 256).each{|keylen|
+ klass = Class.new(Cipher){
+ define_method(:initialize){|mode|
+ mode ||= "CBC"
+ cipher_name = "AES-#{keylen}-#{mode}"
+ super(cipher_name)
+ }
+ }
+ const_set("AES#{keylen}", klass)
+ }
+
+ # Generate, set, and return a random key.
+ # You must call cipher.encrypt or cipher.decrypt before calling this method.
+ def random_key
+ str = OpenSSL::Random.random_bytes(self.key_len)
+ self.key = str
+ return str
+ end
+
+ # Generate, set, and return a random iv.
+ # You must call cipher.encrypt or cipher.decrypt before calling this method.
+ def random_iv
+ str = OpenSSL::Random.random_bytes(self.iv_len)
+ self.iv = str
+ return str
+ end
+
+ # This class is only provided for backwards compatibility. Use OpenSSL::Digest in the future.
+ class Cipher < Cipher
+ # add warning
+ end
+ end # Cipher
+end # OpenSSL
diff --git a/lib/ruby/1.8/openssl/config.rb b/lib/ruby/1.8/openssl/config.rb
new file mode 100644
index 00000000000..9fc42c623a8
--- /dev/null
+++ b/lib/ruby/1.8/openssl/config.rb
@@ -0,0 +1,316 @@
+=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
+
+##
+# Should we care what if somebody require this file directly?
+#require 'openssl'
+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/ruby/1.8/openssl/digest.rb b/lib/ruby/1.8/openssl/digest.rb
new file mode 100644
index 00000000000..e64b0cfd373
--- /dev/null
+++ b/lib/ruby/1.8/openssl/digest.rb
@@ -0,0 +1,61 @@
+=begin
+= $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: digest.rb 15600 2008-02-25 08:48:57Z technorama $
+=end
+
+##
+# Should we care what if somebody require this file directly?
+#require 'openssl'
+
+module OpenSSL
+ class Digest
+
+ alg = %w(DSS DSS1 MD2 MD4 MD5 MDC2 RIPEMD160 SHA SHA1)
+ if OPENSSL_VERSION_NUMBER > 0x00908000
+ alg += %w(SHA224 SHA256 SHA384 SHA512)
+ end
+
+ def self.digest(name, data)
+ super(data, name)
+ end
+
+ alg.each{|name|
+ klass = Class.new(Digest){
+ define_method(:initialize){|*data|
+ if data.length > 1
+ raise ArgumentError,
+ "wrong number of arguments (#{data.length} for 1)"
+ end
+ super(name, data.first)
+ }
+ }
+ singleton = (class <
+ All rights reserved.
+
+= Licence
+ This program is licenced under the same licence as Ruby.
+ (See the file 'LICENCE'.)
+
+= Version
+ $Id: ssl.rb 16193 2008-04-25 06:51:21Z knu $
+=end
+
+require "openssl"
+require "openssl/buffering"
+require "fcntl"
+
+module OpenSSL
+ module SSL
+ class SSLContext
+ 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,
+ }
+
+ 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
+
+ def set_params(params={})
+ params = DEFAULT_PARAMS.merge(params)
+ self.ssl_version = params.delete(:ssl_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
+ 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
+
+ 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|
+ if /\ADNS:(.*)/ =~ general_name
+ should_verify_common_name = false
+ reg = Regexp.escape($1).gsub(/\\\*/, "[^.]+")
+ return true if /\A#{reg}\z/i =~ hostname
+ elsif /\AIP Address:(.*)/ =~ general_name
+ should_verify_common_name = false
+ return true if $1 == hostname
+ 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 post_connection_check(hostname)
+ unless OpenSSL::SSL.verify_certificate_identity(peer_cert, hostname)
+ raise SSLError, "hostname was not match with the server certificate"
+ end
+ return true
+ end
+
+ def session
+ SSL::Session.new(self)
+ rescue SSL::Session::SessionError
+ nil
+ 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/ruby/1.8/openssl/x509.rb b/lib/ruby/1.8/openssl/x509.rb
new file mode 100644
index 00000000000..99f239ce372
--- /dev/null
+++ b/lib/ruby/1.8/openssl/x509.rb
@@ -0,0 +1,154 @@
+=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: x509.rb 11708 2007-02-12 23:01:19Z shyouhei $
+=end
+
+require "openssl"
+
+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 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){|hex| Integer("0x#{hex}").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)
+ matched = md.to_s
+ 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 <
+ All rights reserved.
+
+= Licence
+ This program is licenced under the same licence as Ruby.
+ (See the file 'LICENCE'.)
+
+= Version
+ $Id$
+=end
+
+require 'jopenssl'
+require 'openssl/bn'
+require 'openssl/cipher'
+require 'openssl/config'
+require 'openssl/digest'
+require 'openssl/ssl-internal'
+require 'openssl/x509-internal'
diff --git a/lib/ruby/1.9/openssl/bn.rb b/lib/ruby/1.9/openssl/bn.rb
new file mode 100644
index 00000000000..a527a10a0fa
--- /dev/null
+++ b/lib/ruby/1.9/openssl/bn.rb
@@ -0,0 +1,35 @@
+#--
+#
+# $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: bn.rb 33067 2011-08-25 00:52:10Z drbrain $
+#
+#++
+
+module OpenSSL
+ class BN
+ include Comparable
+ end # BN
+end # OpenSSL
+
+##
+# Add double dispatch to Integer
+#
+class Integer
+ def to_bn
+ OpenSSL::BN::new(self.to_s(16), 16)
+ end
+end # Integer
+
diff --git a/lib/ruby/1.9/openssl/buffering.rb b/lib/ruby/1.9/openssl/buffering.rb
new file mode 100644
index 00000000000..e2f3235b262
--- /dev/null
+++ b/lib/ruby/1.9/openssl/buffering.rb
@@ -0,0 +1,448 @@
+=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: buffering.rb 32012 2011-06-11 14:07:42Z nahi $
+=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
+ @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.length
+ 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/ruby/1.9/openssl/cipher.rb b/lib/ruby/1.9/openssl/cipher.rb
new file mode 100644
index 00000000000..f36547771f2
--- /dev/null
+++ b/lib/ruby/1.9/openssl/cipher.rb
@@ -0,0 +1,65 @@
+#--
+#
+# $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: cipher.rb 33067 2011-08-25 00:52:10Z drbrain $
+#
+#++
+
+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)
+ }
+ }
+ const_set(name, klass)
+ }
+
+ %w(128 192 256).each{|keylen|
+ klass = Class.new(Cipher){
+ define_method(:initialize){|mode|
+ mode ||= "CBC"
+ cipher_name = "AES-#{keylen}-#{mode}"
+ super(cipher_name)
+ }
+ }
+ const_set("AES#{keylen}", klass)
+ }
+
+ # Generate, set, and return a random key.
+ # You must call cipher.encrypt or cipher.decrypt before calling this method.
+ def random_key
+ str = OpenSSL::Random.random_bytes(self.key_len)
+ self.key = str
+ return str
+ end
+
+ # Generate, set, and return a random iv.
+ # You must call cipher.encrypt or cipher.decrypt before calling this method.
+ def random_iv
+ str = OpenSSL::Random.random_bytes(self.iv_len)
+ self.iv = str
+ return str
+ end
+
+ # This class is only provided for backwards compatibility. Use OpenSSL::Cipher in the future.
+ class Cipher < Cipher
+ # add warning
+ end
+ end # Cipher
+end # OpenSSL
diff --git a/lib/ruby/1.9/openssl/config.rb b/lib/ruby/1.9/openssl/config.rb
new file mode 100644
index 00000000000..24a54c91eca
--- /dev/null
+++ b/lib/ruby/1.9/openssl/config.rb
@@ -0,0 +1,313 @@
+=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/ruby/1.9/openssl/digest.rb b/lib/ruby/1.9/openssl/digest.rb
new file mode 100644
index 00000000000..09bd8588986
--- /dev/null
+++ b/lib/ruby/1.9/openssl/digest.rb
@@ -0,0 +1,72 @@
+#--
+#
+# $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: digest.rb 33067 2011-08-25 00:52:10Z drbrain $
+#
+#++
+
+module OpenSSL
+ class Digest
+
+ alg = %w(DSS DSS1 MD2 MD4 MD5 MDC2 RIPEMD160 SHA SHA1)
+ if OPENSSL_VERSION_NUMBER > 0x00908000
+ alg += %w(SHA224 SHA256 SHA384 SHA512)
+ end
+
+ # Return the +data+ hash 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::SHA256.digest("abc")
+
+ def self.digest(name, data)
+ super(data, name)
+ end
+
+ alg.each{|name|
+ klass = Class.new(Digest){
+ define_method(:initialize){|*data|
+ if data.length > 1
+ raise ArgumentError,
+ "wrong number of arguments (#{data.length} for 1)"
+ end
+ super(name, data.first)
+ }
+ }
+ singleton = (class << klass; self; end)
+ singleton.class_eval{
+ define_method(:digest){|data| Digest.digest(name, data) }
+ define_method(:hexdigest){|data| Digest.hexdigest(name, data) }
+ }
+ const_set(name, klass)
+ }
+
+ # 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/ruby/1.9/openssl/ssl-internal.rb b/lib/ruby/1.9/openssl/ssl-internal.rb
new file mode 100644
index 00000000000..9c0320c6d29
--- /dev/null
+++ b/lib/ruby/1.9/openssl/ssl-internal.rb
@@ -0,0 +1,177 @@
+=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: ssl-internal.rb 29189 2010-09-06 01:53:00Z nahi $
+=end
+
+require "openssl/buffering"
+require "fcntl"
+
+module OpenSSL
+ module SSL
+ class SSLContext
+ 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,
+ }
+
+ 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
+
+ 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
+ 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
+
+ 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|
+ if /\ADNS:(.*)/ =~ general_name
+ should_verify_common_name = false
+ reg = Regexp.escape($1).gsub(/\\\*/, "[^.]+")
+ return true if /\A#{reg}\z/i =~ hostname
+ elsif /\AIP Address:(.*)/ =~ general_name
+ should_verify_common_name = false
+ return true if $1 == hostname
+ 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 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
+
+ def session
+ SSL::Session.new(self)
+ rescue SSL::Session::SessionError
+ nil
+ 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/ruby/1.9/openssl/ssl.rb b/lib/ruby/1.9/openssl/ssl.rb
new file mode 100644
index 00000000000..15f42d60918
--- /dev/null
+++ b/lib/ruby/1.9/openssl/ssl.rb
@@ -0,0 +1,2 @@
+warn 'deprecated openssl/ssl use: require "openssl" instead of "openssl/ssl"'
+require 'openssl'
diff --git a/lib/ruby/1.9/openssl/x509-internal.rb b/lib/ruby/1.9/openssl/x509-internal.rb
new file mode 100644
index 00000000000..d92d5d9721b
--- /dev/null
+++ b/lib/ruby/1.9/openssl/x509-internal.rb
@@ -0,0 +1,158 @@
+=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: x509-internal.rb 32663 2011-07-25 04:51:26Z nahi $
+=end
+
+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 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)
+ 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/ruby/1.9/openssl/x509.rb b/lib/ruby/1.9/openssl/x509.rb
new file mode 100644
index 00000000000..f1777cdf061
--- /dev/null
+++ b/lib/ruby/1.9/openssl/x509.rb
@@ -0,0 +1,2 @@
+warn 'deprecated openssl/x509 use: require "openssl" instead of "openssl/x509"'
+require 'openssl'
diff --git a/lib/ruby/shared/jopenssl/version.rb b/lib/ruby/shared/jopenssl/version.rb
new file mode 100644
index 00000000000..ea579b0fa78
--- /dev/null
+++ b/lib/ruby/shared/jopenssl/version.rb
@@ -0,0 +1,5 @@
+module Jopenssl
+ module Version
+ VERSION = "0.7.5.dev"
+ end
+end
diff --git a/lib/ruby/shared/jruby/openssl/autoloads/asn1.rb b/lib/ruby/shared/jruby/openssl/autoloads/asn1.rb
deleted file mode 100644
index 5dacf185e42..00000000000
--- a/lib/ruby/shared/jruby/openssl/autoloads/asn1.rb
+++ /dev/null
@@ -1,9 +0,0 @@
-require 'rubygems'
-
-# try to activate jruby-openssl gem for OpenSSL::SSL, raising error if gem not present
-begin
- gem 'jruby-openssl'
- require 'openssl.rb'
-rescue Gem::LoadError => e
- raise LoadError.new("OpenSSL::ASN1 requires the jruby-openssl gem")
-end
diff --git a/lib/ruby/shared/jruby/openssl/autoloads/bn.rb b/lib/ruby/shared/jruby/openssl/autoloads/bn.rb
deleted file mode 100644
index 314f944a257..00000000000
--- a/lib/ruby/shared/jruby/openssl/autoloads/bn.rb
+++ /dev/null
@@ -1,9 +0,0 @@
-require 'rubygems'
-
-# try to activate jruby-openssl gem for OpenSSL::SSL, raising error if gem not present
-begin
- gem 'jruby-openssl'
- require 'openssl.rb'
-rescue Gem::LoadError => e
- raise LoadError.new("OpenSSL::BN requires the jruby-openssl gem")
-end
diff --git a/lib/ruby/shared/jruby/openssl/autoloads/cipher.rb b/lib/ruby/shared/jruby/openssl/autoloads/cipher.rb
deleted file mode 100644
index b4b4a6b5f36..00000000000
--- a/lib/ruby/shared/jruby/openssl/autoloads/cipher.rb
+++ /dev/null
@@ -1,9 +0,0 @@
-require 'rubygems'
-
-# try to activate jruby-openssl gem for OpenSSL::SSL, raising error if gem not present
-begin
- gem 'jruby-openssl'
- require 'openssl.rb'
-rescue Gem::LoadError => e
- raise LoadError.new("OpenSSL::Cipher requires the jruby-openssl gem")
-end
diff --git a/lib/ruby/shared/jruby/openssl/autoloads/config.rb b/lib/ruby/shared/jruby/openssl/autoloads/config.rb
deleted file mode 100644
index 4f259f0fc72..00000000000
--- a/lib/ruby/shared/jruby/openssl/autoloads/config.rb
+++ /dev/null
@@ -1,9 +0,0 @@
-require 'rubygems'
-
-# try to activate jruby-openssl gem for OpenSSL::SSL, raising error if gem not present
-begin
- gem 'jruby-openssl'
- require 'openssl.rb'
-rescue Gem::LoadError => e
- raise LoadError.new("OpenSSL::Config requires the jruby-openssl gem")
-end
diff --git a/lib/ruby/shared/jruby/openssl/autoloads/netscape.rb b/lib/ruby/shared/jruby/openssl/autoloads/netscape.rb
deleted file mode 100644
index 6d9e6d3f23c..00000000000
--- a/lib/ruby/shared/jruby/openssl/autoloads/netscape.rb
+++ /dev/null
@@ -1,9 +0,0 @@
-require 'rubygems'
-
-# try to activate jruby-openssl gem for OpenSSL::SSL, raising error if gem not present
-begin
- gem 'jruby-openssl'
- require 'openssl.rb'
-rescue Gem::LoadError => e
- raise LoadError.new("OpenSSL::Netscape requires the jruby-openssl gem")
-end
diff --git a/lib/ruby/shared/jruby/openssl/autoloads/pkcs7.rb b/lib/ruby/shared/jruby/openssl/autoloads/pkcs7.rb
deleted file mode 100644
index cbc1d93a9a8..00000000000
--- a/lib/ruby/shared/jruby/openssl/autoloads/pkcs7.rb
+++ /dev/null
@@ -1,9 +0,0 @@
-require 'rubygems'
-
-# try to activate jruby-openssl gem for OpenSSL::SSL, raising error if gem not present
-begin
- gem 'jruby-openssl'
- require 'openssl.rb'
-rescue Gem::LoadError => e
- raise LoadError.new("OpenSSL::PKCS7 requires the jruby-openssl gem")
-end
diff --git a/lib/ruby/shared/jruby/openssl/autoloads/pkey.rb b/lib/ruby/shared/jruby/openssl/autoloads/pkey.rb
deleted file mode 100644
index 6d2a8114776..00000000000
--- a/lib/ruby/shared/jruby/openssl/autoloads/pkey.rb
+++ /dev/null
@@ -1,9 +0,0 @@
-require 'rubygems'
-
-# try to activate jruby-openssl gem for OpenSSL::SSL, raising error if gem not present
-begin
- gem 'jruby-openssl'
- require 'openssl.rb'
-rescue Gem::LoadError => e
- raise LoadError.new("OpenSSL::PKey requires the jruby-openssl gem")
-end
diff --git a/lib/ruby/shared/jruby/openssl/autoloads/random.rb b/lib/ruby/shared/jruby/openssl/autoloads/random.rb
deleted file mode 100644
index 26980dc4b7a..00000000000
--- a/lib/ruby/shared/jruby/openssl/autoloads/random.rb
+++ /dev/null
@@ -1,9 +0,0 @@
-require 'rubygems'
-
-# try to activate jruby-openssl gem for OpenSSL::SSL, raising error if gem not present
-begin
- gem 'jruby-openssl'
- require 'openssl.rb'
-rescue Gem::LoadError => e
- raise LoadError.new("OpenSSL::Random requires the jruby-openssl gem")
-end
diff --git a/lib/ruby/shared/jruby/openssl/autoloads/ssl.rb b/lib/ruby/shared/jruby/openssl/autoloads/ssl.rb
deleted file mode 100644
index b8e2d7c5c4c..00000000000
--- a/lib/ruby/shared/jruby/openssl/autoloads/ssl.rb
+++ /dev/null
@@ -1,9 +0,0 @@
-require 'rubygems'
-
-# try to activate jruby-openssl gem for OpenSSL::SSL, raising error if gem not present
-begin
- gem 'jruby-openssl'
- require 'openssl.rb'
-rescue Gem::LoadError => e
- raise LoadError.new("OpenSSL::SSL requires the jruby-openssl gem")
-end
diff --git a/lib/ruby/shared/jruby/openssl/autoloads/x509.rb b/lib/ruby/shared/jruby/openssl/autoloads/x509.rb
deleted file mode 100644
index 7e419b8da00..00000000000
--- a/lib/ruby/shared/jruby/openssl/autoloads/x509.rb
+++ /dev/null
@@ -1,9 +0,0 @@
-require 'rubygems'
-
-# try to activate jruby-openssl gem for OpenSSL::SSL, raising error if gem not present
-begin
- gem 'jruby-openssl'
- require 'openssl.rb'
-rescue Gem::LoadError => e
- raise LoadError.new("OpenSSL::X509 requires the jruby-openssl gem")
-end
diff --git a/lib/ruby/shared/jruby/openssl/builtin.rb b/lib/ruby/shared/jruby/openssl/builtin.rb
deleted file mode 100644
index 65ade65f2e4..00000000000
--- a/lib/ruby/shared/jruby/openssl/builtin.rb
+++ /dev/null
@@ -1,196 +0,0 @@
-# this will load the gem and library only if it's available
-require 'jruby/openssl/gem'
-
-unless defined?(OpenSSL)
- module OpenSSL
- VERSION = "1.0.0"
- OPENSSL_VERSION = "OpenSSL 0.9.8b 04 May 2006 (JRuby fake)"
- OPENSSL_DUMMY = true
-
- class OpenSSLError < StandardError; end
- # These require the gem, so we present an error if they're accessed
- %w[
- ASN1
- BN
- Cipher
- Config
- Netscape
- PKCS7
- PKey
- Random
- SSL
- X509
- ].each {|c| autoload c, "jruby/openssl/autoloads/#{c.downcase}"}
-
- def self.determine_version
- require 'java'
- java.security.MessageDigest.getInstance("SHA224", PROVIDER);
- 9469999
- rescue Exception
- 9469952
- end
- OPENSSL_VERSION_NUMBER = determine_version
-
- require 'java'
- class HMACError < OpenSSLError; end
- class DigestError < OpenSSLError; end
-
- class Digest
- class << self
- def digest(name, data)
- self.new(name, data).digest
- end
- def hexdigest(name, data)
- self.new(name, data).hexdigest
- end
- end
-
- attr_reader :algorithm, :name, :data
-
- def initialize(name, data = nil)
- @name = name
- @data = ""
- create_digest
- update(data) if data
- end
-
- def initialize_copy(dig)
- initialize(dig.name, dig.data)
- end
-
- def update(d)
- @data << d
- @md.update(d.to_java_bytes)
- self
- end
- alias_method :<<, :update
-
- def digest
- @md.reset
- String.from_java_bytes @md.digest(@data.to_java_bytes)
- end
-
- def hexdigest
- digest.unpack("H*")[0]
- end
- alias_method :to_s, :hexdigest
- alias_method :inspect, :hexdigest
-
- def ==(oth)
- return false unless oth.kind_of? OpenSSL::Digest
- self.algorithm == oth.algorithm && self.digest == oth.digest
- end
-
- def reset
- @md.reset
- @data = ""
- end
-
- def size
- @md.getDigestLength
- end
-
- private
- def create_digest
- @algorithm = case @name
- when "DSS"
- "SHA"
- when "DSS1"
- "SHA1"
- else
- @name
- end
- @md = java.security.MessageDigest.getInstance(@algorithm)
- end
-
- begin
- old_verbose, $VERBOSE = $VERBOSE, nil # silence warnings
- # from openssl/digest.rb -- define the concrete digest classes
- alg = %w(DSS DSS1 MD2 MD4 MD5 MDC2 RIPEMD160 SHA SHA1)
- if ::OpenSSL::OPENSSL_VERSION_NUMBER > 0x00908000
- alg += %w(SHA224 SHA256 SHA384 SHA512)
- end
- alg.each{|name|
- klass = Class.new(Digest){
- define_method(:initialize){|*data|
- if data.length > 1
- raise ArgumentError,
- "wrong number of arguments (#{data.length} for 1)"
- end
- super(name, data.first)
- }
- }
- singleton = (class << klass; self; end)
- singleton.class_eval{
- define_method(:digest){|data| Digest.digest(name, data) }
- define_method(:hexdigest){|data| Digest.hexdigest(name, data) }
- }
- const_set(name, klass)
- }
- ensure
- $VERBOSE = old_verbose
- end
-
- class Digest < Digest
- def initialize(*args)
- # add warning
- super(*args)
- end
- end
- end
-
- class HMAC
- class << self
- def digest(dig, key, data)
- self.new(key, dig).update(data).digest
- end
-
- def hexdigest(dig, key, data)
- self.new(key, dig).update(data).hexdigest
- end
- end
-
- attr_reader :name, :key, :data
-
- def initialize(key, dig)
- @name = "HMAC" + dig.algorithm
- @key = key
- @data = ""
- create_mac
- end
-
- def initialize_copy(hmac)
- @name = hmac.name
- @key = hmac.key
- @data = hmac.data
- create_mac
- end
-
- def update(d)
- @data << d
- self
- end
- alias_method :<<, :update
-
- def digest
- @mac.reset
- String.from_java_bytes @mac.doFinal(@data.to_java_bytes)
- end
-
- def hexdigest
- digest.unpack("H*")[0]
- end
-
- alias_method :inspect, :hexdigest
- alias_method :to_s, :hexdigest
-
- private
- def create_mac
- @mac = javax.crypto.Mac.getInstance(@name)
- @mac.init(javax.crypto.spec.SecretKeySpec.new(@key.to_java_bytes, @name))
- end
- end
- warn %{JRuby limited openssl loaded. http://jruby.org/openssl
-gem install jruby-openssl for full support.}
- end
-end
diff --git a/lib/ruby/shared/jruby/openssl/gem.rb b/lib/ruby/shared/jruby/openssl/gem.rb
deleted file mode 100644
index c7010eb6e78..00000000000
--- a/lib/ruby/shared/jruby/openssl/gem.rb
+++ /dev/null
@@ -1,15 +0,0 @@
-# try to activate jruby-openssl gem, and only require from the gem if it's there
-tried_gem = false
-begin
- require 'openssl.rb'
-rescue LoadError
- unless tried_gem
- require 'rubygems'
- begin
- gem 'jruby-openssl'
- rescue LoadError
- end
- tried_gem = true
- retry
- end
-end
diff --git a/lib/ruby/shared/jruby/openssl/gem_only.rb b/lib/ruby/shared/jruby/openssl/gem_only.rb
deleted file mode 100644
index 464c88014c4..00000000000
--- a/lib/ruby/shared/jruby/openssl/gem_only.rb
+++ /dev/null
@@ -1,5 +0,0 @@
-module JRuby
- module OpenSSL
- GEM_ONLY = true
- end
-end
\ No newline at end of file
diff --git a/lib/ruby/shared/jruby/openssl/stub.rb b/lib/ruby/shared/jruby/openssl/stub.rb
deleted file mode 100644
index 34affb4bb8b..00000000000
--- a/lib/ruby/shared/jruby/openssl/stub.rb
+++ /dev/null
@@ -1,11 +0,0 @@
-module JRuby
- module OpenSSL
- GEM_ONLY = false unless defined?(GEM_ONLY)
- end
-end
-
-if JRuby::OpenSSL::GEM_ONLY
- require 'jruby/openssl/gem'
-else
- require "jruby/openssl/builtin"
-end
\ No newline at end of file
diff --git a/src/org/jruby/Ruby.java b/src/org/jruby/Ruby.java
index 37c991e51c4..4775eb14e52 100644
--- a/src/org/jruby/Ruby.java
+++ b/src/org/jruby/Ruby.java
@@ -1583,6 +1583,7 @@ private void initBuiltins() {
addLazyBuiltin("fcntl.rb", "fcntl", "org.jruby.ext.fcntl.FcntlLibrary");
addLazyBuiltin("rubinius.jar", "rubinius", "org.jruby.ext.rubinius.RubiniusLibrary");
addLazyBuiltin("yecht.jar", "yecht", "YechtService");
+ addLazyBuiltin("jopenssl.jar", "jopenssl", "org.jruby.ext.openssl.OSSLLibrary");
if (is1_9()) {
addLazyBuiltin("mathn/complex.jar", "mathn/complex", "org.jruby.ext.mathn.Complex");
@@ -1606,12 +1607,6 @@ public void load(Ruby runtime, boolean wrap) throws IOException {
if(RubyInstanceConfig.NATIVE_NET_PROTOCOL) {
addLazyBuiltin("net/protocol.rb", "net/protocol", "org.jruby.ext.net.protocol.NetProtocolBufferedIOLibrary");
}
-
- addBuiltinIfAllowed("openssl.jar", new Library() {
- public void load(Ruby runtime, boolean wrap) throws IOException {
- runtime.getLoadService().require("jruby/openssl/stub");
- }
- });
addBuiltinIfAllowed("win32ole.jar", new Library() {
public void load(Ruby runtime, boolean wrap) throws IOException {
diff --git a/src/org/jruby/RubyModule.java b/src/org/jruby/RubyModule.java
index 8caac9af667..824df641029 100644
--- a/src/org/jruby/RubyModule.java
+++ b/src/org/jruby/RubyModule.java
@@ -1971,7 +1971,8 @@ public IRubyObject attr_accessor(IRubyObject[] args) {
}
/** rb_mod_attr_accessor
- *
+ * Note: this method should not be called from Java in most cases, since
+ * it depends on Ruby frame state for visibility. Use add[Read/Write]Attribute instead.
*/
@JRubyMethod(name = "attr_accessor", rest = true, visibility = PRIVATE, reads = VISIBILITY)
public IRubyObject attr_accessor(ThreadContext context, IRubyObject[] args) {
diff --git a/src/org/jruby/ext/openssl/ASN1.java b/src/org/jruby/ext/openssl/ASN1.java
new file mode 100644
index 00000000000..3cfa66200da
--- /dev/null
+++ b/src/org/jruby/ext/openssl/ASN1.java
@@ -0,0 +1,938 @@
+/***** BEGIN LICENSE BLOCK *****
+ * Version: CPL 1.0/GPL 2.0/LGPL 2.1
+ *
+ * The contents of this file are subject to the Common Public
+ * License Version 1.0 (the "License"); you may not use this file
+ * except in compliance with the License. You may obtain a copy of
+ * the License at http://www.eclipse.org/legal/cpl-v10.html
+ *
+ * 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, 2007 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 CPL, 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 CPL, the GPL or the LGPL.
+ ***** END LICENSE BLOCK *****/
+package org.jruby.ext.openssl;
+
+import java.math.BigInteger;
+import java.text.DateFormat;
+import java.text.SimpleDateFormat;
+import java.util.ArrayList;
+import java.util.Calendar;
+import java.util.Date;
+import java.util.Enumeration;
+import java.util.HashMap;
+import java.util.IdentityHashMap;
+import java.util.Iterator;
+import java.util.List;
+import java.util.Map;
+import java.io.IOException;
+
+import org.bouncycastle.asn1.ASN1Encodable;
+import org.bouncycastle.asn1.ASN1EncodableVector;
+import org.bouncycastle.asn1.ASN1InputStream;
+import org.bouncycastle.asn1.ASN1Sequence;
+import org.bouncycastle.asn1.DERBitString;
+import org.bouncycastle.asn1.DERBoolean;
+import org.bouncycastle.asn1.DERInteger;
+import org.bouncycastle.asn1.DERNull;
+import org.bouncycastle.asn1.DERObjectIdentifier;
+import org.bouncycastle.asn1.DEROctetString;
+import org.bouncycastle.asn1.DERSequence;
+import org.bouncycastle.asn1.DERSet;
+import org.bouncycastle.asn1.DERString;
+import org.bouncycastle.asn1.DERTaggedObject;
+import org.bouncycastle.asn1.DERUTCTime;
+import org.bouncycastle.asn1.DERUTF8String;
+import org.jruby.Ruby;
+import org.jruby.RubyArray;
+import org.jruby.RubyBignum;
+import org.jruby.RubyClass;
+import org.jruby.RubyModule;
+import org.jruby.RubyNumeric;
+import org.jruby.RubyObject;
+import org.jruby.RubyString;
+import org.jruby.RubySymbol;
+import org.jruby.RubyTime;
+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.util.ByteList;
+
+/**
+ * @author Ola Bini
+ */
+public class ASN1 {
+ private static Map> SYM_TO_OID = new IdentityHashMap>();
+ private static Map> OID_TO_SYM = new IdentityHashMap>();
+ private static Map> OID_TO_NID = new IdentityHashMap>();
+ private static Map> NID_TO_OID = new IdentityHashMap>();
+ private static Map> NID_TO_SN = new IdentityHashMap>();
+ private static Map> NID_TO_LN = new IdentityHashMap>();
+
+
+ static void addObject(Ruby runtime, int nid, String sn, String ln, String oid) {
+ Map s2o = SYM_TO_OID.get(runtime);
+ Map o2s = OID_TO_SYM.get(runtime);
+ Map o2n = OID_TO_NID.get(runtime);
+ Map n2o = NID_TO_OID.get(runtime);
+ Map n2s = NID_TO_SN.get(runtime);
+ Map n2l = NID_TO_LN.get(runtime);
+ if(null != oid && (null != sn || null != ln)) {
+ DERObjectIdentifier ident = new DERObjectIdentifier(oid);
+ if(sn != null) {
+ s2o.put(sn.toLowerCase(),ident);
+ }
+ if(ln != null) {
+ s2o.put(ln.toLowerCase(),ident);
+ }
+ o2s.put(ident,sn == null ? ln : sn);
+ o2n.put(ident,nid);
+ n2o.put(nid,ident);
+ n2s.put(nid,sn);
+ n2l.put(nid,ln);
+ }
+ }
+
+ @SuppressWarnings("unchecked")
+ private synchronized static void initMaps(Ruby runtime) {
+ Map val = new HashMap(org.bouncycastle.asn1.x509.X509Name.DefaultLookUp);
+ Map val2 = new HashMap(org.bouncycastle.asn1.x509.X509Name.DefaultSymbols);
+ SYM_TO_OID.put(runtime,val);
+ OID_TO_SYM.put(runtime,val2);
+ OID_TO_NID.put(runtime,new HashMap());
+ NID_TO_OID.put(runtime,new HashMap());
+ NID_TO_SN.put(runtime,new HashMap());
+ NID_TO_LN.put(runtime,new HashMap());
+ OpenSSLImpl.defaultObjects(runtime);
+ }
+
+ synchronized static Integer obj2nid(Ruby runtime, String oid) {
+ return obj2nid(runtime, new DERObjectIdentifier(oid));
+ }
+
+ synchronized static String ln2oid(Ruby runtime, String ln) {
+ Map val = SYM_TO_OID.get(runtime);
+ if(null == val) {
+ initMaps(runtime);
+ val = SYM_TO_OID.get(runtime);
+ }
+ return val.get(ln).getId();
+ }
+
+ synchronized static Integer obj2nid(Ruby runtime, DERObjectIdentifier oid) {
+ Map o2n = OID_TO_NID.get(runtime);
+ if(null == o2n) {
+ initMaps(runtime);
+ o2n = OID_TO_NID.get(runtime);
+ }
+ return o2n.get(oid);
+ }
+
+ synchronized static String o2a(Ruby runtime, DERObjectIdentifier obj) {
+ Integer nid = obj2nid(runtime,obj);
+ Map n2l = NID_TO_LN.get(runtime);
+ Map n2s = NID_TO_SN.get(runtime);
+ String one = n2l.get(nid);
+ if(one == null) {
+ one = n2s.get(nid);
+ }
+ return one;
+ }
+
+ synchronized static String nid2ln(Ruby runtime, int nid) {
+ return nid2ln(runtime, new Integer(nid));
+ }
+
+ synchronized static String nid2ln(Ruby runtime, Integer nid) {
+ Map n2l = NID_TO_LN.get(runtime);
+ if(null == n2l) {
+ initMaps(runtime);
+ n2l = NID_TO_LN.get(runtime);
+ }
+ return n2l.get(nid);
+ }
+
+ synchronized static Map getOIDLookup(Ruby runtime) {
+ Map val = SYM_TO_OID.get(runtime);
+ if(null == val) {
+ initMaps(runtime);
+ val = SYM_TO_OID.get(runtime);
+ }
+ return val;
+ }
+
+ synchronized static Map getSymLookup(Ruby runtime) {
+ Map val = OID_TO_SYM.get(runtime);
+ if(null == val) {
+ initMaps(runtime);
+ val = OID_TO_SYM.get(runtime);
+ }
+ return val;
+ }
+
+ private final static Object[][] ASN1_INFO = {
+ {"EOC", null, null },
+ {"BOOLEAN", org.bouncycastle.asn1.DERBoolean.class, "Boolean" },
+ {"INTEGER", org.bouncycastle.asn1.DERInteger.class, "Integer" },
+ {"BIT_STRING", org.bouncycastle.asn1.DERBitString.class, "BitString" },
+ {"OCTET_STRING", org.bouncycastle.asn1.DEROctetString.class, "OctetString" },
+ {"NULL", org.bouncycastle.asn1.DERNull.class, "Null" },
+ {"OBJECT", org.bouncycastle.asn1.DERObjectIdentifier.class, "ObjectId" },
+ {"OBJECT_DESCRIPTOR", null, null },
+ {"EXTERNAL", null, null },
+ {"REAL", null, null },
+ {"ENUMERATED", org.bouncycastle.asn1.DEREnumerated.class, "Enumerated" },
+ {"EMBEDDED_PDV", null, null },
+ {"UTF8STRING", org.bouncycastle.asn1.DERUTF8String.class, "UTF8String" },
+ {"RELATIVE_OID", null, null },
+ {"[UNIVERSAL 14]", null, null },
+ {"[UNIVERSAL 15]", null, null },
+ {"SEQUENCE", org.bouncycastle.asn1.DERSequence.class, "Sequence" },
+ {"SET", org.bouncycastle.asn1.DERSet.class, "Set" },
+ {"NUMERICSTRING", org.bouncycastle.asn1.DERNumericString.class, "NumericString" },
+ {"PRINTABLESTRING", org.bouncycastle.asn1.DERPrintableString.class, "PrintableString" },
+ {"T61STRING", org.bouncycastle.asn1.DERT61String.class, "T61String" },
+ {"VIDEOTEXSTRING", null, null },
+ {"IA5STRING", org.bouncycastle.asn1.DERIA5String.class, "IA5String" },
+ {"UTCTIME", org.bouncycastle.asn1.DERUTCTime.class, "UTCTime" },
+ {"GENERALIZEDTIME", org.bouncycastle.asn1.DERGeneralizedTime.class, "GeneralizedTime" },
+ {"GRAPHICSTRING", null, null },
+ {"ISO64STRING", null, null },
+ {"GENERALSTRING", org.bouncycastle.asn1.DERGeneralString.class, "GeneralString" },
+ {"UNIVERSALSTRING", org.bouncycastle.asn1.DERUniversalString.class, "UniversalString" },
+ {"CHARACTER_STRING", null, null },
+ {"BMPSTRING", org.bouncycastle.asn1.DERBMPString.class, "BMPString" }};
+
+ private final static Map CLASS_TO_ID = new HashMap();
+ private final static Map RUBYNAME_TO_ID = new HashMap();
+
+ static {
+ for(int i=0;i classForId(int id) {
+ @SuppressWarnings("unchecked")
+ Class extends ASN1Encodable> result = (Class extends ASN1Encodable>)(ASN1_INFO[id][1]);
+ return result;
+ }
+
+ public static void createASN1(Ruby runtime, RubyModule ossl) {
+ ThreadContext context = runtime.getCurrentContext();
+ RubyModule mASN1 = ossl.defineModuleUnder("ASN1");
+ RubyClass openSSLError = ossl.getClass("OpenSSLError");
+ mASN1.defineClassUnder("ASN1Error",openSSLError, openSSLError.getAllocator());
+
+ mASN1.defineAnnotatedMethods(ASN1.class);
+
+ List ary = new ArrayList();
+ mASN1.setConstant("UNIVERSAL_TAG_NAME",runtime.newArray(ary));
+ for(int i=0;i em = getOIDLookup(runtime);
+ String name = null;
+ for(Iterator iter = em.keySet().iterator();iter.hasNext();) {
+ String key = iter.next();
+ if(oid.equals(em.get(key))) {
+ if(name == null || key.length() < name.length()) {
+ name = key;
+ }
+ }
+ }
+ return name;
+ }
+
+ private static String getLongNameFor(Ruby runtime, String nameOrOid) {
+ DERObjectIdentifier oid = getObjectIdentifier(runtime,nameOrOid);
+ Map em = getOIDLookup(runtime);
+ String name = null;
+ for(Iterator iter = em.keySet().iterator();iter.hasNext();) {
+ String key = iter.next();
+ if(oid.equals(em.get(key))) {
+ if(name == null || key.length() > name.length()) {
+ name = key;
+ }
+ }
+ }
+ return name;
+ }
+
+ private static DERObjectIdentifier getObjectIdentifier(Ruby runtime, String nameOrOid) {
+ Object val1 = ASN1.getOIDLookup(runtime).get(nameOrOid.toLowerCase());
+ if(null != val1) {
+ return (DERObjectIdentifier)val1;
+ }
+ DERObjectIdentifier val2 = new DERObjectIdentifier(nameOrOid);
+ return val2;
+ }
+
+ @JRubyMethod(name="Boolean", module=true, rest=true)
+ public static IRubyObject fact_Boolean(IRubyObject recv, IRubyObject[] args) {
+ return ((RubyModule)recv).getClass("Boolean").callMethod(recv.getRuntime().getCurrentContext(),"new",args);
+ }
+
+ @JRubyMethod(name="Integer", module=true, rest=true)
+ public static IRubyObject fact_Integer(IRubyObject recv, IRubyObject[] args) {
+ return ((RubyModule)recv).getClass("Integer").callMethod(recv.getRuntime().getCurrentContext(),"new",args);
+ }
+
+ @JRubyMethod(name="Enumerated", module=true, rest=true)
+ public static IRubyObject fact_Enumerated(IRubyObject recv, IRubyObject[] args) {
+ return ((RubyModule)recv).getClass("Enumerated").callMethod(recv.getRuntime().getCurrentContext(),"new",args);
+ }
+
+ @JRubyMethod(name="BitString", module=true, rest=true)
+ public static IRubyObject fact_BitString(IRubyObject recv, IRubyObject[] args) {
+ return ((RubyModule)recv).getClass("BitString").callMethod(recv.getRuntime().getCurrentContext(),"new",args);
+ }
+
+ @JRubyMethod(name="OctetString", module=true, rest=true)
+ public static IRubyObject fact_OctetString(IRubyObject recv, IRubyObject[] args) {
+ return ((RubyModule)recv).getClass("OctetString").callMethod(recv.getRuntime().getCurrentContext(),"new",args);
+ }
+
+ @JRubyMethod(name="UTF8String", module=true, rest=true)
+ public static IRubyObject fact_UTF8String(IRubyObject recv, IRubyObject[] args) {
+ return ((RubyModule)recv).getClass("UTF8String").callMethod(recv.getRuntime().getCurrentContext(),"new",args);
+ }
+
+ @JRubyMethod(name="NumericString", module=true, rest=true)
+ public static IRubyObject fact_NumericString(IRubyObject recv, IRubyObject[] args) {
+ return ((RubyModule)recv).getClass("NumericString").callMethod(recv.getRuntime().getCurrentContext(),"new",args);
+ }
+
+ @JRubyMethod(name="PrintableString", module=true, rest=true)
+ public static IRubyObject fact_PrintableString(IRubyObject recv, IRubyObject[] args) {
+ return ((RubyModule)recv).getClass("PrintableString").callMethod(recv.getRuntime().getCurrentContext(),"new",args);
+ }
+
+ @JRubyMethod(name="T61String", module=true, rest=true)
+ public static IRubyObject fact_T61String(IRubyObject recv, IRubyObject[] args) {
+ return ((RubyModule)recv).getClass("T61String").callMethod(recv.getRuntime().getCurrentContext(),"new",args);
+ }
+
+ @JRubyMethod(name="VideotexString", module=true, rest=true)
+ public static IRubyObject fact_VideotexString(IRubyObject recv, IRubyObject[] args) {
+ return ((RubyModule)recv).getClass("VideotexString").callMethod(recv.getRuntime().getCurrentContext(),"new",args);
+ }
+
+ @JRubyMethod(name="IA5String", module=true, rest=true)
+ public static IRubyObject fact_IA5String(IRubyObject recv, IRubyObject[] args) {
+ return ((RubyModule)recv).getClass("IA5String").callMethod(recv.getRuntime().getCurrentContext(),"new",args);
+ }
+
+ @JRubyMethod(name="GraphicString", module=true, rest=true)
+ public static IRubyObject fact_GraphicString(IRubyObject recv, IRubyObject[] args) {
+ return ((RubyModule)recv).getClass("GraphicString").callMethod(recv.getRuntime().getCurrentContext(),"new",args);
+ }
+
+ @JRubyMethod(name="ISO64String", module=true, rest=true)
+ public static IRubyObject fact_ISO64String(IRubyObject recv, IRubyObject[] args) {
+ return ((RubyModule)recv).getClass("ISO64String").callMethod(recv.getRuntime().getCurrentContext(),"new",args);
+ }
+
+ @JRubyMethod(name="GeneralString", module=true, rest=true)
+ public static IRubyObject fact_GeneralString(IRubyObject recv, IRubyObject[] args) {
+ return ((RubyModule)recv).getClass("GeneralString").callMethod(recv.getRuntime().getCurrentContext(),"new",args);
+ }
+
+ @JRubyMethod(name="UniversalString", module=true, rest=true)
+ public static IRubyObject fact_UniversalString(IRubyObject recv, IRubyObject[] args) {
+ return ((RubyModule)recv).getClass("UniversalString").callMethod(recv.getRuntime().getCurrentContext(),"new",args);
+ }
+
+ @JRubyMethod(name="BMPString", module=true, rest=true)
+ public static IRubyObject fact_BMPString(IRubyObject recv, IRubyObject[] args) {
+ return ((RubyModule)recv).getClass("BMPString").callMethod(recv.getRuntime().getCurrentContext(),"new",args);
+ }
+
+ @JRubyMethod(name="Nul", module=true, rest=true)
+ public static IRubyObject fact_Null(IRubyObject recv, IRubyObject[] args) {
+ return ((RubyModule)recv).getClass("Null").callMethod(recv.getRuntime().getCurrentContext(),"new",args);
+ }
+
+ @JRubyMethod(name="ObjectId", module=true, rest=true)
+ public static IRubyObject fact_ObjectId(IRubyObject recv, IRubyObject[] args) {
+ return ((RubyModule)recv).getClass("ObjectId").callMethod(recv.getRuntime().getCurrentContext(),"new",args);
+ }
+
+ @JRubyMethod(name="UTCTime", module=true, rest=true)
+ public static IRubyObject fact_UTCTime(IRubyObject recv, IRubyObject[] args) {
+ return ((RubyModule)recv).getClass("UTCTime").callMethod(recv.getRuntime().getCurrentContext(),"new",args);
+ }
+
+ @JRubyMethod(name="GeneralizedTime", module=true, rest=true)
+ public static IRubyObject fact_GeneralizedTime(IRubyObject recv, IRubyObject[] args) {
+ return ((RubyModule)recv).getClass("GeneralizedTime").callMethod(recv.getRuntime().getCurrentContext(),"new",args);
+ }
+
+ @JRubyMethod(name="Sequence", module=true, rest=true)
+ public static IRubyObject fact_Sequence(IRubyObject recv, IRubyObject[] args) {
+ return ((RubyModule)recv).getClass("Sequence").callMethod(recv.getRuntime().getCurrentContext(),"new",args);
+ }
+
+ @JRubyMethod(name="Set", module=true, rest=true)
+ public static IRubyObject fact_Set(IRubyObject recv, IRubyObject[] args) {
+ return ((RubyModule)recv).getClass("Set").callMethod(recv.getRuntime().getCurrentContext(),"new",args);
+ }
+
+ @JRubyMethod(meta=true, required=1)
+ public static IRubyObject traverse(IRubyObject recv, IRubyObject a) {
+ System.err.println("WARNING: unimplemented method called: traverse");
+ return null;
+ }
+
+ public static class ObjectId {
+ @JRubyMethod(meta=true, rest=true)
+ public static IRubyObject register(IRubyObject recv, IRubyObject[] args) {
+ DERObjectIdentifier deroi = new DERObjectIdentifier(args[0].toString());
+ getOIDLookup(recv.getRuntime()).put(args[1].toString().toLowerCase(),deroi);
+ getOIDLookup(recv.getRuntime()).put(args[2].toString().toLowerCase(),deroi);
+ getSymLookup(recv.getRuntime()).put(deroi,args[1].toString());
+ return recv.getRuntime().getTrue();
+ }
+
+ @JRubyMethod(name={"sn","short_name"})
+ public static IRubyObject sn(IRubyObject self) {
+ return self.getRuntime().newString(getShortNameFor(self.getRuntime(),self.callMethod(self.getRuntime().getCurrentContext(),"value").toString()));
+ }
+
+ @JRubyMethod(name={"ln","long_name"})
+ public static IRubyObject ln(IRubyObject self) {
+ return self.getRuntime().newString(getLongNameFor(self.getRuntime(),self.callMethod(self.getRuntime().getCurrentContext(),"value").toString()));
+ }
+
+ @JRubyMethod
+ public static IRubyObject oid(IRubyObject self) {
+ return self.getRuntime().newString(getObjectIdentifier(self.getRuntime(),self.callMethod(self.getRuntime().getCurrentContext(),"value").toString()).getId());
+ }
+ }
+
+ private final static DateFormat dateF = new SimpleDateFormat("yyyyMMddHHmmssz");
+ private static IRubyObject decodeObj(RubyModule asnM,Object v) throws IOException, java.text.ParseException {
+ int ix = idForClass(v.getClass());
+ String v_name = ix == -1 ? null : (String)(ASN1_INFO[ix][2]);
+ ThreadContext tc = asnM.getRuntime().getCurrentContext();
+ if(null != v_name) {
+ RubyClass c = asnM.getClass(v_name);
+ if(v instanceof DERBitString) {
+ ByteList bl = new ByteList(((DERBitString)v).getBytes(), false);
+ IRubyObject bString = c.callMethod(tc,"new",asnM.getRuntime().newString(bl));
+ bString.callMethod(tc,"unused_bits=",asnM.getRuntime().newFixnum(((DERBitString)v).getPadBits()));
+ return bString;
+ } else if(v instanceof DERString) {
+ ByteList val;
+ if (v instanceof DERUTF8String) {
+ val = new ByteList(((DERUTF8String) v).getString().getBytes("UTF-8"));
+ } else {
+ val = ByteList.create(((DERString)v).getString());
+ }
+ return c.callMethod(tc,"new",asnM.getRuntime().newString(val));
+ } else if(v instanceof ASN1Sequence) {
+ List l = new ArrayList();
+ for(Enumeration enm = ((ASN1Sequence)v).getObjects(); enm.hasMoreElements(); ) {
+ l.add(decodeObj(asnM,enm.nextElement()));
+ }
+ return c.callMethod(tc,"new",asnM.getRuntime().newArray(l));
+ } else if(v instanceof DERSet) {
+ List l = new ArrayList();
+ for(Enumeration enm = ((DERSet)v).getObjects(); enm.hasMoreElements(); ) {
+ l.add(decodeObj(asnM,enm.nextElement()));
+ }
+ return c.callMethod(tc,"new",asnM.getRuntime().newArray(l));
+ } else if(v instanceof DERNull) {
+ return c.callMethod(tc,"new",asnM.getRuntime().getNil());
+ } else if(v instanceof DERInteger) {
+ return c.callMethod(tc,"new",RubyNumeric.str2inum(asnM.getRuntime(),asnM.getRuntime().newString(((DERInteger)v).getValue().toString()),10));
+ } else if(v instanceof DERUTCTime) {
+ Date d = dateF.parse(((DERUTCTime)v).getAdjustedTime());
+ Calendar cal = Calendar.getInstance();
+ cal.setTime(d);
+ IRubyObject[] argv = new IRubyObject[6];
+ argv[0] = asnM.getRuntime().newFixnum(cal.get(Calendar.YEAR));
+ argv[1] = asnM.getRuntime().newFixnum(cal.get(Calendar.MONTH)+1);
+ argv[2] = asnM.getRuntime().newFixnum(cal.get(Calendar.DAY_OF_MONTH));
+ argv[3] = asnM.getRuntime().newFixnum(cal.get(Calendar.HOUR_OF_DAY));
+ argv[4] = asnM.getRuntime().newFixnum(cal.get(Calendar.MINUTE));
+ argv[5] = asnM.getRuntime().newFixnum(cal.get(Calendar.SECOND));
+ return c.callMethod(tc,"new",asnM.getRuntime().getClass("Time").callMethod(tc,"local",argv));
+ } else if(v instanceof DERObjectIdentifier) {
+ String av = ((DERObjectIdentifier)v).getId();
+ return c.callMethod(tc,"new",asnM.getRuntime().newString(av));
+ } else if(v instanceof DEROctetString) {
+ ByteList bl = new ByteList(((DEROctetString)v).getOctets(), false);
+ return c.callMethod(tc,"new",asnM.getRuntime().newString(bl));
+ } else if(v instanceof DERBoolean) {
+ return c.callMethod(tc,"new",((DERBoolean)v).isTrue() ? asnM.getRuntime().getTrue() : asnM.getRuntime().getFalse());
+ } else {
+ System.out.println("Should handle: " + v.getClass().getName());
+ }
+ } else if(v instanceof DERTaggedObject) {
+ RubyClass c = asnM.getClass("ASN1Data");
+ IRubyObject val = decodeObj(asnM, ((DERTaggedObject)v).getObject());
+ IRubyObject tag = asnM.getRuntime().newFixnum(((DERTaggedObject)v).getTagNo());
+ IRubyObject tag_class = asnM.getRuntime().newSymbol("CONTEXT_SPECIFIC");
+ return c.callMethod(tc,"new",new IRubyObject[]{asnM.getRuntime().newArray(val),tag,tag_class});
+ }
+
+ // System.err.println("v: " + v + "[" + v.getClass().getName() + "]");
+ return null;
+ }
+
+ @JRubyMethod(meta = true)
+ public static IRubyObject decode(IRubyObject recv, IRubyObject obj) {
+ try {
+ IRubyObject obj2 = OpenSSLImpl.to_der_if_possible(obj);
+ RubyModule asnM = (RubyModule)recv;
+ ASN1InputStream asis = new ASN1InputStream(obj2.convertToString().getBytes());
+ IRubyObject ret = decodeObj(asnM, asis.readObject());
+ return ret;
+ } catch(IOException e) {
+ throw recv.getRuntime().newIOErrorFromException(e);
+ } catch(Exception e) {
+ throw recv.getRuntime().newArgumentError(e.getMessage());
+ }
+ }
+
+ @JRubyMethod(meta=true, required=1)
+ public static IRubyObject decode_all(IRubyObject recv, IRubyObject a) {
+ System.err.println("WARNING: unimplemented method called: decode_all");
+ return null;
+ }
+
+ public static class ASN1Data extends RubyObject {
+ private static final long serialVersionUID = 6117598347932209839L;
+
+ public static ObjectAllocator ALLOCATOR = new ObjectAllocator() {
+ public IRubyObject allocate(Ruby runtime, RubyClass klass) {
+ return new ASN1Data(runtime, klass);
+ }
+ };
+ public ASN1Data(Ruby runtime, RubyClass type) {
+ super(runtime,type);
+ }
+
+ protected void asn1Error() {
+ asn1Error(null);
+ }
+
+ protected void asn1Error(String msg) {
+ throw Utils.newError(getRuntime(), "OpenSSL::ASN1::ASN1Error", msg);
+ }
+
+ @JRubyMethod
+ public IRubyObject initialize(IRubyObject value, IRubyObject tag, IRubyObject tag_class) {
+ if(!(tag_class instanceof RubySymbol)) {
+ asn1Error("invalid tag class");
+ }
+ if(tag_class.toString().equals(":UNIVERSAL") && RubyNumeric.fix2int(tag) > 31) {
+ asn1Error("tag number for Universal too large");
+ }
+ ThreadContext tc = getRuntime().getCurrentContext();
+ this.callMethod(tc,"tag=", tag);
+ this.callMethod(tc,"value=", value);
+ this.callMethod(tc,"tag_class=", tag_class);
+
+ return this;
+ }
+
+ ASN1Encodable toASN1() {
+ ThreadContext tc = getRuntime().getCurrentContext();
+ int tag = RubyNumeric.fix2int(callMethod(tc,"tag"));
+ IRubyObject val = callMethod(tc,"value");
+ if(val instanceof RubyArray) {
+ RubyArray arr = (RubyArray)callMethod(tc,"value");
+ if(arr.size() > 1) {
+ ASN1EncodableVector vec = new ASN1EncodableVector();
+ for (IRubyObject obj : arr.toJavaArray()) {
+ vec.add(((ASN1Data)obj).toASN1());
+ }
+ return new DERTaggedObject(tag, new DERSequence(vec));
+ } else {
+ return new DERTaggedObject(tag,((ASN1Data)(arr.getList().get(0))).toASN1());
+ }
+ } else {
+ return new DERTaggedObject(tag, ((ASN1Data)val).toASN1());
+ }
+ }
+
+ @JRubyMethod
+ public IRubyObject to_der() {
+ return getRuntime().newString(new ByteList(toASN1().getDEREncoded(),false));
+ }
+
+ protected IRubyObject defaultTag() {
+ int i = idForRubyName(getMetaClass().getRealClass().getBaseName());
+ if(i != -1) {
+ return getRuntime().newFixnum(i);
+ } else {
+ return getRuntime().getNil();
+ }
+ }
+
+ protected void print() {
+ print(0);
+ }
+
+ protected void printIndent(int indent) {
+ for(int i=0;i1) {
+ tag = args[1];
+ if(args.length>2) {
+ tagging = args[2];
+ if(args.length>3) {
+ tag_class = args[3];
+ }
+ }
+ if(tag.isNil()) {
+ asn1Error("must specify tag number");
+ }
+ if(tagging.isNil()) {
+ tagging = getRuntime().newSymbol("EXPLICIT");
+ }
+ if(!(tagging instanceof RubySymbol)) {
+ asn1Error("invalid tag default");
+ }
+ if(tag_class.isNil()) {
+ tag_class = getRuntime().newSymbol("CONTEXT_SPECIFIC");
+ }
+ if(!(tag_class instanceof RubySymbol)) {
+ asn1Error("invalid tag class");
+ }
+ if(tagging.toString().equals(":IMPLICIT") && RubyNumeric.fix2int(tag) > 31) {
+ asn1Error("tag number for Universal too large");
+ }
+ } else {
+ tag = defaultTag();
+ tagging = getRuntime().getNil();
+ tag_class = getRuntime().newSymbol("UNIVERSAL");
+ }
+ if("ObjectId".equals(getMetaClass().getRealClass().getBaseName())) {
+ String v = getSymLookup(getRuntime()).get(getObjectIdentifier(value.toString()));
+ if(v != null) {
+ value = getRuntime().newString(v);
+ }
+ }
+ ThreadContext tc = getRuntime().getCurrentContext();
+ this.callMethod(tc,"tag=",tag);
+ this.callMethod(tc,"value=",value);
+ this.callMethod(tc,"tagging=",tagging);
+ this.callMethod(tc,"tag_class=",tag_class);
+
+ return this;
+ }
+
+ private DERObjectIdentifier getObjectIdentifier(String nameOrOid) {
+ Object val1 = ASN1.getOIDLookup(getRuntime()).get(nameOrOid.toLowerCase());
+ if(null != val1) {
+ return (DERObjectIdentifier)val1;
+ }
+ DERObjectIdentifier val2 = new DERObjectIdentifier(nameOrOid);
+ return val2;
+ }
+
+ ASN1Encodable toASN1() {
+ // System.err.println(getMetaClass().getRealClass().getBaseName()+"#toASN1");
+ int tag = idForRubyName(getMetaClass().getRealClass().getBaseName());
+ @SuppressWarnings("unchecked") Class extends ASN1Encodable> imp = (Class extends ASN1Encodable>)ASN1_INFO[tag][1];
+ IRubyObject val = callMethod(getRuntime().getCurrentContext(),"value");
+ if(imp == DERObjectIdentifier.class) {
+ return getObjectIdentifier(val.toString());
+ } else if(imp == DERNull.class) {
+ return new DERNull();
+ } else if(imp == DERBoolean.class) {
+ return new DERBoolean(val.isTrue());
+ } else if(imp == DERUTCTime.class) {
+ return new DERUTCTime(((RubyTime)val).getJavaDate());
+ } else if(imp == DERInteger.class && val instanceof RubyBignum) {
+ return new DERInteger(((RubyBignum)val).getValue());
+ } else if(imp == DERInteger.class) {
+ return new DERInteger(new BigInteger(val.toString()));
+ } else if(imp == DEROctetString.class) {
+ return new DEROctetString(val.convertToString().getBytes());
+ } else if(imp == DERBitString.class) {
+ byte[] bs = val.convertToString().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;
+ }
+ }
+ return new DERBitString(bs,unused);
+ } else if(val instanceof RubyString) {
+ try {
+ return imp.getConstructor(String.class).newInstance(val.toString());
+ } catch (Exception ex) {
+ throw RaiseException.createNativeRaiseException(getRuntime(), ex);
+ }
+ }
+
+ System.err.println("object with tag: " + tag + " and value: " + val + " and val.class: " + val.getClass().getName() + " and impl: " + imp.getName());
+ System.err.println("WARNING: unimplemented method called: asn1data#toASN1");
+ return null;
+ }
+
+ protected void print(int indent) {
+ printIndent(indent);
+ System.out.println(getMetaClass().getRealClass().getBaseName() + ": " + callMethod(getRuntime().getCurrentContext(),"value").callMethod(getRuntime().getCurrentContext(),"inspect").toString());
+ }
+ }
+
+ public static class ASN1Constructive extends ASN1Data {
+ private static final long serialVersionUID = -7166662655104776828L;
+
+ public static ObjectAllocator ALLOCATOR = new ObjectAllocator() {
+ public IRubyObject allocate(Ruby runtime, RubyClass klass) {
+ return new ASN1Constructive(runtime, klass);
+ }
+ };
+ public ASN1Constructive(Ruby runtime, RubyClass type) {
+ super(runtime,type);
+ }
+
+ @JRubyMethod
+ public IRubyObject to_der() {
+ return super.to_der();
+ }
+
+ @JRubyMethod(required=1, optional=3)
+ public IRubyObject initialize(IRubyObject[] args) {
+ IRubyObject value = args[0];
+ IRubyObject tag = getRuntime().getNil();
+ IRubyObject tagging = getRuntime().getNil();
+ IRubyObject tag_class = getRuntime().getNil();
+ if(args.length>1) {
+ tag = args[1];
+ if(args.length>2) {
+ tagging = args[2];
+ if(args.length>3) {
+ tag_class = args[3];
+ }
+ }
+ if(tag.isNil()) {
+ asn1Error("must specify tag number");
+ }
+ if(tagging.isNil()) {
+ tagging = getRuntime().newSymbol("EXPLICIT");
+ }
+ if(!(tagging instanceof RubySymbol)) {
+ asn1Error("invalid tag default");
+ }
+ if(tag_class.isNil()) {
+ tag_class = getRuntime().newSymbol("CONTEXT_SPECIFIC");
+ }
+ if(!(tag_class instanceof RubySymbol)) {
+ asn1Error("invalid tag class");
+ }
+ if(tagging.toString().equals(":IMPLICIT") && RubyNumeric.fix2int(tag) > 31) {
+ asn1Error("tag number for Universal too large");
+ }
+ } else {
+ tag = defaultTag();
+ tagging = getRuntime().getNil();
+ tag_class = getRuntime().newSymbol("UNIVERSAL");
+ }
+ ThreadContext tc = getRuntime().getCurrentContext();
+ this.callMethod(tc,"tag=",tag);
+ this.callMethod(tc,"value=",value);
+ this.callMethod(tc,"tagging=",tagging);
+ this.callMethod(tc,"tag_class=",tag_class);
+
+ return this;
+ }
+
+ ASN1Encodable toASN1() {
+ // System.err.println(getMetaClass().getRealClass().getBaseName()+"#toASN1");
+ int id = idForRubyName(getMetaClass().getRealClass().getBaseName());
+ if(id != -1) {
+ ASN1EncodableVector vec = new ASN1EncodableVector();
+ RubyArray arr = (RubyArray)callMethod(getRuntime().getCurrentContext(),"value");
+ for (IRubyObject obj : arr.toJavaArray()) {
+ if(obj instanceof ASN1Data) {
+ vec.add(((ASN1Data)obj).toASN1());
+ } else {
+ vec.add(((ASN1Data) ASN1.decode(getRuntime().getClassFromPath("OpenSSL::ASN1"), OpenSSLImpl.to_der_if_possible(obj))).toASN1());
+ }
+ }
+ try {
+ @SuppressWarnings("unchecked")
+ ASN1Encodable result = ((Class extends ASN1Encodable>) (ASN1_INFO[id][1])).getConstructor(new Class[] { ASN1EncodableVector.class })
+ .newInstance(new Object[] { vec });
+ return result;
+ } catch (Exception e) {
+ // TODO: deprecated
+ throw RaiseException.createNativeRaiseException(getRuntime(), e);
+ }
+ }
+ return null;
+ }
+
+ @JRubyMethod(frame=true)
+ public IRubyObject each(Block block) {
+ RubyArray arr = (RubyArray) callMethod(getRuntime().getCurrentContext(), "value");
+ for (IRubyObject obj : arr.toJavaArray()) {
+ block.yield(getRuntime().getCurrentContext(), obj);
+ }
+ return getRuntime().getNil();
+ }
+
+ protected void print(int indent) {
+ printIndent(indent);
+ System.out.println(getMetaClass().getRealClass().getBaseName() + ": ");
+ RubyArray arr = (RubyArray)callMethod(getRuntime().getCurrentContext(),"value");
+ for (IRubyObject obj : arr.toJavaArray()) {
+ ((ASN1Data)obj).print(indent+1);
+ }
+ }
+ }
+}// ASN1
diff --git a/src/org/jruby/ext/openssl/Attribute.java b/src/org/jruby/ext/openssl/Attribute.java
new file mode 100644
index 00000000000..e915f50b4b6
--- /dev/null
+++ b/src/org/jruby/ext/openssl/Attribute.java
@@ -0,0 +1,133 @@
+/***** BEGIN LICENSE BLOCK *****
+ * Version: CPL 1.0/GPL 2.0/LGPL 2.1
+ *
+ * The contents of this file are subject to the Common Public
+ * License Version 1.0 (the "License"); you may not use this file
+ * except in compliance with the License. You may obtain a copy of
+ * the License at http://www.eclipse.org/legal/cpl-v10.html
+ *
+ * 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 CPL, 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 CPL, the GPL or the LGPL.
+ ***** END LICENSE BLOCK *****/
+package org.jruby.ext.openssl;
+
+import org.bouncycastle.asn1.ASN1EncodableVector;
+import org.bouncycastle.asn1.DERObject;
+import org.bouncycastle.asn1.DERObjectIdentifier;
+import org.bouncycastle.asn1.DERSequence;
+import org.bouncycastle.asn1.DERSet;
+import org.jruby.Ruby;
+import org.jruby.RubyClass;
+import org.jruby.RubyModule;
+import org.jruby.RubyObject;
+import org.jruby.anno.JRubyMethod;
+import org.jruby.runtime.ObjectAllocator;
+import org.jruby.runtime.builtin.IRubyObject;
+
+/**
+ * @author Ola Bini
+ */
+public class Attribute extends RubyObject {
+ private static final long serialVersionUID = 5569940260019783275L;
+
+ private static ObjectAllocator ATTRIBUTE_ALLOCATOR = new ObjectAllocator() {
+ public IRubyObject allocate(Ruby runtime, RubyClass klass) {
+ return new Attribute(runtime, klass);
+ }
+ };
+
+ public static void createAttribute(Ruby runtime, RubyModule mX509) {
+ RubyClass cAttribute = mX509.defineClassUnder("Attribute",runtime.getObject(), ATTRIBUTE_ALLOCATOR);
+
+ RubyClass openSSLError = runtime.getModule("OpenSSL").getClass("OpenSSLError");
+ mX509.defineClassUnder("AttributeError",openSSLError, openSSLError.getAllocator());
+
+ cAttribute.defineAnnotatedMethods(Attribute.class);
+ }
+
+ public Attribute(Ruby runtime, RubyClass type) {
+ super(runtime,type);
+ }
+
+ private IRubyObject oid;
+ private IRubyObject value;
+
+ private DERObjectIdentifier getObjectIdentifier(String nameOrOid) {
+ Object val1 = ASN1.getOIDLookup(getRuntime()).get(nameOrOid.toLowerCase());
+ if(null != val1) {
+ return (DERObjectIdentifier)val1;
+ }
+ DERObjectIdentifier val2 = new DERObjectIdentifier(nameOrOid);
+ return val2;
+ }
+
+ DERObject toASN1() {
+ ASN1EncodableVector v1 = new ASN1EncodableVector();
+ v1.add(getObjectIdentifier(oid.toString()));
+ if(value instanceof ASN1.ASN1Constructive) {
+ v1.add(((ASN1.ASN1Constructive)value).toASN1());
+ } else {
+ ASN1EncodableVector v2 = new ASN1EncodableVector();
+ v2.add(((ASN1.ASN1Data)value).toASN1());
+ v1.add(new DERSet(v2));
+ }
+ return new DERSequence(v1);
+ }
+
+ @JRubyMethod(name="initialize", required=1, optional=1)
+ public IRubyObject _initialize(IRubyObject[] str) {
+ if(org.jruby.runtime.Arity.checkArgumentCount(getRuntime(),str,1,2) == 1) {
+ IRubyObject _oid = OpenSSLImpl.to_der_if_possible(str[0]);
+ set_oid(_oid);
+ return this;
+ }
+ set_oid(str[0]);
+ set_value(str[1]);
+ return this;
+ }
+
+ @JRubyMethod
+ public IRubyObject to_der() {
+ System.err.println("WARNING: unimplemented method called: attr#to_der");
+ return getRuntime().getNil();
+ }
+
+ @JRubyMethod
+ public IRubyObject oid() {
+ return oid;
+ }
+
+ @JRubyMethod(name="oid=")
+ public IRubyObject set_oid(IRubyObject val) {
+ this.oid = val;
+ return val;
+ }
+
+ @JRubyMethod
+ public IRubyObject value() {
+ return value;
+ }
+
+ @JRubyMethod(name="value=")
+ public IRubyObject set_value(IRubyObject val) {
+ IRubyObject tmp = OpenSSLImpl.to_der_if_possible(val);
+ this.value = ASN1.decode(getRuntime().getClassFromPath("OpenSSL::ASN1"), tmp);
+ return val;
+ }
+}// Attribute
diff --git a/src/org/jruby/ext/openssl/BN.java b/src/org/jruby/ext/openssl/BN.java
new file mode 100644
index 00000000000..18a77e35e9e
--- /dev/null
+++ b/src/org/jruby/ext/openssl/BN.java
@@ -0,0 +1,766 @@
+/***** BEGIN LICENSE BLOCK *****
+ * Version: CPL 1.0/GPL 2.0/LGPL 2.1
+ *
+ * The contents of this file are subject to the Common Public
+ * License Version 1.0 (the "License"); you may not use this file
+ * except in compliance with the License. You may obtain a copy of
+ * the License at http://www.eclipse.org/legal/cpl-v10.html
+ *
+ * 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) 2007 William N Dortch
+ *
+ * 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 CPL, 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 CPL, the GPL or the LGPL.
+ ***** END LICENSE BLOCK *****/
+package org.jruby.ext.openssl;
+
+import java.math.BigInteger;
+import java.security.SecureRandom;
+import java.util.Random;
+
+import org.jruby.Ruby;
+import org.jruby.RubyClass;
+import org.jruby.RubyFixnum;
+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.Arity;
+import org.jruby.runtime.ClassIndex;
+import org.jruby.runtime.ObjectAllocator;
+import org.jruby.runtime.builtin.IRubyObject;
+import org.jruby.util.ByteList;
+
+/**
+ * OpenSSL::BN implementation. Wraps java.math.BigInteger, which provides
+ * most functionality directly; the rest is easily derived.
+ *
+ * Beware that BN's are mutable -- I don't agree with this approach, but
+ * must conform for compatibility with MRI's implementation. The offending methods
+ * are set_bit!, clear_bit!, mask_bits! and copy.
+ *
+ * I've included a few operations (& | ^ ~) that aren't defined by MRI/OpenSSL.
+ * These are non-portable (i.e., won't work in C-Ruby), so use at your own risk.
+ *
+ * @author Bill Dortch
+ */
+public class BN extends RubyObject {
+ private static final long serialVersionUID = -5660938062191525498L;
+
+ private static final BigInteger MAX_INT = BigInteger.valueOf(Integer.MAX_VALUE);
+ private static final BigInteger TWO = BigInteger.valueOf(2);
+ private static final int DEFAULT_CERTAINTY = 100;
+ private static Random _random;
+ private static SecureRandom _secureRandom;
+
+ private static ObjectAllocator BN_ALLOCATOR = new ObjectAllocator() {
+ public IRubyObject allocate(Ruby runtime, RubyClass klass) {
+ return new BN(runtime, klass, BigInteger.ZERO);
+ }
+ };
+
+ public static BN newBN(Ruby runtime, BigInteger value) {
+ return new BN(runtime, value != null ? value : BigInteger.ZERO);
+ }
+
+ public static void createBN(Ruby runtime, RubyModule ossl) {
+ RubyClass openSSLError = ossl.getClass("OpenSSLError");
+ ossl.defineClassUnder("BNError", openSSLError, openSSLError.getAllocator());
+
+ RubyClass bn = ossl.defineClassUnder("BN", runtime.getObject(), BN_ALLOCATOR);
+
+ bn.defineAnnotatedMethods(BN.class);
+ }
+
+ private volatile BigInteger value;
+
+ private BN(Ruby runtime, RubyClass clazz, BigInteger value) {
+ super(runtime, clazz);
+ this.value = value;
+ }
+
+ private BN(Ruby runtime, BigInteger value) {
+ super(runtime, runtime.getModule("OpenSSL").getClass("BN"));
+ this.value = value;
+ }
+
+ public BigInteger getValue() {
+ return value;
+ }
+
+ // TODO: check whether this is really needed for JRuby 1.0x (not used in 1.1x)
+ public IRubyObject doClone() {
+ return newBN(getRuntime(), this.value);
+ }
+
+ public IRubyObject initialize_copy(IRubyObject original) {
+ super.initialize_copy(original);
+ if (this != original) {
+ this.value = ((BN)original).value;
+ }
+ return this;
+ }
+
+ @JRubyMethod(name="initialize", required=1, optional=1)
+ public synchronized IRubyObject bn_initialize(IRubyObject[] args) {
+ Ruby runtime = getRuntime();
+ if (this.value != BigInteger.ZERO) { // already initialized
+ throw newBNError(runtime, "illegal initialization");
+ }
+ int argc = Arity.checkArgumentCount(runtime, args, 1, 2);
+ int base = argc == 2 ? RubyNumeric.num2int(args[1]) : 10;
+ RubyString str = RubyString.stringValue(args[0]);
+ switch (base) {
+ case 2:
+ // this seems wrong to me, but is the behavior of the
+ // MRI implementation. rather than interpreting the string
+ // as ASCII-encoded binary digits, the raw binary value of
+ // the string is used instead. the value is always interpreted
+ // as positive, hence the use of the signum version of the BI
+ // constructor here:
+ this.value = new BigInteger(1, str.getBytes());
+ break;
+ case 10:
+ case 16:
+ // here, the ASCII-encoded decimal or hex string is used
+ try {
+ this.value = new BigInteger(str.toString(), base);
+ break;
+ } catch (NumberFormatException e) {
+ throw runtime.newArgumentError("value " + str + " is not legal for radix " + base);
+ }
+ case 0: // FIXME: not currently supporting BN_mpi2bn
+ throw runtime.newArgumentError("unsupported radix: " + base);
+ default:
+ throw runtime.newArgumentError("illegal radix: " + base);
+ }
+ return this;
+ }
+
+ @JRubyMethod(name="copy")
+ public synchronized IRubyObject bn_copy(IRubyObject other) {
+ if (this != other) {
+ this.value = getBigInteger(other);
+ }
+ return this;
+ }
+
+ @JRubyMethod(name="to_s", rest=true)
+ public IRubyObject bn_to_s(IRubyObject[] args) {
+ Ruby runtime = getRuntime();
+ int argc = Arity.checkArgumentCount(runtime, args, 0, 1);
+ int base = argc == 1 ? RubyNumeric.num2int(args[0]) : 10;
+ switch (base) {
+ case 2:
+ // again, following MRI implementation, wherein base 2 deals
+ // with strings as byte arrays rather than ASCII-encoded binary
+ // digits. note that negative values are returned as though positive:
+
+ byte[] bytes = this.value.abs().toByteArray();
+
+ // suppress leading 0 byte to conform to MRI behavior
+ if (bytes[0] == 0) {
+ return runtime.newString(new ByteList(bytes, 1, bytes.length - 1));
+ }
+ return runtime.newString(new ByteList(bytes, false));
+ case 10:
+ case 16:
+ return runtime.newString(value.toString(base).toUpperCase());
+ case 0: // FIXME: not currently supporting BN_mpi2bn
+ throw runtime.newArgumentError("unsupported radix: " + base);
+ default:
+ throw runtime.newArgumentError("illegal radix: " + base);
+ }
+ }
+
+ @JRubyMethod(name="to_i")
+ public IRubyObject bn_to_i() {
+ Ruby runtime = getRuntime();
+ // FIXME: s/b faster way to convert than going through RubyString
+ return RubyNumeric.str2inum(runtime, runtime.newString(value.toString()), 10, true);
+ }
+
+ @JRubyMethod(name="to_bn")
+ public IRubyObject bn_to_bn() {
+ return this;
+ }
+
+ @JRubyMethod(name="coerce")
+ // FIXME: is this right? don't see how it would be useful...
+ public IRubyObject bn_coerce(IRubyObject other) {
+ Ruby runtime = getRuntime();
+ IRubyObject self;
+ switch (other.getMetaClass().index) {
+ case ClassIndex.STRING:
+ self = runtime.newString(value.toString());
+ break;
+ case ClassIndex.FIXNUM:
+ case ClassIndex.BIGNUM:
+ // FIXME: s/b faster way to convert than going through RubyString
+ self = RubyNumeric.str2inum(runtime, runtime.newString(value.toString()), 10, true);
+ break;
+ default:
+ if (other instanceof BN) {
+ self = this;
+ } else {
+ throw runtime.newTypeError("Don't know how to coerce");
+ }
+ }
+ return runtime.newArray(other, self);
+ }
+
+ @JRubyMethod(name="zero?")
+ public IRubyObject bn_is_zero() {
+ return getRuntime().newBoolean(value.equals(BigInteger.ZERO));
+ }
+
+ @JRubyMethod(name="one?")
+ public IRubyObject bn_is_one() {
+ return getRuntime().newBoolean(value.equals(BigInteger.ONE));
+ }
+
+ @JRubyMethod(name="odd?")
+ public IRubyObject bn_is_odd() {
+ return getRuntime().newBoolean(value.testBit(0));
+ }
+
+ @JRubyMethod(name={"cmp", "<=>"})
+ public IRubyObject bn_cmp(IRubyObject other) {
+ return getRuntime().newFixnum(value.compareTo(getBigInteger(other)));
+ }
+
+ @JRubyMethod(name="ucmp")
+ public IRubyObject bn_ucmp(IRubyObject other) {
+ return getRuntime().newFixnum(value.abs().compareTo(getBigInteger(other).abs()));
+ }
+
+ @JRubyMethod(name={"eql?", "==", "==="})
+ public IRubyObject bn_eql(IRubyObject other) {
+ return getRuntime().newBoolean(value.equals(getBigInteger(other)));
+ }
+
+ @JRubyMethod(name="sqr")
+ public IRubyObject bn_sqr() {
+ // TODO: check whether mult n * n is faster
+ return newBN(getRuntime(), value.pow(2));
+ }
+
+ @JRubyMethod(name="~")
+ public IRubyObject bn_not() {
+ return newBN(getRuntime(), value.not());
+ }
+
+ @JRubyMethod(name="+")
+ public IRubyObject bn_add(IRubyObject other) {
+ return newBN(getRuntime(), value.add(getBigInteger(other)));
+ }
+
+ @JRubyMethod(name="-")
+ public IRubyObject bn_sub(IRubyObject other) {
+ return newBN(getRuntime(), value.subtract(getBigInteger(other)));
+ }
+
+ @JRubyMethod(name="*")
+ public IRubyObject bn_mul(IRubyObject other) {
+ return newBN(getRuntime(), value.multiply(getBigInteger(other)));
+ }
+
+ @JRubyMethod(name="%")
+ public IRubyObject bn_mod(IRubyObject other) {
+ try {
+ return newBN(getRuntime(), value.mod(getBigInteger(other)));
+ } catch (ArithmeticException e) {
+ throw getRuntime().newZeroDivisionError();
+ }
+ }
+
+ @JRubyMethod(name="/")
+ public IRubyObject bn_div(IRubyObject other) {
+ Ruby runtime = getRuntime();
+ try {
+ BigInteger[] result = value.divideAndRemainder(getBigInteger(other));
+ return runtime.newArray(newBN(runtime, result[0]), newBN(runtime, result[1]));
+ } catch (ArithmeticException e) {
+ throw runtime.newZeroDivisionError();
+ }
+ }
+
+ @JRubyMethod(name="&")
+ public IRubyObject bn_and(IRubyObject other) {
+ return newBN(getRuntime(), value.and(getBigInteger(other)));
+ }
+
+ @JRubyMethod(name="|")
+ public IRubyObject bn_or(IRubyObject other) {
+ return newBN(getRuntime(), value.or(getBigInteger(other)));
+ }
+
+ @JRubyMethod(name="^")
+ public IRubyObject bn_xor(IRubyObject other) {
+ return newBN(getRuntime(), value.xor(getBigInteger(other)));
+ }
+
+ @JRubyMethod(name="**")
+ public IRubyObject bn_exp(IRubyObject other) {
+ // somewhat strangely, BigInteger takes int rather than BigInteger
+ // as the argument to pow. so we'll have to narrow the value, and
+ // raise an exception if data would be lost. (on the other hand, an
+ // exponent even approaching Integer.MAX_VALUE would be silly big, and
+ // the value would take a very, very long time to calculate.)
+ // we'll check for values < 0 (illegal) while we're at it
+ int exp;
+ switch(other.getMetaClass().index) {
+ case ClassIndex.FIXNUM: {
+ long val = ((RubyFixnum)other).getLongValue();
+ if (val >= 0 && val <= Integer.MAX_VALUE) {
+ exp = (int)val;
+ break;
+ }
+ }
+ case ClassIndex.BIGNUM:
+ // Bignum is inherently too big
+ throw newBNError(getRuntime(), "invalid exponent");
+ default: {
+ if (!(other instanceof BN)) {
+ throw getRuntime().newTypeError("Cannot convert into OpenSSL::BN");
+ }
+ BigInteger val = ((BN)other).value;
+ if (val.compareTo(BigInteger.ZERO) < 0 || val.compareTo(MAX_INT) > 0) {
+ throw newBNError(getRuntime(), "invalid exponent");
+ }
+ exp = val.intValue();
+ break;
+ }
+ }
+ try {
+ return newBN(getRuntime(), value.pow(exp));
+ } catch (ArithmeticException e) {
+ // shouldn't happen, we've already checked for < 0
+ throw newBNError(getRuntime(), "invalid exponent");
+ }
+ }
+
+ @JRubyMethod(name="gcd")
+ public IRubyObject bn_gcd(IRubyObject other) {
+ return newBN(getRuntime(), value.gcd(getBigInteger(other)));
+ }
+
+ @JRubyMethod(name="mod_sqr")
+ public IRubyObject bn_mod_sqr(IRubyObject other) {
+ try {
+ return newBN(getRuntime(), value.modPow(TWO, getBigInteger(other)));
+ } catch (ArithmeticException e) {
+ throw getRuntime().newZeroDivisionError();
+ }
+ }
+
+ @JRubyMethod(name="mod_inverse")
+ public IRubyObject bn_mod_inverse(IRubyObject other) {
+ try {
+ return newBN(getRuntime(), value.modInverse(getBigInteger(other)));
+ } catch (ArithmeticException e) {
+ throw getRuntime().newZeroDivisionError();
+ }
+ }
+
+ @JRubyMethod(name="mod_add")
+ public IRubyObject bn_mod_add(IRubyObject other, IRubyObject mod) {
+ try {
+ return newBN(getRuntime(), value.add(getBigInteger(other)).mod(getBigInteger(mod)));
+ } catch (ArithmeticException e) {
+ throw getRuntime().newZeroDivisionError();
+ }
+ }
+
+ @JRubyMethod(name="mod_sub")
+ public IRubyObject bn_mod_sub(IRubyObject other, IRubyObject mod) {
+ try {
+ return newBN(getRuntime(), value.subtract(getBigInteger(other)).mod(getBigInteger(mod)));
+ } catch (ArithmeticException e) {
+ throw getRuntime().newZeroDivisionError();
+ }
+ }
+
+ @JRubyMethod(name="mod_mul")
+ public IRubyObject bn_mod_mul(IRubyObject other, IRubyObject mod) {
+ try {
+ return newBN(getRuntime(), value.multiply(getBigInteger(other)).mod(getBigInteger(mod)));
+ } catch (ArithmeticException e) {
+ throw getRuntime().newZeroDivisionError();
+ }
+ }
+
+ @JRubyMethod(name="mod_exp")
+ public IRubyObject bn_mod_exp(IRubyObject other, IRubyObject mod) {
+ try {
+ return newBN(getRuntime(), value.modPow(getBigInteger(other), getBigInteger(mod)));
+ } catch (ArithmeticException e) {
+ throw getRuntime().newZeroDivisionError();
+ }
+ }
+
+ @JRubyMethod(name="set_bit!")
+ public synchronized IRubyObject bn_set_bit(IRubyObject n) {
+ // evil mutable BN
+ int pos = RubyNumeric.num2int(n);
+ BigInteger oldValue = this.value;
+ // FIXME? in MRI/OSSL-BIGNUM, the original sign of a BN is remembered, so if
+ // you set the value of an (originally) negative number to zero (through some
+ // combination of clear_bit! and/or mask_bits! calls), and later call set_bit!,
+ // the resulting value will be negative. this seems unintuitive and, frankly,
+ // wrong, not to mention expensive to carry the extra sign field.
+ // I'm not duplicating this behavior here at this time. -BD
+ try {
+ if (oldValue.signum() >= 0) {
+ this.value = oldValue.setBit(pos);
+ } else {
+ this.value = oldValue.abs().setBit(pos).negate();
+ }
+ } catch (ArithmeticException e) {
+ throw newBNError(getRuntime(), "invalid pos");
+ }
+ return this;
+ }
+
+ @JRubyMethod(name="clear_bit!")
+ public synchronized IRubyObject bn_clear_bit(IRubyObject n) {
+ // evil mutable BN
+ int pos = RubyNumeric.num2int(n);
+ BigInteger oldValue = this.value;
+ try {
+ if (oldValue.signum() >= 0) {
+ this.value = oldValue.clearBit(pos);
+ } else {
+ this.value = oldValue.abs().clearBit(pos).negate();
+ }
+ } catch (ArithmeticException e) {
+ throw newBNError(getRuntime(), "invalid pos");
+ }
+ return this;
+ }
+
+ /**
+ * Truncates value to n bits
+ */
+ @JRubyMethod(name="mask_bits!")
+ public synchronized IRubyObject bn_mask_bits(IRubyObject n) {
+ // evil mutable BN
+
+ int pos = RubyNumeric.num2int(n);
+ if (pos < 0) throw newBNError(getRuntime(), "invalid pos");
+
+ BigInteger oldValue = this.value;
+
+ // TODO: cache 2 ** n values?
+ if (oldValue.signum() >= 0) {
+ if (oldValue.bitLength() < pos) throw newBNError(getRuntime(), "invalid pos");
+ this.value = oldValue.mod(TWO.pow(pos));
+ } else {
+ BigInteger absValue = oldValue.abs();
+ if (absValue.bitLength() < pos) throw newBNError(getRuntime(), "invalid pos");
+ this.value = absValue.mod(TWO.pow(pos)).negate();
+ }
+
+ return this;
+ }
+
+ @JRubyMethod(name="bit_set?")
+ public IRubyObject bn_is_bit_set(IRubyObject n) {
+ int pos = RubyNumeric.num2int(n);
+ BigInteger val = this.value;
+ try {
+ if (val.signum() >= 0) {
+ return getRuntime().newBoolean(val.testBit(pos));
+ } else {
+ return getRuntime().newBoolean(val.abs().testBit(pos));
+ }
+ } catch (ArithmeticException e) {
+ throw newBNError(getRuntime(), "invalid pos");
+ }
+ }
+
+ @JRubyMethod(name="<<")
+ public IRubyObject bn_lshift(IRubyObject n) {
+ int nbits = RubyNumeric.num2int(n);
+ BigInteger val = this.value;
+ if (val.signum() >= 0) {
+ return newBN(getRuntime(), val.shiftLeft(nbits));
+ } else {
+ return newBN(getRuntime(), val.abs().shiftLeft(nbits).negate());
+ }
+ }
+
+ @JRubyMethod(name=">>")
+ public IRubyObject bn_rshift(IRubyObject n) {
+ int nbits = RubyNumeric.num2int(n);
+ BigInteger val = this.value;
+ if (val.signum() >= 0) {
+ return newBN(getRuntime(), val.shiftRight(nbits));
+ } else {
+ return newBN(getRuntime(), val.abs().shiftRight(nbits).negate());
+ }
+ }
+
+ @JRubyMethod(name="num_bits")
+ public IRubyObject bn_num_bits() {
+ return getRuntime().newFixnum(this.value.abs().bitLength());
+ }
+
+ @JRubyMethod(name="num_bytes")
+ public IRubyObject bn_num_bytes() {
+ return getRuntime().newFixnum((this.value.abs().bitLength() + 7) / 8);
+ }
+
+ @JRubyMethod(name="num_bits_set")
+ public IRubyObject bn_num_bits_set() {
+ return getRuntime().newFixnum(this.value.abs().bitCount());
+ }
+
+ // note that there is a bug in the MRI version, in argument handling,
+ // so apparently no one ever calls this...
+ @JRubyMethod(name="prime?", rest=true)
+ public IRubyObject bn_is_prime(IRubyObject[] args) {
+ Ruby runtime = getRuntime();
+ int argc = Arity.checkArgumentCount(runtime, args, 0, 1);
+ // BigInteger#isProbablePrime will actually limit checks to a maximum of 50,
+ // depending on bit count.
+ int certainty = argc == 0 ? DEFAULT_CERTAINTY : RubyNumeric.fix2int(args[0]);
+ return runtime.newBoolean(this.value.isProbablePrime(certainty));
+ }
+
+ // FIXME? BigInteger doesn't supply this, so right now this is (essentially)
+ // the same as bn_is_prime
+ @JRubyMethod(name="prime_fasttest?", rest=true)
+ public IRubyObject bn_is_prime_fasttest(IRubyObject[] args) {
+ Ruby runtime = getRuntime();
+ int argc = Arity.checkArgumentCount(runtime, args, 0, 2);
+ // BigInteger#isProbablePrime will actually limit checks to a maximum of 50,
+ // depending on bit count.
+ int certainty = argc == 0 ? DEFAULT_CERTAINTY : RubyNumeric.fix2int(args[0]);
+ return runtime.newBoolean(this.value.isProbablePrime(certainty));
+ }
+
+ @JRubyMethod(name="generate_prime", meta=true, rest=true)
+ public static IRubyObject bn_generate_prime(IRubyObject recv, IRubyObject[] args) {
+ Ruby runtime = recv.getRuntime();
+ int argc = Arity.checkArgumentCount(runtime, args, 1, 4);
+ int bits = RubyNumeric.num2int(args[0]);
+ boolean safe = argc > 1 ? args[1] != runtime.getFalse() : true;
+ BigInteger add = argc > 2 ? getBigInteger(args[2]) : null;
+ BigInteger rem = argc > 3 ? getBigInteger(args[3]) : null;
+ if (bits < 3) {
+ if (safe) throw runtime.newArgumentError("bits < 3");
+ if (bits < 2) throw runtime.newArgumentError("bits < 2");
+ }
+ return newBN(runtime, generatePrime(bits, safe, add, rem));
+ }
+
+ public static BigInteger generatePrime(int bits, boolean safe, BigInteger add, BigInteger rem) {
+ // From OpenSSL man page BN_generate_prime(3):
+ //
+ // "If add is not NULL, the prime will fulfill the condition p % add == rem
+ // (p % add == 1 if rem == NULL) in order to suit a given generator."
+ //
+ // "If safe is true, it will be a safe prime (i.e. a prime p so that
+ // (p-1)/2 is also prime)."
+ //
+ // see [ossl]/crypto/bn/bn_prime.c #BN_generate_prime_ex
+ //
+
+ if (add != null && rem == null) {
+ rem = BigInteger.ONE;
+ }
+
+ // borrowing technique from org.bouncycastle.crypto.generators.DHParametersHelper
+ // (unfortunately the code has package visibility), wherein for safe primes,
+ // we'll use the lowest useful certainty (2) for generation of q, then if
+ // p ( = 2q + 1) is prime to our required certainty (100), we'll verify that q
+ // is as well.
+ //
+ // for typical bit lengths ( >= 1024), this should speed things up by reducing
+ // initial Miller-Rabin iterations from 2 to 1 for candidate values of q.
+ //
+ // it's still painfully slow...
+ //
+ BigInteger p, q;
+ int qbits = bits - 1;
+ SecureRandom secureRandom = getSecureRandom();
+ do {
+ if (safe) {
+ do {
+ q = new BigInteger(qbits, 2, secureRandom);
+ p = q.shiftLeft(1).setBit(0);
+ } while (!(p.isProbablePrime(DEFAULT_CERTAINTY) && q.isProbablePrime(DEFAULT_CERTAINTY)));
+ } else {
+ p = BigInteger.probablePrime(bits, secureRandom);
+ }
+ } while (add != null && !p.mod(add).equals(rem));
+ return p;
+ }
+
+ public static BigInteger generatePrime(int bits, boolean safe) {
+ return generatePrime(bits, safe, null, null);
+ }
+
+ @JRubyMethod(name="rand", meta=true, rest=true)
+ public static IRubyObject bn_rand(IRubyObject recv, IRubyObject[] args) {
+ return getRandomBN(recv.getRuntime(), args, getSecureRandom());
+ }
+
+ @JRubyMethod(name="pseudo_rand", meta=true, rest=true)
+ public static IRubyObject bn_pseudo_rand(IRubyObject recv, IRubyObject[] args) {
+ return getRandomBN(recv.getRuntime(), args, getRandom());
+ }
+
+ public static BN getRandomBN(Ruby runtime, IRubyObject[] args, Random random) {
+ int argc = Arity.checkArgumentCount(runtime, args, 1, 3);
+ int bits = RubyNumeric.num2int(args[0]);
+ int top;
+ boolean bottom;
+ if (argc > 1) {
+ top = RubyNumeric.fix2int(args[1]);
+ bottom = argc == 3 ? args[2].isTrue() : false;
+ } else {
+ top = 0;
+ bottom = false;
+ }
+
+ BigInteger value;
+ try {
+ value = getRandomBI(bits, top, bottom, random);
+ } catch (IllegalArgumentException e) {
+ throw runtime.newArgumentError(e.getMessage());
+ }
+ return newBN(runtime, value);
+ }
+
+ public static BigInteger getRandomBI(int bits, int top, boolean bottom, Random random) {
+ // From OpenSSL man page BN_rand(3):
+ //
+ // "If top is -1, the most significant bit of the random number can be zero.
+ // If top is 0, it is set to 1, and if top is 1, the two most significant bits
+ // of the number will be set to 1, so that the product of two such random numbers
+ // will always have 2*bits length."
+ //
+ // "If bottom is true, the number will be odd."
+ //
+ if (bits <= 0) {
+ if (bits == 0) return BigInteger.ZERO;
+ throw new IllegalArgumentException("Illegal bit length");
+ }
+ if (top < -1 || top > 1) {
+ throw new IllegalArgumentException("Illegal top value");
+ }
+
+ // top/bottom handling adapted from OpenSSL's crypto/bn/bn_rand.c
+ int bytes = (bits + 7) / 8;
+ int bit = (bits - 1) % 8;
+ int mask = 0xff << (bit + 1);
+
+ byte[] buf;
+ random.nextBytes(buf = new byte[bytes]);
+ if (top >= 0) {
+ if (top == 0) {
+ buf[0] |= (1 << bit);
+ } else {
+ if (bit == 0) {
+ buf[0] = 1;
+ buf[1] |= 0x80;
+ }
+ else {
+ buf[0] |= (3 << (bit - 1));
+ }
+ }
+ }
+ buf[0] &= ~mask;
+ if (bottom) {
+ buf[bytes-1] |= 1;
+ }
+
+ // treating result as unsigned
+ return new BigInteger(1, buf);
+ }
+
+ @JRubyMethod(name="rand_range", meta=true)
+ public static IRubyObject bn_rand_range(IRubyObject recv, IRubyObject arg) {
+ return getRandomBNInRange(recv.getRuntime(), getBigInteger(arg), getSecureRandom());
+ }
+
+ @JRubyMethod(name="pseudo_rand_range", meta=true)
+ public static IRubyObject bn_pseudo_rand_range(IRubyObject recv, IRubyObject arg) {
+ return getRandomBNInRange(recv.getRuntime(), getBigInteger(arg), getRandom());
+ }
+
+ private static BN getRandomBNInRange(Ruby runtime, BigInteger limit, Random random) {
+ BigInteger value;
+ try {
+ value = getRandomBIInRange(limit, random);
+ } catch (IllegalArgumentException e) {
+ throw newBNError(runtime, "illegal range");
+ }
+ return newBN(runtime, value);
+ }
+
+ public static BigInteger getRandomBIInRange(BigInteger limit, Random random) {
+ if (limit.signum() < 0) {
+ throw new IllegalArgumentException("illegal range");
+ }
+ int bits = limit.bitLength();
+ BigInteger value;
+ do {
+ value = new BigInteger(bits, random);
+ } while (value.compareTo(limit) >= 0);
+ return value;
+ }
+
+ private static Random getRandom() {
+ Random rand;
+ if ((rand = _random) != null) {
+ return rand;
+ }
+ return _random = new Random();
+ }
+
+ private static SecureRandom getSecureRandom() {
+ SecureRandom rand;
+ if ((rand = _secureRandom) != null) {
+ return rand;
+ }
+ // FIXME: do we want a particular algorithm / provider? BC?
+ return _secureRandom = new SecureRandom();
+ }
+
+ public static RaiseException newBNError(Ruby runtime, String message) {
+ return new RaiseException(runtime, runtime.getModule("OpenSSL").getClass("BNError"), message, true);
+ }
+
+ public static BigInteger getBigInteger(IRubyObject arg) {
+ if (arg.isNil()) return null;
+ switch(arg.getMetaClass().index) {
+ case ClassIndex.FIXNUM:
+ case ClassIndex.BIGNUM:
+ return new BigInteger(arg.toString());
+ default:
+ if (arg instanceof BN) {
+ return ((BN)arg).value;
+ }
+ throw arg.getRuntime().newTypeError("Cannot convert into OpenSSL::BN");
+ }
+ }
+
+}
diff --git a/src/org/jruby/ext/openssl/BouncyCastlePEMHandler.java b/src/org/jruby/ext/openssl/BouncyCastlePEMHandler.java
new file mode 100644
index 00000000000..0babf5e84b6
--- /dev/null
+++ b/src/org/jruby/ext/openssl/BouncyCastlePEMHandler.java
@@ -0,0 +1,69 @@
+/***** BEGIN LICENSE BLOCK *****
+ * Version: CPL 1.0/GPL 2.0/LGPL 2.1
+ *
+ * The contents of this file are subject to the Common Public
+ * License Version 1.0 (the "License"); you may not use this file
+ * except in compliance with the License. You may obtain a copy of
+ * the License at http://www.eclipse.org/legal/cpl-v10.html
+ *
+ * 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 CPL, 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 CPL, the GPL or the LGPL.
+ ***** END LICENSE BLOCK *****/
+package org.jruby.ext.openssl;
+
+import java.io.Reader;
+import java.io.Writer;
+
+import org.bouncycastle.openssl.PEMReader;
+import org.bouncycastle.openssl.PEMWriter;
+import org.bouncycastle.openssl.PasswordFinder;
+
+/**
+ * @author Ola Bini
+ */
+public class BouncyCastlePEMHandler implements PEMHandler {
+ public Object readPEM(Reader read, String password) throws Exception {
+ return new PEMReader(read,new BasicPasswordFinder(password)).readObject();
+ }
+
+ public void writePEM(Writer writ, Object obj, String algorithm, char[] password) throws Exception {
+ PEMWriter p = new PEMWriter(writ);
+ p.writeObject(obj,algorithm,password,null);
+ p.flush();
+ }
+
+ public void writePEM(Writer writ, Object obj) throws Exception {
+ PEMWriter p = new PEMWriter(writ);
+ p.writeObject(obj);
+ p.flush();
+ }
+
+ private static class BasicPasswordFinder implements PasswordFinder {
+ private char[] pwd;
+ BasicPasswordFinder(String pwd) {
+ if(pwd != null) {
+ this.pwd = pwd.toCharArray();
+ }
+ }
+
+ public char[] getPassword() {
+ return pwd;
+ }
+ }
+}// BouncyCastlePEMHandler
diff --git a/src/org/jruby/ext/openssl/Cipher.java b/src/org/jruby/ext/openssl/Cipher.java
new file mode 100644
index 00000000000..68761630a6b
--- /dev/null
+++ b/src/org/jruby/ext/openssl/Cipher.java
@@ -0,0 +1,756 @@
+/***** BEGIN LICENSE BLOCK *****
+ * Version: CPL 1.0/GPL 2.0/LGPL 2.1
+ *
+ * The contents of this file are subject to the Common Public
+ * License Version 1.0 (the "License"); you may not use this file
+ * except in compliance with the License. You may obtain a copy of
+ * the License at http://www.eclipse.org/legal/cpl-v10.html
+ *
+ * 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, 2007 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 CPL, 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 CPL, the GPL or the LGPL.
+ ***** END LICENSE BLOCK *****/
+package org.jruby.ext.openssl;
+
+import java.security.GeneralSecurityException;
+import java.security.MessageDigest;
+import java.security.NoSuchAlgorithmException;
+import java.util.ArrayList;
+import java.util.HashSet;
+import java.util.List;
+import java.util.Set;
+
+import javax.crypto.spec.IvParameterSpec;
+
+import javax.crypto.spec.RC2ParameterSpec;
+import org.jruby.Ruby;
+import org.jruby.RubyClass;
+import org.jruby.RubyModule;
+import org.jruby.RubyNumeric;
+import org.jruby.RubyObject;
+import org.jruby.common.IRubyWarnings;
+import org.jruby.common.IRubyWarnings.ID;
+import org.jruby.anno.JRubyMethod;
+import org.jruby.anno.JRubyModule;
+import org.jruby.exceptions.RaiseException;
+import org.jruby.runtime.ObjectAllocator;
+import org.jruby.runtime.builtin.IRubyObject;
+import org.jruby.util.ByteList;
+
+/**
+ * @author Ola Bini
+ */
+public class Cipher extends RubyObject {
+ private static final long serialVersionUID = 7727377435222646536L;
+
+ // set to enable debug output
+ private static final boolean DEBUG = false;
+ private static ObjectAllocator CIPHER_ALLOCATOR = new ObjectAllocator() {
+
+ public IRubyObject allocate(Ruby runtime, RubyClass klass) {
+ return new Cipher(runtime, klass);
+ }
+ };
+
+ public static void createCipher(Ruby runtime, RubyModule mOSSL) {
+ RubyClass cCipher = mOSSL.defineClassUnder("Cipher", runtime.getObject(), CIPHER_ALLOCATOR);
+ cCipher.defineAnnotatedMethods(Cipher.class);
+ cCipher.defineAnnotatedMethods(CipherModule.class);
+ RubyClass openSSLError = mOSSL.getClass("OpenSSLError");
+ cCipher.defineClassUnder("CipherError", openSSLError, openSSLError.getAllocator());
+ }
+
+ @JRubyModule(name = "OpenSSL::Cipher")
+ public static class CipherModule {
+
+ @JRubyMethod(meta = true)
+ public static IRubyObject ciphers(IRubyObject recv) {
+ initializeCiphers();
+ List result = new ArrayList();
+ for (String cipher : CIPHERS) {
+ result.add(recv.getRuntime().newString(cipher));
+ result.add(recv.getRuntime().newString(cipher.toLowerCase()));
+ }
+ return recv.getRuntime().newArray(result);
+ }
+
+ static boolean isSupportedCipher(String name) {
+ initializeCiphers();
+ return CIPHERS.indexOf(name.toUpperCase()) != -1;
+ }
+ private static boolean initialized = false;
+ private static final List CIPHERS = new ArrayList();
+
+ private static void initializeCiphers() {
+ synchronized (CIPHERS) {
+ if (initialized) {
+ return;
+ }
+ String[] other = {"AES128", "AES192", "AES256", "BLOWFISH", "RC2-40-CBC", "RC2-64-CBC", "RC4", "RC4-40", "CAST", "CAST-CBC"};
+ String[] bases = {"AES-128", "AES-192", "AES-256", "BF", "DES", "DES-EDE", "DES-EDE3", "RC2", "CAST5"};
+ String[] suffixes = {"", "-CBC", "-CFB", "-CFB1", "-CFB8", "-ECB", "-OFB"};
+ for (int i = 0, j = bases.length; i < j; i++) {
+ for (int k = 0, l = suffixes.length; k < l; k++) {
+ String val = bases[i] + suffixes[k];
+ if (tryCipher(val)) {
+ CIPHERS.add(val.toUpperCase());
+ }
+ }
+ }
+ for (int i = 0, j = other.length; i < j; i++) {
+ if (tryCipher(other[i])) {
+ CIPHERS.add(other[i].toUpperCase());
+ }
+ }
+ initialized = true;
+ }
+ }
+ }
+
+ public static class Algorithm {
+
+ private static final Set BLOCK_MODES;
+
+ static {
+ BLOCK_MODES = new HashSet();
+
+ BLOCK_MODES.add("CBC");
+ BLOCK_MODES.add("CFB");
+ BLOCK_MODES.add("CFB1");
+ BLOCK_MODES.add("CFB8");
+ BLOCK_MODES.add("ECB");
+ BLOCK_MODES.add("OFB");
+ }
+
+ public static String jsseToOssl(String inName, int keyLen) {
+ String cryptoBase = null;
+ String cryptoVersion = null;
+ String cryptoMode = null;
+ String[] parts = inName.split("/");
+ if (parts.length != 1 && parts.length != 3) {
+ return null;
+ }
+ cryptoBase = parts[0];
+ if (parts.length > 2) {
+ cryptoMode = parts[1];
+ // padding: parts[2] is not used
+ }
+ if (!BLOCK_MODES.contains(cryptoMode)) {
+ cryptoVersion = cryptoMode;
+ cryptoMode = "CBC";
+ }
+ if (cryptoMode == null) {
+ cryptoMode = "CBC";
+ }
+ if (cryptoBase.equals("DESede")) {
+ cryptoBase = "DES";
+ cryptoVersion = "EDE3";
+ } else if (cryptoBase.equals("Blowfish")) {
+ cryptoBase = "BF";
+ }
+ if (cryptoVersion == null) {
+ cryptoVersion = String.valueOf(keyLen);
+ }
+ return cryptoBase + "-" + cryptoVersion + "-" + cryptoMode;
+ }
+
+ public static String[] osslToJsse(String inName) {
+ // assume PKCS5Padding
+ return osslToJsse(inName, null);
+ }
+
+ public static String[] osslToJsse(String inName, String padding) {
+ String[] split = inName.split("-");
+ String cryptoBase = split[0];
+ String cryptoVersion = null;
+ String cryptoMode = null;
+ String realName = null;
+
+ String paddingType;
+ if (padding == null || padding.equalsIgnoreCase("PKCS5Padding")) {
+ paddingType = "PKCS5Padding";
+ } else if (padding.equals("0") || padding.equalsIgnoreCase("NoPadding")) {
+ paddingType = "NoPadding";
+ } else if (padding.equalsIgnoreCase("ISO10126Padding")) {
+ paddingType = "ISO10126Padding";
+ } else {
+ paddingType = "PKCS5Padding";
+ }
+
+ if ("bf".equalsIgnoreCase(cryptoBase)) {
+ cryptoBase = "Blowfish";
+ }
+
+ if (split.length == 3) {
+ cryptoVersion = split[1];
+ cryptoMode = split[2];
+ } else if (split.length == 2) {
+ cryptoMode = split[1];
+ } else {
+ cryptoMode = "CBC";
+ }
+
+ if (cryptoBase.equalsIgnoreCase("CAST")) {
+ realName = "CAST5";
+ } else if (cryptoBase.equalsIgnoreCase("DES") && "EDE3".equalsIgnoreCase(cryptoVersion)) {
+ realName = "DESede";
+ } else {
+ realName = cryptoBase;
+ }
+
+ if (!BLOCK_MODES.contains(cryptoMode.toUpperCase())) {
+ cryptoVersion = cryptoMode;
+ cryptoMode = "CBC";
+ } else if (cryptoMode.equalsIgnoreCase("CFB1")) {
+ // uglish SunJCE cryptoMode normalization.
+ cryptoMode = "CFB";
+ }
+
+ if (realName.equalsIgnoreCase("RC4")) {
+ realName = "RC4";
+ cryptoMode = "NONE";
+ paddingType = "NoPadding";
+ } else {
+ realName = realName + "/" + cryptoMode + "/" + paddingType;
+ }
+
+ return new String[]{cryptoBase, cryptoVersion, cryptoMode, realName, paddingType};
+ }
+ }
+
+ private static boolean tryCipher(final String rubyName) {
+ String cryptoMode = Algorithm.osslToJsse(rubyName, null)[3];
+ try {
+ javax.crypto.Cipher.getInstance(cryptoMode);
+ return true;
+ } catch (NoSuchAlgorithmException nsae) {
+ try {
+ OpenSSLReal.getCipherBC(cryptoMode);
+ return true;
+ } catch (GeneralSecurityException gse) {
+ return false;
+ }
+ } catch (Exception e) {
+ return false;
+ }
+ }
+
+ public Cipher(Ruby runtime, RubyClass type) {
+ super(runtime, type);
+ }
+
+ private javax.crypto.Cipher ciph;
+ private String name;
+ private String cryptoBase;
+ private String cryptoVersion;
+ private String cryptoMode;
+ private String padding_type;
+ private String realName;
+ private int keyLen = -1;
+ private int generateKeyLen = -1;
+ private int ivLen = -1;
+ private boolean encryptMode = true;
+ //private IRubyObject[] modeParams;
+ private boolean ciphInited = false;
+ private byte[] key;
+ private byte[] realIV;
+ private byte[] orgIV;
+ private String padding;
+
+ void dumpVars() {
+ System.out.println("***** Cipher instance vars ****");
+ System.out.println("name = " + name);
+ System.out.println("cryptoBase = " + cryptoBase);
+ System.out.println("cryptoVersion = " + cryptoVersion);
+ System.out.println("cryptoMode = " + cryptoMode);
+ System.out.println("padding_type = " + padding_type);
+ System.out.println("realName = " + realName);
+ System.out.println("keyLen = " + keyLen);
+ System.out.println("ivLen = " + ivLen);
+ System.out.println("ciph block size = " + ciph.getBlockSize());
+ System.out.println("encryptMode = " + encryptMode);
+ System.out.println("ciphInited = " + ciphInited);
+ System.out.println("key.length = " + (key == null ? 0 : key.length));
+ System.out.println("iv.length = " + (this.realIV == null ? 0 : this.realIV.length));
+ System.out.println("padding = " + padding);
+ System.out.println("ciphAlgo = " + ciph.getAlgorithm());
+ System.out.println("*******************************");
+ }
+
+ @JRubyMethod(required = 1)
+ public IRubyObject initialize(IRubyObject str) {
+ name = str.toString();
+ if (!CipherModule.isSupportedCipher(name)) {
+ throw newCipherError(getRuntime(), String.format("unsupported cipher algorithm (%s)", name));
+ }
+ String[] values = Algorithm.osslToJsse(name, padding);
+ cryptoBase = values[0];
+ cryptoVersion = values[1];
+ cryptoMode = values[2];
+ realName = values[3];
+ padding_type = values[4];
+ ciph = getCipher();
+
+ if (hasLen(cryptoBase) && null != cryptoVersion) {
+ try {
+ keyLen = Integer.parseInt(cryptoVersion) / 8;
+ } catch (NumberFormatException e) {
+ keyLen = -1;
+ }
+ }
+ if (keyLen == -1) {
+ if ("DES".equalsIgnoreCase(cryptoBase)) {
+ ivLen = 8;
+ if ("EDE3".equalsIgnoreCase(cryptoVersion)) {
+ keyLen = 24;
+ } else {
+ keyLen = 8;
+ }
+ generateKeyLen = keyLen / 8 * 7;
+ } else if ("RC4".equalsIgnoreCase(cryptoBase)) {
+ ivLen = 0;
+ keyLen = 16;
+ } else {
+ keyLen = 16;
+ try {
+ if ((javax.crypto.Cipher.getMaxAllowedKeyLength(name) / 8) < keyLen) {
+ keyLen = javax.crypto.Cipher.getMaxAllowedKeyLength(name) / 8;
+ }
+ } catch (Exception e) {
+ // I hate checked exceptions
+ }
+ }
+ }
+
+ if (ivLen == -1) {
+ if ("AES".equalsIgnoreCase(cryptoBase)) {
+ ivLen = 16;
+ } else {
+ ivLen = 8;
+ }
+ }
+
+ // given 'rc4' must be 'RC4' here. OpenSSL checks it as a LN of object
+ // ID and set SN. We don't check 'name' is allowed as a LN in ASN.1 for
+ // the possibility of JCE specific algorithm so just do upperCase here
+ // for OpenSSL compatibility.
+ name = name.toUpperCase();
+
+ return this;
+ }
+
+ @Override
+ @JRubyMethod(required = 1)
+ public IRubyObject initialize_copy(IRubyObject obj) {
+ if (this == obj) {
+ return this;
+ }
+
+ checkFrozen();
+
+ cryptoBase = ((Cipher) obj).cryptoBase;
+ cryptoVersion = ((Cipher) obj).cryptoVersion;
+ cryptoMode = ((Cipher) obj).cryptoMode;
+ padding_type = ((Cipher) obj).padding_type;
+ realName = ((Cipher) obj).realName;
+ name = ((Cipher) obj).name;
+ keyLen = ((Cipher) obj).keyLen;
+ ivLen = ((Cipher) obj).ivLen;
+ encryptMode = ((Cipher) obj).encryptMode;
+ ciphInited = false;
+ if (((Cipher) obj).key != null) {
+ key = new byte[((Cipher) obj).key.length];
+ System.arraycopy(((Cipher) obj).key, 0, key, 0, key.length);
+ } else {
+ key = null;
+ }
+ if (((Cipher) obj).realIV != null) {
+ this.realIV = new byte[((Cipher) obj).realIV.length];
+ System.arraycopy(((Cipher) obj).realIV, 0, this.realIV, 0, this.realIV.length);
+ } else {
+ this.realIV = null;
+ }
+ this.orgIV = this.realIV;
+ padding = ((Cipher) obj).padding;
+
+ ciph = getCipher();
+
+ return this;
+ }
+
+ @JRubyMethod
+ public IRubyObject name() {
+ return getRuntime().newString(name);
+ }
+
+ @JRubyMethod
+ public IRubyObject key_len() {
+ return getRuntime().newFixnum(keyLen);
+ }
+
+ @JRubyMethod
+ public IRubyObject iv_len() {
+ return getRuntime().newFixnum(ivLen);
+ }
+
+ @JRubyMethod(name = "key_len=", required = 1)
+ public IRubyObject set_key_len(IRubyObject len) {
+ this.keyLen = RubyNumeric.fix2int(len);
+ return len;
+ }
+
+ @JRubyMethod(name = "key=", required = 1)
+ public IRubyObject set_key(IRubyObject key) {
+ byte[] keyBytes;
+ try {
+ keyBytes = key.convertToString().getBytes();
+ } catch (Exception e) {
+ if (DEBUG) {
+ e.printStackTrace();
+ }
+ throw newCipherError(getRuntime(), e.getMessage());
+ }
+ if (keyBytes.length < keyLen) {
+ throw newCipherError(getRuntime(), "key length to short");
+ }
+
+ if (keyBytes.length > keyLen) {
+ byte[] keys = new byte[keyLen];
+ System.arraycopy(keyBytes, 0, keys, 0, keyLen);
+ keyBytes = keys;
+ }
+
+ this.key = keyBytes;
+ return key;
+ }
+
+ @JRubyMethod(name = "iv=", required = 1)
+ public IRubyObject set_iv(IRubyObject iv) {
+ byte[] ivBytes;
+ try {
+ ivBytes = iv.convertToString().getBytes();
+ } catch (Exception e) {
+ if (DEBUG) {
+ e.printStackTrace();
+ }
+ throw newCipherError(getRuntime(), e.getMessage());
+ }
+ if (ivBytes.length < ivLen) {
+ throw newCipherError(getRuntime(), "iv length to short");
+ } else {
+ // EVP_CipherInit_ex uses leading IV length of given sequence.
+ byte[] iv2 = new byte[ivLen];
+ System.arraycopy(ivBytes, 0, iv2, 0, ivLen);
+ this.realIV = iv2;
+ }
+ this.orgIV = this.realIV;
+ if (!isStreamCipher()) {
+ ciphInited = false;
+ }
+ return iv;
+ }
+
+ @JRubyMethod
+ public IRubyObject block_size() {
+ if (isStreamCipher()) {
+ // getBlockSize() returns 0 for stream cipher in JCE. OpenSSL returns 1 for RC4.
+ return getRuntime().newFixnum(1);
+ }
+ return getRuntime().newFixnum(ciph.getBlockSize());
+ }
+
+ protected void init(IRubyObject[] args, boolean encrypt) {
+ org.jruby.runtime.Arity.checkArgumentCount(getRuntime(), args, 0, 2);
+
+ encryptMode = encrypt;
+ ciphInited = false;
+
+ if (args.length > 0) {
+ /*
+ * oops. this code mistakes salt for IV.
+ * We deprecated the arguments for this method, but we decided
+ * keeping this behaviour for backward compatibility.
+ */
+ byte[] pass = args[0].convertToString().getBytes();
+ byte[] iv = null;
+ try {
+ iv = "OpenSSL for Ruby rulez!".getBytes("ISO8859-1");
+ byte[] iv2 = new byte[this.ivLen];
+ System.arraycopy(iv, 0, iv2, 0, this.ivLen);
+ iv = iv2;
+ } catch (Exception e) {
+ }
+
+ if (args.length > 1 && !args[1].isNil()) {
+ getRuntime().getWarnings().warning(ID.MISCELLANEOUS, "key derivation by " + getMetaClass().getRealClass().getName() + "#encrypt is deprecated; use " + getMetaClass().getRealClass().getName() + "::pkcs5_keyivgen instead");
+ iv = args[1].convertToString().getBytes();
+ if (iv.length > this.ivLen) {
+ byte[] iv2 = new byte[this.ivLen];
+ System.arraycopy(iv, 0, iv2, 0, this.ivLen);
+ iv = iv2;
+ }
+ }
+
+ MessageDigest digest = Digest.getDigest("MD5", getRuntime());
+ OpenSSLImpl.KeyAndIv result = OpenSSLImpl.EVP_BytesToKey(keyLen, ivLen, digest, iv, pass, 2048);
+ this.key = result.getKey();
+ this.realIV = iv;
+ this.orgIV = this.realIV;
+ }
+ }
+
+ @JRubyMethod(optional = 2)
+ public IRubyObject encrypt(IRubyObject[] args) {
+ this.realIV = orgIV;
+ init(args, true);
+ return this;
+ }
+
+ @JRubyMethod(optional = 2)
+ public IRubyObject decrypt(IRubyObject[] args) {
+ this.realIV = orgIV;
+ init(args, false);
+ return this;
+ }
+
+ @JRubyMethod
+ public IRubyObject reset() {
+ if (!isStreamCipher()) {
+ this.realIV = orgIV;
+ doInitialize();
+ }
+ return this;
+ }
+
+ javax.crypto.Cipher getCipher() {
+ try {
+ return javax.crypto.Cipher.getInstance(realName);
+ } catch (NoSuchAlgorithmException e) {
+ try {
+ return OpenSSLReal.getCipherBC(realName);
+ } catch (GeneralSecurityException ignore) {
+ }
+ throw newCipherError(getRuntime(), "unsupported cipher algorithm (" + realName + ")");
+ } catch (javax.crypto.NoSuchPaddingException e) {
+ throw newCipherError(getRuntime(), "unsupported cipher padding (" + realName + ")");
+ }
+ }
+
+ private static boolean hasLen(String cryptoBase) {
+ return "AES".equalsIgnoreCase(cryptoBase) || "RC2".equalsIgnoreCase(cryptoBase) || "RC4".equalsIgnoreCase(cryptoBase);
+ }
+
+ @JRubyMethod(required = 1, optional = 3)
+ public IRubyObject pkcs5_keyivgen(IRubyObject[] args) {
+ org.jruby.runtime.Arity.checkArgumentCount(getRuntime(), args, 1, 4);
+ byte[] pass = args[0].convertToString().getBytes();
+ byte[] salt = null;
+ int iter = 2048;
+ IRubyObject vdigest = getRuntime().getNil();
+ if (args.length > 1) {
+ if (!args[1].isNil()) {
+ salt = args[1].convertToString().getBytes();
+ }
+ if (args.length > 2) {
+ if (!args[2].isNil()) {
+ iter = RubyNumeric.fix2int(args[2]);
+ }
+ if (args.length > 3) {
+ vdigest = args[3];
+ }
+ }
+ }
+ if (null != salt) {
+ if (salt.length != 8) {
+ throw newCipherError(getRuntime(), "salt must be an 8-octet string");
+ }
+ }
+
+ final String algorithm = vdigest.isNil() ? "MD5" : ((Digest) vdigest).getAlgorithm();
+ MessageDigest digest = Digest.getDigest(algorithm, getRuntime());
+ OpenSSLImpl.KeyAndIv result = OpenSSLImpl.EVP_BytesToKey(keyLen, ivLen, digest, salt, pass, iter);
+ this.key = result.getKey();
+ this.realIV = result.getIv();
+ this.orgIV = this.realIV;
+
+ doInitialize();
+
+ return getRuntime().getNil();
+ }
+
+ private void doInitialize() {
+ if (DEBUG) {
+ System.out.println("*** doInitialize");
+ dumpVars();
+ }
+ ciphInited = true;
+ try {
+ assert (key.length * 8 == keyLen) || (key.length == keyLen) : "Key wrong length";
+ assert (this.realIV.length * 8 == ivLen) || (this.realIV.length == ivLen) : "IV wrong length";
+ if (!"ECB".equalsIgnoreCase(cryptoMode)) {
+ if (this.realIV == null) {
+ this.realIV = new byte[ivLen];
+ System.arraycopy("OpenSSL for JRuby rulez".getBytes(), 0,
+ this.realIV, 0, ivLen);
+ }
+ if ("RC2".equalsIgnoreCase(cryptoBase)) {
+ this.ciph.init(encryptMode ? javax.crypto.Cipher.ENCRYPT_MODE : javax.crypto.Cipher.DECRYPT_MODE, new SimpleSecretKey("RC2", this.key), new RC2ParameterSpec(this.key.length * 8, this.realIV));
+ } else if ("RC4".equalsIgnoreCase(cryptoBase)) {
+ this.ciph.init(encryptMode ? javax.crypto.Cipher.ENCRYPT_MODE : javax.crypto.Cipher.DECRYPT_MODE, new SimpleSecretKey("RC4", this.key));
+ } else {
+ this.ciph.init(encryptMode ? javax.crypto.Cipher.ENCRYPT_MODE : javax.crypto.Cipher.DECRYPT_MODE, new SimpleSecretKey(realName.split("/")[0], this.key), new IvParameterSpec(this.realIV));
+ }
+ } else {
+ this.ciph.init(encryptMode ? javax.crypto.Cipher.ENCRYPT_MODE : javax.crypto.Cipher.DECRYPT_MODE, new SimpleSecretKey(realName.split("/")[0], this.key));
+ }
+ } catch (Exception e) {
+ if (DEBUG) {
+ e.printStackTrace();
+ }
+ throw newCipherError(getRuntime(), e.getMessage());
+ }
+ }
+ private byte[] lastIv = null;
+
+ @JRubyMethod
+ public IRubyObject update(IRubyObject data) {
+ if (DEBUG) {
+ System.out.println("*** update [" + data + "]");
+ }
+
+ byte[] val = data.convertToString().getBytes();
+ if (val.length == 0) {
+ throw getRuntime().newArgumentError("data must not be empty");
+ }
+
+ if (!ciphInited) {
+ if (DEBUG) {
+ System.out.println("BEFORE INITING");
+ }
+ doInitialize();
+ if (DEBUG) {
+ System.out.println("AFTER INITING");
+ }
+ }
+
+ byte[] str = new byte[0];
+ try {
+ byte[] out = ciph.update(val);
+ if (out != null) {
+ str = out;
+
+ if (this.realIV != null) {
+ if (lastIv == null) {
+ lastIv = new byte[ivLen];
+ }
+ byte[] tmpIv = encryptMode ? out : val;
+ if (tmpIv.length >= ivLen) {
+ System.arraycopy(tmpIv, tmpIv.length - ivLen, lastIv, 0, ivLen);
+ }
+ }
+ }
+ } catch (Exception e) {
+ if (DEBUG) {
+ e.printStackTrace();
+ }
+ throw newCipherError(getRuntime(), e.getMessage());
+ }
+
+ return getRuntime().newString(new ByteList(str, false));
+ }
+
+ @JRubyMethod(name = "<<")
+ public IRubyObject update_deprecated(IRubyObject data) {
+ getRuntime().getWarnings().warn(IRubyWarnings.ID.DEPRECATED_METHOD, "" + this.getMetaClass().getRealClass().getName() + "#<< is deprecated; use " + this.getMetaClass().getRealClass().getName() + "#update instead");
+ return update(data);
+ }
+
+ @JRubyMethod(name = "final")
+ public IRubyObject _final() {
+ if (!ciphInited) {
+ doInitialize();
+ }
+ // trying to allow update after final like cruby-openssl. Bad idea.
+ if ("RC4".equalsIgnoreCase(cryptoBase)) {
+ return getRuntime().newString("");
+ }
+ ByteList str = new ByteList(ByteList.NULL_ARRAY);
+ try {
+ byte[] out = ciph.doFinal();
+ if (out != null) {
+ str = new ByteList(out, false);
+ // TODO: Modifying this line appears to fix the issue, but I do
+ // not have a good reason for why. Best I can tell, lastIv needs
+ // to be set regardless of encryptMode, so we'll go with this
+ // for now. JRUBY-3335.
+ //if(this.realIV != null && encryptMode) {
+ if (this.realIV != null) {
+ if (lastIv == null) {
+ lastIv = new byte[ivLen];
+ }
+ byte[] tmpIv = out;
+ if (tmpIv.length >= ivLen) {
+ System.arraycopy(tmpIv, tmpIv.length - ivLen, lastIv, 0, ivLen);
+ }
+ }
+ }
+ if (this.realIV != null) {
+ this.realIV = lastIv;
+ doInitialize();
+ }
+ } catch (Exception e) {
+ throw newCipherError(getRuntime(), e.getMessage());
+ }
+ return getRuntime().newString(str);
+ }
+
+ @JRubyMethod(name = "padding=")
+ public IRubyObject set_padding(IRubyObject padding) {
+ this.padding = padding.toString();
+ initialize(getRuntime().newString(name));
+ return padding;
+ }
+
+ String getAlgorithm() {
+ return this.ciph.getAlgorithm();
+ }
+
+ String getName() {
+ return this.name;
+ }
+
+ String getCryptoBase() {
+ return this.cryptoBase;
+ }
+
+ String getCryptoMode() {
+ return this.cryptoMode;
+ }
+
+ int getGenerateKeyLen() {
+ return (generateKeyLen == -1) ? keyLen : generateKeyLen;
+ }
+
+ private boolean isStreamCipher() {
+ return ciph.getBlockSize() == 0;
+ }
+
+ private static RaiseException newCipherError(Ruby runtime, String message) {
+ return Utils.newError(runtime, "OpenSSL::Cipher::CipherError", message);
+ }
+}
diff --git a/src/org/jruby/ext/openssl/CipherStrings.java b/src/org/jruby/ext/openssl/CipherStrings.java
new file mode 100644
index 00000000000..15cb992cead
--- /dev/null
+++ b/src/org/jruby/ext/openssl/CipherStrings.java
@@ -0,0 +1,1856 @@
+/***** BEGIN LICENSE BLOCK *****
+ * Version: CPL 1.0/GPL 2.0/LGPL 2.1
+ *
+ * The contents of this file are subject to the Common Public
+ * License Version 1.0 (the "License"); you may not use this file
+ * except in compliance with the License. You may obtain a copy of
+ * the License at http://www.eclipse.org/legal/cpl-v10.html
+ *
+ * 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) 2008 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 CPL, 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 CPL, the GPL or the LGPL.
+ ***** END LICENSE BLOCK *****/
+package org.jruby.ext.openssl;
+
+import java.util.ArrayList;
+import java.util.Collections;
+import java.util.Comparator;
+import java.util.HashMap;
+import java.util.HashSet;
+import java.util.List;
+import java.util.Map;
+import java.util.Set;
+
+/**
+ *
+ * @author Ola Bini
+ */
+public class CipherStrings {
+ public final static String SSL2_TXT_DES_64_CFB64_WITH_MD5_1 = "DES-CFB-M1";
+ public final static String SSL2_TXT_NULL_WITH_MD5 = "NULL-MD5";
+ public final static String SSL2_TXT_RC4_128_WITH_MD5 = "RC4-MD5";
+ public final static String SSL2_TXT_RC4_128_EXPORT40_WITH_MD5 = "EXP-RC4-MD5";
+ public final static String SSL2_TXT_RC2_128_CBC_WITH_MD5 = "RC2-CBC-MD5";
+ public final static String SSL2_TXT_RC2_128_CBC_EXPORT40_WITH_MD5 = "EXP-RC2-CBC-MD5";
+ public final static String SSL2_TXT_IDEA_128_CBC_WITH_MD5 = "IDEA-CBC-MD5";
+ public final static String SSL2_TXT_DES_64_CBC_WITH_MD5 = "DES-CBC-MD5";
+ public final static String SSL2_TXT_DES_64_CBC_WITH_SHA = "DES-CBC-SHA";
+ public final static String SSL2_TXT_DES_192_EDE3_CBC_WITH_MD5 = "DES-CBC3-MD5";
+ public final static String SSL2_TXT_DES_192_EDE3_CBC_WITH_SHA = "DES-CBC3-SHA";
+ public final static String SSL2_TXT_RC4_64_WITH_MD5 = "RC4-64-MD5";
+ public final static String SSL2_TXT_NULL = "NULL";
+
+ public final static String SSL3_TXT_RSA_NULL_MD5 = "NULL-MD5";
+ public final static String SSL3_TXT_RSA_NULL_SHA = "NULL-SHA";
+ public final static String SSL3_TXT_RSA_RC4_40_MD5 = "EXP-RC4-MD5";
+ public final static String SSL3_TXT_RSA_RC4_128_MD5 = "RC4-MD5";
+ public final static String SSL3_TXT_RSA_RC4_128_SHA = "RC4-SHA";
+ public final static String SSL3_TXT_RSA_RC2_40_MD5 = "EXP-RC2-CBC-MD5";
+ public final static String SSL3_TXT_RSA_IDEA_128_SHA = "IDEA-CBC-SHA";
+ public final static String SSL3_TXT_RSA_DES_40_CBC_SHA = "EXP-DES-CBC-SHA";
+ public final static String SSL3_TXT_RSA_DES_64_CBC_SHA = "DES-CBC-SHA";
+ public final static String SSL3_TXT_RSA_DES_192_CBC3_SHA = "DES-CBC3-SHA";
+ public final static String SSL3_TXT_DH_DSS_DES_40_CBC_SHA = "EXP-DH-DSS-DES-CBC-SHA";
+ public final static String SSL3_TXT_DH_DSS_DES_64_CBC_SHA = "DH-DSS-DES-CBC-SHA";
+ public final static String SSL3_TXT_DH_DSS_DES_192_CBC3_SHA = "DH-DSS-DES-CBC3-SHA";
+ public final static String SSL3_TXT_DH_RSA_DES_40_CBC_SHA = "EXP-DH-RSA-DES-CBC-SHA";
+ public final static String SSL3_TXT_DH_RSA_DES_64_CBC_SHA = "DH-RSA-DES-CBC-SHA";
+ public final static String SSL3_TXT_DH_RSA_DES_192_CBC3_SHA = "DH-RSA-DES-CBC3-SHA";
+ public final static String SSL3_TXT_EDH_DSS_DES_40_CBC_SHA = "EXP-EDH-DSS-DES-CBC-SHA";
+ public final static String SSL3_TXT_EDH_DSS_DES_64_CBC_SHA = "EDH-DSS-DES-CBC-SHA";
+ public final static String SSL3_TXT_EDH_DSS_DES_192_CBC3_SHA = "EDH-DSS-DES-CBC3-SHA";
+ public final static String SSL3_TXT_EDH_RSA_DES_40_CBC_SHA = "EXP-EDH-RSA-DES-CBC-SHA";
+ public final static String SSL3_TXT_EDH_RSA_DES_64_CBC_SHA = "EDH-RSA-DES-CBC-SHA";
+ public final static String SSL3_TXT_EDH_RSA_DES_192_CBC3_SHA = "EDH-RSA-DES-CBC3-SHA";
+ public final static String SSL3_TXT_ADH_RC4_40_MD5 = "EXP-ADH-RC4-MD5";
+ public final static String SSL3_TXT_ADH_RC4_128_MD5 = "ADH-RC4-MD5";
+ public final static String SSL3_TXT_ADH_DES_40_CBC_SHA = "EXP-ADH-DES-CBC-SHA";
+ public final static String SSL3_TXT_ADH_DES_64_CBC_SHA = "ADH-DES-CBC-SHA";
+ public final static String SSL3_TXT_ADH_DES_192_CBC_SHA = "ADH-DES-CBC3-SHA";
+ public final static String SSL3_TXT_FZA_DMS_NULL_SHA = "FZA-NULL-SHA";
+ public final static String SSL3_TXT_FZA_DMS_FZA_SHA = "FZA-FZA-CBC-SHA";
+ public final static String SSL3_TXT_FZA_DMS_RC4_SHA = "FZA-RC4-SHA";
+ public final static String SSL3_TXT_KRB5_DES_64_CBC_SHA = "KRB5-DES-CBC-SHA";
+ public final static String SSL3_TXT_KRB5_DES_192_CBC3_SHA = "KRB5-DES-CBC3-SHA";
+ public final static String SSL3_TXT_KRB5_RC4_128_SHA = "KRB5-RC4-SHA";
+ public final static String SSL3_TXT_KRB5_IDEA_128_CBC_SHA = "KRB5-IDEA-CBC-SHA";
+ public final static String SSL3_TXT_KRB5_DES_64_CBC_MD5 = "KRB5-DES-CBC-MD5";
+ public final static String SSL3_TXT_KRB5_DES_192_CBC3_MD5 = "KRB5-DES-CBC3-MD5";
+ public final static String SSL3_TXT_KRB5_RC4_128_MD5 = "KRB5-RC4-MD5";
+ public final static String SSL3_TXT_KRB5_IDEA_128_CBC_MD5 = "KRB5-IDEA-CBC-MD5";
+ public final static String SSL3_TXT_KRB5_DES_40_CBC_SHA = "EXP-KRB5-DES-CBC-SHA";
+ public final static String SSL3_TXT_KRB5_RC2_40_CBC_SHA = "EXP-KRB5-RC2-CBC-SHA";
+ public final static String SSL3_TXT_KRB5_RC4_40_SHA = "EXP-KRB5-RC4-SHA";
+ public final static String SSL3_TXT_KRB5_DES_40_CBC_MD5 = "EXP-KRB5-DES-CBC-MD5";
+ public final static String SSL3_TXT_KRB5_RC2_40_CBC_MD5 = "EXP-KRB5-RC2-CBC-MD5";
+ public final static String SSL3_TXT_KRB5_RC4_40_MD5 = "EXP-KRB5-RC4-MD5";
+
+ public final static String SSL_TXT_NULL_WITH_MD5 = SSL2_TXT_NULL_WITH_MD5;
+ public final static String SSL_TXT_RC4_128_WITH_MD5 = SSL2_TXT_RC4_128_WITH_MD5;
+ public final static String SSL_TXT_RC4_128_EXPORT40_WITH_MD5 = SSL2_TXT_RC4_128_EXPORT40_WITH_MD5;
+ public final static String SSL_TXT_RC2_128_CBC_WITH_MD5 = SSL2_TXT_RC2_128_CBC_WITH_MD5;
+ public final static String SSL_TXT_RC2_128_CBC_EXPORT40_WITH_MD5 = SSL2_TXT_RC2_128_CBC_EXPORT40_WITH_MD5;
+ public final static String SSL_TXT_IDEA_128_CBC_WITH_MD5 = SSL2_TXT_IDEA_128_CBC_WITH_MD5;
+ public final static String SSL_TXT_DES_64_CBC_WITH_MD5 = SSL2_TXT_DES_64_CBC_WITH_MD5;
+ public final static String SSL_TXT_DES_64_CBC_WITH_SHA = SSL2_TXT_DES_64_CBC_WITH_SHA;
+ public final static String SSL_TXT_DES_192_EDE3_CBC_WITH_MD5 = SSL2_TXT_DES_192_EDE3_CBC_WITH_MD5;
+ public final static String SSL_TXT_DES_192_EDE3_CBC_WITH_SHA = SSL2_TXT_DES_192_EDE3_CBC_WITH_SHA;
+
+ public final static String SSL_TXT_KRB5_DES_64_CBC_SHA = SSL3_TXT_KRB5_DES_64_CBC_SHA;
+ public final static String SSL_TXT_KRB5_DES_192_CBC3_SHA = SSL3_TXT_KRB5_DES_192_CBC3_SHA;
+ public final static String SSL_TXT_KRB5_RC4_128_SHA = SSL3_TXT_KRB5_RC4_128_SHA;
+ public final static String SSL_TXT_KRB5_IDEA_128_CBC_SHA = SSL3_TXT_KRB5_IDEA_128_CBC_SHA;
+ public final static String SSL_TXT_KRB5_DES_64_CBC_MD5 = SSL3_TXT_KRB5_DES_64_CBC_MD5;
+ public final static String SSL_TXT_KRB5_DES_192_CBC3_MD5 = SSL3_TXT_KRB5_DES_192_CBC3_MD5;
+ public final static String SSL_TXT_KRB5_RC4_128_MD5 = SSL3_TXT_KRB5_RC4_128_MD5;
+ public final static String SSL_TXT_KRB5_IDEA_128_CBC_MD5 = SSL3_TXT_KRB5_IDEA_128_CBC_MD5;
+
+ public final static String SSL_TXT_KRB5_DES_40_CBC_SHA = SSL3_TXT_KRB5_DES_40_CBC_SHA;
+ public final static String SSL_TXT_KRB5_RC2_40_CBC_SHA = SSL3_TXT_KRB5_RC2_40_CBC_SHA;
+ public final static String SSL_TXT_KRB5_RC4_40_SHA = SSL3_TXT_KRB5_RC4_40_SHA;
+ public final static String SSL_TXT_KRB5_DES_40_CBC_MD5 = SSL3_TXT_KRB5_DES_40_CBC_MD5;
+ public final static String SSL_TXT_KRB5_RC2_40_CBC_MD5 = SSL3_TXT_KRB5_RC2_40_CBC_MD5;
+ public final static String SSL_TXT_KRB5_RC4_40_MD5 = SSL3_TXT_KRB5_RC4_40_MD5;
+
+ public final static String SSL_TXT_LOW = "LOW";
+ public final static String SSL_TXT_MEDIUM = "MEDIUM";
+ public final static String SSL_TXT_HIGH = "HIGH";
+ public final static String SSL_TXT_kFZA = "kFZA";
+ public final static String SSL_TXT_aFZA = "aFZA";
+ public final static String SSL_TXT_eFZA = "eFZA";
+ public final static String SSL_TXT_FZA = "FZA";
+
+ public final static String SSL_TXT_aNULL = "aNULL";
+ public final static String SSL_TXT_eNULL = "eNULL";
+ public final static String SSL_TXT_NULL = "NULL";
+
+ public final static String SSL_TXT_kKRB5 = "kKRB5";
+ public final static String SSL_TXT_aKRB5 = "aKRB5";
+ public final static String SSL_TXT_KRB5 = "KRB5";
+
+ 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_aRSA = "aRSA";
+ public final static String SSL_TXT_aDSS = "aDSS";
+ public final static String SSL_TXT_aDH = "aDH";
+ 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_ADH = "ADH";
+ public final static String SSL_TXT_RSA = "RSA";
+ 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_AES = "AES";
+ 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_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_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";
+
+ public final static long SSL_MKEY_MASK = 0x000000FFL;
+ 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;
+ public final static long SSL_kKRB5 = 0x00000020L;
+ public final static long SSL_kECDH = 0x00000040L;
+ public final static long SSL_kECDHE = 0x00000080L;
+ 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));
+ public final static long SSL_aRSA = 0x00000100L;
+ public final static long SSL_aDSS = 0x00000200L;
+ public final static long SSL_DSS = SSL_aDSS;
+ public final static long SSL_aFZA = 0x00000400L;
+ 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);
+ public final static long SSL_ECDH = (SSL_kECDH|SSL_kECDHE);
+ 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;
+ public final static long SSL_MAC_MASK = 0x00c00000L;
+ public final static long SSL_MD5 = 0x00400000L;
+ public final static long SSL_SHA1 = 0x00800000L;
+ public final static long SSL_SHA = (SSL_SHA1);
+ public final static long SSL_SSL_MASK = 0x03000000L;
+ public final static long SSL_SSLV2 = 0x01000000L;
+ public final static long SSL_SSLV3 = 0x02000000L;
+ public final static long SSL_TLSV1 = SSL_SSLV3;
+ public final static long SSL_EXP_MASK = 0x00000003L;
+ public final static long SSL_NOT_EXP = 0x00000001L;
+ public final static long SSL_EXPORT = 0x00000002L;
+ public final static long SSL_STRONG_MASK = 0x000000fcL;
+ public final static long SSL_STRONG_NONE = 0x00000004L;
+ public final static long SSL_EXP40 = 0x00000008L;
+ 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;
+ public final static long SSL_ALL_CIPHERS = (SSL_MKEY_MASK|SSL_AUTH_MASK|SSL_ENC_MASK|SSL_MAC_MASK);
+ 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;
+ public final static long SSL_PKEY_DSA_SIGN = 2;
+ public final static long SSL_PKEY_DH_RSA = 3;
+ public final static long SSL_PKEY_DH_DSA = 4;
+ public final static long SSL_PKEY_ECC = 5;
+ public final static long SSL_PKEY_NUM = 6;
+
+ public final static long SSL3_CK_RSA_NULL_MD5 = 0x03000001;
+ public final static long SSL3_CK_RSA_NULL_SHA = 0x03000002;
+ public final static long SSL3_CK_RSA_RC4_40_MD5 = 0x03000003;
+ public final static long SSL3_CK_RSA_RC4_128_MD5 = 0x03000004;
+ public final static long SSL3_CK_RSA_RC4_128_SHA = 0x03000005;
+ public final static long SSL3_CK_RSA_RC2_40_MD5 = 0x03000006;
+ public final static long SSL3_CK_RSA_IDEA_128_SHA = 0x03000007;
+ public final static long SSL3_CK_RSA_DES_40_CBC_SHA = 0x03000008;
+ public final static long SSL3_CK_RSA_DES_64_CBC_SHA = 0x03000009;
+ public final static long SSL3_CK_RSA_DES_192_CBC3_SHA = 0x0300000A;
+ public final static long SSL3_CK_DH_DSS_DES_40_CBC_SHA = 0x0300000B;
+ public final static long SSL3_CK_DH_DSS_DES_64_CBC_SHA = 0x0300000C;
+ public final static long SSL3_CK_DH_DSS_DES_192_CBC3_SHA = 0x0300000D;
+ public final static long SSL3_CK_DH_RSA_DES_40_CBC_SHA = 0x0300000E;
+ public final static long SSL3_CK_DH_RSA_DES_64_CBC_SHA = 0x0300000F;
+ public final static long SSL3_CK_DH_RSA_DES_192_CBC3_SHA = 0x03000010;
+ public final static long SSL3_CK_EDH_DSS_DES_40_CBC_SHA = 0x03000011;
+ public final static long SSL3_CK_EDH_DSS_DES_64_CBC_SHA = 0x03000012;
+ public final static long SSL3_CK_EDH_DSS_DES_192_CBC3_SHA = 0x03000013;
+ public final static long SSL3_CK_EDH_RSA_DES_40_CBC_SHA = 0x03000014;
+ public final static long SSL3_CK_EDH_RSA_DES_64_CBC_SHA = 0x03000015;
+ public final static long SSL3_CK_EDH_RSA_DES_192_CBC3_SHA = 0x03000016;
+ public final static long SSL3_CK_ADH_RC4_40_MD5 = 0x03000017;
+ public final static long SSL3_CK_ADH_RC4_128_MD5 = 0x03000018;
+ public final static long SSL3_CK_ADH_DES_40_CBC_SHA = 0x03000019;
+ public final static long SSL3_CK_ADH_DES_64_CBC_SHA = 0x0300001A;
+ public final static long SSL3_CK_ADH_DES_192_CBC_SHA = 0x0300001B;
+ public final static long SSL3_CK_FZA_DMS_NULL_SHA = 0x0300001C;
+ public final static long SSL3_CK_FZA_DMS_FZA_SHA = 0x0300001D;
+ public final static long SSL3_CK_KRB5_DES_64_CBC_SHA = 0x0300001E;
+ public final static long SSL3_CK_KRB5_DES_192_CBC3_SHA = 0x0300001F;
+ public final static long SSL3_CK_KRB5_RC4_128_SHA = 0x03000020;
+ public final static long SSL3_CK_KRB5_IDEA_128_CBC_SHA = 0x03000021;
+ public final static long SSL3_CK_KRB5_DES_64_CBC_MD5 = 0x03000022;
+ public final static long SSL3_CK_KRB5_DES_192_CBC3_MD5 = 0x03000023;
+ public final static long SSL3_CK_KRB5_RC4_128_MD5 = 0x03000024;
+ public final static long SSL3_CK_KRB5_IDEA_128_CBC_MD5 = 0x03000025;
+ public final static long SSL3_CK_KRB5_DES_40_CBC_SHA = 0x03000026;
+ public final static long SSL3_CK_KRB5_RC2_40_CBC_SHA = 0x03000027;
+ public final static long SSL3_CK_KRB5_RC4_40_SHA = 0x03000028;
+ public final static long SSL3_CK_KRB5_DES_40_CBC_MD5 = 0x03000029;
+ public final static long SSL3_CK_KRB5_RC2_40_CBC_MD5 = 0x0300002A;
+ public final static long SSL3_CK_KRB5_RC4_40_MD5 = 0x0300002B;
+
+
+ public final static long TLS1_CK_RSA_EXPORT1024_WITH_RC4_56_MD5 = 0x03000060;
+ public final static long TLS1_CK_RSA_EXPORT1024_WITH_RC2_CBC_56_MD5 = 0x03000061;
+ public final static long TLS1_CK_RSA_EXPORT1024_WITH_DES_CBC_SHA = 0x03000062;
+ public final static long TLS1_CK_DHE_DSS_EXPORT1024_WITH_DES_CBC_SHA = 0x03000063;
+ public final static long TLS1_CK_RSA_EXPORT1024_WITH_RC4_56_SHA = 0x03000064;
+ public final static long TLS1_CK_DHE_DSS_EXPORT1024_WITH_RC4_56_SHA = 0x03000065;
+ public final static long TLS1_CK_DHE_DSS_WITH_RC4_128_SHA = 0x03000066;
+ public final static long TLS1_CK_RSA_WITH_AES_128_SHA = 0x0300002F;
+ public final static long TLS1_CK_DH_DSS_WITH_AES_128_SHA = 0x03000030;
+ public final static long TLS1_CK_DH_RSA_WITH_AES_128_SHA = 0x03000031;
+ public final static long TLS1_CK_DHE_DSS_WITH_AES_128_SHA = 0x03000032;
+ public final static long TLS1_CK_DHE_RSA_WITH_AES_128_SHA = 0x03000033;
+ public final static long TLS1_CK_ADH_WITH_AES_128_SHA = 0x03000034;
+ public final static long TLS1_CK_RSA_WITH_AES_256_SHA = 0x03000035;
+ public final static long TLS1_CK_DH_DSS_WITH_AES_256_SHA = 0x03000036;
+ public final static long TLS1_CK_DH_RSA_WITH_AES_256_SHA = 0x03000037;
+ public final static long TLS1_CK_DHE_DSS_WITH_AES_256_SHA = 0x03000038;
+ public final static long TLS1_CK_DHE_RSA_WITH_AES_256_SHA = 0x03000039;
+ public final static long TLS1_CK_ADH_WITH_AES_256_SHA = 0x0300003A;
+ public final static long TLS1_CK_ECDH_ECDSA_WITH_NULL_SHA = 0x0300C001;
+ public final static long TLS1_CK_ECDH_ECDSA_WITH_RC4_128_SHA = 0x0300C002;
+ public final static long TLS1_CK_ECDH_ECDSA_WITH_DES_192_CBC3_SHA = 0x0300C003;
+ public final static long TLS1_CK_ECDH_ECDSA_WITH_AES_128_CBC_SHA = 0x0300C004;
+ public final static long TLS1_CK_ECDH_ECDSA_WITH_AES_256_CBC_SHA = 0x0300C005;
+ public final static long TLS1_CK_ECDHE_ECDSA_WITH_NULL_SHA = 0x0300C006;
+ public final static long TLS1_CK_ECDHE_ECDSA_WITH_RC4_128_SHA = 0x0300C007;
+ public final static long TLS1_CK_ECDHE_ECDSA_WITH_DES_192_CBC3_SHA = 0x0300C008;
+ public final static long TLS1_CK_ECDHE_ECDSA_WITH_AES_128_CBC_SHA = 0x0300C009;
+ public final static long TLS1_CK_ECDHE_ECDSA_WITH_AES_256_CBC_SHA = 0x0300C00A;
+ public final static long TLS1_CK_ECDH_RSA_WITH_NULL_SHA = 0x0300C00B;
+ public final static long TLS1_CK_ECDH_RSA_WITH_RC4_128_SHA = 0x0300C00C;
+ public final static long TLS1_CK_ECDH_RSA_WITH_DES_192_CBC3_SHA = 0x0300C00D;
+ public final static long TLS1_CK_ECDH_RSA_WITH_AES_128_CBC_SHA = 0x0300C00E;
+ public final static long TLS1_CK_ECDH_RSA_WITH_AES_256_CBC_SHA = 0x0300C00F;
+ public final static long TLS1_CK_ECDHE_RSA_WITH_NULL_SHA = 0x0300C010;
+ public final static long TLS1_CK_ECDHE_RSA_WITH_RC4_128_SHA = 0x0300C011;
+ public final static long TLS1_CK_ECDHE_RSA_WITH_DES_192_CBC3_SHA = 0x0300C012;
+ public final static long TLS1_CK_ECDHE_RSA_WITH_AES_128_CBC_SHA = 0x0300C013;
+ public final static long TLS1_CK_ECDHE_RSA_WITH_AES_256_CBC_SHA = 0x0300C014;
+ public final static long TLS1_CK_ECDH_anon_WITH_NULL_SHA = 0x0300C015;
+ public final static long TLS1_CK_ECDH_anon_WITH_RC4_128_SHA = 0x0300C016;
+ public final static long TLS1_CK_ECDH_anon_WITH_DES_192_CBC3_SHA = 0x0300C017;
+ public final static long TLS1_CK_ECDH_anon_WITH_AES_128_CBC_SHA = 0x0300C018;
+ public final static long TLS1_CK_ECDH_anon_WITH_AES_256_CBC_SHA = 0x0300C019;
+
+ public final static String TLS1_TXT_RSA_EXPORT1024_WITH_RC4_56_MD5 = "EXP1024-RC4-MD5";
+ public final static String TLS1_TXT_RSA_EXPORT1024_WITH_RC2_CBC_56_MD5 = "EXP1024-RC2-CBC-MD5";
+ public final static String TLS1_TXT_RSA_EXPORT1024_WITH_DES_CBC_SHA = "EXP1024-DES-CBC-SHA";
+ public final static String TLS1_TXT_DHE_DSS_EXPORT1024_WITH_DES_CBC_SHA = "EXP1024-DHE-DSS-DES-CBC-SHA";
+ public final static String TLS1_TXT_RSA_EXPORT1024_WITH_RC4_56_SHA = "EXP1024-RC4-SHA";
+ public final static String TLS1_TXT_DHE_DSS_EXPORT1024_WITH_RC4_56_SHA = "EXP1024-DHE-DSS-RC4-SHA";
+ public final static String TLS1_TXT_DHE_DSS_WITH_RC4_128_SHA = "DHE-DSS-RC4-SHA";
+ public final static String TLS1_TXT_RSA_WITH_AES_128_SHA = "AES128-SHA";
+ public final static String TLS1_TXT_DH_DSS_WITH_AES_128_SHA = "DH-DSS-AES128-SHA";
+ public final static String TLS1_TXT_DH_RSA_WITH_AES_128_SHA = "DH-RSA-AES128-SHA";
+ public final static String TLS1_TXT_DHE_DSS_WITH_AES_128_SHA = "DHE-DSS-AES128-SHA";
+ public final static String TLS1_TXT_DHE_RSA_WITH_AES_128_SHA = "DHE-RSA-AES128-SHA";
+ public final static String TLS1_TXT_ADH_WITH_AES_128_SHA = "ADH-AES128-SHA";
+ public final static String TLS1_TXT_RSA_WITH_AES_256_SHA = "AES256-SHA";
+ public final static String TLS1_TXT_DH_DSS_WITH_AES_256_SHA = "DH-DSS-AES256-SHA";
+ public final static String TLS1_TXT_DH_RSA_WITH_AES_256_SHA = "DH-RSA-AES256-SHA";
+ public final static String TLS1_TXT_DHE_DSS_WITH_AES_256_SHA = "DHE-DSS-AES256-SHA";
+ public final static String TLS1_TXT_DHE_RSA_WITH_AES_256_SHA = "DHE-RSA-AES256-SHA";
+ public final static String TLS1_TXT_ADH_WITH_AES_256_SHA = "ADH-AES256-SHA";
+ public final static String TLS1_TXT_ECDH_ECDSA_WITH_NULL_SHA = "ECDH-ECDSA-NULL-SHA";
+ public final static String TLS1_TXT_ECDH_ECDSA_WITH_RC4_128_SHA = "ECDH-ECDSA-RC4-SHA";
+ public final static String TLS1_TXT_ECDH_ECDSA_WITH_DES_192_CBC3_SHA = "ECDH-ECDSA-DES-CBC3-SHA";
+ public final static String TLS1_TXT_ECDH_ECDSA_WITH_AES_128_CBC_SHA = "ECDH-ECDSA-AES128-SHA";
+ public final static String TLS1_TXT_ECDH_ECDSA_WITH_AES_256_CBC_SHA = "ECDH-ECDSA-AES256-SHA";
+ public final static String TLS1_TXT_ECDHE_ECDSA_WITH_NULL_SHA = "ECDHE-ECDSA-NULL-SHA";
+ public final static String TLS1_TXT_ECDHE_ECDSA_WITH_RC4_128_SHA = "ECDHE-ECDSA-RC4-SHA";
+ public final static String TLS1_TXT_ECDHE_ECDSA_WITH_DES_192_CBC3_SHA = "ECDHE-ECDSA-DES-CBC3-SHA";
+ public final static String TLS1_TXT_ECDHE_ECDSA_WITH_AES_128_CBC_SHA = "ECDHE-ECDSA-AES128-SHA";
+ public final static String TLS1_TXT_ECDHE_ECDSA_WITH_AES_256_CBC_SHA = "ECDHE-ECDSA-AES256-SHA";
+ public final static String TLS1_TXT_ECDH_RSA_WITH_NULL_SHA = "ECDH-RSA-NULL-SHA";
+ public final static String TLS1_TXT_ECDH_RSA_WITH_RC4_128_SHA = "ECDH-RSA-RC4-SHA";
+ public final static String TLS1_TXT_ECDH_RSA_WITH_DES_192_CBC3_SHA = "ECDH-RSA-DES-CBC3-SHA";
+ public final static String TLS1_TXT_ECDH_RSA_WITH_AES_128_CBC_SHA = "ECDH-RSA-AES128-SHA";
+ public final static String TLS1_TXT_ECDH_RSA_WITH_AES_256_CBC_SHA = "ECDH-RSA-AES256-SHA";
+ public final static String TLS1_TXT_ECDHE_RSA_WITH_NULL_SHA = "ECDHE-RSA-NULL-SHA";
+ public final static String TLS1_TXT_ECDHE_RSA_WITH_RC4_128_SHA = "ECDHE-RSA-RC4-SHA";
+ public final static String TLS1_TXT_ECDHE_RSA_WITH_DES_192_CBC3_SHA = "ECDHE-RSA-DES-CBC3-SHA";
+ public final static String TLS1_TXT_ECDHE_RSA_WITH_AES_128_CBC_SHA = "ECDHE-RSA-AES128-SHA";
+ public final static String TLS1_TXT_ECDHE_RSA_WITH_AES_256_CBC_SHA = "ECDHE-RSA-AES256-SHA";
+ public final static String TLS1_TXT_ECDH_anon_WITH_NULL_SHA = "AECDH-NULL-SHA";
+ public final static String TLS1_TXT_ECDH_anon_WITH_RC4_128_SHA = "AECDH-RC4-SHA";
+ public final static String TLS1_TXT_ECDH_anon_WITH_DES_192_CBC3_SHA = "AECDH-DES-CBC3-SHA";
+ public final static String TLS1_TXT_ECDH_anon_WITH_AES_128_CBC_SHA = "AECDH-AES128-SHA";
+ public final static String TLS1_TXT_ECDH_anon_WITH_AES_256_CBC_SHA = "AECDH-AES256-SHA";
+
+ public static class Def {
+ public final int valid;
+ public final String name;
+ public final long id;
+ public final long algorithms;
+ public final long algo_strength;
+ public final long algorithm2;
+ public final int strength_bits;
+ public final int alg_bits;
+ public final long mask;
+ public final long mask_strength;
+ public String cipherSuite;
+ public Def(int valid, String name, long id, long algorithms, long algo_strength, long algorithm2, int strength_bits, int alg_bits, long mask, long mask_strength) {
+ this.valid = valid;
+ this.name = name;
+ this.id = id;
+ this.algorithms = algorithms;
+ this.algo_strength = algo_strength;
+ this.algorithm2 = algorithm2;
+ this.strength_bits = strength_bits;
+ this.alg_bits = alg_bits;
+ this.mask = mask;
+ this.mask_strength = mask_strength;
+ }
+
+ @Override
+ public int hashCode() {
+ return name.hashCode();
+ }
+
+ @Override
+ public boolean equals(Object other) {
+ boolean ret = this == other;
+ if(!ret && (other instanceof Def)) {
+ ret = this.name.equals(((Def)other).name);
+ }
+ return ret;
+ }
+
+ @Override
+ public String toString() {
+ return "Cipher<" + name + ">";
+ }
+
+ // from ssl_cipher_apply_rule
+ public boolean matches(Def current) {
+// ma = mask & cp->algorithms;
+// ma_s = mask_strength & cp->algo_strength;
+//
+// // Select: if none of the mask bit was met from the
+// // cipher or not all of the bits were met, the
+// // selection does not apply.
+// if (((ma == 0) && (ma_s == 0)) ||
+// ((ma & algorithms) != ma) ||
+// ((ma_s & algo_strength) != ma_s))
+// continue; // does not apply
+// }
+ long ma = mask & current.algorithms;
+ long ma_s = mask_strength & current.algo_strength;
+ if (((ma == 0) && (ma_s == 0)) ||
+ ((ma & algorithms) != ma) ||
+ ((ma_s & algo_strength) != ma_s)) {
+ return false;
+ }
+ return true;
+ }
+ }
+
+ public final static Map Definitions = new HashMap();
+ public final static List Ciphers = new ArrayList();
+ public final static Map CipherNames = new HashMap();
+ public final static Map SuiteToOSSL = new HashMap();
+
+ public static List getMatchingCiphers(String str, String[] all) {
+ String[] parts = str.split("[:, ]+");
+ List currentList = new ArrayList();
+ Set removed = new HashSet();
+
+ for (String part : parts) {
+ if (part.equals("@STRENGTH")) {
+ Collections.sort(currentList, new Comparator() {
+
+ public int compare(Def first, Def second) {
+ return second.strength_bits - first.strength_bits;
+ }
+ });
+ continue;
+ }
+ int index = 0;
+ switch (part.charAt(0)) {
+ case '!':
+ index++;
+ break;
+ case '+':
+ index++;
+ break;
+ case '-':
+ index++;
+ break;
+ }
+ List matching = getMatching(part.substring(index), all);
+ if (matching != null) {
+ if (index > 0) {
+ switch (part.charAt(0)) {
+ case '!':
+ currentList.removeAll(matching);
+ removed.addAll(matching);
+ break;
+ case '+': // '+' is for moving entry in the list.
+ for (Def ele : matching) {
+ if (!removed.contains(ele) && currentList.contains(ele)) {
+ currentList.remove(ele);
+ currentList.add(ele);
+ }
+ }
+ break;
+ case '-':
+ currentList.removeAll(matching);
+ break;
+ }
+ } else {
+ for (Def ele : matching) {
+ if (!removed.contains(ele) && !currentList.contains(ele)) {
+ currentList.add(ele);
+ }
+ }
+ }
+ }
+ }
+ return currentList;
+ }
+
+ private static List getMatching(String definition, String[] all) {
+ List matching = null;
+ for (String name : definition.split("[+]")) {
+ Def pattern = Definitions.get(name);
+ if (pattern != null) {
+ if (matching == null) {
+ matching = getMatchingPattern(pattern, all);
+ } else {
+ List updated = new ArrayList();
+ for (Def ele : getMatchingPattern(pattern, all)) {
+ if (matching.contains(ele)) {
+ updated.add(ele);
+ }
+ }
+ matching = updated;
+ }
+ }
+ }
+ return matching;
+ }
+
+ private static List getMatchingPattern(Def pattern, String[] all) {
+ List matching = new ArrayList();
+ for (String entry : all) {
+ String ossl = SuiteToOSSL.get(entry);
+ if (ossl != null) {
+ Def def = CipherNames.get(ossl);
+ if (def != null) {
+ def.cipherSuite = entry;
+ if (pattern.matches(def)) {
+ matching.add(def);
+ }
+ }
+ }
+ }
+ return matching;
+ }
+
+ private static void addAlias(String cipherSuite, String ossl) {
+ SuiteToOSSL.put(cipherSuite, ossl);
+ }
+
+ static {
+ Definitions.put(SSL_TXT_ALL,new Def(0,SSL_TXT_ALL, 0,SSL_ALL & ~SSL_eNULL & ~SSL_kECDH & ~SSL_kECDHE, SSL_ALL ,0,0,0,SSL_ALL,SSL_ALL));
+ Definitions.put(SSL_TXT_CMPALL,new Def(0,SSL_TXT_CMPALL,0,SSL_eNULL,0,0,0,0,SSL_ENC_MASK,0));
+ Definitions.put(SSL_TXT_CMPDEF,new Def(0,SSL_TXT_CMPDEF,0,SSL_ADH, 0,0,0,0,SSL_AUTH_MASK,0));
+ Definitions.put(SSL_TXT_kKRB5,new Def(0,SSL_TXT_kKRB5,0,SSL_kKRB5,0,0,0,0,SSL_MKEY_MASK,0));
+ Definitions.put(SSL_TXT_kRSA,new Def(0,SSL_TXT_kRSA,0,SSL_kRSA, 0,0,0,0,SSL_MKEY_MASK,0));
+ Definitions.put(SSL_TXT_kDHr,new Def(0,SSL_TXT_kDHr,0,SSL_kDHr, 0,0,0,0,SSL_MKEY_MASK,0));
+ Definitions.put(SSL_TXT_kDHd,new Def(0,SSL_TXT_kDHd,0,SSL_kDHd, 0,0,0,0,SSL_MKEY_MASK,0));
+ Definitions.put(SSL_TXT_kEDH,new Def(0,SSL_TXT_kEDH,0,SSL_kEDH, 0,0,0,0,SSL_MKEY_MASK,0));
+ Definitions.put(SSL_TXT_kFZA,new Def(0,SSL_TXT_kFZA,0,SSL_kFZA, 0,0,0,0,SSL_MKEY_MASK,0));
+ Definitions.put(SSL_TXT_DH,new Def(0,SSL_TXT_DH, 0,SSL_DH, 0,0,0,0,SSL_MKEY_MASK,0));
+ Definitions.put(SSL_TXT_ECC,new Def(0,SSL_TXT_ECC, 0,(SSL_kECDH|SSL_kECDHE), 0,0,0,0,SSL_MKEY_MASK,0));
+ Definitions.put(SSL_TXT_EDH,new Def(0,SSL_TXT_EDH, 0,SSL_EDH, 0,0,0,0,SSL_MKEY_MASK|SSL_AUTH_MASK,0));
+ Definitions.put(SSL_TXT_aKRB5,new Def(0,SSL_TXT_aKRB5,0,SSL_aKRB5,0,0,0,0,SSL_AUTH_MASK,0));
+ Definitions.put(SSL_TXT_aRSA,new Def(0,SSL_TXT_aRSA,0,SSL_aRSA, 0,0,0,0,SSL_AUTH_MASK,0));
+ Definitions.put(SSL_TXT_aDSS,new Def(0,SSL_TXT_aDSS,0,SSL_aDSS, 0,0,0,0,SSL_AUTH_MASK,0));
+ Definitions.put(SSL_TXT_aFZA,new Def(0,SSL_TXT_aFZA,0,SSL_aFZA, 0,0,0,0,SSL_AUTH_MASK,0));
+ Definitions.put(SSL_TXT_aNULL,new Def(0,SSL_TXT_aNULL,0,SSL_aNULL,0,0,0,0,SSL_AUTH_MASK,0));
+ Definitions.put(SSL_TXT_aDH,new Def(0,SSL_TXT_aDH, 0,SSL_aDH, 0,0,0,0,SSL_AUTH_MASK,0));
+ Definitions.put(SSL_TXT_DSS,new Def(0,SSL_TXT_DSS, 0,SSL_DSS, 0,0,0,0,SSL_AUTH_MASK,0));
+ Definitions.put(SSL_TXT_DES,new Def(0,SSL_TXT_DES, 0,SSL_DES, 0,0,0,0,SSL_ENC_MASK,0));
+ Definitions.put(SSL_TXT_3DES,new Def(0,SSL_TXT_3DES,0,SSL_3DES, 0,0,0,0,SSL_ENC_MASK,0));
+ Definitions.put(SSL_TXT_RC4,new Def(0,SSL_TXT_RC4, 0,SSL_RC4, 0,0,0,0,SSL_ENC_MASK,0));
+ Definitions.put(SSL_TXT_RC2,new Def(0,SSL_TXT_RC2, 0,SSL_RC2, 0,0,0,0,SSL_ENC_MASK,0));
+ Definitions.put(SSL_TXT_IDEA,new Def(0,SSL_TXT_IDEA,0,SSL_IDEA, 0,0,0,0,SSL_ENC_MASK,0));
+ Definitions.put(SSL_TXT_eNULL,new Def(0,SSL_TXT_eNULL,0,SSL_eNULL,0,0,0,0,SSL_ENC_MASK,0));
+ Definitions.put(SSL_TXT_eFZA,new Def(0,SSL_TXT_eFZA,0,SSL_eFZA, 0,0,0,0,SSL_ENC_MASK,0));
+ Definitions.put(SSL_TXT_AES,new Def(0,SSL_TXT_AES, 0,SSL_AES, 0,0,0,0,SSL_ENC_MASK,0));
+ 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_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));
+ Definitions.put(SSL_TXT_FZA,new Def(0,SSL_TXT_FZA, 0,SSL_FZA, 0,0,0,0,SSL_AUTH_MASK|SSL_MKEY_MASK|SSL_ENC_MASK,0));
+ Definitions.put(SSL_TXT_SSLV2,new Def(0,SSL_TXT_SSLV2, 0,SSL_SSLV2, 0,0,0,0,SSL_SSL_MASK,0));
+ Definitions.put(SSL_TXT_SSLV3,new Def(0,SSL_TXT_SSLV3, 0,SSL_SSLV3, 0,0,0,0,SSL_SSL_MASK,0));
+ Definitions.put(SSL_TXT_TLSV1,new Def(0,SSL_TXT_TLSV1, 0,SSL_TLSV1, 0,0,0,0,SSL_SSL_MASK,0));
+ Definitions.put(SSL_TXT_EXP,new Def(0,SSL_TXT_EXP ,0, 0,SSL_EXPORT, 0,0,0,0,SSL_EXP_MASK));
+ Definitions.put(SSL_TXT_EXPORT,new Def(0,SSL_TXT_EXPORT,0, 0,SSL_EXPORT, 0,0,0,0,SSL_EXP_MASK));
+ Definitions.put(SSL_TXT_EXP40,new Def(0,SSL_TXT_EXP40, 0, 0, SSL_EXP40, 0,0,0,0,SSL_STRONG_MASK));
+ Definitions.put(SSL_TXT_EXP56,new Def(0,SSL_TXT_EXP56, 0, 0, SSL_EXP56, 0,0,0,0,SSL_STRONG_MASK));
+ Definitions.put(SSL_TXT_LOW,new Def(0,SSL_TXT_LOW, 0, 0, SSL_LOW, 0,0,0,0,SSL_STRONG_MASK));
+ Definitions.put(SSL_TXT_MEDIUM,new Def(0,SSL_TXT_MEDIUM,0, 0,SSL_MEDIUM, 0,0,0,0,SSL_STRONG_MASK));
+ Definitions.put(SSL_TXT_HIGH,new Def(0,SSL_TXT_HIGH, 0, 0, SSL_HIGH, 0,0,0,0,SSL_STRONG_MASK));
+
+ /* Cipher 01 */
+ Ciphers.add(new Def(
+ 1,
+ SSL3_TXT_RSA_NULL_MD5,
+ SSL3_CK_RSA_NULL_MD5,
+ SSL_kRSA|SSL_aRSA|SSL_eNULL |SSL_MD5|SSL_SSLV3,
+ SSL_NOT_EXP|SSL_STRONG_NONE,
+ 0,
+ 0,
+ 0,
+ SSL_ALL_CIPHERS,
+ SSL_ALL_STRENGTHS
+ ));
+ /* Cipher 02 */
+ Ciphers.add(new Def(
+ 1,
+ SSL3_TXT_RSA_NULL_SHA,
+ SSL3_CK_RSA_NULL_SHA,
+ SSL_kRSA|SSL_aRSA|SSL_eNULL |SSL_SHA1|SSL_SSLV3,
+ SSL_NOT_EXP|SSL_STRONG_NONE,
+ 0,
+ 0,
+ 0,
+ SSL_ALL_CIPHERS,
+ SSL_ALL_STRENGTHS
+ ));
+ /* Cipher 03 */
+ Ciphers.add(new Def(
+ 1,
+ SSL3_TXT_RSA_RC4_40_MD5,
+ SSL3_CK_RSA_RC4_40_MD5,
+ SSL_kRSA|SSL_aRSA|SSL_RC4 |SSL_MD5 |SSL_SSLV3,
+ SSL_EXPORT|SSL_EXP40,
+ 0,
+ 40,
+ 128,
+ SSL_ALL_CIPHERS,
+ SSL_ALL_STRENGTHS
+ ));
+ /* Cipher 04 */
+ Ciphers.add(new Def(
+ 1,
+ SSL3_TXT_RSA_RC4_128_MD5,
+ SSL3_CK_RSA_RC4_128_MD5,
+ SSL_kRSA|SSL_aRSA|SSL_RC4 |SSL_MD5|SSL_SSLV3,
+ SSL_NOT_EXP|SSL_MEDIUM,
+ 0,
+ 128,
+ 128,
+ SSL_ALL_CIPHERS,
+ SSL_ALL_STRENGTHS
+ ));
+ /* Cipher 05 */
+ Ciphers.add(new Def(
+ 1,
+ SSL3_TXT_RSA_RC4_128_SHA,
+ SSL3_CK_RSA_RC4_128_SHA,
+ SSL_kRSA|SSL_aRSA|SSL_RC4 |SSL_SHA1|SSL_SSLV3,
+ SSL_NOT_EXP|SSL_MEDIUM,
+ 0,
+ 128,
+ 128,
+ SSL_ALL_CIPHERS,
+ SSL_ALL_STRENGTHS
+ ));
+ /* Cipher 06 */
+ Ciphers.add(new Def(
+ 1,
+ SSL3_TXT_RSA_RC2_40_MD5,
+ SSL3_CK_RSA_RC2_40_MD5,
+ SSL_kRSA|SSL_aRSA|SSL_RC2 |SSL_MD5 |SSL_SSLV3,
+ SSL_EXPORT|SSL_EXP40,
+ 0,
+ 40,
+ 128,
+ SSL_ALL_CIPHERS,
+ SSL_ALL_STRENGTHS
+ ));
+ /* Cipher 07 */
+ Ciphers.add(new Def(
+ 1,
+ SSL3_TXT_RSA_IDEA_128_SHA,
+ SSL3_CK_RSA_IDEA_128_SHA,
+ SSL_kRSA|SSL_aRSA|SSL_IDEA |SSL_SHA1|SSL_SSLV3,
+ SSL_NOT_EXP|SSL_MEDIUM,
+ 0,
+ 128,
+ 128,
+ SSL_ALL_CIPHERS,
+ SSL_ALL_STRENGTHS
+ ));
+ /* Cipher 08 */
+ Ciphers.add(new Def(
+ 1,
+ SSL3_TXT_RSA_DES_40_CBC_SHA,
+ SSL3_CK_RSA_DES_40_CBC_SHA,
+ SSL_kRSA|SSL_aRSA|SSL_DES|SSL_SHA1|SSL_SSLV3,
+ SSL_EXPORT|SSL_EXP40,
+ 0,
+ 40,
+ 56,
+ SSL_ALL_CIPHERS,
+ SSL_ALL_STRENGTHS
+ ));
+ /* Cipher 09 */
+ Ciphers.add(new Def(
+ 1,
+ SSL3_TXT_RSA_DES_64_CBC_SHA,
+ SSL3_CK_RSA_DES_64_CBC_SHA,
+ SSL_kRSA|SSL_aRSA|SSL_DES |SSL_SHA1|SSL_SSLV3,
+ SSL_NOT_EXP|SSL_LOW,
+ 0,
+ 56,
+ 56,
+ SSL_ALL_CIPHERS,
+ SSL_ALL_STRENGTHS
+ ));
+ /* Cipher 0A */
+ Ciphers.add(new Def(
+ 1,
+ SSL3_TXT_RSA_DES_192_CBC3_SHA,
+ SSL3_CK_RSA_DES_192_CBC3_SHA,
+ SSL_kRSA|SSL_aRSA|SSL_3DES |SSL_SHA1|SSL_SSLV3,
+ SSL_NOT_EXP|SSL_HIGH,
+ 0,
+ 168,
+ 168,
+ SSL_ALL_CIPHERS,
+ SSL_ALL_STRENGTHS
+ ));
+ /* The DH ciphers */
+ /* Cipher 0B */
+ Ciphers.add(new Def(
+ 0,
+ SSL3_TXT_DH_DSS_DES_40_CBC_SHA,
+ SSL3_CK_DH_DSS_DES_40_CBC_SHA,
+ SSL_kDHd |SSL_aDH|SSL_DES|SSL_SHA1|SSL_SSLV3,
+ SSL_EXPORT|SSL_EXP40,
+ 0,
+ 40,
+ 56,
+ SSL_ALL_CIPHERS,
+ SSL_ALL_STRENGTHS
+ ));
+ /* Cipher 0C */
+ Ciphers.add(new Def(
+ 0,
+ SSL3_TXT_DH_DSS_DES_64_CBC_SHA,
+ SSL3_CK_DH_DSS_DES_64_CBC_SHA,
+ SSL_kDHd |SSL_aDH|SSL_DES |SSL_SHA1|SSL_SSLV3,
+ SSL_NOT_EXP|SSL_LOW,
+ 0,
+ 56,
+ 56,
+ SSL_ALL_CIPHERS,
+ SSL_ALL_STRENGTHS
+ ));
+ /* Cipher 0D */
+ Ciphers.add(new Def(
+ 0,
+ SSL3_TXT_DH_DSS_DES_192_CBC3_SHA,
+ SSL3_CK_DH_DSS_DES_192_CBC3_SHA,
+ SSL_kDHd |SSL_aDH|SSL_3DES |SSL_SHA1|SSL_SSLV3,
+ SSL_NOT_EXP|SSL_HIGH,
+ 0,
+ 168,
+ 168,
+ SSL_ALL_CIPHERS,
+ SSL_ALL_STRENGTHS
+ ));
+ /* Cipher 0E */
+ Ciphers.add(new Def(
+ 0,
+ SSL3_TXT_DH_RSA_DES_40_CBC_SHA,
+ SSL3_CK_DH_RSA_DES_40_CBC_SHA,
+ SSL_kDHr |SSL_aDH|SSL_DES|SSL_SHA1|SSL_SSLV3,
+ SSL_EXPORT|SSL_EXP40,
+ 0,
+ 40,
+ 56,
+ SSL_ALL_CIPHERS,
+ SSL_ALL_STRENGTHS
+ ));
+ /* Cipher 0F */
+ Ciphers.add(new Def(
+ 0,
+ SSL3_TXT_DH_RSA_DES_64_CBC_SHA,
+ SSL3_CK_DH_RSA_DES_64_CBC_SHA,
+ SSL_kDHr |SSL_aDH|SSL_DES |SSL_SHA1|SSL_SSLV3,
+ SSL_NOT_EXP|SSL_LOW,
+ 0,
+ 56,
+ 56,
+ SSL_ALL_CIPHERS,
+ SSL_ALL_STRENGTHS
+ ));
+ /* Cipher 10 */
+ Ciphers.add(new Def(
+ 0,
+ SSL3_TXT_DH_RSA_DES_192_CBC3_SHA,
+ SSL3_CK_DH_RSA_DES_192_CBC3_SHA,
+ SSL_kDHr |SSL_aDH|SSL_3DES |SSL_SHA1|SSL_SSLV3,
+ SSL_NOT_EXP|SSL_HIGH,
+ 0,
+ 168,
+ 168,
+ SSL_ALL_CIPHERS,
+ SSL_ALL_STRENGTHS
+ ));
+
+ /* The Ephemeral DH ciphers */
+ /* Cipher 11 */
+ Ciphers.add(new Def(
+ 1,
+ SSL3_TXT_EDH_DSS_DES_40_CBC_SHA,
+ SSL3_CK_EDH_DSS_DES_40_CBC_SHA,
+ SSL_kEDH|SSL_aDSS|SSL_DES|SSL_SHA1|SSL_SSLV3,
+ SSL_EXPORT|SSL_EXP40,
+ 0,
+ 40,
+ 56,
+ SSL_ALL_CIPHERS,
+ SSL_ALL_STRENGTHS
+ ));
+ /* Cipher 12 */
+ Ciphers.add(new Def(
+ 1,
+ SSL3_TXT_EDH_DSS_DES_64_CBC_SHA,
+ SSL3_CK_EDH_DSS_DES_64_CBC_SHA,
+ SSL_kEDH|SSL_aDSS|SSL_DES |SSL_SHA1|SSL_SSLV3,
+ SSL_NOT_EXP|SSL_LOW,
+ 0,
+ 56,
+ 56,
+ SSL_ALL_CIPHERS,
+ SSL_ALL_STRENGTHS
+ ));
+ /* Cipher 13 */
+ Ciphers.add(new Def(
+ 1,
+ SSL3_TXT_EDH_DSS_DES_192_CBC3_SHA,
+ SSL3_CK_EDH_DSS_DES_192_CBC3_SHA,
+ SSL_kEDH|SSL_aDSS|SSL_3DES |SSL_SHA1|SSL_SSLV3,
+ SSL_NOT_EXP|SSL_HIGH,
+ 0,
+ 168,
+ 168,
+ SSL_ALL_CIPHERS,
+ SSL_ALL_STRENGTHS
+ ));
+ /* Cipher 14 */
+ Ciphers.add(new Def(
+ 1,
+ SSL3_TXT_EDH_RSA_DES_40_CBC_SHA,
+ SSL3_CK_EDH_RSA_DES_40_CBC_SHA,
+ SSL_kEDH|SSL_aRSA|SSL_DES|SSL_SHA1|SSL_SSLV3,
+ SSL_EXPORT|SSL_EXP40,
+ 0,
+ 40,
+ 56,
+ SSL_ALL_CIPHERS,
+ SSL_ALL_STRENGTHS
+ ));
+ /* Cipher 15 */
+ Ciphers.add(new Def(
+ 1,
+ SSL3_TXT_EDH_RSA_DES_64_CBC_SHA,
+ SSL3_CK_EDH_RSA_DES_64_CBC_SHA,
+ SSL_kEDH|SSL_aRSA|SSL_DES |SSL_SHA1|SSL_SSLV3,
+ SSL_NOT_EXP|SSL_LOW,
+ 0,
+ 56,
+ 56,
+ SSL_ALL_CIPHERS,
+ SSL_ALL_STRENGTHS
+ ));
+ /* Cipher 16 */
+ Ciphers.add(new Def(
+ 1,
+ SSL3_TXT_EDH_RSA_DES_192_CBC3_SHA,
+ SSL3_CK_EDH_RSA_DES_192_CBC3_SHA,
+ SSL_kEDH|SSL_aRSA|SSL_3DES |SSL_SHA1|SSL_SSLV3,
+ SSL_NOT_EXP|SSL_HIGH,
+ 0,
+ 168,
+ 168,
+ SSL_ALL_CIPHERS,
+ SSL_ALL_STRENGTHS
+ ));
+ /* Cipher 17 */
+ Ciphers.add(new Def(
+ 1,
+ SSL3_TXT_ADH_RC4_40_MD5,
+ SSL3_CK_ADH_RC4_40_MD5,
+ SSL_kEDH |SSL_aNULL|SSL_RC4 |SSL_MD5 |SSL_SSLV3,
+ SSL_EXPORT|SSL_EXP40,
+ 0,
+ 40,
+ 128,
+ SSL_ALL_CIPHERS,
+ SSL_ALL_STRENGTHS
+ ));
+ /* Cipher 18 */
+ Ciphers.add(new Def(
+ 1,
+ SSL3_TXT_ADH_RC4_128_MD5,
+ SSL3_CK_ADH_RC4_128_MD5,
+ SSL_kEDH |SSL_aNULL|SSL_RC4 |SSL_MD5 |SSL_SSLV3,
+ SSL_NOT_EXP|SSL_MEDIUM,
+ 0,
+ 128,
+ 128,
+ SSL_ALL_CIPHERS,
+ SSL_ALL_STRENGTHS
+ ));
+ /* Cipher 19 */
+ Ciphers.add(new Def(
+ 1,
+ SSL3_TXT_ADH_DES_40_CBC_SHA,
+ SSL3_CK_ADH_DES_40_CBC_SHA,
+ SSL_kEDH |SSL_aNULL|SSL_DES|SSL_SHA1|SSL_SSLV3,
+ SSL_EXPORT|SSL_EXP40,
+ 0,
+ 40,
+ 128,
+ SSL_ALL_CIPHERS,
+ SSL_ALL_STRENGTHS
+ ));
+ /* Cipher 1A */
+ Ciphers.add(new Def(
+ 1,
+ SSL3_TXT_ADH_DES_64_CBC_SHA,
+ SSL3_CK_ADH_DES_64_CBC_SHA,
+ SSL_kEDH |SSL_aNULL|SSL_DES |SSL_SHA1|SSL_SSLV3,
+ SSL_NOT_EXP|SSL_LOW,
+ 0,
+ 56,
+ 56,
+ SSL_ALL_CIPHERS,
+ SSL_ALL_STRENGTHS
+ ));
+ /* Cipher 1B */
+ Ciphers.add(new Def(
+ 1,
+ SSL3_TXT_ADH_DES_192_CBC_SHA,
+ SSL3_CK_ADH_DES_192_CBC_SHA,
+ SSL_kEDH |SSL_aNULL|SSL_3DES |SSL_SHA1|SSL_SSLV3,
+ SSL_NOT_EXP|SSL_HIGH,
+ 0,
+ 168,
+ 168,
+ SSL_ALL_CIPHERS,
+ SSL_ALL_STRENGTHS
+ ));
+
+ /* Fortezza */
+ /* Cipher 1C */
+ Ciphers.add(new Def(
+ 0,
+ SSL3_TXT_FZA_DMS_NULL_SHA,
+ SSL3_CK_FZA_DMS_NULL_SHA,
+ SSL_kFZA|SSL_aFZA |SSL_eNULL |SSL_SHA1|SSL_SSLV3,
+ SSL_NOT_EXP|SSL_STRONG_NONE,
+ 0,
+ 0,
+ 0,
+ SSL_ALL_CIPHERS,
+ SSL_ALL_STRENGTHS
+ ));
+
+ /* Cipher 1D */
+ Ciphers.add(new Def(
+ 0,
+ SSL3_TXT_FZA_DMS_FZA_SHA,
+ SSL3_CK_FZA_DMS_FZA_SHA,
+ SSL_kFZA|SSL_aFZA |SSL_eFZA |SSL_SHA1|SSL_SSLV3,
+ SSL_NOT_EXP|SSL_STRONG_NONE,
+ 0,
+ 0,
+ 0,
+ SSL_ALL_CIPHERS,
+ SSL_ALL_STRENGTHS
+ ));
+
+ /* Cipher 1E VRS */
+ Ciphers.add(new Def(
+ 1,
+ SSL3_TXT_KRB5_DES_64_CBC_SHA,
+ SSL3_CK_KRB5_DES_64_CBC_SHA,
+ SSL_kKRB5|SSL_aKRB5| SSL_DES|SSL_SHA1 |SSL_SSLV3,
+ SSL_NOT_EXP|SSL_LOW,
+ 0,
+ 56,
+ 56,
+ SSL_ALL_CIPHERS,
+ SSL_ALL_STRENGTHS
+ ));
+
+ /* Cipher 1F VRS */
+ Ciphers.add(new Def(
+ 1,
+ SSL3_TXT_KRB5_DES_192_CBC3_SHA,
+ SSL3_CK_KRB5_DES_192_CBC3_SHA,
+ SSL_kKRB5|SSL_aKRB5| SSL_3DES|SSL_SHA1 |SSL_SSLV3,
+ SSL_NOT_EXP|SSL_HIGH,
+ 0,
+ 112,
+ 168,
+ SSL_ALL_CIPHERS,
+ SSL_ALL_STRENGTHS
+ ));
+
+ /* Cipher 20 VRS */
+ Ciphers.add(new Def(
+ 1,
+ SSL3_TXT_KRB5_RC4_128_SHA,
+ SSL3_CK_KRB5_RC4_128_SHA,
+ SSL_kKRB5|SSL_aKRB5| SSL_RC4|SSL_SHA1 |SSL_SSLV3,
+ SSL_NOT_EXP|SSL_MEDIUM,
+ 0,
+ 128,
+ 128,
+ SSL_ALL_CIPHERS,
+ SSL_ALL_STRENGTHS
+ ));
+
+ /* Cipher 21 VRS */
+ Ciphers.add(new Def(
+ 1,
+ SSL3_TXT_KRB5_IDEA_128_CBC_SHA,
+ SSL3_CK_KRB5_IDEA_128_CBC_SHA,
+ SSL_kKRB5|SSL_aKRB5| SSL_IDEA|SSL_SHA1 |SSL_SSLV3,
+ SSL_NOT_EXP|SSL_MEDIUM,
+ 0,
+ 128,
+ 128,
+ SSL_ALL_CIPHERS,
+ SSL_ALL_STRENGTHS
+ ));
+
+ /* Cipher 22 VRS */
+ Ciphers.add(new Def(
+ 1,
+ SSL3_TXT_KRB5_DES_64_CBC_MD5,
+ SSL3_CK_KRB5_DES_64_CBC_MD5,
+ SSL_kKRB5|SSL_aKRB5| SSL_DES|SSL_MD5 |SSL_SSLV3,
+ SSL_NOT_EXP|SSL_LOW,
+ 0,
+ 56,
+ 56,
+ SSL_ALL_CIPHERS,
+ SSL_ALL_STRENGTHS
+ ));
+
+ /* Cipher 23 VRS */
+ Ciphers.add(new Def(
+ 1,
+ SSL3_TXT_KRB5_DES_192_CBC3_MD5,
+ SSL3_CK_KRB5_DES_192_CBC3_MD5,
+ SSL_kKRB5|SSL_aKRB5| SSL_3DES|SSL_MD5 |SSL_SSLV3,
+ SSL_NOT_EXP|SSL_HIGH,
+ 0,
+ 112,
+ 168,
+ SSL_ALL_CIPHERS,
+ SSL_ALL_STRENGTHS
+ ));
+
+ /* Cipher 24 VRS */
+ Ciphers.add(new Def(
+ 1,
+ SSL3_TXT_KRB5_RC4_128_MD5,
+ SSL3_CK_KRB5_RC4_128_MD5,
+ SSL_kKRB5|SSL_aKRB5| SSL_RC4|SSL_MD5 |SSL_SSLV3,
+ SSL_NOT_EXP|SSL_MEDIUM,
+ 0,
+ 128,
+ 128,
+ SSL_ALL_CIPHERS,
+ SSL_ALL_STRENGTHS
+ ));
+
+ /* Cipher 25 VRS */
+ Ciphers.add(new Def(
+ 1,
+ SSL3_TXT_KRB5_IDEA_128_CBC_MD5,
+ SSL3_CK_KRB5_IDEA_128_CBC_MD5,
+ SSL_kKRB5|SSL_aKRB5| SSL_IDEA|SSL_MD5 |SSL_SSLV3,
+ SSL_NOT_EXP|SSL_MEDIUM,
+ 0,
+ 128,
+ 128,
+ SSL_ALL_CIPHERS,
+ SSL_ALL_STRENGTHS
+ ));
+
+ /* Cipher 26 VRS */
+ Ciphers.add(new Def(
+ 1,
+ SSL3_TXT_KRB5_DES_40_CBC_SHA,
+ SSL3_CK_KRB5_DES_40_CBC_SHA,
+ SSL_kKRB5|SSL_aKRB5| SSL_DES|SSL_SHA1 |SSL_SSLV3,
+ SSL_EXPORT|SSL_EXP40,
+ 0,
+ 40,
+ 56,
+ SSL_ALL_CIPHERS,
+ SSL_ALL_STRENGTHS
+ ));
+
+ /* Cipher 27 VRS */
+ Ciphers.add(new Def(
+ 1,
+ SSL3_TXT_KRB5_RC2_40_CBC_SHA,
+ SSL3_CK_KRB5_RC2_40_CBC_SHA,
+ SSL_kKRB5|SSL_aKRB5| SSL_RC2|SSL_SHA1 |SSL_SSLV3,
+ SSL_EXPORT|SSL_EXP40,
+ 0,
+ 40,
+ 128,
+ SSL_ALL_CIPHERS,
+ SSL_ALL_STRENGTHS
+ ));
+
+ /* Cipher 28 VRS */
+ Ciphers.add(new Def(
+ 1,
+ SSL3_TXT_KRB5_RC4_40_SHA,
+ SSL3_CK_KRB5_RC4_40_SHA,
+ SSL_kKRB5|SSL_aKRB5| SSL_RC4|SSL_SHA1 |SSL_SSLV3,
+ SSL_EXPORT|SSL_EXP40,
+ 0,
+ 128,
+ 128,
+ SSL_ALL_CIPHERS,
+ SSL_ALL_STRENGTHS
+ ));
+
+ /* Cipher 29 VRS */
+ Ciphers.add(new Def(
+ 1,
+ SSL3_TXT_KRB5_DES_40_CBC_MD5,
+ SSL3_CK_KRB5_DES_40_CBC_MD5,
+ SSL_kKRB5|SSL_aKRB5| SSL_DES|SSL_MD5 |SSL_SSLV3,
+ SSL_EXPORT|SSL_EXP40,
+ 0,
+ 40,
+ 56,
+ SSL_ALL_CIPHERS,
+ SSL_ALL_STRENGTHS
+ ));
+
+ /* Cipher 2A VRS */
+ Ciphers.add(new Def(
+ 1,
+ SSL3_TXT_KRB5_RC2_40_CBC_MD5,
+ SSL3_CK_KRB5_RC2_40_CBC_MD5,
+ SSL_kKRB5|SSL_aKRB5| SSL_RC2|SSL_MD5 |SSL_SSLV3,
+ SSL_EXPORT|SSL_EXP40,
+ 0,
+ 40,
+ 128,
+ SSL_ALL_CIPHERS,
+ SSL_ALL_STRENGTHS
+ ));
+
+ /* Cipher 2B VRS */
+ Ciphers.add(new Def(
+ 1,
+ SSL3_TXT_KRB5_RC4_40_MD5,
+ SSL3_CK_KRB5_RC4_40_MD5,
+ SSL_kKRB5|SSL_aKRB5| SSL_RC4|SSL_MD5 |SSL_SSLV3,
+ SSL_EXPORT|SSL_EXP40,
+ 0,
+ 128,
+ 128,
+ SSL_ALL_CIPHERS,
+ SSL_ALL_STRENGTHS
+ ));
+
+ /* Cipher 2F */
+ Ciphers.add(new Def(
+ 1,
+ TLS1_TXT_RSA_WITH_AES_128_SHA,
+ TLS1_CK_RSA_WITH_AES_128_SHA,
+ SSL_kRSA|SSL_aRSA|SSL_AES|SSL_SHA |SSL_TLSV1,
+ SSL_NOT_EXP|SSL_HIGH,
+ 0,
+ 128,
+ 128,
+ SSL_ALL_CIPHERS,
+ SSL_ALL_STRENGTHS
+ ));
+ /* Cipher 30 */
+ Ciphers.add(new Def(
+ 0,
+ TLS1_TXT_DH_DSS_WITH_AES_128_SHA,
+ TLS1_CK_DH_DSS_WITH_AES_128_SHA,
+ SSL_kDHd|SSL_aDH|SSL_AES|SSL_SHA|SSL_TLSV1,
+ SSL_NOT_EXP|SSL_HIGH,
+ 0,
+ 128,
+ 128,
+ SSL_ALL_CIPHERS,
+ SSL_ALL_STRENGTHS
+ ));
+ /* Cipher 31 */
+ Ciphers.add(new Def(
+ 0,
+ TLS1_TXT_DH_RSA_WITH_AES_128_SHA,
+ TLS1_CK_DH_RSA_WITH_AES_128_SHA,
+ SSL_kDHr|SSL_aDH|SSL_AES|SSL_SHA|SSL_TLSV1,
+ SSL_NOT_EXP|SSL_HIGH,
+ 0,
+ 128,
+ 128,
+ SSL_ALL_CIPHERS,
+ SSL_ALL_STRENGTHS
+ ));
+ /* Cipher 32 */
+ Ciphers.add(new Def(
+ 1,
+ TLS1_TXT_DHE_DSS_WITH_AES_128_SHA,
+ TLS1_CK_DHE_DSS_WITH_AES_128_SHA,
+ SSL_kEDH|SSL_aDSS|SSL_AES|SSL_SHA|SSL_TLSV1,
+ SSL_NOT_EXP|SSL_HIGH,
+ 0,
+ 128,
+ 128,
+ SSL_ALL_CIPHERS,
+ SSL_ALL_STRENGTHS
+ ));
+ /* Cipher 33 */
+ Ciphers.add(new Def(
+ 1,
+ TLS1_TXT_DHE_RSA_WITH_AES_128_SHA,
+ TLS1_CK_DHE_RSA_WITH_AES_128_SHA,
+ SSL_kEDH|SSL_aRSA|SSL_AES|SSL_SHA|SSL_TLSV1,
+ SSL_NOT_EXP|SSL_HIGH,
+ 0,
+ 128,
+ 128,
+ SSL_ALL_CIPHERS,
+ SSL_ALL_STRENGTHS
+ ));
+ /* Cipher 34 */
+ Ciphers.add(new Def(
+ 1,
+ TLS1_TXT_ADH_WITH_AES_128_SHA,
+ TLS1_CK_ADH_WITH_AES_128_SHA,
+ SSL_kEDH|SSL_aNULL|SSL_AES|SSL_SHA|SSL_TLSV1,
+ SSL_NOT_EXP|SSL_HIGH,
+ 0,
+ 128,
+ 128,
+ SSL_ALL_CIPHERS,
+ SSL_ALL_STRENGTHS
+ ));
+
+ /* Cipher 35 */
+ Ciphers.add(new Def(
+ 1,
+ TLS1_TXT_RSA_WITH_AES_256_SHA,
+ TLS1_CK_RSA_WITH_AES_256_SHA,
+ SSL_kRSA|SSL_aRSA|SSL_AES|SSL_SHA |SSL_TLSV1,
+ SSL_NOT_EXP|SSL_HIGH,
+ 0,
+ 256,
+ 256,
+ SSL_ALL_CIPHERS,
+ SSL_ALL_STRENGTHS
+ ));
+ /* Cipher 36 */
+ Ciphers.add(new Def(
+ 0,
+ TLS1_TXT_DH_DSS_WITH_AES_256_SHA,
+ TLS1_CK_DH_DSS_WITH_AES_256_SHA,
+ SSL_kDHd|SSL_aDH|SSL_AES|SSL_SHA|SSL_TLSV1,
+ SSL_NOT_EXP|SSL_HIGH,
+ 0,
+ 256,
+ 256,
+ SSL_ALL_CIPHERS,
+ SSL_ALL_STRENGTHS
+ ));
+ /* Cipher 37 */
+ Ciphers.add(new Def(
+ 0,
+ TLS1_TXT_DH_RSA_WITH_AES_256_SHA,
+ TLS1_CK_DH_RSA_WITH_AES_256_SHA,
+ SSL_kDHr|SSL_aDH|SSL_AES|SSL_SHA|SSL_TLSV1,
+ SSL_NOT_EXP|SSL_HIGH,
+ 0,
+ 256,
+ 256,
+ SSL_ALL_CIPHERS,
+ SSL_ALL_STRENGTHS
+ ));
+ /* Cipher 38 */
+ Ciphers.add(new Def(
+ 1,
+ TLS1_TXT_DHE_DSS_WITH_AES_256_SHA,
+ TLS1_CK_DHE_DSS_WITH_AES_256_SHA,
+ SSL_kEDH|SSL_aDSS|SSL_AES|SSL_SHA|SSL_TLSV1,
+ SSL_NOT_EXP|SSL_HIGH,
+ 0,
+ 256,
+ 256,
+ SSL_ALL_CIPHERS,
+ SSL_ALL_STRENGTHS
+ ));
+ /* Cipher 39 */
+ Ciphers.add(new Def(
+ 1,
+ TLS1_TXT_DHE_RSA_WITH_AES_256_SHA,
+ TLS1_CK_DHE_RSA_WITH_AES_256_SHA,
+ SSL_kEDH|SSL_aRSA|SSL_AES|SSL_SHA|SSL_TLSV1,
+ SSL_NOT_EXP|SSL_HIGH,
+ 0,
+ 256,
+ 256,
+ SSL_ALL_CIPHERS,
+ SSL_ALL_STRENGTHS
+ ));
+ /* Cipher 3A */
+ Ciphers.add(new Def(
+ 1,
+ TLS1_TXT_ADH_WITH_AES_256_SHA,
+ TLS1_CK_ADH_WITH_AES_256_SHA,
+ SSL_kEDH|SSL_aNULL|SSL_AES|SSL_SHA|SSL_TLSV1,
+ SSL_NOT_EXP|SSL_HIGH,
+ 0,
+ 256,
+ 256,
+ SSL_ALL_CIPHERS,
+ SSL_ALL_STRENGTHS
+ ));
+
+ /* New TLS Export CipherSuites */
+ /* Cipher 60 */
+ Ciphers.add(new Def(
+ 1,
+ TLS1_TXT_RSA_EXPORT1024_WITH_RC4_56_MD5,
+ TLS1_CK_RSA_EXPORT1024_WITH_RC4_56_MD5,
+ SSL_kRSA|SSL_aRSA|SSL_RC4|SSL_MD5|SSL_TLSV1,
+ SSL_EXPORT|SSL_EXP56,
+ 0,
+ 56,
+ 128,
+ SSL_ALL_CIPHERS,
+ SSL_ALL_STRENGTHS
+ ));
+ /* Cipher 61 */
+ Ciphers.add(new Def(
+ 1,
+ TLS1_TXT_RSA_EXPORT1024_WITH_RC2_CBC_56_MD5,
+ TLS1_CK_RSA_EXPORT1024_WITH_RC2_CBC_56_MD5,
+ SSL_kRSA|SSL_aRSA|SSL_RC2|SSL_MD5|SSL_TLSV1,
+ SSL_EXPORT|SSL_EXP56,
+ 0,
+ 56,
+ 128,
+ SSL_ALL_CIPHERS,
+ SSL_ALL_STRENGTHS
+ ));
+ /* Cipher 62 */
+ Ciphers.add(new Def(
+ 1,
+ TLS1_TXT_RSA_EXPORT1024_WITH_DES_CBC_SHA,
+ TLS1_CK_RSA_EXPORT1024_WITH_DES_CBC_SHA,
+ SSL_kRSA|SSL_aRSA|SSL_DES|SSL_SHA|SSL_TLSV1,
+ SSL_EXPORT|SSL_EXP56,
+ 0,
+ 56,
+ 56,
+ SSL_ALL_CIPHERS,
+ SSL_ALL_STRENGTHS
+ ));
+ /* Cipher 63 */
+ Ciphers.add(new Def(
+ 1,
+ TLS1_TXT_DHE_DSS_EXPORT1024_WITH_DES_CBC_SHA,
+ TLS1_CK_DHE_DSS_EXPORT1024_WITH_DES_CBC_SHA,
+ SSL_kEDH|SSL_aDSS|SSL_DES|SSL_SHA|SSL_TLSV1,
+ SSL_EXPORT|SSL_EXP56,
+ 0,
+ 56,
+ 56,
+ SSL_ALL_CIPHERS,
+ SSL_ALL_STRENGTHS
+ ));
+ /* Cipher 64 */
+ Ciphers.add(new Def(
+ 1,
+ TLS1_TXT_RSA_EXPORT1024_WITH_RC4_56_SHA,
+ TLS1_CK_RSA_EXPORT1024_WITH_RC4_56_SHA,
+ SSL_kRSA|SSL_aRSA|SSL_RC4|SSL_SHA|SSL_TLSV1,
+ SSL_EXPORT|SSL_EXP56,
+ 0,
+ 56,
+ 128,
+ SSL_ALL_CIPHERS,
+ SSL_ALL_STRENGTHS
+ ));
+ /* Cipher 65 */
+ Ciphers.add(new Def(
+ 1,
+ TLS1_TXT_DHE_DSS_EXPORT1024_WITH_RC4_56_SHA,
+ TLS1_CK_DHE_DSS_EXPORT1024_WITH_RC4_56_SHA,
+ SSL_kEDH|SSL_aDSS|SSL_RC4|SSL_SHA|SSL_TLSV1,
+ SSL_EXPORT|SSL_EXP56,
+ 0,
+ 56,
+ 128,
+ SSL_ALL_CIPHERS,
+ SSL_ALL_STRENGTHS
+ ));
+ /* Cipher 66 */
+ Ciphers.add(new Def(
+ 1,
+ TLS1_TXT_DHE_DSS_WITH_RC4_128_SHA,
+ TLS1_CK_DHE_DSS_WITH_RC4_128_SHA,
+ SSL_kEDH|SSL_aDSS|SSL_RC4|SSL_SHA|SSL_TLSV1,
+ SSL_NOT_EXP|SSL_MEDIUM,
+ 0,
+ 128,
+ 128,
+ SSL_ALL_CIPHERS,
+ SSL_ALL_STRENGTHS
+ ));
+ /* Cipher C001 */
+ Ciphers.add(new Def(
+ 1,
+ TLS1_TXT_ECDH_ECDSA_WITH_NULL_SHA,
+ TLS1_CK_ECDH_ECDSA_WITH_NULL_SHA,
+ SSL_kECDH|SSL_aECDSA|SSL_eNULL|SSL_SHA|SSL_TLSV1,
+ SSL_NOT_EXP,
+ 0,
+ 0,
+ 0,
+ SSL_ALL_CIPHERS,
+ SSL_ALL_STRENGTHS
+ ));
+
+ /* Cipher C002 */
+ Ciphers.add(new Def(
+ 1,
+ TLS1_TXT_ECDH_ECDSA_WITH_RC4_128_SHA,
+ TLS1_CK_ECDH_ECDSA_WITH_RC4_128_SHA,
+ SSL_kECDH|SSL_aECDSA|SSL_RC4|SSL_SHA|SSL_TLSV1,
+ SSL_NOT_EXP,
+ 0,
+ 128,
+ 128,
+ SSL_ALL_CIPHERS,
+ SSL_ALL_STRENGTHS
+ ));
+
+ /* Cipher C003 */
+ Ciphers.add(new Def(
+ 1,
+ TLS1_TXT_ECDH_ECDSA_WITH_DES_192_CBC3_SHA,
+ TLS1_CK_ECDH_ECDSA_WITH_DES_192_CBC3_SHA,
+ SSL_kECDH|SSL_aECDSA|SSL_3DES|SSL_SHA|SSL_TLSV1,
+ SSL_NOT_EXP|SSL_HIGH,
+ 0,
+ 168,
+ 168,
+ SSL_ALL_CIPHERS,
+ SSL_ALL_STRENGTHS
+ ));
+
+ /* Cipher C004 */
+ Ciphers.add(new Def(
+ 1,
+ TLS1_TXT_ECDH_ECDSA_WITH_AES_128_CBC_SHA,
+ TLS1_CK_ECDH_ECDSA_WITH_AES_128_CBC_SHA,
+ SSL_kECDH|SSL_aECDSA|SSL_AES|SSL_SHA|SSL_TLSV1,
+ SSL_NOT_EXP|SSL_HIGH,
+ 0,
+ 128,
+ 128,
+ SSL_ALL_CIPHERS,
+ SSL_ALL_STRENGTHS
+ ));
+
+ /* Cipher C005 */
+ Ciphers.add(new Def(
+ 1,
+ TLS1_TXT_ECDH_ECDSA_WITH_AES_256_CBC_SHA,
+ TLS1_CK_ECDH_ECDSA_WITH_AES_256_CBC_SHA,
+ SSL_kECDH|SSL_aECDSA|SSL_AES|SSL_SHA|SSL_TLSV1,
+ SSL_NOT_EXP|SSL_HIGH,
+ 0,
+ 256,
+ 256,
+ SSL_ALL_CIPHERS,
+ SSL_ALL_STRENGTHS
+ ));
+
+ /* Cipher C006 */
+ Ciphers.add(new Def(
+ 1,
+ TLS1_TXT_ECDHE_ECDSA_WITH_NULL_SHA,
+ TLS1_CK_ECDHE_ECDSA_WITH_NULL_SHA,
+ SSL_kECDHE|SSL_aECDSA|SSL_eNULL|SSL_SHA|SSL_TLSV1,
+ SSL_NOT_EXP,
+ 0,
+ 0,
+ 0,
+ SSL_ALL_CIPHERS,
+ SSL_ALL_STRENGTHS
+ ));
+
+ /* Cipher C007 */
+ Ciphers.add(new Def(
+ 1,
+ TLS1_TXT_ECDHE_ECDSA_WITH_RC4_128_SHA,
+ TLS1_CK_ECDHE_ECDSA_WITH_RC4_128_SHA,
+ SSL_kECDHE|SSL_aECDSA|SSL_RC4|SSL_SHA|SSL_TLSV1,
+ SSL_NOT_EXP,
+ 0,
+ 128,
+ 128,
+ SSL_ALL_CIPHERS,
+ SSL_ALL_STRENGTHS
+ ));
+
+ /* Cipher C008 */
+ Ciphers.add(new Def(
+ 1,
+ TLS1_TXT_ECDHE_ECDSA_WITH_DES_192_CBC3_SHA,
+ TLS1_CK_ECDHE_ECDSA_WITH_DES_192_CBC3_SHA,
+ SSL_kECDHE|SSL_aECDSA|SSL_3DES|SSL_SHA|SSL_TLSV1,
+ SSL_NOT_EXP|SSL_HIGH,
+ 0,
+ 168,
+ 168,
+ SSL_ALL_CIPHERS,
+ SSL_ALL_STRENGTHS
+ ));
+
+ /* Cipher C009 */
+ Ciphers.add(new Def(
+ 1,
+ TLS1_TXT_ECDHE_ECDSA_WITH_AES_128_CBC_SHA,
+ TLS1_CK_ECDHE_ECDSA_WITH_AES_128_CBC_SHA,
+ SSL_kECDHE|SSL_aECDSA|SSL_AES|SSL_SHA|SSL_TLSV1,
+ SSL_NOT_EXP|SSL_HIGH,
+ 0,
+ 128,
+ 128,
+ SSL_ALL_CIPHERS,
+ SSL_ALL_STRENGTHS
+ ));
+
+ /* Cipher C00A */
+ Ciphers.add(new Def(
+ 1,
+ TLS1_TXT_ECDHE_ECDSA_WITH_AES_256_CBC_SHA,
+ TLS1_CK_ECDHE_ECDSA_WITH_AES_256_CBC_SHA,
+ SSL_kECDHE|SSL_aECDSA|SSL_AES|SSL_SHA|SSL_TLSV1,
+ SSL_NOT_EXP|SSL_HIGH,
+ 0,
+ 256,
+ 256,
+ SSL_ALL_CIPHERS,
+ SSL_ALL_STRENGTHS
+ ));
+
+ /* Cipher C00B */
+ Ciphers.add(new Def(
+ 1,
+ TLS1_TXT_ECDH_RSA_WITH_NULL_SHA,
+ TLS1_CK_ECDH_RSA_WITH_NULL_SHA,
+ SSL_kECDH|SSL_aRSA|SSL_eNULL|SSL_SHA|SSL_TLSV1,
+ SSL_NOT_EXP,
+ 0,
+ 0,
+ 0,
+ SSL_ALL_CIPHERS,
+ SSL_ALL_STRENGTHS
+ ));
+
+ /* Cipher C00C */
+ Ciphers.add(new Def(
+ 1,
+ TLS1_TXT_ECDH_RSA_WITH_RC4_128_SHA,
+ TLS1_CK_ECDH_RSA_WITH_RC4_128_SHA,
+ SSL_kECDH|SSL_aRSA|SSL_RC4|SSL_SHA|SSL_TLSV1,
+ SSL_NOT_EXP,
+ 0,
+ 128,
+ 128,
+ SSL_ALL_CIPHERS,
+ SSL_ALL_STRENGTHS
+ ));
+
+ /* Cipher C00D */
+ Ciphers.add(new Def(
+ 1,
+ TLS1_TXT_ECDH_RSA_WITH_DES_192_CBC3_SHA,
+ TLS1_CK_ECDH_RSA_WITH_DES_192_CBC3_SHA,
+ SSL_kECDH|SSL_aRSA|SSL_3DES|SSL_SHA|SSL_TLSV1,
+ SSL_NOT_EXP|SSL_HIGH,
+ 0,
+ 168,
+ 168,
+ SSL_ALL_CIPHERS,
+ SSL_ALL_STRENGTHS
+ ));
+
+ /* Cipher C00E */
+ Ciphers.add(new Def(
+ 1,
+ TLS1_TXT_ECDH_RSA_WITH_AES_128_CBC_SHA,
+ TLS1_CK_ECDH_RSA_WITH_AES_128_CBC_SHA,
+ SSL_kECDH|SSL_aRSA|SSL_AES|SSL_SHA|SSL_TLSV1,
+ SSL_NOT_EXP|SSL_HIGH,
+ 0,
+ 128,
+ 128,
+ SSL_ALL_CIPHERS,
+ SSL_ALL_STRENGTHS
+ ));
+
+ /* Cipher C00F */
+ Ciphers.add(new Def(
+ 1,
+ TLS1_TXT_ECDH_RSA_WITH_AES_256_CBC_SHA,
+ TLS1_CK_ECDH_RSA_WITH_AES_256_CBC_SHA,
+ SSL_kECDH|SSL_aRSA|SSL_AES|SSL_SHA|SSL_TLSV1,
+ SSL_NOT_EXP|SSL_HIGH,
+ 0,
+ 256,
+ 256,
+ SSL_ALL_CIPHERS,
+ SSL_ALL_STRENGTHS
+ ));
+
+ /* Cipher C010 */
+ Ciphers.add(new Def(
+ 1,
+ TLS1_TXT_ECDHE_RSA_WITH_NULL_SHA,
+ TLS1_CK_ECDHE_RSA_WITH_NULL_SHA,
+ SSL_kECDHE|SSL_aRSA|SSL_eNULL|SSL_SHA|SSL_TLSV1,
+ SSL_NOT_EXP,
+ 0,
+ 0,
+ 0,
+ SSL_ALL_CIPHERS,
+ SSL_ALL_STRENGTHS
+ ));
+
+ /* Cipher C011 */
+ Ciphers.add(new Def(
+ 1,
+ TLS1_TXT_ECDHE_RSA_WITH_RC4_128_SHA,
+ TLS1_CK_ECDHE_RSA_WITH_RC4_128_SHA,
+ SSL_kECDHE|SSL_aRSA|SSL_RC4|SSL_SHA|SSL_TLSV1,
+ SSL_NOT_EXP,
+ 0,
+ 128,
+ 128,
+ SSL_ALL_CIPHERS,
+ SSL_ALL_STRENGTHS
+ ));
+
+ /* Cipher C012 */
+ Ciphers.add(new Def(
+ 1,
+ TLS1_TXT_ECDHE_RSA_WITH_DES_192_CBC3_SHA,
+ TLS1_CK_ECDHE_RSA_WITH_DES_192_CBC3_SHA,
+ SSL_kECDHE|SSL_aRSA|SSL_3DES|SSL_SHA|SSL_TLSV1,
+ SSL_NOT_EXP|SSL_HIGH,
+ 0,
+ 168,
+ 168,
+ SSL_ALL_CIPHERS,
+ SSL_ALL_STRENGTHS
+ ));
+
+ /* Cipher C013 */
+ Ciphers.add(new Def(
+ 1,
+ TLS1_TXT_ECDHE_RSA_WITH_AES_128_CBC_SHA,
+ TLS1_CK_ECDHE_RSA_WITH_AES_128_CBC_SHA,
+ SSL_kECDHE|SSL_aRSA|SSL_AES|SSL_SHA|SSL_TLSV1,
+ SSL_NOT_EXP|SSL_HIGH,
+ 0,
+ 128,
+ 128,
+ SSL_ALL_CIPHERS,
+ SSL_ALL_STRENGTHS
+ ));
+
+ /* Cipher C014 */
+ Ciphers.add(new Def(
+ 1,
+ TLS1_TXT_ECDHE_RSA_WITH_AES_256_CBC_SHA,
+ TLS1_CK_ECDHE_RSA_WITH_AES_256_CBC_SHA,
+ SSL_kECDHE|SSL_aRSA|SSL_AES|SSL_SHA|SSL_TLSV1,
+ SSL_NOT_EXP|SSL_HIGH,
+ 0,
+ 256,
+ 256,
+ SSL_ALL_CIPHERS,
+ SSL_ALL_STRENGTHS
+ ));
+
+ /* Cipher C015 */
+ Ciphers.add(new Def(
+ 1,
+ TLS1_TXT_ECDH_anon_WITH_NULL_SHA,
+ TLS1_CK_ECDH_anon_WITH_NULL_SHA,
+ SSL_kECDHE|SSL_aNULL|SSL_eNULL|SSL_SHA|SSL_TLSV1,
+ SSL_NOT_EXP,
+ 0,
+ 0,
+ 0,
+ SSL_ALL_CIPHERS,
+ SSL_ALL_STRENGTHS
+ ));
+
+ /* Cipher C016 */
+ Ciphers.add(new Def(
+ 1,
+ TLS1_TXT_ECDH_anon_WITH_RC4_128_SHA,
+ TLS1_CK_ECDH_anon_WITH_RC4_128_SHA,
+ SSL_kECDHE|SSL_aNULL|SSL_RC4|SSL_SHA|SSL_TLSV1,
+ SSL_NOT_EXP,
+ 0,
+ 128,
+ 128,
+ SSL_ALL_CIPHERS,
+ SSL_ALL_STRENGTHS
+ ));
+
+ /* Cipher C017 */
+ Ciphers.add(new Def(
+ 1,
+ TLS1_TXT_ECDH_anon_WITH_DES_192_CBC3_SHA,
+ TLS1_CK_ECDH_anon_WITH_DES_192_CBC3_SHA,
+ SSL_kECDHE|SSL_aNULL|SSL_3DES|SSL_SHA|SSL_TLSV1,
+ SSL_NOT_EXP|SSL_HIGH,
+ 0,
+ 168,
+ 168,
+ SSL_ALL_CIPHERS,
+ SSL_ALL_STRENGTHS
+ ));
+
+ /* Cipher C018 */
+ Ciphers.add(new Def(
+ 1,
+ TLS1_TXT_ECDH_anon_WITH_AES_128_CBC_SHA,
+ TLS1_CK_ECDH_anon_WITH_AES_128_CBC_SHA,
+ SSL_kECDHE|SSL_aNULL|SSL_AES|SSL_SHA|SSL_TLSV1,
+ SSL_NOT_EXP|SSL_HIGH,
+ 0,
+ 128,
+ 128,
+ SSL_ALL_CIPHERS,
+ SSL_ALL_STRENGTHS
+ ));
+
+ /* Cipher C019 */
+ Ciphers.add(new Def(
+ 1,
+ TLS1_TXT_ECDH_anon_WITH_AES_256_CBC_SHA,
+ TLS1_CK_ECDH_anon_WITH_AES_256_CBC_SHA,
+ SSL_kECDHE|SSL_aNULL|SSL_AES|SSL_SHA|SSL_TLSV1,
+ SSL_NOT_EXP|SSL_HIGH,
+ 0,
+ 256,
+ 256,
+ SSL_ALL_CIPHERS,
+ SSL_ALL_STRENGTHS
+ ));
+
+ for(Def def : Ciphers) {
+ CipherNames.put(def.name, def);
+ }
+
+ addAlias("SSL_RSA_WITH_NULL_MD5","NULL-MD5");
+ addAlias("SSL_RSA_WITH_NULL_SHA","NULL-SHA");
+ addAlias("SSL_RSA_EXPORT_WITH_RC4_40_MD5","EXP-RC4-MD5");
+ addAlias("SSL_RSA_WITH_RC4_128_MD5","RC4-MD5");
+ addAlias("SSL_RSA_WITH_RC4_128_SHA","RC4-SHA");
+ addAlias("SSL_RSA_EXPORT_WITH_RC2_CBC_40_MD5","EXP-RC2-CBC-MD5");
+ addAlias("SSL_RSA_WITH_IDEA_CBC_SHA","IDEA-CBC-SHA");
+ addAlias("SSL_RSA_EXPORT_WITH_DES40_CBC_SHA","EXP-DES-CBC-SHA");
+ addAlias("SSL_RSA_WITH_DES_CBC_SHA","DES-CBC-SHA");
+ addAlias("SSL_RSA_WITH_3DES_EDE_CBC_SHA","DES-CBC3-SHA");
+ addAlias("SSL_DHE_DSS_EXPORT_WITH_DES40_CBC_SHA","EXP-EDH-DSS-DES-CBC-SHA");
+ addAlias("SSL_DHE_DSS_WITH_DES_CBC_SHA","EDH-DSS-CBC-SHA");
+ addAlias("SSL_DHE_DSS_WITH_3DES_EDE_CBC_SHA","EDH-DSS-DES-CBC3-SHA");
+ addAlias("SSL_DHE_RSA_EXPORT_WITH_DES40_CBC_SHA","EXP-EDH-RSA-DES-CBC-SHA");
+ addAlias("SSL_DHE_RSA_WITH_DES_CBC_SHA","EDH-RSA-DES-CBC-SHA");
+ addAlias("SSL_DHE_RSA_WITH_3DES_EDE_CBC_SHA","EDH-RSA-DES-CBC3-SHA");
+ addAlias("SSL_DH_anon_EXPORT_WITH_RC4_40_MD5","EXP-ADH-RC4-MD5");
+ addAlias("SSL_DH_anon_WITH_RC4_128_MD5","ADH-RC4-MD5");
+ addAlias("SSL_DH_anon_EXPORT_WITH_DES40_CBC_SHA","EXP-ADH-DES-CBC-SHA");
+ addAlias("SSL_DH_anon_WITH_DES_CBC_SHA","ADH-DES-CBC-SHA");
+ addAlias("SSL_DH_anon_WITH_3DES_EDE_CBC_SHA","ADH-DES-CBC3-SHA");
+ addAlias("TLS_RSA_WITH_NULL_MD5","NULL-MD5");
+ addAlias("TLS_RSA_WITH_NULL_SHA","NULL-SHA");
+ addAlias("TLS_RSA_EXPORT_WITH_RC4_40_MD5","EXP-RC4-MD5");
+ addAlias("TLS_RSA_WITH_RC4_128_MD5","RC4-MD5");
+ addAlias("TLS_RSA_WITH_RC4_128_SHA","RC4-SHA");
+ addAlias("TLS_RSA_EXPORT_WITH_RC2_CBC_40_MD5","EXP-RC2-CBC-MD5");
+ addAlias("TLS_RSA_WITH_IDEA_CBC_SHA","IDEA-CBC-SHA");
+ addAlias("TLS_RSA_EXPORT_WITH_DES40_CBC_SHA","EXP-DES-CBC-SHA");
+ addAlias("TLS_RSA_WITH_DES_CBC_SHA","DES-CBC-SHA");
+ addAlias("TLS_RSA_WITH_3DES_EDE_CBC_SHA","DES-CBC3-SHA");
+ addAlias("TLS_DHE_DSS_EXPORT_WITH_DES40_CBC_SHA","EXP-EDH-DSS-DES-CBC-SHA");
+ addAlias("TLS_DHE_DSS_WITH_DES_CBC_SHA","EDH-DSS-CBC-SHA");
+ addAlias("TLS_DHE_DSS_WITH_3DES_EDE_CBC_SHA","EDH-DSS-DES-CBC3-SHA");
+ addAlias("TLS_DHE_RSA_EXPORT_WITH_DES40_CBC_SHA","EXP-EDH-RSA-DES-CBC-SHA");
+ addAlias("TLS_DHE_RSA_WITH_DES_CBC_SHA","EDH-RSA-DES-CBC-SHA");
+ addAlias("TLS_DHE_RSA_WITH_3DES_EDE_CBC_SHA","EDH-RSA-DES-CBC3-SHA");
+ addAlias("TLS_DH_anon_EXPORT_WITH_RC4_40_MD5","EXP-ADH-RC4-MD5");
+ addAlias("TLS_DH_anon_WITH_RC4_128_MD5","ADH-RC4-MD5");
+ addAlias("TLS_DH_anon_EXPORT_WITH_DES40_CBC_SHA","EXP-ADH-DES-CBC-SHA");
+ addAlias("TLS_DH_anon_WITH_DES_CBC_SHA","ADH-DES-CBC-SHA");
+ addAlias("TLS_DH_anon_WITH_3DES_EDE_CBC_SHA","ADH-DES-CBC3-SHA");
+ addAlias("TLS_RSA_WITH_AES_128_CBC_SHA","AES128-SHA");
+ addAlias("TLS_RSA_WITH_AES_256_CBC_SHA","AES256-SHA");
+ addAlias("TLS_DH_DSS_WITH_AES_128_CBC_SHA","DH-DSS-AES128-SHA");
+ addAlias("TLS_DH_DSS_WITH_AES_256_CBC_SHA","DH-DSS-AES256-SHA");
+ addAlias("TLS_DH_RSA_WITH_AES_128_CBC_SHA","DH-RSA-AES128-SHA");
+ addAlias("TLS_DH_RSA_WITH_AES_256_CBC_SHA","DH-RSA-AES256-SHA");
+ addAlias("TLS_DHE_DSS_WITH_AES_128_CBC_SHA","DHE-DSS-AES128-SHA");
+ addAlias("TLS_DHE_DSS_WITH_AES_256_CBC_SHA","DHE-DSS-AES256-SHA");
+ addAlias("TLS_DHE_RSA_WITH_AES_128_CBC_SHA","DHE-RSA-AES128-SHA");
+ addAlias("TLS_DHE_RSA_WITH_AES_256_CBC_SHA","DHE-RSA-AES256-SHA");
+ addAlias("TLS_DH_anon_WITH_AES_128_CBC_SHA","ADH-AES128-SHA");
+ addAlias("TLS_DH_anon_WITH_AES_256_CBC_SHA","ADH-AES256-SHA");
+ addAlias("TLS_RSA_EXPORT1024_WITH_DES_CBC_SHA","EXP1024-DES-CBC-SHA");
+ addAlias("TLS_RSA_EXPORT1024_WITH_RC4_56_SHA","EXP1024-RC4-SHA");
+ addAlias("TLS_DHE_DSS_EXPORT1024_WITH_DES_CBC_SHA","EXP1024-DHE-DSS-DES-CBC-SHA");
+ addAlias("TLS_DHE_DSS_EXPORT1024_WITH_RC4_56_SHA","EXP1024-DHE-DSS-RC4-SHA");
+ addAlias("TLS_DHE_DSS_WITH_RC4_128_SHA","DHE-DSS-RC4-SHA");
+ addAlias("SSL_CK_RC4_128_WITH_MD5","RC4-MD5");
+ addAlias("SSL_CK_RC4_128_EXPORT40_WITH_MD5","EXP-RC4-MD5");
+ addAlias("SSL_CK_RC2_128_CBC_WITH_MD5","RC2-MD5");
+ addAlias("SSL_CK_RC2_128_CBC_EXPORT40_WITH_MD5","EXP-RC2-MD5");
+ addAlias("SSL_CK_IDEA_128_CBC_WITH_MD5","IDEA-CBC-MD5");
+ addAlias("SSL_CK_DES_64_CBC_WITH_MD5","DES-CBC-MD5");
+ addAlias("SSL_CK_DES_192_EDE3_CBC_WITH_MD5","DES-CBC3-MD5");
+ }
+}// CipherStrings
diff --git a/src/org/jruby/ext/openssl/Config.java b/src/org/jruby/ext/openssl/Config.java
new file mode 100644
index 00000000000..dd8fd302c2c
--- /dev/null
+++ b/src/org/jruby/ext/openssl/Config.java
@@ -0,0 +1,49 @@
+/***** BEGIN LICENSE BLOCK *****
+ * Version: CPL 1.0/GPL 2.0/LGPL 2.1
+ *
+ * The contents of this file are subject to the Common Public
+ * License Version 1.0 (the "License"); you may not use this file
+ * except in compliance with the License. You may obtain a copy of
+ * the License at http://www.eclipse.org/legal/cpl-v10.html
+ *
+ * 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 CPL, 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 CPL, 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;
+
+/**
+ * @author Ola Bini
+ */
+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 mOSSL) {
+ RubyClass cConfig = mOSSL.defineClassUnder("Config", runtime.getObject(), runtime.getObject().getAllocator());
+ cConfig.defineAnnotatedMethods(Config.class);
+ RubyClass openSSLError = mOSSL.getClass("OpenSSLError");
+ mOSSL.defineClassUnder("ConfigError", openSSLError, openSSLError.getAllocator());
+ // TODO: we should define this constant with proper path. (see above)
+ //cConfig.setConstant("DEFAULT_CONFIG_FILE", runtime.newString(DEFAULT_CONFIG_FILE));
+ }
+}// Config
diff --git a/src/org/jruby/ext/openssl/DefaultPEMHandler.java b/src/org/jruby/ext/openssl/DefaultPEMHandler.java
new file mode 100644
index 00000000000..d47337c03aa
--- /dev/null
+++ b/src/org/jruby/ext/openssl/DefaultPEMHandler.java
@@ -0,0 +1,44 @@
+/***** BEGIN LICENSE BLOCK *****
+ * Version: CPL 1.0/GPL 2.0/LGPL 2.1
+ *
+ * The contents of this file are subject to the Common Public
+ * License Version 1.0 (the "License"); you may not use this file
+ * except in compliance with the License. You may obtain a copy of
+ * the License at http://www.eclipse.org/legal/cpl-v10.html
+ *
+ * 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 CPL, 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 CPL, the GPL or the LGPL.
+ ***** END LICENSE BLOCK *****/
+package org.jruby.ext.openssl;
+
+import java.io.Reader;
+import java.io.Writer;
+
+/**
+ * @author Ola Bini
+ */
+public class DefaultPEMHandler implements PEMHandler {
+ public Object readPEM(Reader read, String password) {
+ return null;
+ }
+ public void writePEM(Writer writ, Object obj, String algorithm, char[] password) {
+ }
+ public void writePEM(Writer writ, Object obj) {
+ }
+}// DefaultPEMHandler
diff --git a/src/org/jruby/ext/openssl/Digest.java b/src/org/jruby/ext/openssl/Digest.java
new file mode 100644
index 00000000000..a36337f85b3
--- /dev/null
+++ b/src/org/jruby/ext/openssl/Digest.java
@@ -0,0 +1,191 @@
+/***** BEGIN LICENSE BLOCK *****
+ * Version: CPL 1.0/GPL 2.0/LGPL 2.1
+ *
+ * The contents of this file are subject to the Common Public
+ * License Version 1.0 (the "License"); you may not use this file
+ * except in compliance with the License. You may obtain a copy of
+ * the License at http://www.eclipse.org/legal/cpl-v10.html
+ *
+ * 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, 2007 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 CPL, 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 CPL, the GPL or the LGPL.
+ ***** END LICENSE BLOCK *****/
+package org.jruby.ext.openssl;
+
+import java.security.GeneralSecurityException;
+import java.security.MessageDigest;
+import java.security.NoSuchAlgorithmException;
+
+import org.jruby.Ruby;
+import org.jruby.RubyClass;
+import org.jruby.RubyFixnum;
+import org.jruby.RubyModule;
+import org.jruby.RubyObject;
+import org.jruby.RubyString;
+import org.jruby.anno.JRubyMethod;
+import org.jruby.runtime.ObjectAllocator;
+import org.jruby.runtime.builtin.IRubyObject;
+import org.jruby.util.ByteList;
+
+/**
+ * @author Ola Bini
+ */
+public class Digest extends RubyObject {
+ private static final long serialVersionUID = 1L;
+
+ private static ObjectAllocator DIGEST_ALLOCATOR = new ObjectAllocator() {
+ public IRubyObject allocate(Ruby runtime, RubyClass klass) {
+ return new Digest(runtime, klass);
+ }
+ };
+
+ public static void createDigest(Ruby runtime, RubyModule mOSSL) {
+ runtime.getLoadService().require("digest");
+ RubyModule mDigest = runtime.fastGetModule("Digest");
+ RubyClass cDigestClass = mDigest.fastGetClass("Class");
+ RubyClass cDigest = mOSSL.defineClassUnder("Digest", cDigestClass, DIGEST_ALLOCATOR);
+ cDigest.defineAnnotatedMethods(Digest.class);
+ RubyClass openSSLError = mOSSL.getClass("OpenSSLError");
+ mOSSL.defineClassUnder("DigestError", openSSLError, openSSLError.getAllocator());
+ }
+
+ static MessageDigest getDigest(final String name, final Ruby runtime) {
+ String algorithm = transformDigest(name);
+ try {
+ return MessageDigest.getInstance(algorithm);
+ } catch (NoSuchAlgorithmException e) {
+ try {
+ return OpenSSLReal.getMessageDigestBC(algorithm);
+ } catch (GeneralSecurityException ignore) {
+ }
+ throw runtime.newNotImplementedError("Unsupported digest algorithm (" + name + ")");
+ }
+ }
+
+ // name mapping for openssl -> JCE
+ private static String transformDigest(String inp) {
+ String[] sp = inp.split("::");
+ if (sp.length > 1) { // We only want Digest names from the last part of class name
+ inp = sp[sp.length - 1];
+ }
+ // MessageDigest algorithm name normalization.
+ // BC accepts "SHA1" but it should be "SHA-1" per spec.
+ if ("DSS".equalsIgnoreCase(inp)) {
+ return "SHA"; // why?
+ } else if ("DSS1".equalsIgnoreCase(inp)) {
+ return "SHA-1";
+ } else if (inp.toUpperCase().startsWith("SHA") && inp.length() > 3 && inp.charAt(3) != '-') {
+ inp = "SHA-" + inp.substring(3);
+ }
+ return inp;
+ }
+
+ public Digest(Ruby runtime, RubyClass type) {
+ super(runtime,type);
+ // do not initialize MessageDigest at allocation time (same as the ruby-openssl)
+ name = null;
+ algo = null;
+ }
+ private MessageDigest algo;
+ private String name;
+
+ public String getRealName() {
+ return transformDigest(name);
+ }
+
+ public String getName() {
+ return name;
+ }
+
+ @JRubyMethod(required = 1, optional = 1)
+ public IRubyObject initialize(IRubyObject[] args) {
+ IRubyObject type = args[0];
+ IRubyObject data = getRuntime().getNil();
+ if (args.length > 1) {
+ data = args[1];
+ }
+ name = type.toString();
+ algo = getDigest(name, getRuntime());
+ if (!data.isNil()) {
+ update(data.convertToString());
+ }
+ return this;
+ }
+
+ @Override
+ @JRubyMethod
+ public IRubyObject initialize_copy(IRubyObject obj) {
+ checkFrozen();
+ if(this == obj) {
+ return this;
+ }
+ name = ((Digest)obj).algo.getAlgorithm();
+ try {
+ algo = (MessageDigest)((Digest)obj).algo.clone();
+ } catch(CloneNotSupportedException e) {
+ throw getRuntime().newTypeError("Could not initialize copy of digest (" + name + ")");
+ }
+ return this;
+ }
+
+ @JRubyMethod(name={"update","<<"})
+ public IRubyObject update(IRubyObject obj) {
+ ByteList bytes = obj.convertToString().getByteList();
+ algo.update(bytes.bytes, bytes.begin, bytes.realSize);
+ return this;
+ }
+
+ @JRubyMethod
+ public IRubyObject reset() {
+ algo.reset();
+ return this;
+ }
+
+ @JRubyMethod
+ public IRubyObject finish() {
+ IRubyObject digest = RubyString.newStringNoCopy(getRuntime(), algo.digest());
+ algo.reset();
+ return digest;
+ }
+
+ @JRubyMethod
+ public IRubyObject name() {
+ return getRuntime().newString(name);
+ }
+
+ @JRubyMethod()
+ public IRubyObject digest_length() {
+ return RubyFixnum.newFixnum(getRuntime(), algo.getDigestLength());
+ }
+
+ @JRubyMethod()
+ public IRubyObject block_length() {
+ // TODO: ruby-openssl supports it.
+ throw getRuntime().newRuntimeError(
+ this.getMetaClass() + " doesn't implement block_length()");
+ }
+
+ String getAlgorithm() {
+ return this.algo.getAlgorithm();
+ }
+
+ String getShortAlgorithm() {
+ return getAlgorithm().replace("-", "");
+ }
+}
+
diff --git a/src/org/jruby/ext/openssl/HMAC.java b/src/org/jruby/ext/openssl/HMAC.java
new file mode 100644
index 00000000000..d2b5e6237e2
--- /dev/null
+++ b/src/org/jruby/ext/openssl/HMAC.java
@@ -0,0 +1,178 @@
+/***** BEGIN LICENSE BLOCK *****
+ * Version: CPL 1.0/GPL 2.0/LGPL 2.1
+ *
+ * The contents of this file are subject to the Common Public
+ * License Version 1.0 (the "License"); you may not use this file
+ * except in compliance with the License. You may obtain a copy of
+ * the License at http://www.eclipse.org/legal/cpl-v10.html
+ *
+ * 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, 2007 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 CPL, 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 CPL, the GPL or the LGPL.
+ ***** END LICENSE BLOCK *****/
+package org.jruby.ext.openssl;
+
+import java.security.NoSuchAlgorithmException;
+import javax.crypto.Mac;
+import javax.crypto.SecretKey;
+import javax.crypto.spec.SecretKeySpec;
+
+import org.jruby.Ruby;
+import org.jruby.RubyClass;
+import org.jruby.RubyModule;
+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;
+
+/**
+ * @author Ola Bini
+ */
+public class HMAC extends RubyObject {
+ private static final long serialVersionUID = 7602535792884680307L;
+
+ private static ObjectAllocator HMAC_ALLOCATOR = new ObjectAllocator() {
+ public IRubyObject allocate(Ruby runtime, RubyClass klass) {
+ return new HMAC(runtime, klass);
+ }
+ };
+
+ public static void createHMAC(Ruby runtime, RubyModule ossl) {
+ RubyClass cHMAC = ossl.defineClassUnder("HMAC",runtime.getObject(),HMAC_ALLOCATOR);
+ RubyClass openSSLError = ossl.getClass("OpenSSLError");
+ ossl.defineClassUnder("HMACError",openSSLError,openSSLError.getAllocator());
+
+ cHMAC.defineAnnotatedMethods(HMAC.class);
+ }
+
+ static Mac getMac(String algoName) throws NoSuchAlgorithmException {
+ // some algorithms need the - removed; this is ugly, I know.
+ try {
+ return Mac.getInstance("HMAC" + algoName.replaceAll("-", ""));
+ } catch (NoSuchAlgorithmException nsae) {
+ return Mac.getInstance("HMAC-" + algoName.replaceAll("-", ""));
+ }
+ }
+
+ @JRubyMethod(name = "digest", meta = true)
+ public static IRubyObject s_digest(IRubyObject recv, IRubyObject digest, IRubyObject kay, IRubyObject data) {
+ String algoName = getDigestAlgorithmName(digest);
+ try {
+ Mac mac = getMac(algoName);
+ byte[] key = kay.convertToString().getBytes();
+ SecretKey keysp = new SecretKeySpec(key, mac.getAlgorithm());
+ mac.init(keysp);
+ return RubyString.newString(recv.getRuntime(), mac.doFinal(data.convertToString().getBytes()));
+ } catch (Exception e) {
+ e.printStackTrace();
+ throw recv.getRuntime().newNotImplementedError(e.getMessage());
+ }
+ }
+
+ @JRubyMethod(name = "hexdigest", meta = true)
+ public static IRubyObject s_hexdigest(IRubyObject recv, IRubyObject digest, IRubyObject kay, IRubyObject data) {
+ String algoName = getDigestAlgorithmName(digest);
+ try {
+ Mac mac = getMac(algoName);
+ byte[] key = kay.convertToString().getBytes();
+ SecretKey keysp = new SecretKeySpec(key, mac.getAlgorithm());
+ mac.init(keysp);
+ return RubyString.newString(recv.getRuntime(), ByteList.plain(Utils.toHex(mac.doFinal(data.convertToString().getBytes()))));
+ } catch (Exception e) {
+ throw recv.getRuntime().newNotImplementedError(e.getMessage());
+ }
+ }
+
+ public HMAC(Ruby runtime, RubyClass type) {
+ super(runtime,type);
+ }
+
+ private Mac mac;
+ private byte[] key;
+ private StringBuffer data = new StringBuffer();
+
+ @JRubyMethod
+ public IRubyObject initialize(IRubyObject kay, IRubyObject digest) {
+ String algoName = getDigestAlgorithmName(digest);
+ try {
+ mac = getMac(algoName);
+ key = kay.convertToString().getBytes();
+ SecretKey keysp = new SecretKeySpec(key, mac.getAlgorithm());
+ mac.init(keysp);
+ } catch (Exception e) {
+ throw getRuntime().newNotImplementedError(e.getMessage());
+ }
+ return this;
+ }
+
+ @Override
+ @JRubyMethod
+ public IRubyObject initialize_copy(IRubyObject obj) {
+ if(this == obj) {
+ return this;
+ }
+ checkFrozen();
+ String name = ((HMAC)obj).mac.getAlgorithm();
+ try {
+ mac = Mac.getInstance(name);
+ key = ((HMAC)obj).key;
+ SecretKey keysp = new SecretKeySpec(key,name);
+ mac.init(keysp);
+ } catch(Exception e) {
+ throw getRuntime().newNotImplementedError("Unsupported MAC algorithm (" + name + ")");
+ }
+
+ data = new StringBuffer(((HMAC)obj).data.toString());
+
+ return this;
+ }
+
+ @JRubyMethod(name={"update", "<<"})
+ public IRubyObject update(IRubyObject obj) {
+ data.append(obj);
+ return this;
+ }
+
+ @JRubyMethod
+ public IRubyObject digest() {
+ mac.reset();
+ return RubyString.newString(getRuntime(), mac.doFinal(ByteList.plain(data)));
+ }
+
+ @JRubyMethod(name={"hexdigest","inspect","to_s"})
+ public IRubyObject hexdigest() {
+ mac.reset();
+ return RubyString.newString(getRuntime(), ByteList.plain(Utils.toHex(mac.doFinal(ByteList.plain(data)))));
+ }
+
+ String getAlgorithm() {
+ return this.mac.getAlgorithm();
+ }
+
+ private static String getDigestAlgorithmName(IRubyObject digest) {
+ String algoName = null;
+ if (digest instanceof Digest) {
+ algoName = ((Digest) digest).getShortAlgorithm();
+ } else {
+ algoName = digest.asString().toString();
+ }
+ return algoName;
+ }
+}// HMAC
diff --git a/src/org/jruby/ext/openssl/NetscapeSPKI.java b/src/org/jruby/ext/openssl/NetscapeSPKI.java
new file mode 100644
index 00000000000..59692c766a9
--- /dev/null
+++ b/src/org/jruby/ext/openssl/NetscapeSPKI.java
@@ -0,0 +1,252 @@
+/***** BEGIN LICENSE BLOCK *****
+ * Version: CPL 1.0/GPL 2.0/LGPL 2.1
+ *
+ * The contents of this file are subject to the Common Public
+ * License Version 1.0 (the "License"); you may not use this file
+ * except in compliance with the License. You may obtain a copy of
+ * the License at http://www.eclipse.org/legal/cpl-v10.html
+ *
+ * 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, 2007 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 CPL, 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 CPL, the GPL or the LGPL.
+ ***** END LICENSE BLOCK *****/
+package org.jruby.ext.openssl;
+
+import java.io.IOException;
+import java.security.GeneralSecurityException;
+import java.security.PublicKey;
+import org.bouncycastle.asn1.ASN1EncodableVector;
+import org.bouncycastle.asn1.DERBitString;
+import org.bouncycastle.asn1.DERIA5String;
+import org.bouncycastle.asn1.DERNull;
+import org.bouncycastle.asn1.DERObjectIdentifier;
+import org.bouncycastle.asn1.DERSequence;
+import org.bouncycastle.asn1.x509.AlgorithmIdentifier;
+import org.bouncycastle.jce.netscape.NetscapeCertRequest;
+import org.jruby.Ruby;
+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;
+
+/**
+ * @author Ola Bini
+ */
+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);
+ }
+ };
+
+ public static void createNetscapeSPKI(Ruby runtime, RubyModule ossl) {
+ RubyModule mNetscape = ossl.defineModuleUnder("Netscape");
+ RubyClass cSPKI = mNetscape.defineClassUnder("SPKI",runtime.getObject(),NETSCAPESPKI_ALLOCATOR);
+ RubyClass openSSLError = ossl.getClass("OpenSSLError");
+ mNetscape.defineClassUnder("SPKIError",openSSLError,openSSLError.getAllocator());
+
+ cSPKI.defineAnnotatedMethods(NetscapeSPKI.class);
+ }
+
+ public NetscapeSPKI(Ruby runtime, RubyClass type) {
+ super(runtime,type);
+ }
+
+ private IRubyObject public_key;
+ private IRubyObject challenge;
+
+ private NetscapeCertRequest cert;
+
+ @JRubyMethod(name = "initialize", rest = true)
+ public IRubyObject _initialize(IRubyObject[] args) {
+ if (args.length > 0) {
+ byte[] b = args[0].convertToString().getBytes();
+ b = tryBase64Decode(b);
+ final byte[] b2 = b;
+ String algo = null;
+ byte[] enc = null;
+ try {
+ // NetscapeCertRequest requires "BC" provider.
+ PublicKey pkey = (PublicKey) OpenSSLReal.getWithBCProvider(new OpenSSLReal.Callable() {
+
+ public Object call() throws GeneralSecurityException {
+ try {
+ // NetscapeCertRequest throws java.lang.IllegalArgumentException
+ // when no BC provider allowed, with a message
+ // "java.security.NoSuchProviderException: no such provider: BC"
+ // instead of NoSuchProviderException.
+ cert = new NetscapeCertRequest(b2);
+ challenge = getRuntime().newString(cert.getChallenge());
+ return cert.getPublicKey();
+ } catch (IOException ioe) {
+ throw new GeneralSecurityException(ioe.getMessage(), ioe);
+ }
+ }
+ });
+ algo = pkey.getAlgorithm();
+ enc = pkey.getEncoded();
+ } catch (GeneralSecurityException gse) {
+ throw newSPKIError(getRuntime(), gse.getMessage());
+ }
+
+ if ("RSA".equalsIgnoreCase(algo)) {
+ this.public_key = Utils.newRubyInstance(getRuntime(), "OpenSSL::PKey::RSA", RubyString.newString(getRuntime(), enc));
+ } else if ("DSA".equalsIgnoreCase(algo)) {
+ this.public_key = Utils.newRubyInstance(getRuntime(), "OpenSSL::PKey::DSA", RubyString.newString(getRuntime(), enc));
+ } else {
+ throw getRuntime().newLoadError("not implemented algo for public key: " + algo);
+ }
+ }
+ return this;
+ }
+
+ // just try to decode for the time when the given bytes are base64 encoded.
+ private byte[] tryBase64Decode(byte[] b) {
+ try {
+ b = Base64.decode(b, 0, b.length, Base64.NO_OPTIONS);
+ } catch (Exception ignored) {
+ }
+ return b;
+ }
+
+ @JRubyMethod
+ public IRubyObject to_der() {
+ try {
+ return RubyString.newString(getRuntime(), internalToDer());
+ } catch (IOException ioe) {
+ throw newSPKIError(getRuntime(), ioe.getMessage());
+ }
+ }
+
+ @JRubyMethod(name={"to_pem","to_s"})
+ public IRubyObject to_pem() {
+ try {
+ byte[] source = internalToDer();
+ // no Base64.DO_BREAK_LINES option needed for NSPKI.
+ return getRuntime().newString(Base64.encodeBytes(source, 0, source.length, Base64.NO_OPTIONS));
+ } catch (IOException ioe) {
+ throw newSPKIError(getRuntime(), ioe.getMessage());
+ }
+ }
+
+ private byte[] internalToDer() throws IOException {
+ DERSequence b = (DERSequence)cert.toASN1Object();
+ DERObjectIdentifier encType = null;
+ DERBitString publicKey = new DERBitString(((PKey)public_key).to_der().convertToString().getBytes());
+ DERIA5String encodedChallenge = new DERIA5String(this.challenge.toString());
+ DERObjectIdentifier sigAlg = null;
+ DERBitString sig = null;
+ encType = (DERObjectIdentifier)((DERSequence)((DERSequence)((DERSequence)b.getObjectAt(0)).getObjectAt(0)).getObjectAt(0)).getObjectAt(0);
+ sigAlg = ((AlgorithmIdentifier)b.getObjectAt(1)).getObjectId();
+ sig = (DERBitString)b.getObjectAt(2);
+
+ ASN1EncodableVector v1 = new ASN1EncodableVector();
+ ASN1EncodableVector v1_2 = new ASN1EncodableVector();
+ ASN1EncodableVector v2 = new ASN1EncodableVector();
+ ASN1EncodableVector v3 = new ASN1EncodableVector();
+ ASN1EncodableVector v4 = new ASN1EncodableVector();
+ v4.add(encType);
+ v4.add(new DERNull());
+ v3.add(new DERSequence(v4));
+ v3.add(publicKey);
+ v2.add(new DERSequence(v3));
+ v2.add(encodedChallenge);
+ v1.add(new DERSequence(v2));
+ v1_2.add(sigAlg);
+ v1_2.add(new DERNull());
+ v1.add(new DERSequence(v1_2));
+ v1.add(sig);
+ return new DERSequence(v1).getEncoded();
+ }
+
+ @JRubyMethod
+ public IRubyObject to_text() {
+ System.err.println("WARNING: calling unimplemented method: to_text");
+ return getRuntime().getNil();
+ }
+
+ @JRubyMethod
+ public IRubyObject public_key() {
+ return this.public_key;
+ }
+
+ @JRubyMethod(name="public_key=")
+ public IRubyObject set_public_key(IRubyObject arg) {
+ this.public_key = arg;
+ return arg;
+ }
+
+ @JRubyMethod
+ public IRubyObject sign(final IRubyObject key, IRubyObject digest) {
+ String keyAlg = ((PKey) key).getAlgorithm();
+ String digAlg = ((Digest) digest).getShortAlgorithm();
+ final DERObjectIdentifier alg = ASN1.getOIDLookup(getRuntime()).get(keyAlg.toLowerCase() + "-" + digAlg.toLowerCase());
+ try {
+ // NetscapeCertRequest requires "BC" provider.
+ OpenSSLReal.doWithBCProvider(new OpenSSLReal.Runnable() {
+
+ public void run() throws GeneralSecurityException {
+ cert = new NetscapeCertRequest(challenge.toString(), new AlgorithmIdentifier(alg), ((PKey) public_key).getPublicKey());
+ cert.sign(((PKey) key).getPrivateKey());
+ }
+ });
+ } catch (GeneralSecurityException gse) {
+ throw newSPKIError(getRuntime(), gse.getMessage());
+ }
+ return this;
+ }
+
+ @JRubyMethod
+ public IRubyObject verify(final IRubyObject pkey) {
+ cert.setPublicKey(((PKey) pkey).getPublicKey());
+ try {
+ // NetscapeCertRequest requires "BC" provider.
+ Boolean result = (Boolean) OpenSSLReal.getWithBCProvider(new OpenSSLReal.Callable() {
+
+ public Boolean call() throws GeneralSecurityException {
+ return cert.verify(challenge.toString());
+ }
+ });
+ return result.booleanValue() ? getRuntime().getTrue() : getRuntime().getFalse();
+ } catch (GeneralSecurityException gse) {
+ throw newSPKIError(getRuntime(), gse.getMessage());
+ }
+ }
+
+ @JRubyMethod
+ public IRubyObject challenge() {
+ return this.challenge;
+ }
+
+ @JRubyMethod(name="challenge=")
+ public IRubyObject set_challenge(IRubyObject arg) {
+ this.challenge = arg;
+ return arg;
+ }
+
+ private static RaiseException newSPKIError(Ruby runtime, String message) {
+ return Utils.newError(runtime, "OpenSSL::Netscape::SPKIError", message);
+ }
+}// NetscapeSPKI
diff --git a/src/org/jruby/ext/openssl/OSSLLibrary.java b/src/org/jruby/ext/openssl/OSSLLibrary.java
new file mode 100644
index 00000000000..a1846bcd1d4
--- /dev/null
+++ b/src/org/jruby/ext/openssl/OSSLLibrary.java
@@ -0,0 +1,14 @@
+package org.jruby.ext.openssl;
+
+import org.jruby.Ruby;
+import org.jruby.ext.nkf.RubyNKF;
+import org.jruby.ext.openssl.OpenSSLReal;
+import org.jruby.runtime.load.Library;
+
+import java.io.IOException;
+
+public class OSSLLibrary implements Library {
+ public void load(Ruby runtime, boolean wrap) throws IOException {
+ OpenSSLReal.createOpenSSL(runtime);
+ }
+}
\ No newline at end of file
diff --git a/src/org/jruby/ext/openssl/OpenSSLImpl.java b/src/org/jruby/ext/openssl/OpenSSLImpl.java
new file mode 100644
index 00000000000..7363dd830bf
--- /dev/null
+++ b/src/org/jruby/ext/openssl/OpenSSLImpl.java
@@ -0,0 +1,357 @@
+/***** BEGIN LICENSE BLOCK *****
+ * Version: CPL 1.0/GPL 2.0/LGPL 2.1
+ *
+ * The contents of this file are subject to the Common Public
+ * License Version 1.0 (the "License"); you may not use this file
+ * except in compliance with the License. You may obtain a copy of
+ * the License at http://www.eclipse.org/legal/cpl-v10.html
+ *
+ * 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 CPL, 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 CPL, the GPL or the LGPL.
+ ***** END LICENSE BLOCK *****/
+package org.jruby.ext.openssl;
+
+import java.io.StringReader;
+import java.security.MessageDigest;
+
+import org.jruby.Ruby;
+import org.jruby.RubyString;
+import org.jruby.ext.openssl.x509store.PEMInputOutput;
+import org.jruby.runtime.builtin.IRubyObject;
+
+/**
+ * Static class that holds various OpenSSL methods that aren't
+ * really easy to do any other way.
+ *
+ * @author Ola Bini
+ */
+public class OpenSSLImpl {
+ /**
+ * No instantiating this class...
+ */
+ private OpenSSLImpl() {}
+
+ public static IRubyObject to_der(IRubyObject obj) {
+ return obj.callMethod(obj.getRuntime().getCurrentContext(),"to_der");
+ }
+
+ public static IRubyObject to_der_if_possible(IRubyObject obj) {
+ if(obj.respondsTo("to_der")) {
+ return to_der(obj);
+ } else {
+ return obj;
+ }
+ }
+
+ public static byte[] readX509PEM(IRubyObject arg) {
+ arg = to_der_if_possible(arg);
+ // TODO: should handle RubyFile
+ RubyString str = arg.convertToString();
+ StringReader in = null;
+ try {
+ in = new StringReader(str.getUnicodeValue());
+ byte[] bytes = PEMInputOutput.readX509PEM(in);
+ if (bytes != null) {
+ return bytes;
+ }
+ } catch (Exception e) {
+ // this is not PEM encoded, let's use the default argument
+ if (in != null) {
+ in.close();
+ }
+ }
+ return str.getBytes();
+ }
+
+ public static void defaultObjects(Ruby runtime) {
+ASN1.addObject(runtime, 0, null, null,"1.2.840.113549.1.12.1");
+ASN1.addObject(runtime, 1, null, "rsadsi","1.2.840.113549");
+ASN1.addObject(runtime, 2, null, "pkcs","1.2.840.113549.1");
+ASN1.addObject(runtime, 3, "MD2", "md2","1.2.840.113549.2.2");
+ASN1.addObject(runtime, 4, "MD5", "md5","1.2.840.113549.2.5");
+ASN1.addObject(runtime, 5, "RC4", "rc4","1.2.840.113549.3.4");
+ASN1.addObject(runtime, 6, null, "rsaEncryption","1.2.840.113549.1.1.1");
+ASN1.addObject(runtime, 7, "RSA-MD2", "md2WithRSAEncryption","1.2.840.113549.1.1.2");
+ASN1.addObject(runtime, 8, "RSA-MD5", "md5WithRSAEncryption","1.2.840.113549.1.1.4");
+ASN1.addObject(runtime, 9, "PBE-MD2-DES", "pbeWithMD2AndDES-CBC","1.2.840.113549.1.5.1");
+ASN1.addObject(runtime, 10, "PBE-MD5-DES", "pbeWithMD5AndDES-CBC","1.2.840.113549.1.5.3");
+ASN1.addObject(runtime, 11, null, "X500","2.5");
+ASN1.addObject(runtime, 12, null, "X509","2.5.4");
+ASN1.addObject(runtime, 13, "CN", "commonName","2.5.4.3");
+ASN1.addObject(runtime, 14, "C", "countryName","2.5.4.6");
+ASN1.addObject(runtime, 15, "L", "localityName","2.5.4.7");
+ASN1.addObject(runtime, 16, "ST", "stateOrProvinceName","2.5.4.8");
+ASN1.addObject(runtime, 17, "O", "organizationName","2.5.4.10");
+ASN1.addObject(runtime, 18, "OU", "organizationalUnitName","2.5.4.11");
+ASN1.addObject(runtime, 19, "RSA", "rsa","2.5.8.1.1");
+ASN1.addObject(runtime, 20, null, "pkcs7","1.2.840.113549.1.7");
+ASN1.addObject(runtime, org.jruby.ext.openssl.impl.ASN1Registry.NID_pkcs7_data, null, "pkcs7-data","1.2.840.113549.1.7.1");
+ASN1.addObject(runtime, org.jruby.ext.openssl.impl.ASN1Registry.NID_pkcs7_signed, null, "pkcs7-signedData","1.2.840.113549.1.7.2");
+ASN1.addObject(runtime, org.jruby.ext.openssl.impl.ASN1Registry.NID_pkcs7_enveloped, null, "pkcs7-envelopedData","1.2.840.113549.1.7.3");
+ASN1.addObject(runtime, org.jruby.ext.openssl.impl.ASN1Registry.NID_pkcs7_signedAndEnveloped, null, "pkcs7-signedAndEnvelopedData","1.2.840.113549.1.7.4");
+ASN1.addObject(runtime, org.jruby.ext.openssl.impl.ASN1Registry.NID_pkcs7_digest, null, "pkcs7-digestData","1.2.840.113549.1.7.5");
+ASN1.addObject(runtime, org.jruby.ext.openssl.impl.ASN1Registry.NID_pkcs7_encrypted, null, "pkcs7-encryptedData","1.2.840.113549.1.7.6");
+ASN1.addObject(runtime, 27, null, "pkcs3","1.2.840.113549.1.3");
+ASN1.addObject(runtime, 28, null, "dhKeyAgreement","1.2.840.113549.1.3.1");
+ASN1.addObject(runtime, 29, "DES-ECB", "des-ecb","1.3.14.3.2.6");
+ASN1.addObject(runtime, 30, "DES-CFB", "des-cfb","1.3.14.3.2.9");
+ASN1.addObject(runtime, 31, "DES-CBC", "des-cbc","1.3.14.3.2.7");
+ASN1.addObject(runtime, 32, "DES-EDE", "des-ede","1.3.14.3.2.17");
+ASN1.addObject(runtime, 33, "DES-EDE3", "des-ede3",null);
+ASN1.addObject(runtime, 34, "IDEA-CBC", "idea-cbc","1.3.6.1.4.1.188.7.1.1.2");
+ASN1.addObject(runtime, 35, "IDEA-CFB", "idea-cfb",null);
+ASN1.addObject(runtime, 36, "IDEA-ECB", "idea-ecb",null);
+ASN1.addObject(runtime, 37, "RC2-CBC", "rc2-cbc","1.2.840.113549.3.2");
+ASN1.addObject(runtime, 38, "RC2-ECB", "rc2-ecb",null);
+ASN1.addObject(runtime, 39, "RC2-CFB", "rc2-cfb",null);
+ASN1.addObject(runtime, 40, "RC2-OFB", "rc2-ofb",null);
+ASN1.addObject(runtime, 41, "SHA", "sha","1.3.14.3.2.18");
+ASN1.addObject(runtime, 42, "RSA-SHA", "shaWithRSAEncryption","1.3.14.3.2.15");
+ASN1.addObject(runtime, 43, "DES-EDE-CBC", "des-ede-cbc",null);
+ASN1.addObject(runtime, 44, "DES-EDE3-CBC", "des-ede3-cbc","1.2.840.113549.3.7");
+ASN1.addObject(runtime, 45, "DES-OFB", "des-ofb","1.3.14.3.2.8");
+ASN1.addObject(runtime, 46, "IDEA-OFB", "idea-ofb",null);
+ASN1.addObject(runtime, 47, null, "pkcs9","1.2.840.113549.1.9");
+ASN1.addObject(runtime, 48, "Email", "emailAddress","1.2.840.113549.1.9.1");
+ASN1.addObject(runtime, 49, null, "unstructuredName","1.2.840.113549.1.9.2");
+ASN1.addObject(runtime, 50, null, "contentType","1.2.840.113549.1.9.3");
+ASN1.addObject(runtime, 51, null, "messageDigest","1.2.840.113549.1.9.4");
+ASN1.addObject(runtime, 52, null, "signingTime","1.2.840.113549.1.9.5");
+ASN1.addObject(runtime, 53, null, "countersignature","1.2.840.113549.1.9.6");
+ASN1.addObject(runtime, 54, null, "challengePassword","1.2.840.113549.1.9.7");
+ASN1.addObject(runtime, 55, null, "unstructuredAddress","1.2.840.113549.1.9.8");
+ASN1.addObject(runtime, 56, null, "extendedCertificateAttributes","1.2.840.113549.1.9.9");
+ASN1.addObject(runtime, 57, "Netscape", "Netscape Communications Corp.","2.16.840.1.113730");
+ASN1.addObject(runtime, 58, "nsCertExt", "Netscape Certificate Extension","2.16.840.1.113730.1");
+ASN1.addObject(runtime, 59, "nsDataType", "Netscape Data Type","2.16.840.1.113730.2");
+ASN1.addObject(runtime, 60, "DES-EDE-CFB", "des-ede-cfb",null);
+ASN1.addObject(runtime, 61, "DES-EDE3-CFB", "des-ede3-cfb",null);
+ASN1.addObject(runtime, 62, "DES-EDE-OFB", "des-ede-ofb",null);
+ASN1.addObject(runtime, 63, "DES-EDE3-OFB", "des-ede3-ofb",null);
+ASN1.addObject(runtime, 64, "SHA1", "sha1","1.3.14.3.2.26");
+ASN1.addObject(runtime, 65, "RSA-SHA1", "sha1WithRSAEncryption","1.2.840.113549.1.1.5");
+ASN1.addObject(runtime, 66, "DSA-SHA", "dsaWithSHA","1.3.14.3.2.13");
+ASN1.addObject(runtime, 67, "DSA-old", "dsaEncryption-old","1.3.14.3.2.12");
+ASN1.addObject(runtime, 68, "PBE-SHA1-RC2-64", "pbeWithSHA1AndRC2-CBC","1.2.840.113549.1.5.11");
+ASN1.addObject(runtime, 69, null, "PBKDF2","1.2.840.113549.1.5.12");
+ASN1.addObject(runtime, 70, "DSA-SHA1-old", "dsaWithSHA1-old","1.3.14.3.2.27");
+ASN1.addObject(runtime, 71, "nsCertType", "Netscape Cert Type","2.16.840.1.113730.1.1");
+ASN1.addObject(runtime, 72, "nsBaseUrl", "Netscape Base Url","2.16.840.1.113730.1.2");
+ASN1.addObject(runtime, 73, "nsRevocationUrl", "Netscape Revocation Url","2.16.840.1.113730.1.3");
+ASN1.addObject(runtime, 74, "nsCaRevocationUrl", "Netscape CA Revocation Url","2.16.840.1.113730.1.4");
+ASN1.addObject(runtime, 75, "nsRenewalUrl", "Netscape Renewal Url","2.16.840.1.113730.1.7");
+ASN1.addObject(runtime, 76, "nsCaPolicyUrl", "Netscape CA Policy Url","2.16.840.1.113730.1.8");
+ASN1.addObject(runtime, 77, "nsSslServerName", "Netscape SSL Server Name","2.16.840.1.113730.1.12");
+ASN1.addObject(runtime, 78, "nsComment", "Netscape Comment","2.16.840.1.113730.1.13");
+ASN1.addObject(runtime, 79, "nsCertSequence", "Netscape Certificate Sequence","2.16.840.1.113730.2.5");
+ASN1.addObject(runtime, 80, "DESX-CBC", "desx-cbc",null);
+ASN1.addObject(runtime, 81, "id-ce", null,"2.5.29");
+ASN1.addObject(runtime, 82, "subjectKeyIdentifier", "X509v3 Subject Key Identifier","2.5.29.14");
+ASN1.addObject(runtime, 83, "keyUsage", "X509v3 Key Usage","2.5.29.15");
+ASN1.addObject(runtime, 84, "privateKeyUsagePeriod", "X509v3 Private Key Usage Period","2.5.29.16");
+ASN1.addObject(runtime, 85, "subjectAltName", "X509v3 Subject Alternative Name","2.5.29.17");
+ASN1.addObject(runtime, 86, "issuerAltName", "X509v3 Issuer Alternative Name","2.5.29.18");
+ASN1.addObject(runtime, 87, "basicConstraints", "X509v3 Basic Constraints","2.5.29.19");
+ASN1.addObject(runtime, 88, "crlNumber", "X509v3 CRL Number","2.5.29.20");
+ASN1.addObject(runtime, 89, "certificatePolicies", "X509v3 Certificate Policies","2.5.29.32");
+ASN1.addObject(runtime, 90, "authorityKeyIdentifier", "X509v3 Authority Key Identifier","2.5.29.35");
+ASN1.addObject(runtime, 91, "BF-CBC", "bf-cbc","1.3.6.1.4.1.3029.1.2");
+ASN1.addObject(runtime, 92, "BF-ECB", "bf-ecb",null);
+ASN1.addObject(runtime, 93, "BF-CFB", "bf-cfb",null);
+ASN1.addObject(runtime, 94, "BF-OFB", "bf-ofb",null);
+ASN1.addObject(runtime, 95, "MDC2", "mdc2","2.5.8.3.101");
+ASN1.addObject(runtime, 96, "RSA-MDC2", "mdc2withRSA","2.5.8.3.100");
+ASN1.addObject(runtime, 97, "RC4-40", "rc4-40",null);
+ASN1.addObject(runtime, 98, "RC2-40-CBC", "rc2-40-cbc",null);
+ASN1.addObject(runtime, 99, "G", "givenName","2.5.4.42");
+ASN1.addObject(runtime, 100, "S", "surname","2.5.4.4");
+ASN1.addObject(runtime, 101, "I", "initials","2.5.4.43");
+ASN1.addObject(runtime, 102, "UID", "uniqueIdentifier","2.5.4.45");
+ASN1.addObject(runtime, 103, "crlDistributionPoints", "X509v3 CRL Distribution Points","2.5.29.31");
+ASN1.addObject(runtime, 104, "RSA-NP-MD5", "md5WithRSA","1.3.14.3.2.3");
+ASN1.addObject(runtime, 105, "SN", "serialNumber","2.5.4.5");
+ASN1.addObject(runtime, 106, "T", "title","2.5.4.12");
+ASN1.addObject(runtime, 107, "D", "description","2.5.4.13");
+ASN1.addObject(runtime, 108, "CAST5-CBC", "cast5-cbc","1.2.840.113533.7.66.10");
+ASN1.addObject(runtime, 109, "CAST5-ECB", "cast5-ecb",null);
+ASN1.addObject(runtime, 110, "CAST5-CFB", "cast5-cfb",null);
+ASN1.addObject(runtime, 111, "CAST5-OFB", "cast5-ofb",null);
+ASN1.addObject(runtime, 112, null, "pbeWithMD5AndCast5CBC","1.2.840.113533.7.66.12");
+ASN1.addObject(runtime, 113, "DSA-SHA1", "dsaWithSHA1","1.2.840.10040.4.3");
+ASN1.addObject(runtime, 114, "MD5-SHA1", "md5-sha1",null);
+ASN1.addObject(runtime, 115, "RSA-SHA1-2", "sha1WithRSA","1.3.14.3.2.29");
+ASN1.addObject(runtime, 116, "DSA", "dsaEncryption","1.2.840.10040.4.1");
+ASN1.addObject(runtime, 117, "RIPEMD160", "ripemd160","1.3.36.3.2.1");
+ASN1.addObject(runtime, 118, "RSA-RIPEMD160", "ripemd160WithRSA","1.3.36.3.3.1.2");
+ASN1.addObject(runtime, 119, "RC5-CBC", "rc5-cbc","1.2.840.113549.3.8");
+ASN1.addObject(runtime, 120, "RC5-ECB", "rc5-ecb",null);
+ASN1.addObject(runtime, 121, "RC5-CFB", "rc5-cfb",null);
+ASN1.addObject(runtime, 122, "RC5-OFB", "rc5-ofb",null);
+ASN1.addObject(runtime, 123, "RLE", "run length compression","1.1.1.1.666.1");
+ASN1.addObject(runtime, 124, "ZLIB", "zlib compression","1.1.1.1.666.2");
+ASN1.addObject(runtime, 125, "extendedKeyUsage", "X509v3 Extended Key Usage","2.5.29.37");
+ASN1.addObject(runtime, 126, "PKIX", null,"1.3.6.1.5.5.7");
+ASN1.addObject(runtime, 127, "id-kp", null,"1.3.6.1.5.5.7.3");
+ASN1.addObject(runtime, 128, "serverAuth", "TLS Web Server Authentication","1.3.6.1.5.5.7.3.1");
+ASN1.addObject(runtime, 129, "clientAuth", "TLS Web Client Authentication","1.3.6.1.5.5.7.3.2");
+ASN1.addObject(runtime, 130, "codeSigning", "Code Signing","1.3.6.1.5.5.7.3.3");
+ASN1.addObject(runtime, 131, "emailProtection", "E-mail Protection","1.3.6.1.5.5.7.3.4");
+ASN1.addObject(runtime, 132, "timeStamping", "Time Stamping","1.3.6.1.5.5.7.3.8");
+ASN1.addObject(runtime, 133, "msCodeInd", "Microsoft Individual Code Signing","1.3.6.1.4.1.311.2.1.21");
+ASN1.addObject(runtime, 134, "msCodeCom", "Microsoft Commercial Code Signing","1.3.6.1.4.1.311.2.1.22");
+ASN1.addObject(runtime, 135, "msCTLSign", "Microsoft Trust List Signing","1.3.6.1.4.1.311.10.3.1");
+ASN1.addObject(runtime, 136, "msSGC", "Microsoft Server Gated Crypto","1.3.6.1.4.1.311.10.3.3");
+ASN1.addObject(runtime, 137, "msEFS", "Microsoft Encrypted File System","1.3.6.1.4.1.311.10.3.4");
+ASN1.addObject(runtime, 138, "nsSGC", "Netscape Server Gated Crypto","2.16.840.1.113730.4.1");
+ASN1.addObject(runtime, 139, "deltaCRL", "X509v3 Delta CRL Indicator","2.5.29.27");
+ASN1.addObject(runtime, 140, "CRLReason", "CRL Reason Code","2.5.29.21");
+ASN1.addObject(runtime, 141, "invalidityDate", "Invalidity Date","2.5.29.24");
+ASN1.addObject(runtime, 142, "SXNetID", "Strong Extranet ID","1.3.101.1.4.1");
+ASN1.addObject(runtime, 143, "PBE-SHA1-RC4-128", "pbeWithSHA1And128BitRC4","1.2.840.113549.1.12.1.1");
+ASN1.addObject(runtime, 144, "PBE-SHA1-RC4-40", "pbeWithSHA1And40BitRC4","1.2.840.113549.1.12.1.2");
+ASN1.addObject(runtime, 145, "PBE-SHA1-3DES", "pbeWithSHA1And3-KeyTripleDES-CBC","1.2.840.113549.1.12.1.3");
+ASN1.addObject(runtime, 146, "PBE-SHA1-2DES", "pbeWithSHA1And2-KeyTripleDES-CBC","1.2.840.113549.1.12.1.4");
+ASN1.addObject(runtime, 147, "PBE-SHA1-RC2-128", "pbeWithSHA1And128BitRC2-CBC","1.2.840.113549.1.12.1.5");
+ASN1.addObject(runtime, 148, "PBE-SHA1-RC2-40", "pbeWithSHA1And40BitRC2-CBC","1.2.840.113549.1.12.1.6");
+ASN1.addObject(runtime, 149, null, "keyBag","1.2.840.113549.1.12.10.1.1");
+ASN1.addObject(runtime, 150, null, "pkcs8ShroudedKeyBag","1.2.840.113549.1.12.10.1.2");
+ASN1.addObject(runtime, 151, null, "certBag","1.2.840.113549.1.12.10.1.3");
+ASN1.addObject(runtime, 152, null, "crlBag","1.2.840.113549.1.12.10.1.4");
+ASN1.addObject(runtime, 153, null, "secretBag","1.2.840.113549.1.12.10.1.5");
+ASN1.addObject(runtime, 154, null, "safeContentsBag","1.2.840.113549.1.12.10.1.6");
+ASN1.addObject(runtime, 155, null, "PBES2","1.2.840.113549.1.5.13");
+ASN1.addObject(runtime, 156, null, "PBMAC1","1.2.840.113549.1.5.14");
+ASN1.addObject(runtime, 157, null, "hmacWithSHA1","1.2.840.113549.2.7");
+ASN1.addObject(runtime, 158, "id-qt-cps", "Policy Qualifier CPS","1.3.6.1.5.5.7.2.1");
+ASN1.addObject(runtime, 159, "id-qt-unotice", "Policy Qualifier User Notice","1.3.6.1.5.5.7.2.2");
+ASN1.addObject(runtime, 160, "RC2-64-CBC", "rc2-64-cbc",null);
+ASN1.addObject(runtime, 161, "SMIME-CAPS", "S/MIME Capabilities","1.2.840.113549.1.9.15");
+ASN1.addObject(runtime, 162, "PBE-MD2-RC2-64", "pbeWithMD2AndRC2-CBC","1.2.840.113549.1.5.4");
+ASN1.addObject(runtime, 163, "PBE-MD5-RC2-64", "pbeWithMD5AndRC2-CBC","1.2.840.113549.1.5.6");
+ASN1.addObject(runtime, 164, "PBE-SHA1-DES", "pbeWithSHA1AndDES-CBC","1.2.840.113549.1.5.10");
+ASN1.addObject(runtime, 165, "msExtReq", "Microsoft Extension Request","1.3.6.1.4.1.311.2.1.14");
+ASN1.addObject(runtime, 166, "extReq", "Extension Request","1.2.840.113549.1.9.14");
+ASN1.addObject(runtime, 167, "name", "name","2.5.4.41");
+ASN1.addObject(runtime, 168, "dnQualifier", "dnQualifier","2.5.4.46");
+ASN1.addObject(runtime, 169, "id-pe", null,"1.3.6.1.5.5.7.1");
+ASN1.addObject(runtime, 170, "id-ad", null,"1.3.6.1.5.5.7.48");
+ASN1.addObject(runtime, 171, "authorityInfoAccess", "Authority Information Access","1.3.6.1.5.5.7.1.1");
+ASN1.addObject(runtime, 172, "OCSP", "OCSP","1.3.6.1.5.5.7.48.1");
+ASN1.addObject(runtime, 173, "caIssuers", "CA Issuers","1.3.6.1.5.5.7.48.2");
+ASN1.addObject(runtime, 174, "OCSPSigning", "OCSP Signing","1.3.6.1.5.5.7.3.9");
+ASN1.addObject(runtime, 175, "AES-128-EBC", "aes-128-ebc","2.16.840.1.101.3.4.1.1");
+ASN1.addObject(runtime, 176, "AES-128-CBC", "aes-128-cbc","2.16.840.1.101.3.4.1.2");
+ASN1.addObject(runtime, 177, "AES-128-OFB", "aes-128-ofb","2.16.840.1.101.3.4.1.3");
+ASN1.addObject(runtime, 178, "AES-128-CFB", "aes-128-cfb","2.16.840.1.101.3.4.1.4");
+ASN1.addObject(runtime, 179, "AES-192-EBC", "aes-192-ebc","2.16.840.1.101.3.4.1.21");
+ASN1.addObject(runtime, 180, "AES-192-CBC", "aes-192-cbc","2.16.840.1.101.3.4.1.22");
+ASN1.addObject(runtime, 181, "AES-192-OFB", "aes-192-ofb","2.16.840.1.101.3.4.1.23");
+ASN1.addObject(runtime, 182, "AES-192-CFB", "aes-192-cfb","2.16.840.1.101.3.4.1.24");
+ASN1.addObject(runtime, 183, "AES-256-EBC", "aes-256-ebc","2.16.840.1.101.3.4.1.41");
+ASN1.addObject(runtime, 184, "AES-256-CBC", "aes-256-cbc","2.16.840.1.101.3.4.1.42");
+ASN1.addObject(runtime, 185, "AES-256-OFB", "aes-256-ofb","2.16.840.1.101.3.4.1.43");
+ASN1.addObject(runtime, 186, "AES-256-CFB", "aes-256-cfb","2.16.840.1.101.3.4.1.44");
+ }
+
+ public static interface KeyAndIv {
+ byte[] getKey();
+ byte[] getIv();
+ }
+
+ private static class KeyAndIvImpl implements KeyAndIv {
+ private final byte[] key;
+ private final byte[] iv;
+ public KeyAndIvImpl(byte[] key, byte[] iv) {
+ this.key = key;
+ this.iv = iv;
+ }
+ public byte[] getKey() {
+ return key;
+ }
+ public byte[] getIv() {
+ return iv;
+ }
+ }
+
+ public static PEMHandler getPEMHandler() {
+ try {
+ return new org.jruby.ext.openssl.BouncyCastlePEMHandler();
+ } catch (Exception e) {
+ // fallback to...
+ }
+ return new DefaultPEMHandler();
+ }
+
+ public static KeyAndIv EVP_BytesToKey(int key_len, int iv_len, MessageDigest md, byte[] salt, byte[] data, int count) {
+ byte[] key = new byte[key_len];
+ byte[] iv = new byte[iv_len];
+ int key_ix = 0;
+ int iv_ix = 0;
+ byte[] md_buf = null;
+ int nkey = key_len;
+ int niv = iv_len;
+ int i = 0;
+ if(data == null) {
+ return new KeyAndIvImpl(key,iv);
+ }
+ int addmd = 0;
+ for(;;) {
+ md.reset();
+ if(addmd++ > 0) {
+ md.update(md_buf);
+ }
+ md.update(data);
+ if(null != salt) {
+ md.update(salt,0,8);
+ }
+ md_buf = md.digest();
+ for(i=1;i 0) {
+ for(;;) {
+ if(nkey == 0) break;
+ if(i == md_buf.length) break;
+ key[key_ix++] = md_buf[i];
+ nkey--;
+ i++;
+ }
+ }
+ if(niv > 0 && i != md_buf.length) {
+ for(;;) {
+ if(niv == 0) break;
+ if(i == md_buf.length) break;
+ iv[iv_ix++] = md_buf[i];
+ niv--;
+ i++;
+ }
+ }
+ if(nkey == 0 && niv == 0) {
+ break;
+ }
+ }
+ for(i=0;i
+ *
+ * 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 CPL, 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 CPL, 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;
+
+import javax.crypto.SecretKeyFactory;
+import java.security.GeneralSecurityException;
+import java.security.MessageDigest;
+import java.security.NoSuchAlgorithmException;
+import java.security.NoSuchProviderException;
+import java.security.cert.CertificateFactory;
+
+/**
+ * @author Ola Bini
+ */
+public class OpenSSLReal {
+ private static java.security.Provider BC_PROVIDER = null;
+
+ static {
+ try {
+ BC_PROVIDER = (java.security.Provider) Class.forName("org.bouncycastle.jce.provider.BouncyCastleProvider").newInstance();
+ } catch (Throwable ignored) {
+ // no bouncy castle available
+ }
+ }
+
+ public interface Runnable {
+ public void run() throws GeneralSecurityException;
+ }
+
+ public interface Callable {
+ public Object call() throws GeneralSecurityException;
+ }
+
+ public static void doWithBCProvider(final Runnable toRun) throws GeneralSecurityException {
+ getWithBCProvider(new Callable() {
+
+ public Object call() throws GeneralSecurityException {
+ toRun.run();
+ return null;
+ }
+ });
+ }
+
+ // This method just adds BouncyCastleProvider if it's allowed. Removing
+ // "BC" can remove pre-installed or runtime-added BC provider by elsewhere
+ // and it causes unknown runtime error anywhere. We avoid this. To use
+ // part of jruby-openssl feature (X.509 and PKCS), users must be aware of
+ // dynamic BC provider adding.
+ public static Object getWithBCProvider(Callable toCall) throws GeneralSecurityException {
+ try {
+ if (BC_PROVIDER != null && java.security.Security.getProvider("BC") == null) {
+ java.security.Security.addProvider(BC_PROVIDER);
+ }
+ return toCall.call();
+ } catch (NoSuchProviderException nspe) {
+ throw new GeneralSecurityException(bcExceptionMessage(nspe), nspe);
+ } catch (Exception e) {
+ throw new GeneralSecurityException(e.getMessage(), e);
+ }
+ }
+
+ public static String bcExceptionMessage(NoSuchProviderException nspe) {
+ return "You need to configure JVM/classpath to enable BouncyCastle Security Provider: " + nspe.getMessage();
+ }
+
+ public static String bcExceptionMessage(NoClassDefFoundError ncdfe) {
+ return "You need to configure JVM/classpath to enable BouncyCastle Security Provider: NoClassDefFoundError: " + ncdfe.getMessage();
+ }
+
+ public static void createOpenSSL(Ruby runtime) {
+ RubyModule ossl = runtime.getOrCreateModule("OpenSSL");
+ RubyClass standardError = runtime.getClass("StandardError");
+ ossl.defineClassUnder("OpenSSLError", standardError, standardError.getAllocator());
+
+ // those are BC provider free (uses BC class but does not use BC provider)
+ PKey.createPKey(runtime, ossl);
+ BN.createBN(runtime, ossl);
+ Digest.createDigest(runtime, ossl);
+ Cipher.createCipher(runtime, ossl);
+ Random.createRandom(runtime, ossl);
+ HMAC.createHMAC(runtime, ossl);
+ Config.createConfig(runtime, ossl);
+
+ // these classes depends on BC provider now.
+ try {
+ ASN1.createASN1(runtime, ossl);
+ X509.createX509(runtime, ossl);
+ NetscapeSPKI.createNetscapeSPKI(runtime, ossl);
+ PKCS7.createPKCS7(runtime, ossl);
+ } catch (SecurityException ignore) {
+ // some class might be prohibited to use.
+ runtime.getLoadService().require("openssl/dummy");
+ } catch (Error ignore) {
+ // mainly for rescuing NoClassDefFoundError: no bc*.jar
+ runtime.getLoadService().require("openssl/dummy");
+ }
+ try {
+ SSL.createSSL(runtime, ossl);
+ } catch (SecurityException ignore) {
+ // some class might be prohibited to use. ex. SSL* on GAE/J.
+ runtime.getLoadService().require("openssl/dummyssl");
+ } catch (Error ignore) {
+ // mainly for rescuing NoClassDefFoundError: no bc*.jar
+ runtime.getLoadService().require("openssl/dummyssl");
+ }
+
+ runtime.getLoadService().require("jopenssl/version");
+ String jopensslVersion = runtime.getClassFromPath("Jopenssl::Version").getConstant("VERSION").toString();
+ ossl.setConstant("VERSION", runtime.newString("1.0.0"));
+ ossl.setConstant("OPENSSL_VERSION",
+ runtime.newString("jruby-ossl " + jopensslVersion));
+
+ try {
+ java.security.MessageDigest.getInstance("SHA224", BC_PROVIDER);
+ ossl.setConstant("OPENSSL_VERSION_NUMBER", runtime.newFixnum(9469999));
+ } catch (NoSuchAlgorithmException nsae) {
+ ossl.setConstant("OPENSSL_VERSION_NUMBER", runtime.newFixnum(9469952));
+ }
+ }
+
+ public static javax.crypto.Cipher getCipherBC(final String algorithm) throws GeneralSecurityException {
+ return (javax.crypto.Cipher) getWithBCProvider(new Callable() {
+
+ public Object call() throws GeneralSecurityException {
+ return javax.crypto.Cipher.getInstance(algorithm, "BC");
+ }
+ });
+ }
+
+ public static SecretKeyFactory getSecretKeyFactoryBC(final String algorithm) throws GeneralSecurityException {
+ return (SecretKeyFactory) getWithBCProvider(new Callable() {
+
+ public Object call() throws GeneralSecurityException {
+ return SecretKeyFactory.getInstance(algorithm, "BC");
+ }
+ });
+ }
+
+ public static MessageDigest getMessageDigestBC(final String algorithm) throws GeneralSecurityException {
+ return (MessageDigest) getWithBCProvider(new Callable() {
+
+ public Object call() throws GeneralSecurityException {
+ return MessageDigest.getInstance(algorithm, "BC");
+ }
+ });
+ }
+
+ public static CertificateFactory getX509CertificateFactoryBC() throws GeneralSecurityException {
+ return (CertificateFactory) getWithBCProvider(new Callable() {
+
+ public Object call() throws GeneralSecurityException {
+ return CertificateFactory.getInstance("X.509", "BC");
+ }
+ });
+ }
+}// OpenSSLReal
+
diff --git a/src/org/jruby/ext/openssl/PEMHandler.java b/src/org/jruby/ext/openssl/PEMHandler.java
new file mode 100644
index 00000000000..9e523f266c1
--- /dev/null
+++ b/src/org/jruby/ext/openssl/PEMHandler.java
@@ -0,0 +1,40 @@
+/***** BEGIN LICENSE BLOCK *****
+ * Version: CPL 1.0/GPL 2.0/LGPL 2.1
+ *
+ * The contents of this file are subject to the Common Public
+ * License Version 1.0 (the "License"); you may not use this file
+ * except in compliance with the License. You may obtain a copy of
+ * the License at http://www.eclipse.org/legal/cpl-v10.html
+ *
+ * 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 CPL, 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 CPL, the GPL or the LGPL.
+ ***** END LICENSE BLOCK *****/
+package org.jruby.ext.openssl;
+
+import java.io.Reader;
+import java.io.Writer;
+
+/**
+ * @author Ola Bini
+ */
+public interface PEMHandler {
+ Object readPEM(Reader read, String password) throws Exception;
+ void writePEM(Writer writ, Object obj, String algorithm, char[] password) throws Exception;
+ void writePEM(Writer writ, Object obj) throws Exception;
+}// PEMHandler
diff --git a/src/org/jruby/ext/openssl/PKCS10CertificationRequestExt.java b/src/org/jruby/ext/openssl/PKCS10CertificationRequestExt.java
new file mode 100644
index 00000000000..58575f6063a
--- /dev/null
+++ b/src/org/jruby/ext/openssl/PKCS10CertificationRequestExt.java
@@ -0,0 +1,158 @@
+/***** BEGIN LICENSE BLOCK *****
+ * Version: CPL 1.0/GPL 2.0/LGPL 2.1
+ *
+ * The contents of this file are subject to the Common Public
+ * License Version 1.0 (the "License"); you may not use this file
+ * except in compliance with the License. You may obtain a copy of
+ * the License at http://www.eclipse.org/legal/cpl-v10.html
+ *
+ * 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 CPL, 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 CPL, the GPL or the LGPL.
+ ***** END LICENSE BLOCK *****/
+package org.jruby.ext.openssl;
+
+import java.io.ByteArrayOutputStream;
+import java.security.InvalidKeyException;
+import java.security.NoSuchAlgorithmException;
+import java.security.NoSuchProviderException;
+import java.security.PrivateKey;
+import java.security.PublicKey;
+import java.security.Signature;
+import java.security.SignatureException;
+
+import javax.security.auth.x500.X500Principal;
+
+import org.bouncycastle.asn1.ASN1EncodableVector;
+import org.bouncycastle.asn1.ASN1Sequence;
+import org.bouncycastle.asn1.ASN1Set;
+import org.bouncycastle.asn1.DERInteger;
+import org.bouncycastle.asn1.DEROutputStream;
+import org.bouncycastle.asn1.DERSequence;
+import org.bouncycastle.asn1.DERSet;
+import org.bouncycastle.asn1.DERTaggedObject;
+import org.bouncycastle.asn1.pkcs.CertificationRequestInfo;
+import org.bouncycastle.jce.PKCS10CertificationRequest;
+
+/**
+ * @author Ola Bini
+ */
+public class PKCS10CertificationRequestExt extends PKCS10CertificationRequest {
+ public PKCS10CertificationRequestExt(byte[] bytes) {
+ super(bytes);
+ }
+
+ public PKCS10CertificationRequestExt(ASN1Sequence sequence) {
+ super(sequence);
+ }
+
+ public PKCS10CertificationRequestExt(
+ String signatureAlgorithm,
+ org.bouncycastle.asn1.x509.X509Name subject,
+ PublicKey key,
+ ASN1Set attributes,
+ PrivateKey signingKey)
+ throws NoSuchAlgorithmException, NoSuchProviderException,
+ InvalidKeyException, SignatureException
+ {
+ super(signatureAlgorithm,subject,key,attributes,signingKey);
+ }
+
+ public PKCS10CertificationRequestExt(
+ String signatureAlgorithm,
+ X500Principal subject,
+ PublicKey key,
+ ASN1Set attributes,
+ PrivateKey signingKey)
+ throws NoSuchAlgorithmException, NoSuchProviderException,
+ InvalidKeyException, SignatureException
+ {
+ super(signatureAlgorithm,subject,key,attributes,signingKey);
+ }
+
+ public PKCS10CertificationRequestExt(
+ String signatureAlgorithm,
+ X500Principal subject,
+ PublicKey key,
+ ASN1Set attributes,
+ PrivateKey signingKey,
+ String provider)
+ throws NoSuchAlgorithmException, NoSuchProviderException,
+ InvalidKeyException, SignatureException
+ {
+ super(signatureAlgorithm,subject,key,attributes,signingKey,provider);
+ }
+
+ public PKCS10CertificationRequestExt(
+ String signatureAlgorithm,
+ org.bouncycastle.asn1.x509.X509Name subject,
+ PublicKey key,
+ ASN1Set attributes,
+ PrivateKey signingKey,
+ String provider)
+ throws NoSuchAlgorithmException, NoSuchProviderException,
+ InvalidKeyException, SignatureException
+ {
+ super(signatureAlgorithm,subject,key,attributes,signingKey,provider);
+ }
+
+ public void setAttributes(DERSet attrs) {
+ ASN1Sequence seq = (ASN1Sequence)this.reqInfo.toASN1Object();
+ ASN1EncodableVector v1 = new ASN1EncodableVector();
+ for(int i=0;i<(seq.size()-1);i++) {
+ v1.add(seq.getObjectAt(i));
+ }
+ v1.add(new DERTaggedObject(0,attrs));
+ this.reqInfo = new CertificationRequestInfo(new DERSequence(v1));
+ }
+
+ public void setVersion(int v) {
+ DERInteger nVersion = new DERInteger(v);
+ ASN1Sequence seq = (ASN1Sequence)this.reqInfo.toASN1Object();
+ ASN1EncodableVector v1 = new ASN1EncodableVector();
+ v1.add(nVersion);
+ for(int i=1;i
+ *
+ * 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 CPL, 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 CPL, the GPL or the LGPL.
+ ***** END LICENSE BLOCK *****/
+package org.jruby.ext.openssl;
+
+
+import java.io.IOException;
+import java.io.StringWriter;
+import java.security.GeneralSecurityException;
+import java.security.PrivateKey;
+import java.security.cert.CertificateEncodingException;
+import java.util.ArrayList;
+import java.util.Collection;
+import java.util.HashSet;
+import java.util.List;
+import org.jruby.Ruby;
+import org.jruby.RubyArray;
+import org.jruby.RubyBignum;
+import org.jruby.RubyClass;
+import org.jruby.RubyFile;
+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.ext.openssl.impl.ASN1Registry;
+import org.jruby.ext.openssl.impl.BIO;
+import org.jruby.ext.openssl.impl.CipherSpec;
+import org.jruby.ext.openssl.impl.MemBIO;
+import org.jruby.ext.openssl.impl.Mime;
+import org.jruby.ext.openssl.impl.NotVerifiedPKCS7Exception;
+import org.jruby.ext.openssl.impl.PKCS7Exception;
+import org.jruby.ext.openssl.impl.RecipInfo;
+import org.jruby.ext.openssl.impl.SMIME;
+import org.jruby.ext.openssl.impl.SignerInfoWithPkey;
+import org.jruby.ext.openssl.x509store.PEMInputOutput;
+import org.jruby.ext.openssl.x509store.Store;
+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.builtin.IRubyObject;
+import org.jruby.util.ByteList;
+
+/**
+ * @author Ola Bini
+ */
+public class PKCS7 extends RubyObject {
+ private static final long serialVersionUID = -3925104500966826973L;
+
+ private static ObjectAllocator PKCS7_ALLOCATOR = new ObjectAllocator() {
+ public IRubyObject allocate(Ruby runtime, RubyClass klass) {
+ return new PKCS7(runtime, klass);
+ }
+ };
+
+ public static void createPKCS7(Ruby runtime, RubyModule mOSSL) {
+ ThreadContext context = runtime.getCurrentContext();
+ RubyClass cPKCS7 = mOSSL.defineClassUnder("PKCS7",runtime.getObject(),PKCS7_ALLOCATOR);
+ RubyClass openSSLError = runtime.getModule("OpenSSL").getClass("OpenSSLError");
+ cPKCS7.defineClassUnder("PKCS7Error",openSSLError,openSSLError.getAllocator());
+ cPKCS7.addReadWriteAttribute(context, "data");
+ cPKCS7.addReadWriteAttribute(context, "error_string");
+ cPKCS7.defineAnnotatedMethods(PKCS7.class);
+ cPKCS7.defineAnnotatedMethods(ModuleMethods.class);
+
+ SignerInfo.createSignerInfo(runtime,cPKCS7);
+ RecipientInfo.createRecipientInfo(runtime,cPKCS7);
+
+ cPKCS7.setConstant("TEXT",runtime.newFixnum(1));
+ cPKCS7.setConstant("NOCERTS",runtime.newFixnum(2));
+ cPKCS7.setConstant("NOSIGS",runtime.newFixnum(4));
+ cPKCS7.setConstant("NOCHAIN",runtime.newFixnum(8));
+ cPKCS7.setConstant("NOINTERN",runtime.newFixnum(16));
+ cPKCS7.setConstant("NOVERIFY",runtime.newFixnum(32));
+ cPKCS7.setConstant("DETACHED",runtime.newFixnum(64));
+ cPKCS7.setConstant("BINARY",runtime.newFixnum(128));
+ cPKCS7.setConstant("NOATTR",runtime.newFixnum(256));
+ cPKCS7.setConstant("NOSMIMECAP",runtime.newFixnum(512));
+ }
+
+ public static BIO obj2bio(IRubyObject obj) {
+ if(obj instanceof RubyFile) {
+ throw new IllegalArgumentException("TODO: handle RubyFile correctly");
+// if (TYPE(obj) == T_FILE) {
+// OpenFile *fptr;
+// GetOpenFile(obj, fptr);
+// rb_io_check_readable(fptr);
+// bio = BIO_new_fp(fptr->f, BIO_NOCLOSE);
+ } else {
+ RubyString str = obj.convertToString();
+ ByteList bl = str.getByteList();
+ return BIO.memBuf(bl.bytes, bl.begin, bl.realSize);
+ }
+ }
+
+ public static PKCS7 wrap(RubyClass klass, org.jruby.ext.openssl.impl.PKCS7 p7) {
+ PKCS7 wrapped = new PKCS7(klass.getRuntime(), klass);
+ wrapped.p7 = p7;
+ return wrapped;
+ }
+
+ public static IRubyObject membio2str(Ruby runtime, BIO bio) {
+ return runtime.newString(new ByteList(((MemBIO)bio).getMemCopy(), false));
+ }
+
+ private static List x509_ary2sk(IRubyObject ary) {
+ List certs = new ArrayList();
+ RubyArray arr = (RubyArray)ary;
+ for(int i = 0; i x509s = certs.isNil()
+ ? null
+ : x509_ary2sk(certs);
+
+ try {
+ org.jruby.ext.openssl.impl.PKCS7 p7 = org.jruby.ext.openssl.impl.PKCS7.sign(x509, pkey, x509s, in, flg);
+ PKCS7 ret = wrap(Utils.getClassFromPath(recv.getRuntime(), "OpenSSL::PKCS7::PKCS7"), p7);
+ ret.setData(data);
+ return ret;
+ } catch (PKCS7Exception pkcs7e) {
+ throw newPKCS7Exception(recv.getRuntime(), pkcs7e);
+ }
+ }
+
+ /** ossl_pkcs7_s_encrypt
+ *
+ */
+ @JRubyMethod(meta=true, rest=true)
+ public static IRubyObject encrypt(IRubyObject recv, IRubyObject[] args) {
+ IRubyObject certs, data, cipher = recv.getRuntime().getNil(), flags = recv.getRuntime().getNil();
+ switch(Arity.checkArgumentCount(recv.getRuntime(), args, 2, 4)) {
+ case 4:
+ flags = args[3];
+ case 3:
+ cipher = args[2];
+ }
+ data = args[1];
+ certs = args[0];
+ CipherSpec ciph = null;
+ if (cipher.isNil()) {
+ try {
+ ciph = new CipherSpec(javax.crypto.Cipher.getInstance("RC2/CBC/PKCS5Padding"), Cipher.Algorithm.jsseToOssl("RC2/CBC/PKCS5Padding", 40), 40);
+ } catch (GeneralSecurityException gse) {
+ throw newPKCS7Error(recv.getRuntime(), gse.getMessage());
+ }
+ } else {
+ Cipher c = ((Cipher) cipher);
+ ciph = new CipherSpec(c.getCipher(), c.getName(), c.getGenerateKeyLen() * 8);
+ }
+ int flg = flags.isNil() ? 0 : RubyNumeric.fix2int(flags);
+ byte[] in = data.convertToString().getBytes();
+ List x509s = x509_ary2sk(certs);
+ try {
+ org.jruby.ext.openssl.impl.PKCS7 p7 = org.jruby.ext.openssl.impl.PKCS7.encrypt(x509s, in, ciph, flg);
+ PKCS7 ret = wrap(Utils.getClassFromPath(recv.getRuntime(), "OpenSSL::PKCS7::PKCS7"), p7);
+ ret.setData(data);
+ return ret;
+ } catch (PKCS7Exception pkcs7e) {
+ throw newPKCS7Exception(recv.getRuntime(), pkcs7e);
+ }
+ }
+ }
+
+ public PKCS7(Ruby runtime, RubyClass type) {
+ super(runtime,type);
+ }
+
+ private org.jruby.ext.openssl.impl.PKCS7 p7;
+
+ public void setData(IRubyObject object) {
+ setInstanceVariable("@data", object);
+ }
+
+ public IRubyObject getData() {
+ return getInstanceVariable("@data");
+ }
+
+ @JRubyMethod(name="initialize", rest=true)
+ public IRubyObject _initialize(IRubyObject[] args) {
+ IRubyObject arg = null;
+ if(Arity.checkArgumentCount(getRuntime(), args, 0, 1) == 0) {
+ return this;
+ }
+ arg = args[0];
+ arg = OpenSSLImpl.to_der_if_possible(arg);
+ BIO input = obj2bio(arg);
+ try {
+ p7 = org.jruby.ext.openssl.impl.PKCS7.readPEM(input);
+ if (p7 == null) {
+ input.reset();
+ p7 = org.jruby.ext.openssl.impl.PKCS7.fromASN1(input);
+ }
+ } catch (IOException ioe) {
+ throw newPKCS7Error(getRuntime(), ioe.getMessage());
+ } catch (PKCS7Exception pkcs7e) {
+ throw newPKCS7Exception(getRuntime(), pkcs7e);
+ }
+ setData(getRuntime().getNil());
+ return this;
+ }
+
+ @Override
+ @JRubyMethod
+ public IRubyObject initialize_copy(IRubyObject obj) {
+ System.err.println("WARNING: unimplemented method called PKCS7#init_copy");
+ return this;
+ }
+
+ @JRubyMethod(name="type=")
+ public IRubyObject set_type(IRubyObject obj) {
+ System.err.println("WARNING: unimplemented method called PKCS7#type=");
+ return getRuntime().getNil();
+ }
+
+ @JRubyMethod(name="type")
+ public IRubyObject get_type() {
+ if(p7.isSigned()) {
+ return getRuntime().newSymbol("signed");
+ }
+ if(p7.isEncrypted()) {
+ return getRuntime().newSymbol("encrypted");
+ }
+ if(p7.isEnveloped()) {
+ return getRuntime().newSymbol("enveloped");
+ }
+ if(p7.isSignedAndEnveloped()) {
+ return getRuntime().newSymbol("signedAndEnveloped");
+ }
+ if(p7.isData()) {
+ return getRuntime().newSymbol("data");
+ }
+ return getRuntime().getNil();
+ }
+
+ @JRubyMethod(name="detached=")
+ public IRubyObject set_detached(IRubyObject obj) {
+ System.err.println("WARNING: unimplemented method called PKCS7#detached=");
+ return getRuntime().getNil();
+ }
+
+ @JRubyMethod
+ public IRubyObject detached() {
+ System.err.println("WARNING: unimplemented method called PKCS7#detached");
+ return getRuntime().getNil();
+ }
+
+ @JRubyMethod(name="detached?")
+ public IRubyObject detached_p() {
+ System.err.println("WARNING: unimplemented method called PKCS7#detached?");
+ return getRuntime().getNil();
+ }
+
+ @JRubyMethod(name="cipher=")
+ public IRubyObject set_cipher(IRubyObject obj) {
+ System.err.println("WARNING: unimplemented method called PKCS7#cipher=");
+ return getRuntime().getNil();
+ }
+
+ @JRubyMethod
+ public IRubyObject add_signer(IRubyObject obj) {
+ SignerInfoWithPkey p7si = ((SignerInfo)obj).getSignerInfo().dup();
+
+ try {
+ p7.addSigner(p7si);
+ } catch (PKCS7Exception pkcse) {
+ throw newPKCS7Exception(getRuntime(), pkcse);
+ }
+ // TODO: Handle exception here
+
+ if(p7.isSigned()) {
+ p7si.addSignedAttribute(ASN1Registry.NID_pkcs9_contentType, ASN1Registry.nid2obj(ASN1Registry.NID_pkcs7_data));
+ }
+
+ return this;
+ }
+
+ /** ossl_pkcs7_get_signer
+ *
+ * This seems to return a list of SignerInfo objects.
+ *
+ */
+ @JRubyMethod
+ public IRubyObject signers() {
+ Collection sk = p7.getSignerInfo();
+ RubyArray ary = getRuntime().newArray(sk.size());
+ for(SignerInfoWithPkey si : sk) {
+ ary.append(SignerInfo.create(getRuntime(), si));
+ }
+ return ary;
+ }
+
+ @JRubyMethod
+ public IRubyObject add_recipient(IRubyObject obj) {
+ System.err.println("WARNING: unimplemented method called PKCS7#add_recipient");
+ return getRuntime().getNil();
+ }
+
+ @JRubyMethod
+ public IRubyObject recipients() {
+ Collection sk = null;
+
+ if(p7.isEnveloped()) {
+ sk = p7.getEnveloped().getRecipientInfo();
+ } else if(p7.isSignedAndEnveloped()) {
+ sk = p7.getSignedAndEnveloped().getRecipientInfo();
+ } else {
+ sk = null;
+ }
+ if(sk == null) {
+ return getRuntime().newArray();
+ }
+
+ RubyArray ary = getRuntime().newArray(sk.size());
+ for(RecipInfo ri : sk) {
+ ary.append(RecipientInfo.create(getRuntime(), ri));
+ }
+ return ary;
+ }
+
+ @JRubyMethod
+ public IRubyObject add_certificate(IRubyObject obj) {
+ try {
+ p7.addCertificate(((X509Cert)obj).getAuxCert());
+ } catch (PKCS7Exception pkcse) {
+ throw newPKCS7Exception(getRuntime(), pkcse);
+ }
+ return this;
+ }
+
+ @JRubyMethod(name="certificates=")
+ public IRubyObject set_certificates(IRubyObject obj) {
+ System.err.println("WARNING: unimplemented method called PKCS7#certificates=");
+ return getRuntime().getNil();
+ }
+
+ private Collection getCertificates() {
+ Collection certs;
+ int i = p7.getType();
+ switch(i) {
+ case ASN1Registry.NID_pkcs7_signed:
+ certs = p7.getSign().getCert();
+ break;
+ case ASN1Registry.NID_pkcs7_signedAndEnveloped:
+ certs = p7.getSignedAndEnveloped().getCert();
+ break;
+ default:
+ certs = new HashSet();
+ break;
+ }
+ return certs;
+ }
+
+ private RubyArray certsToArray(Collection certs) throws CertificateEncodingException {
+ RubyArray ary = getRuntime().newArray(certs.size());
+ for(X509AuxCertificate x509 : certs) {
+ ary.append(X509Cert.wrap(getRuntime(), x509));
+ }
+ return ary;
+ }
+
+ @JRubyMethod
+ public IRubyObject certificates() {
+ try {
+ return certsToArray(getCertificates());
+ } catch (CertificateEncodingException cee) {
+ throw newPKCS7Error(getRuntime(), cee.getMessage());
+ }
+ }
+
+ @JRubyMethod
+ public IRubyObject add_crl(IRubyObject obj) {
+ System.err.println("WARNING: unimplemented method called PKCS7#add_crl");
+ return getRuntime().getNil();
+ }
+
+ @JRubyMethod(name="crls=")
+ public IRubyObject set_crls(IRubyObject obj) {
+ System.err.println("WARNING: unimplemented method called PKCS7#crls=");
+ return getRuntime().getNil();
+ }
+
+ @JRubyMethod
+ public IRubyObject crls() {
+ System.err.println("WARNING: unimplemented method called PKCS7#crls");
+ return getRuntime().getNil();
+ }
+
+ @JRubyMethod(name={"add_data", "data="})
+ public IRubyObject add_data(IRubyObject obj) {
+ System.err.println("WARNING: unimplemented method called PKCS7#add_data");
+ return getRuntime().getNil();
+ }
+
+ @JRubyMethod(rest=true)
+ public IRubyObject verify(IRubyObject[] args) {
+ IRubyObject certs = null;
+ IRubyObject store = null;
+ IRubyObject indata = getRuntime().getNil();
+ IRubyObject vflags = getRuntime().getNil();
+
+ switch(Arity.checkArgumentCount(getRuntime(), args, 2, 4)) {
+ case 4:
+ vflags = args[3];
+ case 3:
+ indata = args[2];
+ default:
+ store = args[1];
+ certs = args[0];
+ }
+ int flg = vflags.isNil() ? 0 : RubyNumeric.fix2int(vflags);
+
+ if(indata.isNil()) {
+ indata = getData();
+ }
+
+ BIO in = indata.isNil() ? null : obj2bio(indata);
+
+ List x509s = certs.isNil()
+ ? null
+ : x509_ary2sk(certs);
+
+ Store x509st = ((X509Store)store).getStore();
+ BIO out = BIO.mem();
+
+ boolean result = false;
+ try {
+ p7.verify(x509s, x509st, in, out, flg);
+ result = true;
+ } catch(NotVerifiedPKCS7Exception e) {
+ result = false;
+ } catch(PKCS7Exception pkcs7e) {
+ if (getRuntime().isDebug()) {
+ System.err.println(pkcs7e.toString());
+ pkcs7e.printStackTrace(System.err);
+ }
+ result = false;
+ }
+
+ IRubyObject data = membio2str(getRuntime(), out);
+ setData(data);
+
+ return result ? getRuntime().getTrue() : getRuntime().getFalse();
+ }
+
+ @JRubyMethod(rest=true)
+ public IRubyObject decrypt(IRubyObject[] args) {
+ IRubyObject dflags = getRuntime().getNil();
+ if(Arity.checkArgumentCount(getRuntime(), args, 2, 3) == 3) {
+ dflags = args[2];
+ }
+ IRubyObject pkey = args[0];
+ IRubyObject cert = args[1];
+ PrivateKey key = ((PKey)pkey).getPrivateKey();
+ X509AuxCertificate x509 = ((X509Cert)cert).getAuxCert();
+ int flg = dflags.isNil() ? 0 : RubyNumeric.fix2int(dflags);
+
+ BIO out = BIO.mem();
+ try {
+ p7.decrypt(key, x509, out, flg);
+ } catch (PKCS7Exception pkcs7e) {
+ throw newPKCS7Exception(getRuntime(), pkcs7e);
+ }
+
+ return membio2str(getRuntime(), out);
+ }
+
+ public static RaiseException newPKCS7Exception(Ruby ruby, PKCS7Exception pkcs7e) {
+ if (ruby.isDebug()) {
+ System.err.println(pkcs7e.toString());
+ pkcs7e.printStackTrace(System.err);
+ }
+ return Utils.newError(ruby, "OpenSSL::PKCS7::PKCS7Error", pkcs7e.getMessage());
+ }
+
+ @JRubyMethod(name = {"to_pem", "to_s"})
+ public IRubyObject to_pem() {
+ try {
+ StringWriter w = new StringWriter();
+ PEMInputOutput.writePKCS7(w, p7.toASN1());
+ w.close();
+ return getRuntime().newString(w.toString());
+ } catch (IOException ex) {
+ throw getRuntime().newIOErrorFromException(ex);
+ }
+ }
+
+ @JRubyMethod
+ public IRubyObject to_der() {
+ try {
+ return getRuntime().newString(new ByteList(p7.toASN1(), false));
+ } catch (IOException ioe) {
+ throw newPKCS7Error(getRuntime(), ioe.getMessage());
+ }
+ }
+
+ public static class SignerInfo extends RubyObject {
+ private static final long serialVersionUID = -3799397032272738848L;
+
+ private static ObjectAllocator SIGNERINFO_ALLOCATOR = new ObjectAllocator() {
+ public IRubyObject allocate(Ruby runtime, RubyClass klass) {
+ return new SignerInfo(runtime, klass);
+ }
+ };
+
+ public static void createSignerInfo(Ruby runtime, RubyModule cPKCS7) {
+ RubyClass cPKCS7Signer = cPKCS7.defineClassUnder("SignerInfo",runtime.getObject(),SIGNERINFO_ALLOCATOR);
+ cPKCS7.defineConstant("Signer",cPKCS7Signer);
+
+ cPKCS7Signer.defineAnnotatedMethods(SignerInfo.class);
+ }
+
+ public static SignerInfo create(Ruby runtime, SignerInfoWithPkey info) {
+ SignerInfo sinfo = new SignerInfo(runtime, Utils.getClassFromPath(runtime, "OpenSSL::PKCS7::SignerInfo"));
+ sinfo.initWithSignerInformation(info);
+ return sinfo;
+ }
+
+ public SignerInfo(Ruby runtime, RubyClass type) {
+ super(runtime,type);
+ }
+
+ private SignerInfoWithPkey info;
+
+ private void initWithSignerInformation(SignerInfoWithPkey info) {
+ this.info = info;
+ }
+
+ SignerInfoWithPkey getSignerInfo() {
+ return info;
+ }
+
+ @JRubyMethod
+ public IRubyObject initialize(IRubyObject arg1, IRubyObject arg2, IRubyObject arg3) {
+ System.err.println("WARNING: unimplemented method called SignerInfo#initialize");
+ return this;
+ }
+
+
+ @JRubyMethod(name={"issuer","name"})
+ public IRubyObject issuer() {
+ return X509Name.create(getRuntime(), info.getIssuerAndSerialNumber().getName());
+ }
+
+ @JRubyMethod
+ public IRubyObject serial() {
+ return RubyBignum.bignorm(getRuntime(), info.getIssuerAndSerialNumber().getCertificateSerialNumber().getValue());
+ }
+
+ @JRubyMethod
+ public IRubyObject signed_time() {
+ System.err.println("WARNING: unimplemented method called SignerInfo#signed_time");
+ return getRuntime().getNil();
+ }
+ }
+
+ public static class RecipientInfo extends RubyObject {
+ private static final long serialVersionUID = 6977793206950149902L;
+
+ private static ObjectAllocator RECIPIENTINFO_ALLOCATOR = new ObjectAllocator() {
+ public IRubyObject allocate(Ruby runtime, RubyClass klass) {
+ return new RecipientInfo(runtime, klass);
+ }
+ };
+
+ public static void createRecipientInfo(Ruby runtime, RubyModule cPKCS7) {
+ RubyClass cPKCS7Recipient = cPKCS7.defineClassUnder("RecipientInfo",runtime.getObject(),RECIPIENTINFO_ALLOCATOR);
+
+ cPKCS7Recipient.defineAnnotatedMethods(RecipientInfo.class);
+ }
+
+ public RecipientInfo(Ruby runtime, RubyClass type) {
+ super(runtime,type);
+ }
+
+
+ public static RecipientInfo create(Ruby runtime, RecipInfo info) {
+ RecipientInfo rinfo = new RecipientInfo(runtime, Utils.getClassFromPath(runtime, "OpenSSL::PKCS7::RecipientInfo"));
+ rinfo.initWithRecipientInformation(info);
+ return rinfo;
+ }
+
+ private RecipInfo info;
+
+ private void initWithRecipientInformation(RecipInfo info) {
+ this.info = info;
+ }
+
+ @JRubyMethod
+ public IRubyObject initialize(IRubyObject arg) {
+ System.err.println("WARNING: unimplemented method called RecipientInfo#initialize");
+ return this;
+ }
+
+ @JRubyMethod
+ public IRubyObject issuer() {
+ return X509Name.create(getRuntime(), info.getIssuerAndSerial().getName());
+ }
+
+ @JRubyMethod
+ public IRubyObject serial() {
+ return RubyBignum.bignorm(getRuntime(), info.getIssuerAndSerial().getCertificateSerialNumber().getValue());
+ }
+
+ @JRubyMethod
+ public IRubyObject enc_key() {
+ System.err.println("WARNING: unimplemented method called RecipientInfo#enc_key");
+ return getRuntime().getNil();
+ }
+ }
+
+ private static RaiseException newPKCS7Error(Ruby runtime, String message) {
+ return Utils.newError(runtime, "OpenSSL::PKCS7::PKCS7Error", message);
+ }
+}// PKCS7
diff --git a/src/org/jruby/ext/openssl/PKey.java b/src/org/jruby/ext/openssl/PKey.java
new file mode 100644
index 00000000000..88139980d51
--- /dev/null
+++ b/src/org/jruby/ext/openssl/PKey.java
@@ -0,0 +1,176 @@
+/***** BEGIN LICENSE BLOCK *****
+ * Version: CPL 1.0/GPL 2.0/LGPL 2.1
+ *
+ * The contents of this file are subject to the Common Public
+ * License Version 1.0 (the "License"); you may not use this file
+ * except in compliance with the License. You may obtain a copy of
+ * the License at http://www.eclipse.org/legal/cpl-v10.html
+ *
+ * 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, 2007 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 CPL, 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 CPL, the GPL or the LGPL.
+ ***** END LICENSE BLOCK *****/
+package org.jruby.ext.openssl;
+
+import java.math.BigInteger;
+import java.security.GeneralSecurityException;
+import java.security.InvalidKeyException;
+import java.security.NoSuchAlgorithmException;
+import java.security.PrivateKey;
+import java.security.PublicKey;
+import java.security.Signature;
+import java.security.SignatureException;
+
+import org.jruby.Ruby;
+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.runtime.ObjectAllocator;
+import org.jruby.runtime.builtin.IRubyObject;
+
+/**
+ * @author Ola Bini
+ */
+public abstract class PKey extends RubyObject {
+ private static final long serialVersionUID = 6114668087816965720L;
+
+ public static void createPKey(Ruby runtime, RubyModule ossl) {
+ RubyModule mPKey = ossl.defineModuleUnder("PKey");
+ // PKey is abstract
+ RubyClass cPKey = mPKey.defineClassUnder("PKey",runtime.getObject(),ObjectAllocator.NOT_ALLOCATABLE_ALLOCATOR);
+ RubyClass openSSLError = ossl.getClass("OpenSSLError");
+ mPKey.defineClassUnder("PKeyError",openSSLError,openSSLError.getAllocator());
+
+ cPKey.defineAnnotatedMethods(PKey.class);
+
+ PKeyRSA.createPKeyRSA(runtime,mPKey);
+ PKeyDSA.createPKeyDSA(runtime,mPKey);
+ PKeyDH.createPKeyDH(runtime, mPKey, cPKey);
+ }
+
+ public static RaiseException newPKeyError(Ruby runtime, String message) {
+ return Utils.newError(runtime, "OpenSSL::PKey::PKeyError", message);
+ }
+
+ public PKey(Ruby runtime, RubyClass type) {
+ super(runtime,type);
+ }
+
+ @Override
+ @JRubyMethod
+ public IRubyObject initialize() {
+ return this;
+ }
+
+ PublicKey getPublicKey() {
+ return null;
+ }
+
+ PrivateKey getPrivateKey() {
+ return null;
+ }
+
+ String getAlgorithm() {
+ return "NONE";
+ }
+
+ // FIXME: any compelling reason for abstract method here?
+ public abstract IRubyObject to_der();
+
+ @JRubyMethod
+ public IRubyObject sign(IRubyObject digest, IRubyObject data) {
+ if (!this.callMethod(getRuntime().getCurrentContext(), "private?").isTrue()) {
+ throw getRuntime().newArgumentError("Private key is needed.");
+ }
+ String digAlg = ((Digest) digest).getShortAlgorithm();
+ try {
+ Signature sig = Signature.getInstance(digAlg + "WITH" + getAlgorithm());
+ sig.initSign(getPrivateKey());
+ byte[] inp = data.convertToString().getBytes();
+ sig.update(inp);
+ byte[] sigge = sig.sign();
+ return RubyString.newString(getRuntime(), sigge);
+ } catch (GeneralSecurityException gse) {
+ throw newPKeyError(getRuntime(), gse.getMessage());
+ }
+ /*
+ GetPKey(self, pkey);
+ EVP_SignInit(&ctx, GetDigestPtr(digest));
+ StringValue(data);
+ EVP_SignUpdate(&ctx, RSTRING(data)->ptr, RSTRING(data)->len);
+ str = rb_str_new(0, EVP_PKEY_size(pkey)+16);
+ if (!EVP_SignFinal(&ctx, RSTRING(str)->ptr, &buf_len, pkey))
+ ossl_raise(ePKeyError, NULL);
+ assert(buf_len <= RSTRING(str)->len);
+ RSTRING(str)->len = buf_len;
+ RSTRING(str)->ptr[buf_len] = 0;
+
+ return str;
+ */
+ }
+
+ @JRubyMethod
+ public IRubyObject verify(IRubyObject digest, IRubyObject sig, IRubyObject data) {
+ if (!(digest instanceof Digest)) {
+ throw newPKeyError(getRuntime(), "invalid digest");
+ }
+ if (!(sig instanceof RubyString)) {
+ throw newPKeyError(getRuntime(), "invalid signature");
+ }
+ if (!(data instanceof RubyString)) {
+ throw newPKeyError(getRuntime(), "invalid data");
+ }
+ byte[] sigBytes = ((RubyString)sig).getBytes();
+ byte[] dataBytes = ((RubyString)data).getBytes();
+ String algorithm = ((Digest)digest).getShortAlgorithm() + "WITH" + getAlgorithm();
+ boolean valid;
+ try {
+ Signature signature = Signature.getInstance(algorithm);
+ signature.initVerify(getPublicKey());
+ signature.update(dataBytes);
+ valid = signature.verify(sigBytes);
+ } catch (NoSuchAlgorithmException e) {
+ throw newPKeyError(getRuntime(), "unsupported algorithm: " + algorithm);
+ } catch (SignatureException e) {
+ throw newPKeyError(getRuntime(), "invalid signature");
+ } catch (InvalidKeyException e) {
+ throw newPKeyError(getRuntime(), "invalid key");
+ }
+ return getRuntime().newBoolean(valid);
+ }
+
+ protected static void addSplittedAndFormatted(StringBuilder result, BigInteger value) {
+ String v = value.toString(16);
+ if ((v.length() % 2) != 0) {
+ v = "0" + v;
+ }
+ String sep = "";
+ 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("\n");
+ }
+}// PKey
diff --git a/src/org/jruby/ext/openssl/PKeyDH.java b/src/org/jruby/ext/openssl/PKeyDH.java
new file mode 100644
index 00000000000..79a4b34f6ff
--- /dev/null
+++ b/src/org/jruby/ext/openssl/PKeyDH.java
@@ -0,0 +1,394 @@
+/***** BEGIN LICENSE BLOCK *****
+ * Version: CPL 1.0/GPL 2.0/LGPL 2.1
+ *
+ * The contents of this file are subject to the Common Public
+ * License Version 1.0 (the "License"); you may not use this file
+ * except in compliance with the License. You may obtain a copy of
+ * the License at http://www.eclipse.org/legal/cpl-v10.html
+ *
+ * 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) 2007 William N Dortch
+ *
+ * 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 CPL, 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 CPL, the GPL or the LGPL.
+ ***** END LICENSE BLOCK *****/
+package org.jruby.ext.openssl;
+
+import java.io.IOException;
+import java.io.StringReader;
+import java.io.StringWriter;
+import java.math.BigInteger;
+import java.security.SecureRandom;
+import java.security.spec.InvalidParameterSpecException;
+import java.util.HashMap;
+
+import javax.crypto.spec.DHParameterSpec;
+
+import org.jruby.Ruby;
+import org.jruby.RubyClass;
+import org.jruby.RubyHash;
+import org.jruby.RubyModule;
+import org.jruby.RubyNumeric;
+import org.jruby.RubyString;
+import org.jruby.anno.JRubyMethod;
+import org.jruby.exceptions.RaiseException;
+import org.jruby.ext.openssl.x509store.PEMInputOutput;
+import org.jruby.runtime.Arity;
+import org.jruby.runtime.ObjectAllocator;
+import org.jruby.runtime.builtin.IRubyObject;
+import org.jruby.util.ByteList;
+
+/**
+ * OpenSSL::PKey::DH implementation.
+ *
+ * @author Bill Dortch
+ */
+public class PKeyDH extends PKey {
+ private static final long serialVersionUID = 293266329939132250L;
+
+ // parameters used in generating 'p'; see [ossl]/crypto/dh/dh_gen.c #dh_builtin_genparams
+ private static final BigInteger GEN_2_ADD_PARAM = BigInteger.valueOf(24);
+ private static final BigInteger GEN_2_REM_PARAM = BigInteger.valueOf(11);
+ private static final BigInteger GEN_5_ADD_PARAM = BigInteger.valueOf(10);
+ private static final BigInteger GEN_5_REM_PARAM = BigInteger.valueOf(3);
+ private static final BigInteger DEFAULT_ADD_PARAM = BigInteger.valueOf(2);
+ private static final BigInteger DEFAULT_REM_PARAM = BigInteger.ONE;
+
+ private static final BigInteger TWO = BigInteger.valueOf(2);
+
+ // from [ossl]/crypto/dh/dh.h
+ private static final int OPENSSL_DH_MAX_MODULUS_BITS = 10000;
+
+ private static ObjectAllocator PKEYDH_ALLOCATOR = new ObjectAllocator() {
+ public IRubyObject allocate(Ruby runtime, RubyClass klass) {
+ return new PKeyDH(runtime, klass);
+ }
+ };
+
+ public static void createPKeyDH(Ruby runtime, RubyModule pkeyModule, RubyClass pkeyClass) {
+ RubyClass dh = pkeyModule.defineClassUnder("DH", pkeyClass, PKEYDH_ALLOCATOR);
+
+ RubyClass pkeyError = pkeyModule.getClass("PKeyError");
+ pkeyModule.defineClassUnder("DHError",pkeyError,pkeyError.getAllocator());
+
+ dh.defineAnnotatedMethods(PKeyDH.class);
+ }
+
+ public static RaiseException newDHError(Ruby runtime, String message) {
+ return Utils.newError(runtime, "OpenSSL::PKey::DHError", message);
+ }
+
+ private static SecureRandom _secureRandom;
+
+ private static SecureRandom getSecureRandom() {
+ SecureRandom rand;
+ if ((rand = _secureRandom) != null) {
+ return rand;
+ }
+ // FIXME: do we want a particular algorithm / provider? BC?
+ return _secureRandom = new SecureRandom();
+ }
+
+ // transient because: we do not want these value serialized (insecure)
+ // volatile because: permits unsynchronized reads in some cases
+ private transient volatile BigInteger dh_p;
+ private transient volatile BigInteger dh_g;
+ private transient volatile BigInteger dh_pub_key;
+ private transient volatile BigInteger dh_priv_key;
+
+ // 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(name="initialize", rest=true)
+ public synchronized IRubyObject dh_initialize(IRubyObject[] args) {
+ Ruby runtime = getRuntime();
+ if (this.dh_p != null || this.dh_g != null || this.dh_pub_key != null || this.dh_priv_key != null) {
+ throw newDHError(runtime, "illegal initialization");
+ }
+ int argc = Arity.checkArgumentCount(runtime, args, 0, 2);
+ if (argc > 0) {
+ IRubyObject arg0 = args[0];
+ if (argc == 1 && arg0 instanceof RubyString) {
+ try {
+ DHParameterSpec spec = PEMInputOutput.readDHParameters(new StringReader(arg0.toString()));
+ this.dh_p = spec.getP();
+ this.dh_g = spec.getG();
+ } catch (NoClassDefFoundError ncdfe) {
+ throw newDHError(runtime, OpenSSLReal.bcExceptionMessage(ncdfe));
+ } catch (IOException e) {
+ throw runtime.newIOErrorFromException(e);
+ } catch (InvalidParameterSpecException e) {
+ throw runtime.newArgumentError(e.getMessage());
+ }
+ } else {
+ int bits = RubyNumeric.fix2int(args[0]);
+ // 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_priv_key = x;
+ this.dh_pub_key = y;
+ }
+ }
+ return this;
+ }
+
+ public static BigInteger generateP(int bits, int g) {
+
+ // FIXME? I'm following algorithms used in OpenSSL, could use JCE provider instead.
+ // (Note that I tried that, but got mystifying values of g returned by the param generator.
+ // In any case, in OpenSSL/MRI-OpenSSL, the caller supplies g, or it defaults to 2.)
+
+ // see [ossl]/crypto/dh/dh_gen.c #dh_builtin_genparams
+
+ if (bits < 2) throw new IllegalArgumentException("invalid bit length");
+ if (g < 2) throw new IllegalArgumentException("invalid generator");
+
+ // generate safe prime meeting appropriate add/rem (mod) criteria
+
+ switch(g) {
+ case 2:
+ // add = 24, rem = 11
+ return BN.generatePrime(bits, true, GEN_2_ADD_PARAM, GEN_2_REM_PARAM);
+ case 5:
+ // add = 10, rem = 3
+ return BN.generatePrime(bits, true, GEN_5_ADD_PARAM, GEN_5_REM_PARAM);
+ default:
+ // add = 2, rem = 1
+ return BN.generatePrime(bits, true, DEFAULT_ADD_PARAM, DEFAULT_REM_PARAM);
+ }
+ }
+
+ public static BigInteger generateX(BigInteger p, int limit) {
+ if (limit < 0) throw new IllegalArgumentException("invalid limit");
+
+ BigInteger x;
+ SecureRandom secureRandom = getSecureRandom();
+ // 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) {
+ BigInteger pSub2 = p.subtract(TWO);
+ do {
+ x = BN.getRandomBIInRange(pSub2, secureRandom);
+ } while (x.equals(BigInteger.ZERO));
+ } else {
+ do {
+ // generate potential prime, though with 0 certainty (no Miller-Rabin tests)
+ x = new BigInteger(limit, 0, secureRandom);
+ } while (x.equals(BigInteger.ZERO));
+ }
+ return x;
+ }
+
+ public static BigInteger generateX(BigInteger p) {
+ // OpenSSL default l(imit) is p bits - 1 -- see [ossl]/crypto/dh/dh_key.c #generate_key
+ return generateX(p, p.bitLength() - 1);
+ }
+
+ 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 dh_generate_key() {
+ BigInteger p, g, x, y;
+ if ((p = this.dh_p) == null || (g = this.dh_g) == null) {
+ throw newDHError(getRuntime(), "can't generate key");
+ }
+ if ((x = this.dh_priv_key) == null) {
+ x = generateX(p);
+ }
+ y = generateY(p, g, x);
+ this.dh_priv_key = x;
+ this.dh_pub_key = y;
+ return this;
+ }
+
+ @JRubyMethod(name="compute_key")
+ public synchronized IRubyObject dh_compute_key(IRubyObject other_pub_key) {
+ BigInteger x, y, p;
+ if ((y = BN.getBigInteger(other_pub_key)) == null) {
+ throw getRuntime().newArgumentError("invalid public key");
+ }
+ if ((x = this.dh_priv_key) == null || (p = this.dh_p) == null) {
+ throw newDHError(getRuntime(), "can't compute key");
+ }
+ int plen;
+ if ((plen = p.bitLength()) == 0 || plen > OPENSSL_DH_MAX_MODULUS_BITS) {
+ throw newDHError(getRuntime(), "can't compute key");
+ }
+ return getRuntime().newString(new ByteList(computeKey(y, x, p), false));
+ }
+
+ public static byte[] computeKey(BigInteger y, BigInteger x, BigInteger p) {
+ return y.modPow(x, p).toByteArray();
+ }
+
+ @JRubyMethod(name="public?")
+ public IRubyObject dh_is_public() {
+ return getRuntime().newBoolean(dh_pub_key != null);
+ }
+
+ @JRubyMethod(name="private?")
+ public IRubyObject dh_is_private() {
+ // 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(dh_priv_key != null /* || haveEngine */);
+ }
+
+ @JRubyMethod(name={"export", "to_pem", "to_s"})
+ public IRubyObject dh_export() {
+ BigInteger p, g;
+ synchronized(this) {
+ p = this.dh_p;
+ g = this.dh_g;
+ }
+ StringWriter w = new StringWriter();
+ try {
+ PEMInputOutput.writeDHParameters(w, new DHParameterSpec(p, g));
+ w.flush();
+ w.close();
+ } catch (NoClassDefFoundError ncdfe) {
+ throw newDHError(getRuntime(), OpenSSLReal.bcExceptionMessage(ncdfe));
+ } catch (IOException e) {
+ // shouldn't happen (string/buffer io only)
+ throw getRuntime().newIOErrorFromException(e);
+ }
+ return getRuntime().newString(w.toString());
+ }
+
+ @JRubyMethod(name = "to_der")
+ public IRubyObject dh_to_der() {
+ BigInteger p, g;
+ synchronized (this) {
+ p = this.dh_p;
+ g = this.dh_g;
+ }
+ try {
+ byte[] bytes = org.jruby.ext.openssl.impl.PKey.toDerDHKey(p, g);
+ return RubyString.newString(getRuntime(), bytes);
+ } catch (NoClassDefFoundError ncdfe) {
+ throw newDHError(getRuntime(), OpenSSLReal.bcExceptionMessage(ncdfe));
+ } catch (IOException ioe) {
+ throw newDHError(getRuntime(), ioe.getMessage());
+ }
+ }
+
+ @JRubyMethod(name="params")
+ public IRubyObject dh_get_params() {
+ BigInteger p, g, x, y;
+ synchronized(this) {
+ p = this.dh_p;
+ g = this.dh_g;
+ x = this.dh_priv_key;
+ y = this.dh_pub_key;
+ }
+ Ruby runtime = getRuntime();
+ HashMap params = new HashMap();
+
+ params.put(runtime.newString("p"), BN.newBN(runtime, p));
+ params.put(runtime.newString("g"), BN.newBN(runtime, g));
+ params.put(runtime.newString("pub_key"), BN.newBN(runtime, x));
+ params.put(runtime.newString("priv_key"), BN.newBN(runtime, y));
+
+ return RubyHash.newHash(runtime, params, runtime.getNil());
+ }
+
+ // don't need synchronized as value is volatile
+ @JRubyMethod(name="p")
+ public IRubyObject dh_get_p() {
+ return getBN(dh_p);
+ }
+
+ @JRubyMethod(name="p=")
+ public synchronized IRubyObject dh_set_p(IRubyObject arg) {
+ this.dh_p = BN.getBigInteger(arg);
+ return arg;
+ }
+
+ // don't need synchronized as value is volatile
+ @JRubyMethod(name="g")
+ public IRubyObject dh_get_g() {
+ return getBN(dh_g);
+ }
+
+ @JRubyMethod(name="g=")
+ public synchronized IRubyObject dh_set_g(IRubyObject arg) {
+ this.dh_g = BN.getBigInteger(arg);
+ return arg;
+ }
+
+ // don't need synchronized as value is volatile
+ @JRubyMethod(name="pub_key")
+ public IRubyObject dh_get_pub_key() {
+ return getBN(dh_pub_key);
+ }
+
+ @JRubyMethod(name="pub_key=")
+ public synchronized IRubyObject dh_set_pub_key(IRubyObject arg) {
+ this.dh_pub_key = BN.getBigInteger(arg);
+ return arg;
+ }
+
+ // don't need synchronized as value is volatile
+ @JRubyMethod(name="priv_key")
+ public IRubyObject dh_get_priv_key() {
+ return getBN(dh_priv_key);
+ }
+
+ @JRubyMethod(name="priv_key=")
+ public synchronized IRubyObject dh_set_priv_key(IRubyObject arg) {
+ this.dh_priv_key = BN.getBigInteger(arg);
+ return arg;
+ }
+
+ private IRubyObject getBN(BigInteger value) {
+ if (value != null) {
+ return BN.newBN(getRuntime(), value);
+ }
+ return getRuntime().getNil();
+ }
+
+ // override differently-named abstract method from PKey
+ public IRubyObject to_der() {
+ return dh_to_der();
+ }
+
+}
diff --git a/src/org/jruby/ext/openssl/PKeyDSA.java b/src/org/jruby/ext/openssl/PKeyDSA.java
new file mode 100644
index 00000000000..4aa3fd397d5
--- /dev/null
+++ b/src/org/jruby/ext/openssl/PKeyDSA.java
@@ -0,0 +1,474 @@
+/***** BEGIN LICENSE BLOCK *****
+ * Version: CPL 1.0/GPL 2.0/LGPL 2.1
+ *
+ * The contents of this file are subject to the Common Public
+ * License Version 1.0 (the "License"); you may not use this file
+ * except in compliance with the License. You may obtain a copy of
+ * the License at http://www.eclipse.org/legal/cpl-v10.html
+ *
+ * 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, 2007 Ola Bini
+ * Copyright (C) 2007 Wiliam N Dortch
+ *
+ * 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 CPL, 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 CPL, the GPL or the LGPL.
+ ***** END LICENSE BLOCK *****/
+package org.jruby.ext.openssl;
+
+import java.io.IOException;
+import java.io.StringReader;
+import java.io.StringWriter;
+import java.math.BigInteger;
+import java.security.KeyFactory;
+import java.security.KeyPair;
+import java.security.KeyPairGenerator;
+import java.security.NoSuchAlgorithmException;
+import java.security.PrivateKey;
+import java.security.PublicKey;
+import java.security.SecureRandom;
+import java.security.interfaces.DSAKey;
+import java.security.interfaces.DSAPrivateKey;
+import java.security.interfaces.DSAPublicKey;
+import java.security.spec.DSAPublicKeySpec;
+import java.security.spec.InvalidKeySpecException;
+import java.security.spec.PKCS8EncodedKeySpec;
+import java.security.spec.X509EncodedKeySpec;
+
+import org.jruby.Ruby;
+import org.jruby.RubyClass;
+import org.jruby.RubyFixnum;
+import org.jruby.RubyModule;
+import org.jruby.RubyNumeric;
+import org.jruby.RubyString;
+import org.jruby.anno.JRubyMethod;
+import org.jruby.exceptions.RaiseException;
+import org.jruby.ext.openssl.x509store.PEMInputOutput;
+import org.jruby.runtime.ObjectAllocator;
+import org.jruby.runtime.builtin.IRubyObject;
+
+/**
+ * @author Ola Bini
+ */
+public class PKeyDSA extends PKey {
+ private static final long serialVersionUID = 2359742219218350277L;
+
+ private static ObjectAllocator PKEYDSA_ALLOCATOR = new ObjectAllocator() {
+ public IRubyObject allocate(Ruby runtime, RubyClass klass) {
+ return new PKeyDSA(runtime, klass);
+ }
+ };
+
+ public static void createPKeyDSA(Ruby runtime, RubyModule mPKey) {
+ RubyClass cDSA = mPKey.defineClassUnder("DSA",mPKey.getClass("PKey"),PKEYDSA_ALLOCATOR);
+ RubyClass pkeyError = mPKey.getClass("PKeyError");
+ mPKey.defineClassUnder("DSAError",pkeyError,pkeyError.getAllocator());
+
+
+ cDSA.defineAnnotatedMethods(PKeyDSA.class);
+ }
+
+ public static RaiseException newDSAError(Ruby runtime, String message) {
+ return Utils.newError(runtime, "OpenSSL::PKey::DSAError", message);
+ }
+
+ public PKeyDSA(Ruby runtime, RubyClass type) {
+ super(runtime,type);
+ }
+
+ private DSAPrivateKey privKey;
+ private DSAPublicKey pubKey;
+
+ // specValues holds individual DSAPublicKeySpec components. this allows
+ // a public key to be constructed incrementally, as required by the
+ // current implementation of Net::SSH.
+ // (see net-ssh-1.1.2/lib/net/ssh/transport/ossl/buffer.rb #read_keyblob)
+ private BigInteger[] specValues;
+
+ private static final int SPEC_Y = 0;
+ private static final int SPEC_P = 1;
+ private static final int SPEC_Q = 2;
+ private static final int SPEC_G = 3;
+
+ @Override
+ PublicKey getPublicKey() {
+ return pubKey;
+ }
+
+ @Override
+ PrivateKey getPrivateKey() {
+ return privKey;
+ }
+
+ @Override
+ String getAlgorithm() {
+ return "DSA";
+ }
+
+ @JRubyMethod(name = "generate", meta = true)
+ public static IRubyObject generate(IRubyObject recv, IRubyObject arg) {
+ int keysize = RubyNumeric.fix2int(arg);
+ PKeyDSA dsa = new PKeyDSA(recv.getRuntime(), (RubyClass) recv);
+ dsaGenerate(dsa, keysize);
+ return dsa;
+ }
+
+ /*
+ * c: dsa_generate
+ */
+ private static void dsaGenerate(PKeyDSA dsa, int keysize) throws RaiseException {
+ try {
+ KeyPairGenerator gen = KeyPairGenerator.getInstance("DSA");
+ gen.initialize(keysize, new SecureRandom());
+ KeyPair pair = gen.generateKeyPair();
+ dsa.privKey = (DSAPrivateKey) (pair.getPrivate());
+ dsa.pubKey = (DSAPublicKey) (pair.getPublic());
+ } catch (Exception e) {
+ throw newDSAError(dsa.getRuntime(), e.getMessage());
+ }
+ }
+
+ @JRubyMethod(rest = true)
+ public IRubyObject initialize(IRubyObject[] args) {
+ IRubyObject arg;
+ IRubyObject pass = null;
+ char[] passwd = null;
+ if (org.jruby.runtime.Arity.checkArgumentCount(getRuntime(), args, 0, 2) == 0) {
+ privKey = null;
+ pubKey = null;
+ } else {
+ arg = args[0];
+ if (args.length > 1) {
+ pass = args[1];
+ }
+ if (arg instanceof RubyFixnum) {
+ int keysize = RubyNumeric.fix2int(arg);
+ dsaGenerate(this, keysize);
+ } else {
+ if (pass != null && !pass.isNil()) {
+ passwd = pass.toString().toCharArray();
+ }
+ arg = OpenSSLImpl.to_der_if_possible(arg);
+ RubyString str = arg.convertToString();
+
+ Object val = null;
+ KeyFactory fact = null;
+ try {
+ fact = KeyFactory.getInstance("DSA");
+ } catch (NoSuchAlgorithmException e) {
+ throw getRuntime().newLoadError("unsupported key algorithm (DSA)");
+ }
+ // TODO: ugly NoClassDefFoundError catching for no BC env. How can we remove this?
+ if (null == val) {
+ // PEM_read_bio_DSAPrivateKey
+ try {
+ val = PEMInputOutput.readDSAPrivateKey(new StringReader(str.toString()), passwd);
+ } catch (NoClassDefFoundError e) {
+ val = null;
+ } catch (Exception e) {
+ val = null;
+ }
+ }
+ if (null == val) {
+ // PEM_read_bio_DSAPublicKey
+ try {
+ val = PEMInputOutput.readDSAPublicKey(new StringReader(str.toString()), passwd);
+ } catch (NoClassDefFoundError e) {
+ val = null;
+ } catch (Exception e) {
+ val = null;
+ }
+ }
+ if (null == val) {
+ // PEM_read_bio_DSA_PUBKEY
+ try {
+ val = PEMInputOutput.readDSAPubKey(new StringReader(str.toString()), passwd);
+ } catch (NoClassDefFoundError e) {
+ val = null;
+ } catch (Exception e) {
+ val = null;
+ }
+ }
+ if (null == val) {
+ // d2i_DSAPrivateKey_bio
+ try {
+ val = org.jruby.ext.openssl.impl.PKey.readDSAPrivateKey(str.getBytes());
+ } catch (NoClassDefFoundError e) {
+ val = null;
+ } catch (Exception e) {
+ val = null;
+ }
+ }
+ if (null == val) {
+ // d2i_DSA_PUBKEY_bio
+ try {
+ val = org.jruby.ext.openssl.impl.PKey.readDSAPublicKey(str.getBytes());
+ } catch (NoClassDefFoundError e) {
+ val = null;
+ } catch (Exception e) {
+ val = null;
+ }
+ }
+ if (null == val) {
+ try {
+ val = fact.generatePrivate(new PKCS8EncodedKeySpec(str.getBytes()));
+ } catch (Exception e) {
+ val = null;
+ }
+ }
+ if (null == val) {
+ try {
+ val = fact.generatePublic(new X509EncodedKeySpec(str.getBytes()));
+ } catch (Exception e) {
+ val = null;
+ }
+ }
+ if (null == val) {
+ throw newDSAError(getRuntime(), "Neither PUB key nor PRIV key:");
+ }
+
+ if (val instanceof KeyPair) {
+ privKey = (DSAPrivateKey) (((KeyPair) val).getPrivate());
+ pubKey = (DSAPublicKey) (((KeyPair) val).getPublic());
+ } else if (val instanceof DSAPrivateKey) {
+ privKey = (DSAPrivateKey) val;
+ } else if (val instanceof DSAPublicKey) {
+ pubKey = (DSAPublicKey) val;
+ privKey = null;
+ } else {
+ throw newDSAError(getRuntime(), "Neither PUB key nor PRIV key:");
+ }
+ }
+ }
+ return this;
+ }
+
+ @JRubyMethod(name="public?")
+ public IRubyObject public_p() {
+ return pubKey != null ? getRuntime().getTrue() : getRuntime().getFalse();
+ }
+
+ @JRubyMethod(name="private?")
+ public IRubyObject private_p() {
+ return privKey != null ? getRuntime().getTrue() : getRuntime().getFalse();
+ }
+
+ @JRubyMethod
+ public IRubyObject to_der() {
+ try {
+ byte[] bytes = org.jruby.ext.openssl.impl.PKey.toDerDSAKey(pubKey, privKey);
+ return RubyString.newString(getRuntime(), bytes);
+ } catch (NoClassDefFoundError ncdfe) {
+ throw newDSAError(getRuntime(), OpenSSLReal.bcExceptionMessage(ncdfe));
+ } catch (IOException ioe) {
+ throw newDSAError(getRuntime(), ioe.getMessage());
+ }
+ }
+
+ @JRubyMethod
+ public IRubyObject to_text() {
+ StringBuilder result = new StringBuilder();
+ if (privKey != null) {
+ int len = privKey.getParams().getP().bitLength();
+ result.append("Private-Key: (").append(len).append(" bit)").append("\n");
+ result.append("priv:");
+ addSplittedAndFormatted(result, privKey.getX());
+ }
+ result.append("pub:");
+ addSplittedAndFormatted(result, pubKey.getY());
+ result.append("P:");
+ addSplittedAndFormatted(result, pubKey.getParams().getP());
+ result.append("Q:");
+ addSplittedAndFormatted(result, pubKey.getParams().getQ());
+ result.append("G:");
+ addSplittedAndFormatted(result, pubKey.getParams().getG());
+ return getRuntime().newString(result.toString());
+ }
+
+ @JRubyMethod
+ public IRubyObject public_key() {
+ PKeyDSA val = new PKeyDSA(getRuntime(),getMetaClass().getRealClass());
+ val.privKey = null;
+ val.pubKey = this.pubKey;
+ return val;
+ }
+
+ @JRubyMethod(name = {"export", "to_pem", "to_s"}, rest = true)
+ public IRubyObject export(IRubyObject[] args) {
+ StringWriter w = new StringWriter();
+ org.jruby.runtime.Arity.checkArgumentCount(getRuntime(), args, 0, 2);
+ char[] passwd = null;
+ String algo = null;
+ if (args.length > 0 && !args[0].isNil()) {
+ algo = ((Cipher) args[0]).getAlgorithm();
+ if (args.length > 1 && !args[1].isNil()) {
+ passwd = args[1].toString().toCharArray();
+ }
+ }
+ try {
+ if (privKey != null) {
+ PEMInputOutput.writeDSAPrivateKey(w, privKey, algo, passwd);
+ } else {
+ PEMInputOutput.writeDSAPublicKey(w, pubKey);
+ }
+ w.close();
+ return getRuntime().newString(w.toString());
+ } catch (NoClassDefFoundError ncdfe) {
+ throw newDSAError(getRuntime(), OpenSSLReal.bcExceptionMessage(ncdfe));
+ } catch (IOException ioe) {
+ throw newDSAError(getRuntime(), ioe.getMessage());
+ }
+ }
+
+ @JRubyMethod
+ public IRubyObject syssign(IRubyObject arg) {
+ // TODO
+ return getRuntime().getNil();
+ }
+
+ @JRubyMethod
+ public IRubyObject sysverify(IRubyObject arg, IRubyObject arg2) {
+ // TODO
+ return getRuntime().getNil();
+ }
+
+ @JRubyMethod(name="p")
+ public synchronized IRubyObject get_p() {
+ // FIXME: return only for public?
+ DSAKey key;
+ BigInteger param;
+ if ((key = this.pubKey) != null || (key = this.privKey) != null) {
+ if ((param = key.getParams().getP()) != null) {
+ return BN.newBN(getRuntime(), param);
+ }
+ } else if (specValues != null) {
+ if ((param = specValues[SPEC_P]) != null) {
+ return BN.newBN(getRuntime(), param);
+ }
+ }
+ return getRuntime().getNil();
+ }
+
+ @JRubyMethod(name="p=")
+ public synchronized IRubyObject set_p(IRubyObject p) {
+ return setKeySpecComponent(SPEC_P, p);
+ }
+
+ @JRubyMethod(name="q")
+ public synchronized IRubyObject get_q() {
+ // FIXME: return only for public?
+ DSAKey key;
+ BigInteger param;
+ if ((key = this.pubKey) != null || (key = this.privKey) != null) {
+ if ((param = key.getParams().getQ()) != null) {
+ return BN.newBN(getRuntime(), param);
+ }
+ } else if (specValues != null) {
+ if ((param = specValues[SPEC_Q]) != null) {
+ return BN.newBN(getRuntime(), param);
+ }
+ }
+ return getRuntime().getNil();
+ }
+
+ @JRubyMethod(name="q=")
+ public synchronized IRubyObject set_q(IRubyObject q) {
+ return setKeySpecComponent(SPEC_Q, q);
+ }
+
+ @JRubyMethod(name="g")
+ public synchronized IRubyObject get_g() {
+ // FIXME: return only for public?
+ DSAKey key;
+ BigInteger param;
+ if ((key = this.pubKey) != null || (key = this.privKey) != null) {
+ if ((param = key.getParams().getG()) != null) {
+ return BN.newBN(getRuntime(), param);
+ }
+ } else if (specValues != null) {
+ if ((param = specValues[SPEC_G]) != null) {
+ return BN.newBN(getRuntime(), param);
+ }
+ }
+ return getRuntime().getNil();
+ }
+
+ @JRubyMethod(name="g=")
+ public synchronized IRubyObject set_g(IRubyObject g) {
+ return setKeySpecComponent(SPEC_G, g);
+ }
+
+ @JRubyMethod(name="pub_key")
+ public synchronized IRubyObject get_pub_key() {
+ DSAPublicKey key;
+ BigInteger param;
+ if ((key = this.pubKey) != null) {
+ return BN.newBN(getRuntime(), key.getY());
+ } else if (specValues != null) {
+ if ((param = specValues[SPEC_Y]) != null) {
+ return BN.newBN(getRuntime(), param);
+ }
+ }
+ return getRuntime().getNil();
+ }
+
+ @JRubyMethod(name="pub_key=")
+ public synchronized IRubyObject set_pub_key(IRubyObject pub_key) {
+ return setKeySpecComponent(SPEC_Y, pub_key);
+ }
+
+ private IRubyObject setKeySpecComponent(int index, IRubyObject value) {
+ BigInteger[] vals;
+ // illegal to set if we already have a key for this component
+ // FIXME: allow changes after keys are created? MRI doesn't prevent it...
+ if (this.pubKey != null || this.privKey != null ||
+ (vals = this.specValues) != null && vals[index] != null) {
+ throw newDSAError(getRuntime(), "illegal modification");
+ }
+ // get the BigInteger value
+ BigInteger bival = BN.getBigInteger(value);
+
+ if (vals != null) {
+ // we already have some vals stored, store this one, too
+ vals[index] = bival;
+ // check to see if we have all values yet
+ for (int i = vals.length; --i >= 0; ) {
+ if (vals[i] == null) {
+ // still missing components, return
+ return value;
+ }
+ }
+ // we now have all components. create the key.
+ DSAPublicKeySpec spec = new DSAPublicKeySpec(vals[SPEC_Y], vals[SPEC_P], vals[SPEC_Q], vals[SPEC_G]);
+ try {
+ this.pubKey = (DSAPublicKey)KeyFactory.getInstance("DSA").generatePublic(spec);
+ } catch (InvalidKeySpecException e) {
+ throw newDSAError(getRuntime(), "invalid keyspec");
+ } catch (NoSuchAlgorithmException e) {
+ throw newDSAError(getRuntime(), "unsupported key algorithm (DSA)");
+ }
+ // clear out the specValues
+ this.specValues = null;
+
+ } else {
+
+ // first value received, save
+ this.specValues = new BigInteger[4];
+ this.specValues[index] = bival;
+ }
+ return value;
+ }
+
+}// PKeyDSA
diff --git a/src/org/jruby/ext/openssl/PKeyRSA.java b/src/org/jruby/ext/openssl/PKeyRSA.java
new file mode 100644
index 00000000000..242a035d915
--- /dev/null
+++ b/src/org/jruby/ext/openssl/PKeyRSA.java
@@ -0,0 +1,751 @@
+/***** BEGIN LICENSE BLOCK *****
+ * Version: CPL 1.0/GPL 2.0/LGPL 2.1
+ *
+ * The contents of this file are subject to the Common Public
+ * License Version 1.0 (the "License"); you may not use this file
+ * except in compliance with the License. You may obtain a copy of
+ * the License at http://www.eclipse.org/legal/cpl-v10.html
+ *
+ * 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, 2007 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 CPL, 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 CPL, the GPL or the LGPL.
+ ***** END LICENSE BLOCK *****/
+package org.jruby.ext.openssl;
+
+import java.io.IOException;
+import java.io.StringReader;
+import java.io.StringWriter;
+import java.math.BigInteger;
+import java.security.GeneralSecurityException;
+import java.security.KeyFactory;
+import java.security.KeyPair;
+import java.security.KeyPairGenerator;
+import java.security.PrivateKey;
+import java.security.PublicKey;
+import java.security.SecureRandom;
+import java.security.interfaces.RSAPrivateCrtKey;
+import java.security.interfaces.RSAPublicKey;
+import java.security.spec.InvalidKeySpecException;
+import java.security.spec.PKCS8EncodedKeySpec;
+import java.security.spec.RSAKeyGenParameterSpec;
+import java.security.spec.RSAPrivateCrtKeySpec;
+import java.security.spec.RSAPublicKeySpec;
+import java.security.spec.X509EncodedKeySpec;
+
+import javax.crypto.Cipher;
+
+import org.jruby.Ruby;
+import org.jruby.RubyClass;
+import org.jruby.RubyBignum;
+import org.jruby.RubyFixnum;
+import org.jruby.RubyHash;
+import org.jruby.RubyModule;
+import org.jruby.RubyNumeric;
+import org.jruby.RubyString;
+import org.jruby.anno.JRubyMethod;
+import org.jruby.exceptions.RaiseException;
+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.builtin.IRubyObject;
+
+/**
+ * @author Ola Bini
+ */
+public class PKeyRSA extends PKey {
+ private static final long serialVersionUID = 3675324750727019454L;
+
+ private static ObjectAllocator PKEYRSA_ALLOCATOR = new ObjectAllocator() {
+ public IRubyObject allocate(Ruby runtime, RubyClass klass) {
+ return new PKeyRSA(runtime, klass);
+ }
+ };
+
+ public static void createPKeyRSA(Ruby runtime, RubyModule mPKey) {
+ RubyClass cRSA = mPKey.defineClassUnder("RSA",mPKey.getClass("PKey"),PKEYRSA_ALLOCATOR);
+ RubyClass pkeyError = mPKey.getClass("PKeyError");
+ mPKey.defineClassUnder("RSAError",pkeyError,pkeyError.getAllocator());
+
+ cRSA.defineAnnotatedMethods(PKeyRSA.class);
+
+ cRSA.setConstant("PKCS1_PADDING",runtime.newFixnum(1));
+ cRSA.setConstant("SSLV23_PADDING",runtime.newFixnum(2));
+ cRSA.setConstant("NO_PADDING",runtime.newFixnum(3));
+ cRSA.setConstant("PKCS1_OAEP_PADDING",runtime.newFixnum(4));
+ }
+
+ public static RaiseException newRSAError(Ruby runtime, String message) {
+ return Utils.newError(runtime, "OpenSSL::PKey::RSAError", message);
+ }
+
+ public PKeyRSA(Ruby runtime, RubyClass type) {
+ super(runtime,type);
+ }
+
+ private transient volatile RSAPrivateCrtKey privKey;
+ private transient volatile RSAPublicKey pubKey;
+
+ // fields to hold individual RSAPublicKeySpec components. this allows
+ // a public key to be constructed incrementally, as required by the
+ // current implementation of Net::SSH.
+ // (see net-ssh-1.1.2/lib/net/ssh/transport/ossl/buffer.rb #read_keyblob)
+ private transient volatile BigInteger rsa_e;
+ private transient volatile BigInteger rsa_n;
+
+ private transient volatile BigInteger rsa_d;
+ private transient volatile BigInteger rsa_p;
+ private transient volatile BigInteger rsa_q;
+ private transient volatile BigInteger rsa_dmp1;
+ private transient volatile BigInteger rsa_dmq1;
+ private transient volatile BigInteger rsa_iqmp;
+
+ @Override
+ PublicKey getPublicKey() {
+ return pubKey;
+ }
+
+ @Override
+ PrivateKey getPrivateKey() {
+ return privKey;
+ }
+
+ @Override
+ String getAlgorithm() {
+ return "RSA";
+ }
+
+ @JRubyMethod(name = "generate", meta = true, rest = true)
+ public static IRubyObject generate(IRubyObject recv, IRubyObject[] args) {
+ BigInteger exp = RSAKeyGenParameterSpec.F4;
+ if (Arity.checkArgumentCount(recv.getRuntime(), args, 1, 2) == 2) {
+ if (args[1] instanceof RubyFixnum) {
+ exp = BigInteger.valueOf(RubyNumeric.num2long(args[1]));
+ } else {
+ exp = ((RubyBignum) args[1]).getValue();
+ }
+ }
+ int keysize = RubyNumeric.fix2int(args[0]);
+ PKeyRSA rsa = new PKeyRSA(recv.getRuntime(), (RubyClass) recv);
+ rsaGenerate(rsa, keysize, exp);
+ return rsa;
+ }
+
+ /*
+ * c: rsa_generate
+ */
+ private static void rsaGenerate(PKeyRSA rsa, int keysize, BigInteger exp) throws RaiseException {
+ try {
+ KeyPairGenerator gen = KeyPairGenerator.getInstance("RSA");
+ gen.initialize(new RSAKeyGenParameterSpec(keysize, exp), new SecureRandom());
+ KeyPair pair = gen.generateKeyPair();
+ rsa.privKey = (RSAPrivateCrtKey) (pair.getPrivate());
+ rsa.pubKey = (RSAPublicKey) (pair.getPublic());
+ } catch (Exception e) {
+ throw newRSAError(rsa.getRuntime(), e.getMessage());
+ }
+ }
+
+ @JRubyMethod(frame = true, rest = true)
+ public IRubyObject initialize(IRubyObject[] args, Block block) {
+ IRubyObject arg;
+ IRubyObject pass = null;
+ char[] passwd = null;
+ if (org.jruby.runtime.Arity.checkArgumentCount(getRuntime(), args, 0, 2) == 0) {
+ privKey = null;
+ pubKey = null;
+ } else {
+ arg = args[0];
+ if (args.length > 1) {
+ pass = args[1];
+ }
+ if (arg instanceof RubyFixnum) {
+ int keysize = RubyNumeric.fix2int(arg);
+ BigInteger exp = RSAKeyGenParameterSpec.F4;
+ if (null != pass && !pass.isNil()) {
+ exp = BigInteger.valueOf(RubyNumeric.num2long(pass));
+ }
+ rsaGenerate(this, keysize, exp);
+ } else {
+ if (pass != null && !pass.isNil()) {
+ passwd = pass.toString().toCharArray();
+ }
+ arg = OpenSSLImpl.to_der_if_possible(arg);
+ RubyString str = arg.convertToString();
+
+ Object val = null;
+ KeyFactory fact = null;
+ try {
+ fact = KeyFactory.getInstance("RSA");
+ } catch (Exception e) {
+ throw getRuntime().newRuntimeError("unsupported key algorithm (RSA)");
+ }
+ // TODO: ugly NoClassDefFoundError catching for no BC env. How can we remove this?
+ if (null == val) {
+ // PEM_read_bio_RSAPrivateKey
+ try {
+ val = PEMInputOutput.readPrivateKey(new StringReader(str.toString()), passwd);
+ } catch (NoClassDefFoundError e) {
+ val = null;
+ } catch (Exception e) {
+ val = null;
+ }
+ }
+ if (null == val) {
+ // PEM_read_bio_RSAPublicKey
+ try {
+ val = PEMInputOutput.readRSAPublicKey(new StringReader(str.toString()), passwd);
+ } catch (NoClassDefFoundError e) {
+ val = null;
+ } catch (Exception e) {
+ val = null;
+ }
+ }
+ if (null == val) {
+ // PEM_read_bio_RSA_PUBKEY
+ try {
+ val = PEMInputOutput.readRSAPubKey(new StringReader(str.toString()), passwd);
+ } catch (NoClassDefFoundError e) {
+ val = null;
+ } catch (Exception e) {
+ val = null;
+ }
+ }
+ if (null == val) {
+ // d2i_RSAPrivateKey_bio
+ try {
+ val = org.jruby.ext.openssl.impl.PKey.readRSAPrivateKey(str.getBytes());
+ } catch (NoClassDefFoundError e) {
+ val = null;
+ } catch (Exception e) {
+ val = null;
+ }
+ }
+ if (null == val) {
+ // d2i_RSAPublicKey_bio
+ try {
+ val = org.jruby.ext.openssl.impl.PKey.readRSAPublicKey(str.getBytes());
+ } catch (NoClassDefFoundError e) {
+ val = null;
+ } catch (Exception e) {
+ val = null;
+ }
+ }
+ if (null == val) {
+ // try to read PrivateKeyInfo.
+ try {
+ val = fact.generatePrivate(new PKCS8EncodedKeySpec(str.getBytes()));
+ } catch (Exception e) {
+ val = null;
+ }
+ }
+ if (null == val) {
+ // try to read SubjectPublicKeyInfo.
+ try {
+ val = fact.generatePublic(new X509EncodedKeySpec(str.getBytes()));
+ } catch (Exception e) {
+ val = null;
+ }
+ }
+ if (null == val) {
+ throw newRSAError(getRuntime(), "Neither PUB key nor PRIV key:");
+ }
+
+ if (val instanceof KeyPair) {
+ privKey = (RSAPrivateCrtKey) (((KeyPair) val).getPrivate());
+ pubKey = (RSAPublicKey) (((KeyPair) val).getPublic());
+ } else if (val instanceof RSAPrivateCrtKey) {
+ privKey = (RSAPrivateCrtKey) val;
+ try {
+ pubKey = (RSAPublicKey) (fact.generatePublic(new RSAPublicKeySpec(privKey.getModulus(), privKey.getPublicExponent())));
+ } catch (Exception e) {
+ throw newRSAError(getRuntime(), "Something rotten with private key");
+ }
+ } else if (val instanceof RSAPublicKey) {
+ pubKey = (RSAPublicKey) val;
+ privKey = null;
+ } else {
+ throw newRSAError(getRuntime(), "Neither PUB key nor PRIV key:");
+ }
+ }
+ }
+ return this;
+ }
+
+ @JRubyMethod(name="public?")
+ public IRubyObject public_p() {
+ return pubKey != null ? getRuntime().getTrue() : getRuntime().getFalse();
+ }
+
+ @JRubyMethod(name="private?")
+ public IRubyObject private_p() {
+ return privKey != null ? getRuntime().getTrue() : getRuntime().getFalse();
+ }
+
+ @JRubyMethod
+ public IRubyObject to_der() {
+ try {
+ byte[] bytes = org.jruby.ext.openssl.impl.PKey.toDerRSAKey(pubKey, privKey);
+ return RubyString.newString(getRuntime(), bytes);
+ } catch (NoClassDefFoundError ncdfe) {
+ throw newRSAError(getRuntime(), OpenSSLReal.bcExceptionMessage(ncdfe));
+ } catch (IOException ioe) {
+ throw newRSAError(getRuntime(), ioe.getMessage());
+ }
+ }
+
+ @JRubyMethod
+ public IRubyObject public_key() {
+ PKeyRSA val = new PKeyRSA(getRuntime(),getMetaClass().getRealClass());
+ val.privKey = null;
+ val.pubKey = this.pubKey;
+ return val;
+ }
+
+ @JRubyMethod
+ public IRubyObject params() {
+ ThreadContext ctx = getRuntime().getCurrentContext();
+ RubyHash hash = RubyHash.newHash(getRuntime());
+ if(privKey != null) {
+ hash.op_aset(ctx, getRuntime().newString("iqmp"), BN.newBN(getRuntime(), privKey.getCrtCoefficient()));
+ hash.op_aset(ctx, getRuntime().newString("n"), BN.newBN(getRuntime(), privKey.getModulus()));
+ hash.op_aset(ctx, getRuntime().newString("d"), BN.newBN(getRuntime(), privKey.getPrivateExponent()));
+ hash.op_aset(ctx, getRuntime().newString("p"), BN.newBN(getRuntime(), privKey.getPrimeP()));
+ hash.op_aset(ctx, getRuntime().newString("e"), BN.newBN(getRuntime(), privKey.getPublicExponent()));
+ hash.op_aset(ctx, getRuntime().newString("q"), BN.newBN(getRuntime(), privKey.getPrimeQ()));
+ hash.op_aset(ctx, getRuntime().newString("dmq1"), BN.newBN(getRuntime(), privKey.getPrimeExponentQ()));
+ hash.op_aset(ctx, getRuntime().newString("dmp1"), BN.newBN(getRuntime(), privKey.getPrimeExponentP()));
+
+ } else {
+ hash.op_aset(ctx, getRuntime().newString("iqmp"), BN.newBN(getRuntime(), BigInteger.ZERO));
+ hash.op_aset(ctx, getRuntime().newString("n"), BN.newBN(getRuntime(), pubKey.getModulus()));
+ hash.op_aset(ctx, getRuntime().newString("d"), BN.newBN(getRuntime(), BigInteger.ZERO));
+ hash.op_aset(ctx, getRuntime().newString("p"), BN.newBN(getRuntime(), BigInteger.ZERO));
+ hash.op_aset(ctx, getRuntime().newString("e"), BN.newBN(getRuntime(), pubKey.getPublicExponent()));
+ hash.op_aset(ctx, getRuntime().newString("q"), BN.newBN(getRuntime(), BigInteger.ZERO));
+ hash.op_aset(ctx, getRuntime().newString("dmq1"), BN.newBN(getRuntime(), BigInteger.ZERO));
+ hash.op_aset(ctx, getRuntime().newString("dmp1"), BN.newBN(getRuntime(), BigInteger.ZERO));
+ }
+ return hash;
+ }
+
+ @JRubyMethod
+ public IRubyObject to_text() {
+ StringBuilder result = new StringBuilder();
+ if (privKey != null) {
+ int len = privKey.getModulus().bitLength();
+ result.append("Private-Key: (").append(len).append(" bit)").append("\n");
+ result.append("modulus:");
+ addSplittedAndFormatted(result, privKey.getModulus());
+ result.append("publicExponent: ").append(privKey.getPublicExponent()).append(" (0x").append(privKey.getPublicExponent().toString(16)).append(")\n");
+ result.append("privateExponent:");
+ addSplittedAndFormatted(result, privKey.getPrivateExponent());
+ result.append("prime1:");
+ addSplittedAndFormatted(result, privKey.getPrimeP());
+ result.append("prime2:");
+ addSplittedAndFormatted(result, privKey.getPrimeQ());
+ result.append("exponent1:");
+ addSplittedAndFormatted(result, privKey.getPrimeExponentP());
+ result.append("exponent2:");
+ addSplittedAndFormatted(result, privKey.getPrimeExponentQ());
+ result.append("coefficient:");
+ addSplittedAndFormatted(result, privKey.getCrtCoefficient());
+ } else {
+ int len = pubKey.getModulus().bitLength();
+ result.append("Modulus (").append(len).append(" bit):");
+ addSplittedAndFormatted(result, pubKey.getModulus());
+ result.append("Exponent: ").append(pubKey.getPublicExponent()).append(" (0x").append(pubKey.getPublicExponent().toString(16)).append(")\n");
+ }
+ return getRuntime().newString(result.toString());
+ }
+
+ @JRubyMethod(name = {"export", "to_pem", "to_s"}, rest = true)
+ public IRubyObject export(IRubyObject[] args) {
+ StringWriter w = new StringWriter();
+ org.jruby.runtime.Arity.checkArgumentCount(getRuntime(), args, 0, 2);
+ char[] passwd = null;
+ String algo = null;
+ if (args.length > 0 && !args[0].isNil()) {
+ algo = ((org.jruby.ext.openssl.Cipher) args[0]).getAlgorithm();
+ if (args.length > 1 && !args[1].isNil()) {
+ passwd = args[1].toString().toCharArray();
+ }
+ }
+ try {
+ if (privKey != null) {
+ PEMInputOutput.writeRSAPrivateKey(w, privKey, algo, passwd);
+ } else {
+ PEMInputOutput.writeRSAPublicKey(w, pubKey);
+ }
+ w.close();
+ return getRuntime().newString(w.toString());
+ } catch (NoClassDefFoundError ncdfe) {
+ throw newRSAError(getRuntime(), OpenSSLReal.bcExceptionMessage(ncdfe));
+ } catch (IOException ioe) {
+ throw newRSAError(getRuntime(), ioe.getMessage());
+ }
+ }
+
+ private String getPadding(int padding) {
+ if(padding < 1 || padding > 4) {
+ throw newRSAError(getRuntime(), null);
+ }
+ // BC accepts "/NONE/*" but SunJCE doesn't. use "/ECB/*"
+ String p = "/ECB/PKCS1Padding";
+ if(padding == 3) {
+ p = "/ECB/NoPadding";
+ } else if(padding == 4) {
+ p = "/ECB/OAEPWithMD5AndMGF1Padding";
+ } else if(padding == 2) {
+ p = "/ECB/ISO9796-1Padding";
+ }
+ return p;
+ }
+
+ @JRubyMethod(rest = true)
+ public IRubyObject private_encrypt(IRubyObject[] args) {
+ int padding = 1;
+ if (org.jruby.runtime.Arity.checkArgumentCount(getRuntime(), args, 1, 2) == 2 && !args[1].isNil()) {
+ padding = RubyNumeric.fix2int(args[1]);
+ }
+ String p = getPadding(padding);
+ RubyString buffer = args[0].convertToString();
+ if (privKey == null) {
+ throw newRSAError(getRuntime(), "private key needed.");
+ }
+ try {
+ Cipher engine = Cipher.getInstance("RSA" + p);
+ engine.init(Cipher.ENCRYPT_MODE, privKey);
+ byte[] outp = engine.doFinal(buffer.getBytes());
+ return RubyString.newString(getRuntime(), outp);
+ } catch (GeneralSecurityException gse) {
+ throw newRSAError(getRuntime(), gse.getMessage());
+ }
+ }
+
+ @JRubyMethod(rest = true)
+ public IRubyObject private_decrypt(IRubyObject[] args) {
+ int padding = 1;
+ if (org.jruby.runtime.Arity.checkArgumentCount(getRuntime(), args, 1, 2) == 2 && !args[1].isNil()) {
+ padding = RubyNumeric.fix2int(args[1]);
+ }
+ String p = getPadding(padding);
+ RubyString buffer = args[0].convertToString();
+ if (privKey == null) {
+ throw newRSAError(getRuntime(), "private key needed.");
+ }
+ try {
+ Cipher engine = Cipher.getInstance("RSA" + p);
+ engine.init(Cipher.DECRYPT_MODE, privKey);
+ byte[] outp = engine.doFinal(buffer.getBytes());
+ return RubyString.newString(getRuntime(), outp);
+ } catch (GeneralSecurityException gse) {
+ throw newRSAError(getRuntime(), gse.getMessage());
+ }
+ }
+
+ @JRubyMethod(rest = true)
+ public IRubyObject public_encrypt(IRubyObject[] args) {
+ int padding = 1;
+ if (org.jruby.runtime.Arity.checkArgumentCount(getRuntime(), args, 1, 2) == 2 && !args[1].isNil()) {
+ padding = RubyNumeric.fix2int(args[1]);
+ }
+ String p = getPadding(padding);
+ RubyString buffer = args[0].convertToString();
+ try {
+ Cipher engine = Cipher.getInstance("RSA" + p);
+ engine.init(Cipher.ENCRYPT_MODE, pubKey);
+ byte[] outp = engine.doFinal(buffer.getBytes());
+ return RubyString.newString(getRuntime(), outp);
+ } catch (GeneralSecurityException gse) {
+ throw newRSAError(getRuntime(), gse.getMessage());
+ }
+ }
+
+ @JRubyMethod(rest = true)
+ public IRubyObject public_decrypt(IRubyObject[] args) {
+ int padding = 1;
+ if (org.jruby.runtime.Arity.checkArgumentCount(getRuntime(), args, 1, 2) == 2 && !args[1].isNil()) {
+ padding = RubyNumeric.fix2int(args[1]);
+ }
+ String p = getPadding(padding);
+ RubyString buffer = args[0].convertToString();
+ try {
+ Cipher engine = Cipher.getInstance("RSA" + p);
+ engine.init(Cipher.DECRYPT_MODE, pubKey);
+ byte[] outp = engine.doFinal(buffer.getBytes());
+ return RubyString.newString(getRuntime(), outp);
+ } catch (GeneralSecurityException gse) {
+ throw newRSAError(getRuntime(), gse.getMessage());
+ }
+ }
+
+ @JRubyMethod(name="d=")
+ public synchronized IRubyObject set_d(IRubyObject value) {
+ if (privKey != null) {
+ throw newRSAError(getRuntime(), "illegal modification");
+ }
+ rsa_d = BN.getBigInteger(value);
+ generatePrivateKeyIfParams();
+ return value;
+ }
+
+ @JRubyMethod(name="p=")
+ public synchronized IRubyObject set_p(IRubyObject value) {
+ if (privKey != null) {
+ throw newRSAError(getRuntime(), "illegal modification");
+ }
+ rsa_p = BN.getBigInteger(value);
+ generatePrivateKeyIfParams();
+ return value;
+ }
+
+ @JRubyMethod(name="q=")
+ public synchronized IRubyObject set_q(IRubyObject value) {
+ if (privKey != null) {
+ throw newRSAError(getRuntime(), "illegal modification");
+ }
+ rsa_q = BN.getBigInteger(value);
+ generatePrivateKeyIfParams();
+ return value;
+ }
+
+ @JRubyMethod(name="dmp1=")
+ public synchronized IRubyObject set_dmp1(IRubyObject value) {
+ if (privKey != null) {
+ throw newRSAError(getRuntime(), "illegal modification");
+ }
+ rsa_dmp1 = BN.getBigInteger(value);
+ generatePrivateKeyIfParams();
+ return value;
+ }
+
+ @JRubyMethod(name="dmq1=")
+ public synchronized IRubyObject set_dmq1(IRubyObject value) {
+ if (privKey != null) {
+ throw newRSAError(getRuntime(), "illegal modification");
+ }
+ rsa_dmq1 = BN.getBigInteger(value);
+ generatePrivateKeyIfParams();
+ return value;
+ }
+
+ @JRubyMethod(name="iqmp=")
+ public synchronized IRubyObject set_iqmp(IRubyObject value) {
+ if (privKey != null) {
+ throw newRSAError(getRuntime(), "illegal modification");
+ }
+ rsa_iqmp = BN.getBigInteger(value);
+ generatePrivateKeyIfParams();
+ return value;
+ }
+
+ @JRubyMethod(name="iqmp")
+ public synchronized IRubyObject get_iqmp() {
+ BigInteger iqmp = null;
+ if (privKey != null) {
+ iqmp = privKey.getCrtCoefficient();
+ } else {
+ iqmp = rsa_iqmp;
+ }
+ if (iqmp != null) {
+ return BN.newBN(getRuntime(), iqmp);
+ }
+ return getRuntime().getNil();
+ }
+
+ @JRubyMethod(name="dmp1")
+ public synchronized IRubyObject get_dmp1() {
+ BigInteger dmp1 = null;
+ if (privKey != null) {
+ dmp1 = privKey.getPrimeExponentP();
+ } else {
+ dmp1 = rsa_dmp1;
+ }
+ if (dmp1 != null) {
+ return BN.newBN(getRuntime(), dmp1);
+ }
+ return getRuntime().getNil();
+ }
+
+ @JRubyMethod(name="dmq1")
+ public synchronized IRubyObject get_dmq1() {
+ BigInteger dmq1 = null;
+ if (privKey != null) {
+ dmq1 = privKey.getPrimeExponentQ();
+ } else {
+ dmq1 = rsa_dmq1;
+ }
+ if (dmq1 != null) {
+ return BN.newBN(getRuntime(), dmq1);
+ }
+ return getRuntime().getNil();
+ }
+
+ @JRubyMethod(name="d")
+ public synchronized IRubyObject get_d() {
+ BigInteger d = null;
+ if (privKey != null) {
+ d = privKey.getPrivateExponent();
+ } else {
+ d = rsa_d;
+ }
+ if (d != null) {
+ return BN.newBN(getRuntime(), d);
+ }
+ return getRuntime().getNil();
+ }
+
+ @JRubyMethod(name="p")
+ public synchronized IRubyObject get_p() {
+ BigInteger p = null;
+ if (privKey != null) {
+ p = privKey.getPrimeP();
+ } else {
+ p = rsa_p;
+ }
+ if (p != null) {
+ return BN.newBN(getRuntime(), p);
+ }
+ return getRuntime().getNil();
+ }
+
+ @JRubyMethod(name="q")
+ public synchronized IRubyObject get_q() {
+ BigInteger q = null;
+ if (privKey != null) {
+ q = privKey.getPrimeQ();
+ } else {
+ q = rsa_q;
+ }
+ if (q != null) {
+ return BN.newBN(getRuntime(), q);
+ }
+ return getRuntime().getNil();
+ }
+
+ @JRubyMethod(name="e")
+ public synchronized IRubyObject get_e() {
+ RSAPublicKey key;
+ BigInteger e;
+ if ((key = pubKey) != null) {
+ e = key.getPublicExponent();
+ } else if(privKey != null) {
+ e = privKey.getPublicExponent();
+ } else {
+ e = rsa_e;
+ }
+ if (e != null) {
+ return BN.newBN(getRuntime(), e);
+ }
+ return getRuntime().getNil();
+ }
+
+ @JRubyMethod(name="e=")
+ public synchronized IRubyObject set_e(IRubyObject value) {
+ rsa_e = BN.getBigInteger(value);
+
+ if(privKey == null) {
+ generatePrivateKeyIfParams();
+ }
+ if(pubKey == null) {
+ generatePublicKeyIfParams();
+ }
+ return value;
+ }
+
+ @JRubyMethod(name="n")
+ public synchronized IRubyObject get_n() {
+ RSAPublicKey key;
+ BigInteger n;
+ if ((key = pubKey) != null) {
+ n = key.getModulus();
+ } else if(privKey != null) {
+ n = privKey.getModulus();
+ } else {
+ n = rsa_n;
+ }
+ if (n != null) {
+ return BN.newBN(getRuntime(), n);
+ }
+ return getRuntime().getNil();
+ }
+
+ @JRubyMethod(name="n=")
+ public synchronized IRubyObject set_n(IRubyObject value) {
+ rsa_n = BN.getBigInteger(value);
+
+ if(privKey == null) {
+ generatePrivateKeyIfParams();
+ }
+ if(pubKey == null) {
+ generatePublicKeyIfParams();
+ }
+ return value;
+ }
+
+ private void generatePublicKeyIfParams() {
+ if (pubKey != null) {
+ throw newRSAError(getRuntime(), "illegal modification");
+ }
+ BigInteger e, n;
+ if ((e = rsa_e) != null && (n = rsa_n) != null) {
+ KeyFactory fact;
+ try {
+ fact = KeyFactory.getInstance("RSA");
+ } catch(Exception ex) {
+ throw getRuntime().newLoadError("unsupported key algorithm (RSA)");
+ }
+ try {
+ pubKey = (RSAPublicKey)fact.generatePublic(new RSAPublicKeySpec(n, e));
+ } catch (InvalidKeySpecException ex) {
+ throw newRSAError(getRuntime(), "invalid parameters");
+ }
+ rsa_e = null;
+ rsa_n = null;
+ }
+ }
+
+ private void generatePrivateKeyIfParams() {
+ if (privKey != null) {
+ throw newRSAError(getRuntime(), "illegal modification");
+ }
+ if (rsa_e != null && rsa_n != null && rsa_p != null && rsa_q != null && rsa_d != null && rsa_dmp1 != null && rsa_dmq1 != null && rsa_iqmp != null) {
+ KeyFactory fact;
+ try {
+ fact = KeyFactory.getInstance("RSA");
+ } catch(Exception ex) {
+ throw getRuntime().newLoadError("unsupported key algorithm (RSA)");
+ }
+ try {
+ privKey = (RSAPrivateCrtKey)fact.generatePrivate(new RSAPrivateCrtKeySpec(rsa_n, rsa_e, rsa_d, rsa_p, rsa_q, rsa_dmp1, rsa_dmq1, rsa_iqmp));
+ } catch (InvalidKeySpecException ex) {
+ throw newRSAError(getRuntime(), "invalid parameters");
+ }
+ rsa_n = null;
+ rsa_e = null;
+ rsa_d = null;
+ rsa_p = null;
+ rsa_q = null;
+ rsa_dmp1 = null;
+ rsa_dmq1 = null;
+ rsa_iqmp = null;
+ }
+ }
+}// PKeyRSA
diff --git a/src/org/jruby/ext/openssl/Random.java b/src/org/jruby/ext/openssl/Random.java
new file mode 100644
index 00000000000..66e9d553143
--- /dev/null
+++ b/src/org/jruby/ext/openssl/Random.java
@@ -0,0 +1,103 @@
+/***** BEGIN LICENSE BLOCK *****
+ * Version: CPL 1.0/GPL 2.0/LGPL 2.1
+ *
+ * The contents of this file are subject to the Common Public
+ * License Version 1.0 (the "License"); you may not use this file
+ * except in compliance with the License. You may obtain a copy of
+ * the License at http://www.eclipse.org/legal/cpl-v10.html
+ *
+ * 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 CPL, 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 CPL, the GPL or the LGPL.
+ ***** END LICENSE BLOCK *****/
+package org.jruby.ext.openssl;
+
+import java.security.SecureRandom;
+
+import org.jruby.Ruby;
+import org.jruby.RubyClass;
+import org.jruby.RubyModule;
+import org.jruby.RubyNumeric;
+import org.jruby.RubyString;
+import org.jruby.anno.JRubyMethod;
+import org.jruby.runtime.builtin.IRubyObject;
+import org.jruby.util.ByteList;
+
+/**
+ * @author Ola Bini
+ */
+public class Random {
+ private final static class RandomHolder {
+ public java.util.Random[] randomizers;
+ }
+ public static void createRandom(Ruby runtime, RubyModule ossl) {
+ RubyModule rand = ossl.defineModuleUnder("Random");
+
+ RubyClass osslError = (RubyClass)ossl.getConstant("OpenSSLError");
+ rand.defineClassUnder("RandomError",osslError,osslError.getAllocator());
+
+ rand.defineAnnotatedMethods(Random.class);
+
+ RandomHolder holder = new RandomHolder();
+ holder.randomizers = new java.util.Random[]{new java.util.Random(), new SecureRandom()};
+ rand.dataWrapStruct(holder);
+ }
+
+ @JRubyMethod(meta=true)
+ public static IRubyObject seed(IRubyObject recv, IRubyObject arg) {
+ return recv.getRuntime().getNil();
+ }
+ @JRubyMethod(meta=true)
+ public static IRubyObject load_random_file(IRubyObject recv, IRubyObject arg) {
+ return recv.getRuntime().getNil();
+ }
+ @JRubyMethod(meta=true)
+ public static IRubyObject write_random_file(IRubyObject recv, IRubyObject arg) {
+ return recv.getRuntime().getNil();
+ }
+
+ @JRubyMethod(meta=true)
+ public static IRubyObject random_bytes(IRubyObject recv, IRubyObject arg) {
+ return generate(recv, arg, 1);
+ }
+
+ @JRubyMethod(meta=true)
+ public static IRubyObject pseudo_bytes(IRubyObject recv, IRubyObject arg) {
+ return generate(recv, arg, 0);
+ }
+
+ private static RubyString generate(IRubyObject recv, IRubyObject arg, int ix) {
+ RandomHolder holder = (RandomHolder)recv.dataGetStruct();
+ int len = RubyNumeric.fix2int(arg);
+ if (len < 0 || len > Integer.MAX_VALUE) {
+ throw recv.getRuntime().newArgumentError("negative string size (or size too big)");
+ }
+ byte[] buf = new byte[len];
+ holder.randomizers[ix].nextBytes(buf);
+ return RubyString.newString(recv.getRuntime(), new ByteList(buf,false));
+ }
+
+ @JRubyMethod(meta=true)
+ public static IRubyObject egd(IRubyObject recv, IRubyObject arg) {
+ return recv.getRuntime().getNil();
+ }
+ @JRubyMethod(meta=true)
+ public static IRubyObject egd_bytes(IRubyObject recv, IRubyObject arg1, IRubyObject arg2) {
+ return recv.getRuntime().getNil();
+ }
+}
diff --git a/src/org/jruby/ext/openssl/Request.java b/src/org/jruby/ext/openssl/Request.java
new file mode 100644
index 00000000000..9d350a1b43d
--- /dev/null
+++ b/src/org/jruby/ext/openssl/Request.java
@@ -0,0 +1,328 @@
+/***** BEGIN LICENSE BLOCK *****
+ * Version: CPL 1.0/GPL 2.0/LGPL 2.1
+ *
+ * The contents of this file are subject to the Common Public
+ * License Version 1.0 (the "License"); you may not use this file
+ * except in compliance with the License. You may obtain a copy of
+ * the License at http://www.eclipse.org/legal/cpl-v10.html
+ *
+ * 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, 2007 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 CPL, 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 CPL, the GPL or the LGPL.
+ ***** END LICENSE BLOCK *****/
+package org.jruby.ext.openssl;
+
+import java.util.ArrayList;
+import java.util.Enumeration;
+import java.util.Iterator;
+import java.util.List;
+import java.io.IOException;
+import java.io.StringWriter;
+import java.security.GeneralSecurityException;
+import java.security.PublicKey;
+
+import org.bouncycastle.asn1.ASN1EncodableVector;
+import org.bouncycastle.asn1.ASN1Set;
+import org.bouncycastle.asn1.DERObject;
+import org.bouncycastle.asn1.DERObjectIdentifier;
+import org.bouncycastle.asn1.DERSequence;
+import org.bouncycastle.asn1.DERSet;
+import org.bouncycastle.asn1.DERString;
+import org.jruby.Ruby;
+import org.jruby.RubyArray;
+import org.jruby.RubyClass;
+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.ext.openssl.x509store.PEMInputOutput;
+import org.jruby.runtime.Block;
+import org.jruby.runtime.ObjectAllocator;
+import org.jruby.runtime.builtin.IRubyObject;
+
+/**
+ * @author Ola Bini
+ */
+public class Request extends RubyObject {
+ private static final long serialVersionUID = -5551557929791764918L;
+
+ private static ObjectAllocator REQUEST_ALLOCATOR = new ObjectAllocator() {
+ public IRubyObject allocate(Ruby runtime, RubyClass klass) {
+ return new Request(runtime, klass);
+ }
+ };
+
+ public static void createRequest(Ruby runtime, RubyModule mX509) {
+ RubyClass cRequest = mX509.defineClassUnder("Request",runtime.getObject(),REQUEST_ALLOCATOR);
+ RubyClass openSSLError = runtime.getModule("OpenSSL").getClass("OpenSSLError");
+ mX509.defineClassUnder("RequestError",openSSLError,openSSLError.getAllocator());
+
+ cRequest.defineAnnotatedMethods(Request.class);
+ }
+
+ private IRubyObject version;
+ private IRubyObject subject;
+ private IRubyObject public_key;
+ private boolean valid = false;
+
+ private List attrs;
+
+ private PKCS10CertificationRequestExt req;
+
+ public Request(Ruby runtime, RubyClass type) {
+ super(runtime,type);
+ attrs = new ArrayList();
+ }
+
+ @JRubyMethod(name="initialize", frame=true, rest=true)
+ public IRubyObject _initialize(IRubyObject[] args, Block block) {
+ if(org.jruby.runtime.Arity.checkArgumentCount(getRuntime(),args,0,1) == 0) {
+ return this;
+ }
+
+ byte[] req_bytes = OpenSSLImpl.readX509PEM(args[0]);
+ req = new PKCS10CertificationRequestExt(req_bytes);
+ version = getRuntime().newFixnum(req.getVersion());
+
+ String algo = null;
+ byte[] enc = null;
+ try {
+ PublicKey pkey = (PublicKey) OpenSSLReal.getWithBCProvider(new OpenSSLReal.Callable() {
+
+ public Object call() throws GeneralSecurityException {
+ return req.getPublicKey("BC");
+ }
+ });
+ algo = pkey.getAlgorithm();
+ enc = pkey.getEncoded();
+ } catch (GeneralSecurityException gse) {
+ throw newX509ReqError(getRuntime(), gse.getMessage());
+ }
+
+ if("RSA".equalsIgnoreCase(algo)) {
+ this.public_key = Utils.newRubyInstance(getRuntime(), "OpenSSL::PKey::RSA", RubyString.newString(getRuntime(), enc));
+ } else if("DSA".equalsIgnoreCase(algo)) {
+ this.public_key = Utils.newRubyInstance(getRuntime(), "OpenSSL::PKey::DSA", RubyString.newString(getRuntime(), enc));
+ } else {
+ throw getRuntime().newLoadError("not implemented algo for public key: " + algo);
+ }
+ org.bouncycastle.asn1.x509.X509Name subName = req.getCertificationRequestInfo().getSubject();
+ subject = Utils.newRubyInstance(getRuntime(), "OpenSSL::X509::Name");
+ DERSequence subNameD = (DERSequence)subName.toASN1Object();
+ for(int i=0;i iter = attrs.iterator();iter.hasNext();) {
+ v1.add(((Attribute)iter.next()).toASN1());
+ }
+ try {
+ // PKCS10CertificationRequestExt depends BC.
+ OpenSSLReal.doWithBCProvider(new OpenSSLReal.Runnable() {
+
+ public void run() throws GeneralSecurityException {
+ req = new PKCS10CertificationRequestExt(digAlg + "WITH" + keyAlg,
+ ((X509Name) subject).getRealName(),
+ ((PKey) public_key).getPublicKey(),
+ new DERSet(v1),
+ ((PKey) key).getPrivateKey(),
+ "BC");
+ }
+ });
+ } catch (GeneralSecurityException gse) {
+ throw newX509ReqError(getRuntime(), gse.getMessage());
+ }
+ req.setVersion(RubyNumeric.fix2int(version));
+ valid = true;
+ return this;
+ }
+
+ @JRubyMethod
+ public IRubyObject verify(IRubyObject key) {
+ try {
+ return valid && req.verify(((PKey)(key.callMethod(getRuntime().getCurrentContext(),"public_key"))).getPublicKey()) ? getRuntime().getTrue() : getRuntime().getFalse();
+ } catch(Exception e) {
+ return getRuntime().getFalse();
+ }
+ }
+
+ @JRubyMethod
+ public IRubyObject attributes() {
+ return getRuntime().newArray(attrs);
+ }
+
+ @SuppressWarnings("unchecked")
+ @JRubyMethod(name="attributes=")
+ public IRubyObject set_attributes(IRubyObject val) {
+ valid = false;
+ attrs.clear();
+ attrs.addAll(((RubyArray)val).getList());
+ if(req != null) {
+ ASN1EncodableVector v1 = new ASN1EncodableVector();
+ for(Iterator iter = attrs.iterator();iter.hasNext();) {
+ v1.add(((Attribute)iter.next()).toASN1());
+ }
+ req.setAttributes(new DERSet(v1));
+ }
+ return val;
+ }
+
+ @JRubyMethod
+ public IRubyObject add_attribute(IRubyObject val) {
+ valid = false;
+ attrs.add(val);
+ if(req != null) {
+ ASN1EncodableVector v1 = new ASN1EncodableVector();
+ for(Iterator iter = attrs.iterator();iter.hasNext();) {
+ v1.add(((Attribute)iter.next()).toASN1());
+ }
+ req.setAttributes(new DERSet(v1));
+ }
+ return getRuntime().getNil();
+ }
+
+ private static RaiseException newX509ReqError(Ruby runtime, String message) {
+ return Utils.newError(runtime, "OpenSSL::X509::RequestError", message);
+ }
+}// Request
diff --git a/src/org/jruby/ext/openssl/SSL.java b/src/org/jruby/ext/openssl/SSL.java
new file mode 100644
index 00000000000..46cd504bb65
--- /dev/null
+++ b/src/org/jruby/ext/openssl/SSL.java
@@ -0,0 +1,94 @@
+/***** BEGIN LICENSE BLOCK *****
+ * Version: CPL 1.0/GPL 2.0/LGPL 2.1
+ *
+ * The contents of this file are subject to the Common Public
+ * License Version 1.0 (the "License"); you may not use this file
+ * except in compliance with the License. You may obtain a copy of
+ * the License at http://www.eclipse.org/legal/cpl-v10.html
+ *
+ * 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 CPL, 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 CPL, 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;
+import org.jruby.exceptions.RaiseException;
+
+/**
+ * @author Ola Bini
+ */
+public class SSL {
+
+ public static final int VERIFY_NONE = 0x00;
+ public static final int VERIFY_PEER = 0x01;
+ public static final int VERIFY_FAIL_IF_NO_PEER_CERT = 0x02;
+ public static final int VERIFY_CLIENT_ONCE = 0x04;
+
+ 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_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 void createSSL(Ruby runtime, RubyModule ossl) {
+ RubyModule mSSL = ossl.defineModuleUnder("SSL");
+ RubyClass openSSLError = ossl.getClass("OpenSSLError");
+ mSSL.defineClassUnder("SSLError",openSSLError,openSSLError.getAllocator());
+
+ SSLContext.createSSLContext(runtime,mSSL);
+ SSLSocket.createSSLSocket(runtime,mSSL);
+
+ mSSL.setConstant("VERIFY_NONE", runtime.newFixnum(VERIFY_NONE));
+ mSSL.setConstant("VERIFY_PEER", runtime.newFixnum(VERIFY_PEER));
+ mSSL.setConstant("VERIFY_FAIL_IF_NO_PEER_CERT", runtime.newFixnum(VERIFY_FAIL_IF_NO_PEER_CERT));
+ mSSL.setConstant("VERIFY_CLIENT_ONCE", runtime.newFixnum(VERIFY_CLIENT_ONCE));
+
+ mSSL.setConstant("OP_ALL", runtime.newFixnum(OP_ALL));
+ mSSL.setConstant("OP_NO_TICKET", runtime.newFixnum(OP_NO_TICKET));
+ mSSL.setConstant("OP_NO_SESSION_RESUMPTION_ON_RENEGOTIATION", runtime.newFixnum(OP_NO_SESSION_RESUMPTION_ON_RENEGOTIATION));
+ mSSL.setConstant("OP_SINGLE_ECDH_USE", runtime.newFixnum(OP_SINGLE_ECDH_USE));
+ mSSL.setConstant("OP_SINGLE_DH_USE", runtime.newFixnum(OP_SINGLE_DH_USE));
+ mSSL.setConstant("OP_EPHEMERAL_RSA", runtime.newFixnum(OP_EPHEMERAL_RSA));
+ mSSL.setConstant("OP_CIPHER_SERVER_PREFERENCE", runtime.newFixnum(OP_CIPHER_SERVER_PREFERENCE));
+ mSSL.setConstant("OP_TLS_ROLLBACK_BUG", runtime.newFixnum(OP_TLS_ROLLBACK_BUG));
+ mSSL.setConstant("OP_NO_SSLv2", runtime.newFixnum(OP_NO_SSLv2));
+ mSSL.setConstant("OP_NO_SSLv3", runtime.newFixnum(OP_NO_SSLv3));
+ mSSL.setConstant("OP_NO_TLSv1", runtime.newFixnum(OP_NO_TLSv1));
+ mSSL.setConstant("OP_PKCS1_CHECK_1", runtime.newFixnum(OP_PKCS1_CHECK_1));
+ mSSL.setConstant("OP_PKCS1_CHECK_2", runtime.newFixnum(OP_PKCS1_CHECK_2));
+ mSSL.setConstant("OP_NETSCAPE_CA_DN_BUG", runtime.newFixnum(OP_NETSCAPE_CA_DN_BUG));
+ mSSL.setConstant("OP_NETSCAPE_DEMO_CIPHER_CHANGE_BUG", runtime.newFixnum(OP_NETSCAPE_DEMO_CIPHER_CHANGE_BUG));
+ }
+
+ public static RaiseException newSSLError(Ruby runtime, Throwable t) {
+ throw Utils.newError(runtime, "OpenSSL::SSL::SSLError", t.getMessage());
+ }
+}// SSL
diff --git a/src/org/jruby/ext/openssl/SSLContext.java b/src/org/jruby/ext/openssl/SSLContext.java
new file mode 100644
index 00000000000..72199dd5b69
--- /dev/null
+++ b/src/org/jruby/ext/openssl/SSLContext.java
@@ -0,0 +1,731 @@
+/***** BEGIN LICENSE BLOCK *****
+ * Version: CPL 1.0/GPL 2.0/LGPL 2.1
+ *
+ * The contents of this file are subject to the Common Public
+ * License Version 1.0 (the "License"); you may not use this file
+ * except in compliance with the License. You may obtain a copy of
+ * the License at http://www.eclipse.org/legal/cpl-v10.html
+ *
+ * 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 CPL, 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 CPL, the GPL or the LGPL.
+ ***** END LICENSE BLOCK *****/
+package org.jruby.ext.openssl;
+
+import java.security.GeneralSecurityException;
+import java.security.KeyManagementException;
+import java.security.NoSuchAlgorithmException;
+import java.security.PrivateKey;
+import java.security.cert.CertificateException;
+import java.security.cert.X509Certificate;
+import java.util.ArrayList;
+import java.util.HashMap;
+import java.util.List;
+import java.util.Map;
+import javax.net.ssl.SSLEngine;
+import org.jruby.Ruby;
+import org.jruby.RubyArray;
+import org.jruby.RubyClass;
+import org.jruby.RubyModule;
+import org.jruby.RubyNumeric;
+import org.jruby.RubyObject;
+import org.jruby.RubyString;
+import org.jruby.anno.JRubyMethod;
+import org.jruby.common.IRubyWarnings.ID;
+import org.jruby.exceptions.RaiseException;
+import org.jruby.ext.openssl.x509store.Certificate;
+import org.jruby.ext.openssl.x509store.Name;
+import org.jruby.ext.openssl.x509store.Store;
+import org.jruby.ext.openssl.x509store.StoreContext;
+import org.jruby.ext.openssl.x509store.X509AuxCertificate;
+import org.jruby.ext.openssl.x509store.X509Object;
+import org.jruby.ext.openssl.x509store.X509Utils;
+import org.jruby.javasupport.util.RuntimeHelpers;
+import org.jruby.runtime.Arity;
+import org.jruby.runtime.Block;
+import org.jruby.runtime.BlockCallback;
+import org.jruby.runtime.CallBlock;
+import org.jruby.runtime.ObjectAllocator;
+import org.jruby.runtime.ThreadContext;
+import org.jruby.runtime.builtin.IRubyObject;
+
+/**
+ * @author Ola Bini
+ */
+public class SSLContext extends RubyObject {
+ private static final long serialVersionUID = -6203496135962974777L;
+
+ private final static String[] ctx_attrs = {
+ "cert", "key", "client_ca", "ca_file", "ca_path",
+ "timeout", "verify_mode", "verify_depth",
+ "verify_callback", "options", "cert_store", "extra_chain_cert",
+ "client_cert_cb", "tmp_dh_callback", "session_id_context"};
+
+ // Mapping table for OpenSSL's SSL_METHOD -> JSSE's SSLContext algorithm.
+ private final static Map SSL_VERSION_OSSL2JSSE;
+ // Mapping table for JSEE's enabled protocols for the algorithm.
+ private final static Map ENABLED_PROTOCOLS;
+
+ static {
+ SSL_VERSION_OSSL2JSSE = new HashMap();
+ ENABLED_PROTOCOLS = new HashMap();
+
+ SSL_VERSION_OSSL2JSSE.put("TLSv1", "TLSv1");
+ SSL_VERSION_OSSL2JSSE.put("TLSv1_server", "TLSv1");
+ SSL_VERSION_OSSL2JSSE.put("TLSv1_client", "TLSv1");
+ ENABLED_PROTOCOLS.put("TLSv1", new String[] { "TLSv1" });
+
+ SSL_VERSION_OSSL2JSSE.put("SSLv2", "SSLv2");
+ SSL_VERSION_OSSL2JSSE.put("SSLv2_server", "SSLv2");
+ SSL_VERSION_OSSL2JSSE.put("SSLv2_client", "SSLv2");
+ ENABLED_PROTOCOLS.put("SSLv2", new String[] { "SSLv2" });
+
+ SSL_VERSION_OSSL2JSSE.put("SSLv3", "SSLv3");
+ SSL_VERSION_OSSL2JSSE.put("SSLv3_server", "SSLv3");
+ SSL_VERSION_OSSL2JSSE.put("SSLv3_client", "SSLv3");
+ ENABLED_PROTOCOLS.put("SSLv3", new String[] { "SSLv3" });
+
+ SSL_VERSION_OSSL2JSSE.put("SSLv23", "SSL");
+ SSL_VERSION_OSSL2JSSE.put("SSLv23_server", "SSL");
+ SSL_VERSION_OSSL2JSSE.put("SSLv23_client", "SSL");
+ ENABLED_PROTOCOLS.put("SSL", new String[] { "SSLv2", "SSLv3", "TLSv1" });
+
+ // Followings(TLS, TLSv1.1) are JSSE only methods at present. Let's allow user to use it.
+
+ SSL_VERSION_OSSL2JSSE.put("TLS", "TLS");
+ ENABLED_PROTOCOLS.put("TLS", new String[] { "TLSv1", "TLSv1.1" });
+
+ SSL_VERSION_OSSL2JSSE.put("TLSv1.1", "TLSv1.1");
+ ENABLED_PROTOCOLS.put("TLSv1.1", new String[] { "TLSv1.1" });
+ }
+
+ private static ObjectAllocator SSLCONTEXT_ALLOCATOR = new ObjectAllocator() {
+ public IRubyObject allocate(Ruby runtime, RubyClass klass) {
+ return new SSLContext(runtime, klass);
+ }
+ };
+
+ public static void createSSLContext(Ruby runtime, RubyModule mSSL) {
+ ThreadContext context = runtime.getCurrentContext();
+ RubyClass cSSLContext = mSSL.defineClassUnder("SSLContext",runtime.getObject(),SSLCONTEXT_ALLOCATOR);
+ for(int i=0;i();
+ for (X509Cert ele : convertToX509Certs(value)) {
+ internalCtx.extraChainCert.add(ele.getAuxCert());
+ }
+ }
+
+ value = getInstanceVariable("@key");
+ PKey key = null;
+ if (value != null && !value.isNil()) {
+ Utils.checkKind(getRuntime(), value, "OpenSSL::PKey::PKey");
+ key = (PKey) value;
+ } else {
+ key = getCallbackKey();
+ }
+ value = getInstanceVariable("@cert");
+ X509Cert cert = null;
+ if (value != null && !value.isNil()) {
+ Utils.checkKind(getRuntime(), value, "OpenSSL::X509::Certificate");
+ cert = (X509Cert) value;
+ } else {
+ cert = getCallbackCert();
+ }
+ if (key != null && cert != null) {
+ internalCtx.keyAlgorithm = key.getAlgorithm();
+ internalCtx.privateKey = key.getPrivateKey();
+ internalCtx.cert = cert.getAuxCert();
+ }
+
+ value = getInstanceVariable("@client_ca");
+ if (value != null && !value.isNil()) {
+ if (value.respondsTo("each")) {
+ for (X509Cert ele : convertToX509Certs(value)) {
+ internalCtx.clientCa.add(ele.getAuxCert());
+ }
+ } else {
+ Utils.checkKind(getRuntime(), value, "OpenSSL::X509::Certificate");
+ internalCtx.clientCa.add(((X509Cert) value).getAuxCert());
+ }
+ }
+
+ String caFile = getCaFile();
+ String caPath = getCaPath();
+ if (caFile != null || caPath != null) {
+ try {
+ if (internalCtx.store.loadLocations(caFile, caPath) == 0) {
+ getRuntime().getWarnings().warn(ID.MISCELLANEOUS, "can't set verify locations");
+ }
+ } catch (Exception e) {
+ throw newSSLError(getRuntime(), e.getMessage());
+ }
+ }
+
+ value = getInstanceVariable("@verify_mode");
+ if (value != null && !value.isNil()) {
+ internalCtx.verifyMode = RubyNumeric.fix2int(value);
+ } else {
+ internalCtx.verifyMode = SSL.VERIFY_NONE;
+ }
+ value = getInstanceVariable("@verify_callback");
+ if (value != null && !value.isNil()) {
+ internalCtx.store.setExtraData(1, value);
+ }
+
+ value = getInstanceVariable("@timeout");
+ if (value != null && !value.isNil()) {
+ internalCtx.timeout = RubyNumeric.fix2int(value);
+ }
+
+ value = getInstanceVariable("@verify_depth");
+ if (value != null && !value.isNil()) {
+ internalCtx.store.setDepth(RubyNumeric.fix2int(value));
+ }
+
+ /* TODO: should be implemented for SSLSession
+ val = ossl_sslctx_get_sess_id_ctx(self);
+ if (!NIL_P(val)){
+ StringValue(val);
+ if (!SSL_CTX_set_session_id_context(ctx, (unsigned char *)RSTRING_PTR(val),
+ RSTRING_LEN(val))){
+ ossl_raise(eSSLError, "SSL_CTX_set_session_id_context:");
+ }
+ }
+
+ if (RTEST(rb_iv_get(self, "@session_get_cb"))) {
+ SSL_CTX_sess_set_get_cb(ctx, ossl_sslctx_session_get_cb);
+ OSSL_Debug("SSL SESSION get callback added");
+ }
+ if (RTEST(rb_iv_get(self, "@session_new_cb"))) {
+ SSL_CTX_sess_set_new_cb(ctx, ossl_sslctx_session_new_cb);
+ OSSL_Debug("SSL SESSION new callback added");
+ }
+ if (RTEST(rb_iv_get(self, "@session_remove_cb"))) {
+ SSL_CTX_sess_set_remove_cb(ctx, ossl_sslctx_session_remove_cb);
+ OSSL_Debug("SSL SESSION remove callback added");
+ }
+
+ val = rb_iv_get(self, "@servername_cb");
+ if (!NIL_P(val)) {
+ SSL_CTX_set_tlsext_servername_callback(ctx, ssl_servername_cb);
+ OSSL_Debug("SSL TLSEXT servername callback added");
+ }
+ */
+
+ try {
+ internalCtx.init();
+ } catch(GeneralSecurityException gse) {
+ throw newSSLError(getRuntime(), gse.getMessage());
+ }
+ return getRuntime().getTrue();
+ }
+
+ @JRubyMethod
+ public IRubyObject ciphers() {
+ List list = new ArrayList();
+ Ruby rt = getRuntime();
+ try {
+ String[] supported = getCipherSuites(createDummySSLEngine());
+ List ciphs = CipherStrings.getMatchingCiphers(ciphers, supported);
+ for (CipherStrings.Def def : ciphs) {
+ RubyArray ele = getRuntime().newArray(4);
+ ele.set(0, rt.newString(def.name));
+ ele.set(1, rt.newString(sslVersionString(def.algorithms)));
+ ele.set(2, rt.newFixnum(def.strength_bits));
+ ele.set(3, rt.newFixnum(def.alg_bits));
+ list.add(ele);
+ }
+ } catch (GeneralSecurityException gse) {
+ throw newSSLError(getRuntime(), gse.getMessage());
+ }
+ return rt.newArray(list);
+ }
+
+ @JRubyMethod(name = "ciphers=")
+ public IRubyObject set_ciphers(IRubyObject val) {
+ if (val.isNil()) {
+ ciphers = CipherStrings.SSL_DEFAULT_CIPHER_LIST;
+ } else if (val instanceof RubyArray) {
+ StringBuilder builder = new StringBuilder();
+ String sep = "";
+ for (IRubyObject obj : ((RubyArray) val).toJavaArray()) {
+ builder.append(sep).append(obj.toString());
+ sep = ":";
+ }
+ ciphers = builder.toString();
+ } else {
+ ciphers = val.convertToString().toString();
+ if (ciphers.equals("DEFAULT")) {
+ ciphers = CipherStrings.SSL_DEFAULT_CIPHER_LIST;
+ }
+ }
+ RubyArray ary = (RubyArray)ciphers();
+ if (ary.size() == 0) {
+ throw newSSLError(getRuntime(), "no cipher match");
+ }
+ return val;
+ }
+
+ @JRubyMethod(name = "ssl_version=")
+ public IRubyObject set_ssl_version(IRubyObject val) {
+ RubyString str = val.convertToString();
+ String given = str.toString();
+ String mapped = SSL_VERSION_OSSL2JSSE.get(given);
+ if (mapped == null) {
+ throw newSSLError(getRuntime(), String.format("unknown SSL method `%s'.", given));
+ }
+ protocol = mapped;
+ protocolForServer = protocolForClient = true;
+ if (given.endsWith("_client")) {
+ protocolForServer = false;
+ }
+ if (given.endsWith("_server")) {
+ protocolForClient = false;
+ }
+ return str;
+ }
+
+ boolean isProtocolForServer() {
+ return protocolForServer;
+ }
+
+ boolean isProtocolForClient() {
+ return protocolForClient;
+ }
+
+ int getLastVerifyResult() {
+ return verifyResult;
+ }
+
+ void setLastVerifyResult(int verifyResult) {
+ this.verifyResult = verifyResult;
+ }
+
+ SSLEngine createDummySSLEngine() throws GeneralSecurityException {
+ javax.net.ssl.SSLContext ctx = javax.net.ssl.SSLContext.getInstance(protocol);
+ ctx.init(null, null, null);
+ return ctx.createSSLEngine();
+ }
+
+ // should keep SSLContext as a member for introducin SSLSession. later...
+ SSLEngine createSSLEngine(String peerHost, int peerPort) throws NoSuchAlgorithmException, KeyManagementException {
+ SSLEngine engine = internalCtx.getSSLContext().createSSLEngine(peerHost, peerPort);
+ engine.setEnabledCipherSuites(getCipherSuites(engine));
+ engine.setEnabledProtocols(getEnabledProtocols(engine));
+ return engine;
+ }
+
+ private String[] getCipherSuites(SSLEngine engine) {
+ List ciphs = CipherStrings.getMatchingCiphers(ciphers, engine.getSupportedCipherSuites());
+ String[] result = new String[ciphs.size()];
+ for (int i = 0; i < result.length; i++) {
+ result[i] = ciphs.get(i).cipherSuite;
+ }
+ return result;
+ }
+
+ private String[] getEnabledProtocols(SSLEngine engine) {
+ List candidates = new ArrayList();
+ long options = getOptions();
+ if (ENABLED_PROTOCOLS.get(protocol) != null) {
+ for (String enabled : ENABLED_PROTOCOLS.get(protocol)) {
+ 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 (String allowed : engine.getEnabledProtocols()) {
+ if (allowed.equals(enabled)) {
+ candidates.add(allowed);
+ }
+ }
+ }
+ }
+ return candidates.toArray(new String[candidates.size()]);
+ }
+
+ private String sslVersionString(long bits) {
+ StringBuilder sb = new StringBuilder();
+ boolean first = true;
+ if ((bits & CipherStrings.SSL_SSLV3) != 0) {
+ if (!first) {
+ sb.append("/");
+ }
+ first = false;
+ sb.append("TLSv1/SSLv3");
+ }
+ if ((bits & CipherStrings.SSL_SSLV2) != 0) {
+ if (!first) {
+ sb.append("/");
+ }
+ first = false;
+ sb.append("SSLv2");
+ }
+ return sb.toString();
+ }
+
+ private PKey getCallbackKey() {
+ if (t_key != null) {
+ return t_key;
+ }
+ initFromCallback();
+ return t_key;
+ }
+
+ private X509Cert getCallbackCert() {
+ if (t_cert != null) {
+ return t_cert;
+ }
+ initFromCallback();
+ return t_cert;
+ }
+
+ private void initFromCallback() {
+ IRubyObject value = getInstanceVariable("@client_cert_cb");
+ if (value != null && !value.isNil()) {
+ IRubyObject out = value.callMethod(getRuntime().getCurrentContext(), "call", this);
+ Utils.checkKind(getRuntime(), out, "Array");
+ IRubyObject cert = (IRubyObject) ((RubyArray) out).getList().get(0);
+ IRubyObject key = (IRubyObject) ((RubyArray) out).getList().get(1);
+ Utils.checkKind(getRuntime(), cert, "OpenSSL::X509::Certificate");
+ Utils.checkKind(getRuntime(), key, "OpenSSL::PKey::PKey");
+ t_cert = (X509Cert) cert;
+ t_key = (PKey) key;
+ }
+ }
+
+ private X509Store getCertStore() {
+ IRubyObject value = getInstanceVariable("@cert_store");
+ if (value != null && !value.isNil() && (value instanceof X509Store)) {
+ Utils.checkKind(getRuntime(), value, "OpenSSL::X509::Store");
+ return (X509Store) value;
+ } else {
+ return null;
+ }
+ }
+
+ private String getCaFile() {
+ IRubyObject value = getInstanceVariable("@ca_file");
+ if (value != null && !value.isNil()) {
+ return value.convertToString().toString();
+ } else {
+ return null;
+ }
+ }
+
+ private String getCaPath() {
+ IRubyObject value = getInstanceVariable("@ca_path");
+ if (value != null && !value.isNil()) {
+ return value.convertToString().toString();
+ } else {
+ return null;
+ }
+ }
+
+ private long getOptions() {
+ IRubyObject value = getInstanceVariable("@options");
+ if (value != null && !value.isNil()) {
+ return RubyNumeric.fix2long(value);
+ } else {
+ return 0;
+ }
+ }
+
+ private X509Cert[] convertToX509Certs(IRubyObject value) {
+ final ArrayList result = new ArrayList();
+ ThreadContext ctx = getRuntime().getCurrentContext();
+ RuntimeHelpers.invoke(ctx, value, "each", CallBlock.newCallClosure(value, value.getMetaClass(), Arity.NO_ARGUMENTS, new BlockCallback() {
+
+ public IRubyObject call(ThreadContext context, IRubyObject[] args, Block block) {
+ Utils.checkKind(getRuntime(), args[0], "OpenSSL::X509::Certificate");
+ result.add((X509Cert) args[0]);
+ return context.getRuntime().getNil();
+ }
+ }, ctx));
+ return result.toArray(new X509Cert[0]);
+ }
+
+ /**
+ * c: SSL_CTX
+ */
+ private class InternalContext {
+
+ Store store = null;
+ int verifyMode = SSL.VERIFY_NONE;
+ X509AuxCertificate cert = null;
+ String keyAlgorithm = null;
+ PrivateKey privateKey = null;
+ List extraChainCert = null;
+ List clientCa = new ArrayList();
+ int timeout = 0;
+ String protocol = null;
+ boolean protocolForServer = true;
+ boolean protocolForClient = true;
+ private javax.net.ssl.SSLContext sslCtx = null;
+
+ void setLastVerifyResultInternal(int lastVerifyResult) {
+ setLastVerifyResult(lastVerifyResult);
+ }
+
+ javax.net.ssl.SSLContext getSSLContext() {
+ return sslCtx;
+ }
+
+ void init() throws GeneralSecurityException {
+ KM km = new KM(this);
+ TM tm = new TM(this);
+ sslCtx = javax.net.ssl.SSLContext.getInstance(protocol);
+ if (protocolForClient) {
+ sslCtx.getClientSessionContext().setSessionTimeout(timeout);
+ }
+ if (protocolForServer) {
+ sslCtx.getServerSessionContext().setSessionTimeout(timeout);
+ }
+ sslCtx.init(new javax.net.ssl.KeyManager[]{km}, new javax.net.ssl.TrustManager[]{tm}, null);
+ }
+
+ // part of ssl_verify_cert_chain
+ StoreContext createStoreContext(String purpose) {
+ if (store == null) {
+ return null;
+ }
+ StoreContext ctx = new StoreContext();
+ if (ctx.init(store, null, null) == 0) {
+ return null;
+ }
+ // for verify_cb
+ ctx.setExtraData(1, store.getExtraData(1));
+ if (purpose != null) {
+ ctx.setDefault(purpose);
+ }
+ ctx.param.inherit(store.param);
+ return ctx;
+ }
+ }
+
+ private static class KM extends javax.net.ssl.X509ExtendedKeyManager {
+
+ private final InternalContext ctx;
+
+ public KM(InternalContext ctx) {
+ super();
+ this.ctx = ctx;
+ }
+
+ @Override
+ public String chooseEngineClientAlias(String[] keyType, java.security.Principal[] issuers, javax.net.ssl.SSLEngine engine) {
+ if (ctx == null) {
+ return null;
+ }
+ if (ctx.privateKey == null) {
+ return null;
+ }
+ for (int i = 0; i < keyType.length; i++) {
+ if (keyType[i].equalsIgnoreCase(ctx.keyAlgorithm)) {
+ return keyType[i];
+ }
+ }
+ return null;
+ }
+
+ @Override
+ public String chooseEngineServerAlias(String keyType, java.security.Principal[] issuers, javax.net.ssl.SSLEngine engine) {
+ if (ctx == null || ctx.privateKey == null) {
+ return null;
+ }
+ if (keyType.equalsIgnoreCase(ctx.keyAlgorithm)) {
+ return keyType;
+ }
+ return null;
+ }
+
+ public String chooseClientAlias(String[] keyType, java.security.Principal[] issuers, java.net.Socket socket) {
+ return null;
+ }
+
+ public String chooseServerAlias(String keyType, java.security.Principal[] issuers, java.net.Socket socket) {
+ return null;
+ }
+
+ // c: ssl3_output_cert_chain
+ public java.security.cert.X509Certificate[] getCertificateChain(String alias) {
+ if (ctx == null) {
+ return null;
+ }
+ ArrayList chain = new ArrayList();
+ if (ctx.extraChainCert != null) {
+ chain.addAll(ctx.extraChainCert);
+ } else if (ctx.cert != null) {
+ StoreContext storeCtx = ctx.createStoreContext(null);
+ X509AuxCertificate x = ctx.cert;
+ while (true) {
+ chain.add(x);
+ if (x.getIssuerDN().equals(x.getSubjectDN())) {
+ break;
+ }
+ try {
+ Name xn = new Name(x.getIssuerX500Principal());
+ X509Object[] s_obj = new X509Object[1];
+ if (storeCtx.getBySubject(X509Utils.X509_LU_X509, xn, s_obj) <= 0) {
+ break;
+ }
+ x = ((Certificate) s_obj[0]).x509;
+ } catch (Exception e) {
+ break;
+ }
+ }
+ }
+ return chain.toArray(new java.security.cert.X509Certificate[0]);
+ }
+
+ public String[] getClientAliases(String keyType, java.security.Principal[] issuers) {
+ return null;
+ }
+
+ public java.security.PrivateKey getPrivateKey(String alias) {
+ if (ctx == null || ctx.privateKey == null) {
+ return null;
+ }
+ return ctx.privateKey;
+ }
+
+ public String[] getServerAliases(String keyType, java.security.Principal[] issuers) {
+ return null;
+ }
+ }
+
+ private static class TM implements javax.net.ssl.X509TrustManager {
+
+ private InternalContext ctx;
+
+ public TM(InternalContext ctx) {
+ super();
+ this.ctx = ctx;
+ }
+
+ public void checkClientTrusted(java.security.cert.X509Certificate[] chain, String authType) throws CertificateException {
+ checkTrusted("ssl_client", chain);
+ }
+
+ public void checkServerTrusted(java.security.cert.X509Certificate[] chain, String authType) throws CertificateException {
+ checkTrusted("ssl_server", chain);
+ }
+
+ public java.security.cert.X509Certificate[] getAcceptedIssuers() {
+ if (ctx == null) {
+ return null;
+ }
+ ArrayList chain = new ArrayList();
+ chain.addAll(ctx.clientCa);
+ return chain.toArray(new java.security.cert.X509Certificate[0]);
+ }
+
+ // c: ssl_verify_cert_chain
+ private void checkTrusted(String purpose, X509Certificate[] chain) throws CertificateException {
+ if (ctx == null) {
+ throw new CertificateException("uninitialized trust manager");
+ }
+ if (chain != null && chain.length > 0) {
+ if ((ctx.verifyMode & SSL.VERIFY_PEER) != 0) {
+ // verify_peer
+ StoreContext storeCtx = ctx.createStoreContext(purpose);
+ if (storeCtx == null) {
+ throw new CertificateException("couldn't initialize store");
+ }
+ storeCtx.setCertificate(chain[0]);
+ storeCtx.setChain(chain);
+ verifyChain(storeCtx);
+ }
+ } else {
+ if ((ctx.verifyMode & SSL.VERIFY_FAIL_IF_NO_PEER_CERT) != 0) {
+ // fail if no peer cert
+ throw new CertificateException("no peer certificate");
+ }
+ }
+ }
+
+ private void verifyChain(StoreContext storeCtx) throws CertificateException {
+ try {
+ int ok = storeCtx.verifyCertificate();
+ ctx.setLastVerifyResultInternal(storeCtx.error);
+ if (ok == 0) {
+ throw new CertificateException("certificate verify failed");
+ }
+ } catch (Exception e) {
+ throw new CertificateException("certificate verify failed", e);
+ }
+ }
+ }
+}// SSLContext
diff --git a/src/org/jruby/ext/openssl/SSLSocket.java b/src/org/jruby/ext/openssl/SSLSocket.java
new file mode 100644
index 00000000000..f8aed9b60c3
--- /dev/null
+++ b/src/org/jruby/ext/openssl/SSLSocket.java
@@ -0,0 +1,668 @@
+/***** BEGIN LICENSE BLOCK *****
+ * Version: CPL 1.0/GPL 2.0/LGPL 2.1
+ *
+ * The contents of this file are subject to the Common Public
+ * License Version 1.0 (the "License"); you may not use this file
+ * except in compliance with the License. You may obtain a copy of
+ * the License at http://www.eclipse.org/legal/cpl-v10.html
+ *
+ * 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, 2007 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 CPL, 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 CPL, the GPL or the LGPL.
+ ***** END LICENSE BLOCK *****/
+package org.jruby.ext.openssl;
+
+import java.io.IOException;
+import java.net.Socket;
+import java.nio.ByteBuffer;
+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;
+import java.util.Set;
+
+import javax.net.ssl.SSLEngine;
+import javax.net.ssl.SSLEngineResult;
+import javax.net.ssl.SSLException;
+import javax.net.ssl.SSLHandshakeException;
+import javax.net.ssl.SSLPeerUnverifiedException;
+import javax.net.ssl.SSLSession;
+
+import org.jruby.Ruby;
+import org.jruby.RubyArray;
+import org.jruby.RubyClass;
+import org.jruby.RubyIO;
+import org.jruby.RubyModule;
+import org.jruby.RubyNumeric;
+import org.jruby.RubyObject;
+import org.jruby.RubyObjectAdapter;
+import org.jruby.RubyString;
+import org.jruby.RubyThread;
+import org.jruby.anno.JRubyMethod;
+import org.jruby.exceptions.RaiseException;
+import org.jruby.ext.openssl.x509store.X509Utils;
+import org.jruby.javasupport.JavaEmbedUtils;
+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;
+import org.jruby.util.ByteList;
+
+/**
+ * @author Ola Bini
+ */
+public class SSLSocket extends RubyObject {
+ private static final long serialVersionUID = -2276327900350542644L;
+
+ private static ObjectAllocator SSLSOCKET_ALLOCATOR = new ObjectAllocator() {
+ public IRubyObject allocate(Ruby runtime, RubyClass klass) {
+ return new SSLSocket(runtime, klass);
+ }
+ };
+
+ private static RubyObjectAdapter api = JavaEmbedUtils.newObjectAdapter();
+
+ public static void createSSLSocket(Ruby runtime, RubyModule mSSL) {
+ ThreadContext context = runtime.getCurrentContext();
+ RubyClass cSSLSocket = mSSL.defineClassUnder("SSLSocket",runtime.getObject(),SSLSOCKET_ALLOCATOR);
+
+ cSSLSocket.addReadWriteAttribute(context, "io");
+ cSSLSocket.addReadWriteAttribute(context, "context");
+ cSSLSocket.addReadWriteAttribute(context, "sync_close");
+ cSSLSocket.defineAlias("to_io","io");
+
+ cSSLSocket.defineAnnotatedMethods(SSLSocket.class);
+ }
+
+ public SSLSocket(Ruby runtime, RubyClass type) {
+ super(runtime,type);
+ verifyResult = X509Utils.V_OK;
+ }
+
+ public static RaiseException newSSLError(Ruby runtime, String message) {
+ return Utils.newError(runtime, "OpenSSL::SSL::SSLError", message, false);
+ }
+
+ private org.jruby.ext.openssl.SSLContext rubyCtx;
+ private SSLEngine engine;
+ private RubyIO io = null;
+
+ private ByteBuffer peerAppData;
+ private ByteBuffer peerNetData;
+ private ByteBuffer netData;
+ private ByteBuffer dummy;
+
+ private boolean initialHandshake = false;
+
+ private SSLEngineResult.HandshakeStatus hsStatus;
+ private SSLEngineResult.Status status = null;
+
+ int verifyResult;
+
+ @JRubyMethod(name = "initialize", rest = true, frame = true)
+ public IRubyObject _initialize(IRubyObject[] args, Block unused) {
+ if (Arity.checkArgumentCount(getRuntime(), args, 1, 2) == 1) {
+ RubyClass sslContext = Utils.getClassFromPath(getRuntime(), "OpenSSL::SSL::SSLContext");
+ rubyCtx = (org.jruby.ext.openssl.SSLContext) api.callMethod(sslContext, "new");
+ } else {
+ rubyCtx = (org.jruby.ext.openssl.SSLContext) args[1];
+ }
+ Utils.checkKind(getRuntime(), args[0], "IO");
+ io = (RubyIO) args[0];
+ api.callMethod(this, "io=", io);
+ // This is a bit of a hack: SSLSocket should share code with RubyBasicSocket, which always sets sync to true.
+ // Instead we set it here for now.
+ api.callMethod(io, "sync=", getRuntime().getTrue());
+ api.callMethod(this, "context=", rubyCtx);
+ api.callMethod(this, "sync_close=", getRuntime().getFalse());
+ rubyCtx.setup();
+ return api.callSuper(this, args);
+ }
+
+ private void ossl_ssl_setup() throws NoSuchAlgorithmException, KeyManagementException, IOException {
+ if(null == engine) {
+ Socket socket = getSocketChannel().socket();
+ String peerHost = socket.getInetAddress().getHostName();
+ int peerPort = socket.getPort();
+ engine = rubyCtx.createSSLEngine(peerHost, peerPort);
+ 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);
+ }
+ }
+
+ @JRubyMethod
+ public IRubyObject connect(ThreadContext context) {
+ Ruby runtime = context.getRuntime();
+ if (!rubyCtx.isProtocolForClient()) {
+ throw newSSLError(runtime, "called a function you should not call");
+ }
+ try {
+ ossl_ssl_setup();
+ engine.setUseClientMode(true);
+ engine.beginHandshake();
+ hsStatus = engine.getHandshakeStatus();
+ initialHandshake = true;
+ doHandshake();
+ } catch(SSLHandshakeException e) {
+ // unlike server side, client should close outbound channel even if
+ // we have remaining data to be sent.
+ forceClose();
+ Throwable v = e;
+ while(v.getCause() != null && (v instanceof SSLHandshakeException)) {
+ v = v.getCause();
+ }
+ throw SSL.newSSLError(runtime, v);
+ } catch (NoSuchAlgorithmException ex) {
+ forceClose();
+ throw SSL.newSSLError(runtime, ex);
+ } catch (KeyManagementException ex) {
+ forceClose();
+ throw SSL.newSSLError(runtime, ex);
+ } catch (IOException ex) {
+ forceClose();
+ throw SSL.newSSLError(runtime, ex);
+ }
+ return this;
+ }
+
+ @JRubyMethod
+ public IRubyObject accept(ThreadContext context) {
+ Ruby runtime = context.getRuntime();
+ if (!rubyCtx.isProtocolForServer()) {
+ throw newSSLError(runtime, "called a function you should not call");
+ }
+ try {
+ int vfy = 0;
+ ossl_ssl_setup();
+ engine.setUseClientMode(false);
+ if(!rubyCtx.isNil() && !rubyCtx.callMethod(context,"verify_mode").isNil()) {
+ vfy = RubyNumeric.fix2int(rubyCtx.callMethod(context,"verify_mode"));
+ if(vfy == 0) { //VERIFY_NONE
+ engine.setNeedClientAuth(false);
+ engine.setWantClientAuth(false);
+ }
+ if((vfy & 1) != 0) { //VERIFY_PEER
+ engine.setWantClientAuth(true);
+ }
+ if((vfy & 2) != 0) { //VERIFY_FAIL_IF_NO_PEER_CERT
+ engine.setNeedClientAuth(true);
+ }
+ }
+ engine.beginHandshake();
+ hsStatus = engine.getHandshakeStatus();
+ initialHandshake = true;
+ doHandshake();
+ } catch(SSLHandshakeException e) {
+ throw SSL.newSSLError(runtime, e);
+ } catch (NoSuchAlgorithmException ex) {
+ throw SSL.newSSLError(runtime, ex);
+ } catch (KeyManagementException ex) {
+ throw SSL.newSSLError(runtime, ex);
+ } catch (IOException ex) {
+ throw SSL.newSSLError(runtime, ex);
+ }
+ return this;
+ }
+
+ @JRubyMethod
+ public IRubyObject verify_result() {
+ if (engine == null) {
+ getRuntime().getWarnings().warn("SSL session is not started yet.");
+ return getRuntime().getNil();
+ }
+ return getRuntime().newFixnum(verifyResult);
+ }
+
+ // This select impl is a copy of RubyThread.select, then blockingLock is
+ // removed. This impl just set
+ // SelectableChannel.configureBlocking(false) permanently instead of setting
+ // temporarily. SSLSocket requires wrapping IO to be selectable so it should
+ // be OK to set configureBlocking(false) permanently.
+ private void waitSelect(int operations) throws IOException {
+ if (!(io.getChannel() instanceof SelectableChannel)) {
+ return;
+ }
+ Ruby runtime = getRuntime();
+ RubyThread thread = runtime.getCurrentContext().getThread();
+
+ SelectableChannel selectable = (SelectableChannel)io.getChannel();
+ selectable.configureBlocking(false);
+ SelectionKey key = null;
+ Selector selector = null;
+ try {
+ io.addBlockingThread(thread);
+ selector = runtime.getSelectorPool().get();
+
+ key = selectable.register(selector, operations);
+
+ thread.beforeBlockingCall();
+ int result = selector.select();
+
+ // check for thread events, in case we've been woken up to die
+ thread.pollThreadEvents();
+
+ if (result == 1) {
+ Set keySet = selector.selectedKeys();
+
+ if (keySet.iterator().next() == key) {
+ return;
+ }
+ }
+ } catch (IOException ioe) {
+ throw runtime.newRuntimeError("Error with selector: " + ioe.getMessage());
+ } 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
+ }
+
+ // shut down and null out the selector
+ try {
+ if (selector != null) {
+ runtime.getSelectorPool().put(selector);
+ }
+ } catch (Exception e) {
+ // ignore
+ }
+
+ // remove this thread as a blocker against the given IO
+ io.removeBlockingThread(thread);
+
+ // clear thread state from blocking call
+ thread.afterBlockingCall();
+ }
+ }
+
+ private void doHandshake() throws IOException {
+ while (true) {
+ SSLEngineResult res;
+ waitSelect(SelectionKey.OP_READ | SelectionKey.OP_WRITE);
+ if(hsStatus == SSLEngineResult.HandshakeStatus.FINISHED) {
+ if (initialHandshake) {
+ finishInitialHandshake();
+ }
+ return;
+ } else if(hsStatus == SSLEngineResult.HandshakeStatus.NEED_TASK) {
+ doTasks();
+ } else if(hsStatus == SSLEngineResult.HandshakeStatus.NEED_UNWRAP) {
+ if(readAndUnwrap() == -1 && hsStatus != 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.
+ if (initialHandshake && status == SSLEngineResult.Status.BUFFER_UNDERFLOW) {
+ waitSelect(SelectionKey.OP_READ);
+ }
+ } else if(hsStatus == SSLEngineResult.HandshakeStatus.NEED_WRAP) {
+ if (netData.hasRemaining()) {
+ while(flushData()) {}
+ }
+ netData.clear();
+ res = engine.wrap(dummy, netData);
+ hsStatus = res.getHandshakeStatus();
+ netData.flip();
+ flushData();
+ } else {
+ assert false : "doHandshake() should never reach the NOT_HANDSHAKING state";
+ return;
+ }
+ }
+ }
+
+ private void doTasks() {
+ Runnable task;
+ while ((task = engine.getDelegatedTask()) != null) {
+ task.run();
+ }
+ hsStatus = engine.getHandshakeStatus();
+ verifyResult = rubyCtx.getLastVerifyResult();
+ }
+
+ private boolean flushData() throws IOException {
+ try {
+ writeToChannel(netData);
+ } catch (IOException ioe) {
+ netData.position(netData.limit());
+ throw ioe;
+ }
+ if (netData.hasRemaining()) {
+ return false;
+ } else {
+ return true;
+ }
+ }
+
+ private int writeToChannel(ByteBuffer buffer) throws IOException {
+ int totalWritten = 0;
+ while (buffer.hasRemaining()) {
+ totalWritten += getSocketChannel().write(buffer);
+ }
+ return totalWritten;
+ }
+
+ private void finishInitialHandshake() {
+ initialHandshake = false;
+ }
+
+ public int write(ByteBuffer src) throws SSLException, IOException {
+ if(initialHandshake) {
+ throw new IOException("Writing not possible during handshake");
+ }
+ if(netData.hasRemaining()) {
+ // TODO; remove
+ // This protect should be propagated from
+ // http://www.javadocexamples.com/java_source/ssl/SSLChannel.java.html
+ // to avoid IO selecting problem under MT.
+ // We have different model of selecting so it's safe to be removed I think.
+ throw new IOException("Another thread may be writing");
+ }
+ netData.clear();
+ SSLEngineResult res = engine.wrap(src, netData);
+ netData.flip();
+ flushData();
+ return res.bytesConsumed();
+ }
+
+ public int read(ByteBuffer dst) throws IOException {
+ if(initialHandshake) {
+ return 0;
+ }
+ if (engine.isInboundDone()) {
+ return -1;
+ }
+ if (!peerAppData.hasRemaining()) {
+ int appBytesProduced = readAndUnwrap();
+ if (appBytesProduced == -1 || appBytesProduced == 0) {
+ return appBytesProduced;
+ }
+ }
+ int limit = Math.min(peerAppData.remaining(), dst.remaining());
+ peerAppData.get(dst.array(), dst.arrayOffset(), limit);
+ dst.position(dst.arrayOffset() + limit);
+ return limit;
+ }
+
+ private int readAndUnwrap() throws IOException {
+ int bytesRead = getSocketChannel().read(peerNetData);
+ if (bytesRead == -1) {
+ if (!peerNetData.hasRemaining() || (status == SSLEngineResult.Status.BUFFER_UNDERFLOW)) {
+ closeInbound();
+ return -1;
+ }
+ // inbound channel has been already closed but closeInbound() must
+ // be defered till the last engine.unwrap() call.
+ // peerNetData could not be empty.
+ }
+ peerAppData.clear();
+ peerNetData.flip();
+ SSLEngineResult res;
+ do {
+ res = engine.unwrap(peerNetData, peerAppData);
+ } while (res.getStatus() == SSLEngineResult.Status.OK &&
+ res.getHandshakeStatus() == SSLEngineResult.HandshakeStatus.NEED_UNWRAP &&
+ res.bytesProduced() == 0);
+ if(res.getHandshakeStatus() == SSLEngineResult.HandshakeStatus.FINISHED) {
+ finishInitialHandshake();
+ }
+ if(peerAppData.position() == 0 &&
+ res.getStatus() == SSLEngineResult.Status.OK &&
+ peerNetData.hasRemaining()) {
+ res = engine.unwrap(peerNetData, peerAppData);
+ }
+ status = res.getStatus();
+ hsStatus = res.getHandshakeStatus();
+ if (bytesRead == -1 && !peerNetData.hasRemaining()) {
+ // now it's safe to call closeInbound().
+ closeInbound();
+ }
+ if(status == SSLEngineResult.Status.CLOSED) {
+ doShutdown();
+ return -1;
+ }
+ peerNetData.compact();
+ peerAppData.flip();
+ if(!initialHandshake && (hsStatus == SSLEngineResult.HandshakeStatus.NEED_TASK ||
+ hsStatus == SSLEngineResult.HandshakeStatus.NEED_WRAP ||
+ hsStatus == SSLEngineResult.HandshakeStatus.FINISHED)) {
+ doHandshake();
+ }
+ return peerAppData.remaining();
+ }
+
+ private void closeInbound() {
+ try {
+ engine.closeInbound();
+ } catch (SSLException ssle) {
+ // ignore any error on close. possibly an error like this;
+ // Inbound closed before receiving peer's close_notify: possible truncation attack?
+ }
+ }
+
+ private void doShutdown() throws IOException {
+ if (engine.isOutboundDone()) {
+ return;
+ }
+ netData.clear();
+ try {
+ engine.wrap(dummy, netData);
+ } catch(Exception e1) {
+ return;
+ }
+ netData.flip();
+ flushData();
+ }
+
+ @JRubyMethod(rest = true, required = 1, optional = 1)
+ public IRubyObject sysread(ThreadContext context, IRubyObject[] args) {
+ Ruby runtime = context.getRuntime();
+ int len = RubyNumeric.fix2int(args[0]);
+ RubyString str = null;
+
+ if (args.length == 2 && !args[1].isNil()) {
+ str = args[1].convertToString();
+ } else {
+ str = getRuntime().newString("");
+ }
+ if(len == 0) {
+ str.clear();
+ return str;
+ }
+ if (len < 0) {
+ throw runtime.newArgumentError("negative string size (or size too big)");
+ }
+
+ 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)) {
+ waitSelect(SelectionKey.OP_READ);
+ }
+
+ ByteBuffer dst = ByteBuffer.allocate(len);
+ int rr = -1;
+ // ensure >0 bytes read; sysread is blocking read.
+ while (rr <= 0) {
+ if (engine == null) {
+ rr = getSocketChannel().read(dst);
+ } else {
+ rr = read(dst);
+ }
+ if (rr == -1) {
+ throw getRuntime().newEOFError();
+ }
+ }
+ byte[] bss = new byte[rr];
+ dst.position(dst.position() - rr);
+ dst.get(bss);
+ str.setValue(new ByteList(bss));
+ return str;
+ } catch (IOException ioe) {
+ throw getRuntime().newIOError(ioe.getMessage());
+ }
+ }
+
+ @JRubyMethod
+ public IRubyObject syswrite(ThreadContext context, IRubyObject arg) {
+ Ruby runtime = context.getRuntime();
+ try {
+ checkClosed();
+ waitSelect(SelectionKey.OP_WRITE);
+ byte[] bls = arg.convertToString().getBytes();
+ ByteBuffer b1 = ByteBuffer.wrap(bls);
+ int written;
+ if(engine == null) {
+ written = writeToChannel(b1);
+ } else {
+ written = write(b1);
+ }
+ ((RubyIO)api.callMethod(this,"io")).flush();
+
+ return getRuntime().newFixnum(written);
+ } catch (IOException ioe) {
+ throw runtime.newIOError(ioe.getMessage());
+ }
+ }
+
+ private void checkClosed() {
+ if (!getSocketChannel().isOpen()) {
+ throw getRuntime().newIOError("closed stream");
+ }
+ }
+
+ // do shutdown even if we have remaining data to be sent.
+ // call this when you get an exception from client side.
+ private void forceClose() {
+ close(true);
+ }
+
+ private void close(boolean force) {
+ if (engine == null) throw getRuntime().newEOFError();
+ engine.closeOutbound();
+ if (!force && netData.hasRemaining()) {
+ return;
+ } else {
+ try {
+ doShutdown();
+ } catch (IOException ex) {
+ // ignore?
+ }
+ }
+ }
+
+ @JRubyMethod
+ public IRubyObject sysclose() {
+ // no need to try shutdown when it's a server
+ close(rubyCtx.isProtocolForClient());
+ ThreadContext tc = getRuntime().getCurrentContext();
+ if(callMethod(tc,"sync_close").isTrue()) {
+ callMethod(tc,"io").callMethod(tc,"close");
+ }
+ return getRuntime().getNil();
+ }
+
+ @JRubyMethod
+ public IRubyObject cert() {
+ try {
+ Certificate[] cert = engine.getSession().getLocalCertificates();
+ if (cert.length > 0) {
+ return X509Cert.wrap(getRuntime(), cert[0]);
+ }
+ } catch (CertificateEncodingException ex) {
+ throw X509Cert.newCertificateError(getRuntime(), ex);
+ }
+ return getRuntime().getNil();
+ }
+
+ @JRubyMethod
+ public IRubyObject peer_cert() {
+ try {
+ Certificate[] cert = engine.getSession().getPeerCertificates();
+ if (cert.length > 0) {
+ return X509Cert.wrap(getRuntime(), cert[0]);
+ }
+ } catch (CertificateEncodingException ex) {
+ throw X509Cert.newCertificateError(getRuntime(), ex);
+ } catch (SSLPeerUnverifiedException ex) {
+ if (getRuntime().isVerbose()) {
+ getRuntime().getWarnings().warning(String.format("%s: %s", ex.getClass().getName(), ex.getMessage()));
+ }
+ }
+ return getRuntime().getNil();
+ }
+
+ @JRubyMethod
+ public IRubyObject peer_cert_chain() {
+ try {
+ javax.security.cert.Certificate[] certs = engine.getSession().getPeerCertificateChain();
+
+ RubyArray arr = getRuntime().newArray(certs.length);
+ for(int i = 0 ; i < certs.length; i++ ) {
+ arr.add(X509Cert.wrap(getRuntime(), certs[i]));
+ }
+ return arr;
+ } catch (javax.security.cert.CertificateEncodingException e) {
+ throw X509Cert.newCertificateError(getRuntime(), e);
+ } catch (SSLPeerUnverifiedException ex) {
+ if (getRuntime().isVerbose()) {
+ getRuntime().getWarnings().warning(String.format("%s: %s", ex.getClass().getName(), ex.getMessage()));
+ }
+ }
+ return getRuntime().getNil();
+ }
+
+ @JRubyMethod
+ public IRubyObject cipher() {
+ return getRuntime().newString(engine.getSession().getCipherSuite());
+ }
+
+ @JRubyMethod
+ public IRubyObject state() {
+ System.err.println("WARNING: unimplemented method called: SSLSocket#state");
+ return getRuntime().getNil();
+ }
+
+ @JRubyMethod
+ public IRubyObject pending() {
+ System.err.println("WARNING: unimplemented method called: SSLSocket#pending");
+ return getRuntime().getNil();
+ }
+
+ private SocketChannel getSocketChannel() {
+ return (SocketChannel) io.getChannel();
+ }
+}// SSLSocket
diff --git a/src/org/jruby/ext/openssl/SimpleSecretKey.java b/src/org/jruby/ext/openssl/SimpleSecretKey.java
new file mode 100644
index 00000000000..79ef9dd286e
--- /dev/null
+++ b/src/org/jruby/ext/openssl/SimpleSecretKey.java
@@ -0,0 +1,53 @@
+/***** BEGIN LICENSE BLOCK *****
+ * Version: CPL 1.0/GPL 2.0/LGPL 2.1
+ *
+ * The contents of this file are subject to the Common Public
+ * License Version 1.0 (the "License"); you may not use this file
+ * except in compliance with the License. You may obtain a copy of
+ * the License at http://www.eclipse.org/legal/cpl-v10.html
+ *
+ * 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 CPL, 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 CPL, the GPL or the LGPL.
+ ***** END LICENSE BLOCK *****/
+package org.jruby.ext.openssl;
+
+import javax.crypto.SecretKey;
+
+/**
+ * @author Ola Bini
+ */
+public class SimpleSecretKey implements SecretKey {
+ private static final long serialVersionUID = 1L;
+
+ private final String algorithm;
+ private final byte[] value;
+ public SimpleSecretKey(String algorithm, byte[] value) {
+ this.algorithm = algorithm;
+ this.value = value;
+ }
+ public String getAlgorithm() {
+ return algorithm;
+ }
+ public byte[] getEncoded() {
+ return value;
+ }
+ public String getFormat() {
+ return "RAW";
+ }
+}// SimpleSecretKey
diff --git a/src/org/jruby/ext/openssl/Utils.java b/src/org/jruby/ext/openssl/Utils.java
new file mode 100644
index 00000000000..d05ece95b53
--- /dev/null
+++ b/src/org/jruby/ext/openssl/Utils.java
@@ -0,0 +1,98 @@
+/***** BEGIN LICENSE BLOCK *****
+ * Version: CPL 1.0/GPL 2.0/LGPL 2.1
+ *
+ * The contents of this file are subject to the Common Public
+ * License Version 1.0 (the "License"); you may not use this file
+ * except in compliance with the License. You may obtain a copy of
+ * the License at http://www.eclipse.org/legal/cpl-v10.html
+ *
+ * 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 CPL, 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 CPL, the GPL or the LGPL.
+ ***** END LICENSE BLOCK *****/
+package org.jruby.ext.openssl;
+
+import org.jruby.Ruby;
+import org.jruby.RubyClass;
+import org.jruby.RubyObject;
+import org.jruby.RubyString;
+import org.jruby.exceptions.RaiseException;
+import org.jruby.runtime.builtin.IRubyObject;
+
+/**
+ * @author Ola Bini
+ */
+public class Utils {
+ private Utils() {}
+ public static String toHex(byte[] val) {
+ StringBuffer out = new StringBuffer();
+ for(int i=0,j=val.length;i
+ *
+ * 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 CPL, 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 CPL, the GPL or the LGPL.
+ ***** END LICENSE BLOCK *****/
+package org.jruby.ext.openssl;
+
+import org.jruby.Ruby;
+import org.jruby.RubyModule;
+
+/**
+ * @author Ola Bini
+ */
+public class X509 {
+ public static void createX509(Ruby runtime, RubyModule ossl) {
+ RubyModule mX509 = ossl.defineModuleUnder("X509");
+
+ X509Name.createX509Name(runtime,mX509);
+ X509Cert.createX509Cert(runtime,mX509);
+ X509Extensions.createX509Ext(runtime,mX509);
+ X509CRL.createX509CRL(runtime,mX509);
+ X509Revoked.createX509Revoked(runtime,mX509);
+ X509Store.createX509Store(runtime,mX509);
+ Request.createRequest(runtime,mX509);
+ Attribute.createAttribute(runtime,mX509);
+
+ mX509.setConstant("V_OK",runtime.newFixnum(0));
+ mX509.setConstant("V_ERR_UNABLE_TO_GET_ISSUER_CERT",runtime.newFixnum(2));
+ mX509.setConstant("V_ERR_UNABLE_TO_GET_CRL",runtime.newFixnum(3));
+ mX509.setConstant("V_ERR_UNABLE_TO_DECRYPT_CERT_SIGNATURE",runtime.newFixnum(4));
+ mX509.setConstant("V_ERR_UNABLE_TO_DECRYPT_CRL_SIGNATURE",runtime.newFixnum(5));
+ mX509.setConstant("V_ERR_UNABLE_TO_DECODE_ISSUER_PUBLIC_KEY",runtime.newFixnum(6));
+ mX509.setConstant("V_ERR_CERT_SIGNATURE_FAILURE",runtime.newFixnum(7));
+ mX509.setConstant("V_ERR_CRL_SIGNATURE_FAILURE",runtime.newFixnum(8));
+ mX509.setConstant("V_ERR_CERT_NOT_YET_VALID",runtime.newFixnum(9));
+ mX509.setConstant("V_ERR_CERT_HAS_EXPIRED",runtime.newFixnum(10));
+ mX509.setConstant("V_ERR_CRL_NOT_YET_VALID",runtime.newFixnum(11));
+ mX509.setConstant("V_ERR_CRL_HAS_EXPIRED",runtime.newFixnum(12));
+ mX509.setConstant("V_ERR_ERROR_IN_CERT_NOT_BEFORE_FIELD",runtime.newFixnum(13));
+ mX509.setConstant("V_ERR_ERROR_IN_CERT_NOT_AFTER_FIELD",runtime.newFixnum(14));
+ mX509.setConstant("V_ERR_ERROR_IN_CRL_LAST_UPDATE_FIELD",runtime.newFixnum(15));
+ mX509.setConstant("V_ERR_ERROR_IN_CRL_NEXT_UPDATE_FIELD",runtime.newFixnum(16));
+ mX509.setConstant("V_ERR_OUT_OF_MEM",runtime.newFixnum(17));
+ mX509.setConstant("V_ERR_DEPTH_ZERO_SELF_SIGNED_CERT",runtime.newFixnum(18));
+ mX509.setConstant("V_ERR_SELF_SIGNED_CERT_IN_CHAIN",runtime.newFixnum(19));
+ mX509.setConstant("V_ERR_UNABLE_TO_GET_ISSUER_CERT_LOCALLY",runtime.newFixnum(20));
+ mX509.setConstant("V_ERR_UNABLE_TO_VERIFY_LEAF_SIGNATURE",runtime.newFixnum(21));
+ mX509.setConstant("V_ERR_CERT_CHAIN_TOO_LONG",runtime.newFixnum(22));
+ mX509.setConstant("V_ERR_CERT_REVOKED",runtime.newFixnum(23));
+ mX509.setConstant("V_ERR_INVALID_CA",runtime.newFixnum(24));
+ mX509.setConstant("V_ERR_PATH_LENGTH_EXCEEDED",runtime.newFixnum(25));
+ mX509.setConstant("V_ERR_INVALID_PURPOSE",runtime.newFixnum(26));
+ mX509.setConstant("V_ERR_CERT_UNTRUSTED",runtime.newFixnum(27));
+ mX509.setConstant("V_ERR_CERT_REJECTED",runtime.newFixnum(28));
+ mX509.setConstant("V_ERR_SUBJECT_ISSUER_MISMATCH",runtime.newFixnum(29));
+ mX509.setConstant("V_ERR_AKID_SKID_MISMATCH",runtime.newFixnum(30));
+ mX509.setConstant("V_ERR_AKID_ISSUER_SERIAL_MISMATCH",runtime.newFixnum(31));
+ mX509.setConstant("V_ERR_KEYUSAGE_NO_CERTSIGN",runtime.newFixnum(32));
+ mX509.setConstant("V_ERR_APPLICATION_VERIFICATION",runtime.newFixnum(50));
+ mX509.setConstant("V_FLAG_CRL_CHECK",runtime.newFixnum(4));
+ mX509.setConstant("V_FLAG_CRL_CHECK_ALL",runtime.newFixnum(8));
+ mX509.setConstant("PURPOSE_SSL_CLIENT",runtime.newFixnum(1));
+ mX509.setConstant("PURPOSE_SSL_SERVER",runtime.newFixnum(2));
+ mX509.setConstant("PURPOSE_NS_SSL_SERVER",runtime.newFixnum(3));
+ mX509.setConstant("PURPOSE_SMIME_SIGN",runtime.newFixnum(4));
+ mX509.setConstant("PURPOSE_SMIME_ENCRYPT",runtime.newFixnum(5));
+ mX509.setConstant("PURPOSE_CRL_SIGN",runtime.newFixnum(6));
+ mX509.setConstant("PURPOSE_ANY",runtime.newFixnum(7));
+ mX509.setConstant("PURPOSE_OCSP_HELPER",runtime.newFixnum(8));
+ mX509.setConstant("TRUST_COMPAT",runtime.newFixnum(1));
+ mX509.setConstant("TRUST_SSL_CLIENT",runtime.newFixnum(2));
+ mX509.setConstant("TRUST_SSL_SERVER",runtime.newFixnum(3));
+ mX509.setConstant("TRUST_EMAIL",runtime.newFixnum(4));
+ mX509.setConstant("TRUST_OBJECT_SIGN",runtime.newFixnum(5));
+ mX509.setConstant("TRUST_OCSP_SIGN",runtime.newFixnum(6));
+ mX509.setConstant("TRUST_OCSP_REQUEST",runtime.newFixnum(7));
+
+ // These should eventually point to correct things.
+ mX509.setConstant("DEFAULT_CERT_AREA", runtime.newString("/usr/lib/ssl"));
+ mX509.setConstant("DEFAULT_CERT_DIR", runtime.newString("/usr/lib/ssl/certs"));
+ mX509.setConstant("DEFAULT_CERT_FILE", runtime.newString("/usr/lib/ssl/cert.pem"));
+ mX509.setConstant("DEFAULT_CERT_DIR_ENV", runtime.newString("SSL_CERT_DIR"));
+ mX509.setConstant("DEFAULT_CERT_FILE_ENV", runtime.newString("SSL_CERT_FILE"));
+ mX509.setConstant("DEFAULT_PRIVATE_DIR", runtime.newString("/usr/lib/ssl/private"));
+ }
+}// X509
diff --git a/src/org/jruby/ext/openssl/X509CRL.java b/src/org/jruby/ext/openssl/X509CRL.java
new file mode 100644
index 00000000000..f2b9fadf2c5
--- /dev/null
+++ b/src/org/jruby/ext/openssl/X509CRL.java
@@ -0,0 +1,462 @@
+/***** BEGIN LICENSE BLOCK *****
+ * Version: CPL 1.0/GPL 2.0/LGPL 2.1
+ *
+ * The contents of this file are subject to the Common Public
+ * License Version 1.0 (the "License"); you may not use this file
+ * except in compliance with the License. You may obtain a copy of
+ * the License at http://www.eclipse.org/legal/cpl-v10.html
+ *
+ * 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, 2007 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 CPL, 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 CPL, the GPL or the LGPL.
+ ***** END LICENSE BLOCK *****/
+package org.jruby.ext.openssl;
+
+import java.io.ByteArrayInputStream;
+import java.io.IOException;
+import java.io.StringWriter;
+import java.math.BigInteger;
+import java.security.GeneralSecurityException;
+import java.security.cert.CRLException;
+import java.security.cert.CertificateFactory;
+import java.text.DateFormat;
+import java.text.SimpleDateFormat;
+import java.util.ArrayList;
+import java.util.Hashtable;
+import java.util.Iterator;
+import java.util.List;
+
+import org.bouncycastle.asn1.ASN1EncodableVector;
+import org.bouncycastle.asn1.ASN1InputStream;
+import org.bouncycastle.asn1.DERBoolean;
+import org.bouncycastle.asn1.DEREncodable;
+import org.bouncycastle.asn1.DERInteger;
+import org.bouncycastle.asn1.DERObject;
+import org.bouncycastle.asn1.DERObjectIdentifier;
+import org.bouncycastle.asn1.DERSequence;
+import org.bouncycastle.asn1.DERTaggedObject;
+import org.bouncycastle.x509.X509V2CRLGenerator;
+import org.jruby.Ruby;
+import org.jruby.RubyArray;
+import org.jruby.RubyClass;
+import org.jruby.RubyModule;
+import org.jruby.RubyNumeric;
+import org.jruby.RubyObject;
+import org.jruby.RubyString;
+import org.jruby.RubyTime;
+import org.jruby.anno.JRubyMethod;
+import org.jruby.exceptions.RaiseException;
+import org.jruby.ext.openssl.x509store.PEMInputOutput;
+import org.jruby.runtime.Block;
+import org.jruby.runtime.ObjectAllocator;
+import org.jruby.runtime.builtin.IRubyObject;
+
+/**
+ * @author Ola Bini
+ */
+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);
+ }
+ };
+
+ public static void createX509CRL(Ruby runtime, RubyModule mX509) {
+ RubyClass cX509CRL = mX509.defineClassUnder("CRL",runtime.getObject(),X509CRL_ALLOCATOR);
+ RubyClass openSSLError = runtime.getModule("OpenSSL").getClass("OpenSSLError");
+ mX509.defineClassUnder("CRLError",openSSLError,openSSLError.getAllocator());
+
+ cX509CRL.defineAnnotatedMethods(X509CRL.class);
+ }
+
+ private IRubyObject version;
+ private IRubyObject issuer;
+ private IRubyObject last_update;
+ private IRubyObject next_update;
+ private IRubyObject revoked;
+ private List extensions;
+
+ private IRubyObject sig_alg;
+
+ private boolean changed = true;
+
+ private X509V2CRLGenerator generator = new X509V2CRLGenerator();
+ private java.security.cert.X509CRL crl;
+
+ private DERObject crl_v;
+
+ java.security.cert.X509CRL getCRL() {
+ return crl;
+ }
+
+ public X509CRL(Ruby runtime, RubyClass type) {
+ super(runtime,type);
+ }
+
+ @JRubyMethod(name="initialize", rest=true, frame=true)
+ public IRubyObject _initialize(IRubyObject[] args, Block block) {
+ extensions = new ArrayList();
+ if(org.jruby.runtime.Arity.checkArgumentCount(getRuntime(),args,0,1) == 0) {
+ version = getRuntime().getNil();
+ issuer = getRuntime().getNil();
+ last_update = getRuntime().getNil();
+ next_update = getRuntime().getNil();
+ revoked = getRuntime().newArray();
+ return this;
+ }
+
+ ByteArrayInputStream bis = new ByteArrayInputStream(args[0].convertToString().getBytes());
+ try {
+ // SunJCE throws java.security.cert.CRLException: Invalid encoding of AuthorityKeyIdentifierExtension.
+ // FIXME: use BC for now.
+ CertificateFactory cf = OpenSSLReal.getX509CertificateFactoryBC();
+ crl = (java.security.cert.X509CRL) cf.generateCRL(bis);
+ } catch (GeneralSecurityException gse) {
+ throw newX509CRLError(getRuntime(), gse.getMessage());
+ }
+
+ byte[] crl_bytes = OpenSSLImpl.readX509PEM(args[0]);
+ try {
+ crl_v = new ASN1InputStream(new ByteArrayInputStream(crl_bytes)).readObject();
+ } catch (IOException ioe) {
+ throw newX509CRLError(getRuntime(), ioe.getMessage());
+ }
+
+ DEREncodable v0 = ((DERSequence)(((DERSequence)crl_v).getObjectAt(0))).getObjectAt(0);
+ if(v0 instanceof DERInteger) {
+ set_version(getRuntime().newFixnum(((DERInteger)v0).getValue().intValue()));
+ } else {
+ set_version(getRuntime().newFixnum(2));
+ }
+ set_last_update(RubyTime.newTime(getRuntime(),crl.getThisUpdate().getTime()));
+ set_next_update(RubyTime.newTime(getRuntime(),crl.getNextUpdate().getTime()));
+ RubyString name = RubyString.newString(getRuntime(), crl.getIssuerX500Principal().getEncoded());
+ set_issuer(Utils.newRubyInstance(getRuntime(), "OpenSSL::X509::Name", name));
+
+ revoked = getRuntime().newArray();
+
+ DERSequence seqa = (DERSequence)((DERSequence)crl_v).getObjectAt(0);
+ DERObject maybe_ext = (DERObject)seqa.getObjectAt(seqa.size()-1);
+ if(maybe_ext instanceof DERTaggedObject && ((DERTaggedObject)maybe_ext).getTagNo() == 0) {
+ DERSequence exts = (DERSequence)((DERTaggedObject)maybe_ext).getObject();
+ for(int i=0;i0) {
+ sbe.append(IND8).append("CRL extensions\n");
+ for(Iterator iter = extensions.iterator();iter.hasNext();) {
+ X509Extensions.Extension ext = (X509Extensions.Extension)iter.next();
+ DERObjectIdentifier oiden = ext.getRealOid();
+ sbe.append(IND12).append(ASN1.o2a(getRuntime(),oiden)).append(": ");
+ if(ext.getRealCritical()) {
+ sbe.append("critical");
+ }
+ sbe.append("\n");
+ sbe.append(IND16).append(ext.value()).append("\n");
+ }
+ }
+ /*
+ 114 rev = X509_CRL_get_REVOKED(x);
+ 115
+ 116 if(sk_X509_REVOKED_num(rev) > 0)
+ 117 BIO_printf(out, "Revoked Certificates:\n");
+ 118 else BIO_printf(out, "No Revoked Certificates.\n");
+ 119
+ 120 for(i = 0; i < sk_X509_REVOKED_num(rev); i++) {
+ 121 r = sk_X509_REVOKED_value(rev, i);
+ 122 BIO_printf(out," Serial Number: ");
+ 123 i2a_ASN1_INTEGER(out,r->serialNumber);
+ 124 BIO_printf(out,"\n Revocation Date: ");
+ 125 ASN1_TIME_print(out,r->revocationDate);
+ 126 BIO_printf(out,"\n");
+ 127 X509V3_extensions_print(out, "CRL entry extensions",
+ 128 r->extensions, 0, 8);
+ 129 }
+ 130 X509_signature_print(out, x->sig_alg, x->signature);
+ 131
+ */
+ return getRuntime().newString(sbe.toString());
+ }
+
+ @JRubyMethod
+ public IRubyObject version() {
+ return this.version;
+ }
+
+ @JRubyMethod(name="version=")
+ public IRubyObject set_version(IRubyObject val) {
+ if(!val.equals(this.version)) {
+ changed = true;
+ }
+ this.version = val;
+ return val;
+ }
+
+ @JRubyMethod
+ public IRubyObject signature_algorithm() {
+ return sig_alg;
+ }
+
+ @JRubyMethod
+ public IRubyObject issuer() {
+ return this.issuer;
+ }
+
+ @JRubyMethod(name="issuer=")
+ public IRubyObject set_issuer(IRubyObject val) {
+ if(!val.equals(this.issuer)) {
+ changed = true;
+ }
+ this.issuer = val;
+ generator.setIssuerDN(((X509Name)issuer).getRealName());
+ return val;
+ }
+
+ @JRubyMethod
+ public IRubyObject last_update() {
+ return this.last_update;
+ }
+
+ @JRubyMethod(name="last_update=")
+ public IRubyObject set_last_update(IRubyObject val) {
+ changed = true;
+ last_update = val.callMethod(getRuntime().getCurrentContext(),"getutc");
+ ((RubyTime)last_update).setMicroseconds(0);
+ generator.setThisUpdate(((RubyTime)last_update).getJavaDate());
+ this.last_update = val;
+ return val;
+ }
+
+ @JRubyMethod
+ public IRubyObject next_update() {
+ return this.next_update;
+ }
+
+ @JRubyMethod(name="next_update=")
+ public IRubyObject set_next_update(IRubyObject val) {
+ changed = true;
+ next_update = val.callMethod(getRuntime().getCurrentContext(),"getutc");
+ ((RubyTime)next_update).setMicroseconds(0);
+ generator.setNextUpdate(((RubyTime)next_update).getJavaDate());
+ this.next_update = val;
+ return val;
+ }
+
+ @JRubyMethod
+ public IRubyObject revoked() {
+ return this.revoked;
+ }
+
+ @JRubyMethod(name="revoked=")
+ public IRubyObject set_revoked(IRubyObject val) {
+ changed = true;
+ this.revoked = val;
+ return val;
+ }
+
+ @JRubyMethod
+ public IRubyObject add_revoked(IRubyObject val) {
+ changed = true;
+ this.revoked.callMethod(getRuntime().getCurrentContext(),"<<",val);
+ return val;
+ }
+
+ @JRubyMethod
+ public IRubyObject extensions() {
+ return getRuntime().newArray(this.extensions);
+ }
+
+ @SuppressWarnings("unchecked")
+ @JRubyMethod(name="extensions=")
+ public IRubyObject set_extensions(IRubyObject val) {
+ this.extensions = ((RubyArray)val).getList();
+ return val;
+ }
+
+ @JRubyMethod
+ public IRubyObject add_extension(IRubyObject val) {
+ this.extensions.add(val);
+ return val;
+ }
+
+ @JRubyMethod
+ public IRubyObject sign(final IRubyObject key, IRubyObject digest) {
+ //System.err.println("WARNING: unimplemented method called: CRL#sign");
+ // Have to obey some artificial constraints of the OpenSSL implementation. Stupid.
+ String keyAlg = ((PKey)key).getAlgorithm();
+ String digAlg = ((Digest)digest).getShortAlgorithm();
+
+ if(("DSA".equalsIgnoreCase(keyAlg) && "MD5".equalsIgnoreCase(digAlg)) ||
+ ("RSA".equalsIgnoreCase(keyAlg) && "DSS1".equals(((Digest)digest).name().toString())) ||
+ ("DSA".equalsIgnoreCase(keyAlg) && "SHA1".equals(((Digest)digest).name().toString()))) {
+ throw newX509CRLError(getRuntime(), null);
+ }
+
+ sig_alg = getRuntime().newString(digAlg);
+ generator.setSignatureAlgorithm(digAlg + "WITH" + keyAlg);
+
+ for (IRubyObject obj : ((RubyArray)revoked).toJavaArray()) {
+ X509Revoked rev = (X509Revoked)obj; // TODO: can throw CCE
+ BigInteger serial = new BigInteger(rev.callMethod(getRuntime().getCurrentContext(),"serial").toString());
+ IRubyObject t1 = rev.callMethod(getRuntime().getCurrentContext(),"time").callMethod(getRuntime().getCurrentContext(),"getutc");
+ ((RubyTime)t1).setMicroseconds(0);
+ // Extensions ignored, for now
+ generator.addCRLEntry(serial,((RubyTime)t1).getJavaDate(),new org.bouncycastle.asn1.x509.X509Extensions(new Hashtable()));
+ }
+
+ try {
+ for (Iterator iter = extensions.iterator(); iter.hasNext();) {
+ X509Extensions.Extension ag = (X509Extensions.Extension) iter.next();
+ generator.addExtension(ag.getRealOid(), ag.getRealCritical(), ag.getRealValueBytes());
+ }
+ } catch (IOException ioe) {
+ throw newX509CRLError(getRuntime(), ioe.getMessage());
+ }
+ try {
+ // X509V2CRLGenerator(generator) depends BC.
+ OpenSSLReal.doWithBCProvider(new OpenSSLReal.Runnable() {
+
+ public void run() throws GeneralSecurityException {
+ crl = generator.generate(((PKey) key).getPrivateKey(), "BC");
+ }
+ });
+ } catch (GeneralSecurityException gse) {
+ throw newX509CRLError(getRuntime(), gse.getMessage());
+ }
+
+ try {
+ crl_v = new ASN1InputStream(new ByteArrayInputStream(crl.getEncoded())).readObject();
+ } catch (CRLException crle) {
+ throw newX509CRLError(getRuntime(), crle.getMessage());
+ } catch (IOException ioe) {
+ throw newX509CRLError(getRuntime(), ioe.getMessage());
+ }
+ DERSequence v1 = (DERSequence)(((DERSequence)crl_v).getObjectAt(0));
+ ASN1EncodableVector build1 = new ASN1EncodableVector();
+ int copyIndex = 0;
+ if(v1.getObjectAt(0) instanceof DERInteger) {
+ copyIndex++;
+ }
+ build1.add(new DERInteger(new java.math.BigInteger(version.toString())));
+ while(copyIndex < v1.size()) {
+ build1.add(v1.getObjectAt(copyIndex++));
+ }
+ ASN1EncodableVector build2 = new ASN1EncodableVector();
+ build2.add(new DERSequence(build1));
+ build2.add(((DERSequence)crl_v).getObjectAt(1));
+ build2.add(((DERSequence)crl_v).getObjectAt(2));
+ crl_v = new DERSequence(build2);
+ changed = false;
+ return this;
+ }
+
+ @JRubyMethod
+ public IRubyObject verify(final IRubyObject key) {
+ if (changed) {
+ return getRuntime().getFalse();
+ }
+ try {
+ crl.verify(((PKey) key).getPublicKey());
+ return getRuntime().getTrue();
+ } catch (Exception ignored) {
+ return getRuntime().getFalse();
+ }
+
+ }
+
+ private static RaiseException newX509CRLError(Ruby runtime, String message) {
+ return Utils.newError(runtime, "OpenSSL::X509::CRLError", message);
+ }
+}// X509CRL
diff --git a/src/org/jruby/ext/openssl/X509Cert.java b/src/org/jruby/ext/openssl/X509Cert.java
new file mode 100644
index 00000000000..40ec6bfd044
--- /dev/null
+++ b/src/org/jruby/ext/openssl/X509Cert.java
@@ -0,0 +1,535 @@
+/***** BEGIN LICENSE BLOCK *****
+ * Version: CPL 1.0/GPL 2.0/LGPL 2.1
+ *
+ * The contents of this file are subject to the Common Public
+ * License Version 1.0 (the "License"); you may not use this file
+ * except in compliance with the License. You may obtain a copy of
+ * the License at http://www.eclipse.org/legal/cpl-v10.html
+ *
+ * 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, 2007 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 CPL, 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 CPL, the GPL or the LGPL.
+ ***** END LICENSE BLOCK *****/
+package org.jruby.ext.openssl;
+
+import java.io.ByteArrayInputStream;
+import java.io.IOException;
+import java.io.StringWriter;
+import java.math.BigInteger;
+import java.security.GeneralSecurityException;
+import java.security.InvalidKeyException;
+import java.security.NoSuchAlgorithmException;
+import java.security.NoSuchProviderException;
+import java.security.PublicKey;
+import java.security.SignatureException;
+import java.security.cert.Certificate;
+import java.security.cert.CertificateEncodingException;
+import java.security.cert.CertificateException;
+import java.security.cert.CertificateFactory;
+import java.security.cert.X509Certificate;
+import java.util.ArrayList;
+import java.util.Iterator;
+import java.util.List;
+import java.util.Set;
+
+import org.bouncycastle.asn1.ASN1EncodableVector;
+import org.bouncycastle.asn1.ASN1InputStream;
+import org.bouncycastle.asn1.DERObjectIdentifier;
+import org.bouncycastle.asn1.DERSequence;
+import org.bouncycastle.asn1.x509.GeneralName;
+import org.bouncycastle.asn1.x509.GeneralNames;
+import org.bouncycastle.x509.X509V3CertificateGenerator;
+import org.jruby.Ruby;
+import org.jruby.RubyArray;
+import org.jruby.RubyClass;
+import org.jruby.RubyModule;
+import org.jruby.RubyNumeric;
+import org.jruby.RubyObject;
+import org.jruby.RubyString;
+import org.jruby.RubyTime;
+import org.jruby.anno.JRubyMethod;
+import org.jruby.exceptions.RaiseException;
+import org.jruby.ext.openssl.impl.ASN1Registry;
+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.builtin.IRubyObject;
+import org.jruby.util.ByteList;
+
+/**
+ * @author Ola Bini
+ */
+public class X509Cert extends RubyObject {
+ private static final long serialVersionUID = 5626619026058595493L;
+
+ private static ObjectAllocator X509CERT_ALLOCATOR = new ObjectAllocator() {
+ public IRubyObject allocate(Ruby runtime, RubyClass klass) {
+ return new X509Cert(runtime, klass);
+ }
+ };
+
+ public static void createX509Cert(Ruby runtime, RubyModule mX509) {
+ RubyClass cX509Cert = mX509.defineClassUnder("Certificate",runtime.getObject(),X509CERT_ALLOCATOR);
+ RubyClass openSSLError = runtime.getModule("OpenSSL").getClass("OpenSSLError");
+ mX509.defineClassUnder("CertificateError",openSSLError,openSSLError.getAllocator());
+
+ cX509Cert.defineAnnotatedMethods(X509Cert.class);
+ }
+
+ public X509Cert(Ruby runtime, RubyClass type) {
+ super(runtime,type);
+ }
+
+ private IRubyObject serial;
+ private IRubyObject not_before;
+ private IRubyObject not_after;
+ private IRubyObject issuer;
+ private IRubyObject subject;
+ private IRubyObject public_key;
+
+ private IRubyObject sig_alg;
+ private IRubyObject version;
+
+ private List extensions;
+
+ private boolean changed = true;
+
+ private X509V3CertificateGenerator generator = new X509V3CertificateGenerator();
+ private X509Certificate cert;
+ private String public_key_algorithm;
+ private byte[] public_key_encoded;
+
+ X509AuxCertificate getAuxCert() {
+ if(null == cert) {
+ return null;
+ }
+ if(cert instanceof X509AuxCertificate) {
+ return (X509AuxCertificate)cert;
+ }
+ return new X509AuxCertificate(cert);
+ }
+
+ public static IRubyObject wrap(Ruby runtime, Certificate c) throws CertificateEncodingException {
+ RubyClass cr = Utils.getClassFromPath(runtime, "OpenSSL::X509::Certificate");
+ return cr.callMethod(runtime.getCurrentContext(), "new", RubyString.newString(runtime, c.getEncoded()));
+ }
+
+ // this is the javax.security counterpart of the previous wrap method
+ public static IRubyObject wrap(Ruby runtime, javax.security.cert.Certificate c) throws javax.security.cert.CertificateEncodingException {
+ RubyClass cr = Utils.getClassFromPath(runtime, "OpenSSL::X509::Certificate");
+ return cr.callMethod(runtime.getCurrentContext(), "new", RubyString.newString(runtime, c.getEncoded()));
+ }
+
+ @JRubyMethod(name="initialize", optional = 1, frame=true)
+ public IRubyObject initialize(ThreadContext context, IRubyObject[] args, Block unusedBlock) {
+ Ruby runtime = context.getRuntime();
+ extensions = new ArrayList();
+ if(args.length == 0) {
+ return this;
+ }
+ byte[] bytes = OpenSSLImpl.readX509PEM(args[0]);
+ ByteArrayInputStream bis = new ByteArrayInputStream(bytes);
+
+ CertificateFactory cf;
+
+ RubyModule ossl = runtime.getModule("OpenSSL");
+ RubyModule x509 = (RubyModule)ossl.getConstant("X509");
+ IRubyObject x509Name = x509.getConstant("Name");
+
+ try {
+ cf = CertificateFactory.getInstance("X.509");
+ cert = (X509Certificate)cf.generateCertificate(bis);
+ } catch (CertificateException ex) {
+ throw newCertificateError(runtime, ex);
+ }
+ if (cert == null) {
+ throw newCertificateError(runtime, (String) null);
+ }
+
+ set_serial(RubyNumeric.str2inum(runtime,runtime.newString(cert.getSerialNumber().toString()),10));
+ set_not_before(RubyTime.newTime(runtime,cert.getNotBefore().getTime()));
+ set_not_after(RubyTime.newTime(runtime,cert.getNotAfter().getTime()));
+ set_subject(x509Name.callMethod(context,"new",RubyString.newString(runtime, cert.getSubjectX500Principal().getEncoded())));
+ set_issuer(x509Name.callMethod(context,"new",RubyString.newString(runtime, cert.getIssuerX500Principal().getEncoded())));
+
+ String algorithm = cert.getPublicKey().getAlgorithm();
+ set_public_key(algorithm, cert.getPublicKey().getEncoded());
+
+ IRubyObject extFact = ((RubyClass)(x509.getConstant("ExtensionFactory"))).callMethod(context,"new");
+ extFact.callMethod(context,"subject_certificate=",this);
+
+ Set crit = cert.getCriticalExtensionOIDs();
+ if (crit != null) {
+ for (Iterator iter = crit.iterator(); iter.hasNext();) {
+ String critOid = iter.next();
+ byte[] value = cert.getExtensionValue(critOid);
+ IRubyObject rValue = ASN1.decode(ossl.getConstant("ASN1"), runtime.newString(new ByteList(value, false))).callMethod(context, "value");
+ X509Extensions.Extension ext = (X509Extensions.Extension) x509.getConstant("Extension").callMethod(context, "new",
+ new IRubyObject[] { runtime.newString(critOid), rValue, runtime.getTrue() });
+ add_extension(ext);
+ }
+ }
+
+ Set ncrit = cert.getNonCriticalExtensionOIDs();
+ if (ncrit != null) {
+ for (Iterator iter = ncrit.iterator(); iter.hasNext();) {
+ String ncritOid = iter.next();
+ byte[] value = cert.getExtensionValue(ncritOid);
+ // TODO: wired. J9 returns null for an OID given in getNonCriticalExtensionOIDs()
+ if (value != null) {
+ IRubyObject rValue = ASN1.decode(ossl.getConstant("ASN1"), runtime.newString(new ByteList(value, false))).callMethod(context, "value");
+ X509Extensions.Extension ext = (X509Extensions.Extension) x509.getConstant("Extension").callMethod(context, "new",
+ new IRubyObject[] { runtime.newString(ncritOid), rValue, runtime.getFalse() });
+ add_extension(ext);
+ }
+ }
+ }
+ changed = false;
+
+ return this;
+ }
+
+ //Lazy method for public key instantiation
+ private void set_public_key(String algorithm, byte[] encoded) {
+ this.public_key_algorithm = algorithm;
+ this.public_key_encoded = encoded;
+ }
+
+ public static RaiseException newCertificateError(Ruby runtime, Exception ex) {
+ return newCertificateError(runtime, ex.getMessage());
+ }
+
+ public static RaiseException newCertificateError(Ruby runtime, String message) {
+ throw Utils.newError(runtime, "OpenSSL::X509::CertificateError", message);
+ }
+
+ @Override
+ @JRubyMethod
+ public IRubyObject initialize_copy(IRubyObject obj) {
+ if(this == obj) {
+ return this;
+ }
+ checkFrozen();
+ return this;
+ }
+
+ @JRubyMethod
+ public IRubyObject to_der() {
+ try {
+ return RubyString.newString(getRuntime(), cert.getEncoded());
+ } catch (CertificateEncodingException ex) {
+ throw newCertificateError(getRuntime(), ex);
+ }
+ }
+
+ @JRubyMethod(name={"to_pem","to_s"})
+ public IRubyObject to_pem() {
+ try {
+ StringWriter w = new StringWriter();
+ PEMInputOutput.writeX509Certificate(w, getAuxCert());
+ w.close();
+ return getRuntime().newString(w.toString());
+ } catch (IOException ex) {
+ throw getRuntime().newIOErrorFromException(ex);
+ }
+ }
+
+ @JRubyMethod
+ public IRubyObject to_text() {
+ return getRuntime().newString(getAuxCert().toString());
+ }
+
+ @Override
+ @JRubyMethod
+ public IRubyObject inspect() {
+ return getRuntime().getNil();
+ }
+
+ @JRubyMethod
+ public IRubyObject version() {
+ return version;
+ }
+
+ @JRubyMethod(name="version=")
+ public IRubyObject set_version(IRubyObject arg) {
+ if(!arg.equals(this.version)) {
+ changed = true;
+ }
+ this.version = arg;
+ return arg;
+ }
+
+ @JRubyMethod
+ public IRubyObject signature_algorithm() {
+ return sig_alg;
+ }
+
+ @JRubyMethod
+ public IRubyObject serial() {
+ return serial;
+ }
+
+ @JRubyMethod(name="serial=")
+ public IRubyObject set_serial(IRubyObject num) {
+ if(!num.equals(this.serial)) {
+ changed = true;
+ }
+ serial = num;
+ String s = serial.toString();
+
+ BigInteger bi;
+ if (s.equals("0")) { // MRI compatibility: allow 0 serial number
+ bi = BigInteger.ONE;
+ } else {
+ bi = new BigInteger(s);
+ }
+ generator.setSerialNumber(bi);
+ return num;
+ }
+
+ @JRubyMethod
+ public IRubyObject subject() {
+ return subject;
+ }
+
+ @JRubyMethod(name="subject=")
+ public IRubyObject set_subject(IRubyObject arg) {
+ if(!arg.equals(this.subject)) {
+ changed = true;
+ }
+ subject = arg;
+ generator.setSubjectDN(((X509Name)subject).getRealName());
+ return arg;
+ }
+
+ @JRubyMethod
+ public IRubyObject issuer() {
+ return issuer;
+ }
+
+ @JRubyMethod(name="issuer=")
+ public IRubyObject set_issuer(IRubyObject arg) {
+ if(!arg.equals(this.issuer)) {
+ changed = true;
+ }
+ issuer = arg;
+ generator.setIssuerDN(((X509Name)issuer).getRealName());
+ return arg;
+ }
+
+ @JRubyMethod
+ public IRubyObject not_before() {
+ return not_before;
+ }
+
+ @JRubyMethod(name="not_before=")
+ public IRubyObject set_not_before(IRubyObject arg) {
+ changed = true;
+ not_before = arg.callMethod(getRuntime().getCurrentContext(),"getutc");
+ ((RubyTime)not_before).setMicroseconds(0);
+ generator.setNotBefore(((RubyTime)not_before).getJavaDate());
+ return arg;
+ }
+
+ @JRubyMethod
+ public IRubyObject not_after() {
+ return not_after;
+ }
+
+ @JRubyMethod(name="not_after=")
+ public IRubyObject set_not_after(IRubyObject arg) {
+ changed = true;
+ not_after = arg.callMethod(getRuntime().getCurrentContext(),"getutc");
+ ((RubyTime)not_after).setMicroseconds(0);
+ generator.setNotAfter(((RubyTime)not_after).getJavaDate());
+ return arg;
+ }
+
+ @JRubyMethod
+ public IRubyObject public_key() {
+ if (public_key == null) {
+ lazyInitializePublicKey();
+ }
+ return public_key.callMethod(getRuntime().getCurrentContext(), "public_key");
+ }
+
+ @JRubyMethod(name="public_key=")
+ public IRubyObject set_public_key(IRubyObject arg) {
+ Utils.checkKind(getRuntime(), arg, "OpenSSL::PKey::PKey");
+ if(!arg.equals(this.public_key)) {
+ changed = true;
+ }
+ public_key = arg;
+ generator.setPublicKey(((PKey)public_key).getPublicKey());
+ return arg;
+ }
+
+ private void lazyInitializePublicKey() {
+ if (public_key_encoded == null || public_key_algorithm == null) {
+ throw new IllegalStateException("lazy public key initialization failed");
+ }
+ RubyModule ossl = getRuntime().getModule("OpenSSL");
+ RubyModule pkey = (RubyModule) ossl.getConstant("PKey");
+ ThreadContext tc = getRuntime().getCurrentContext();
+ boolean backupChanged = changed;
+ if ("RSA".equalsIgnoreCase(public_key_algorithm)) {
+ set_public_key(pkey.getConstant("RSA").callMethod(tc, "new", RubyString.newString(getRuntime(), public_key_encoded)));
+ } else if ("DSA".equalsIgnoreCase(public_key_algorithm)) {
+ set_public_key(pkey.getConstant("DSA").callMethod(tc, "new", RubyString.newString(getRuntime(), public_key_encoded)));
+ } else {
+ throw newCertificateError(getRuntime(), "The algorithm " + public_key_algorithm + " is unsupported for public keys");
+ }
+ changed = backupChanged;
+ }
+
+ @JRubyMethod
+ public IRubyObject sign(ThreadContext context, final IRubyObject key, IRubyObject digest) {
+ Ruby runtime = context.getRuntime();
+
+ // Have to obey some artificial constraints of the OpenSSL implementation. Stupid.
+ String keyAlg = ((PKey)key).getAlgorithm();
+ String digAlg = ((Digest)digest).getShortAlgorithm();
+ String digName = ((Digest)digest).name().toString();
+
+ if(("DSA".equalsIgnoreCase(keyAlg) && "MD5".equalsIgnoreCase(digAlg)) ||
+ ("RSA".equalsIgnoreCase(keyAlg) && "DSS1".equals(digName))) {
+ throw newCertificateError(runtime, "signature_algorithm not supported");
+ }
+
+ for(Iterator iter = extensions.iterator();iter.hasNext();) {
+ X509Extensions.Extension ag = (X509Extensions.Extension)iter.next();
+ try {
+ byte[] bytes = ag.getRealValueBytes();
+ generator.addExtension(ag.getRealOid(),ag.getRealCritical(),bytes);
+ } catch (IOException ioe) {
+ throw runtime.newIOErrorFromException(ioe);
+ }
+ }
+
+ generator.setSignatureAlgorithm(digAlg + "WITH" + keyAlg);
+ if (public_key == null) {
+ lazyInitializePublicKey();
+ }
+ try {
+ // X509V3CertificateGenerator depends BC.
+ OpenSSLReal.doWithBCProvider(new OpenSSLReal.Runnable() {
+
+ public void run() throws GeneralSecurityException {
+ cert = generator.generate(((PKey) key).getPrivateKey(), "BC");
+ }
+ });
+ } catch (GeneralSecurityException gse) {
+ throw newCertificateError(getRuntime(), gse.getMessage());
+ }
+ if (cert == null) {
+ throw newCertificateError(runtime, (String) null);
+ }
+ String name = ASN1Registry.o2a(cert.getSigAlgOID());
+ if (name == null) {
+ name = cert.getSigAlgOID();
+ }
+ sig_alg = runtime.newString(name);
+ changed = false;
+ return this;
+ }
+
+ @JRubyMethod
+ public IRubyObject verify(IRubyObject key) {
+ if(changed) {
+ return getRuntime().getFalse();
+ }
+ try {
+ cert.verify(((PKey)key).getPublicKey());
+ return getRuntime().getTrue();
+ } catch (CertificateException ce) {
+ throw newCertificateError(getRuntime(), ce);
+ } catch (NoSuchAlgorithmException nsae) {
+ throw newCertificateError(getRuntime(), nsae);
+ } catch (NoSuchProviderException nspe) {
+ throw newCertificateError(getRuntime(), nspe);
+ } catch (SignatureException se) {
+ throw newCertificateError(getRuntime(), se);
+ } catch(InvalidKeyException e) {
+ return getRuntime().getFalse();
+ }
+ }
+
+ @JRubyMethod
+ public IRubyObject check_private_key(IRubyObject arg) {
+ PKey key = (PKey)arg;
+ PublicKey pkey = key.getPublicKey();
+ PublicKey certPubKey = getAuxCert().getPublicKey();
+ if (certPubKey.equals(pkey))
+ return getRuntime().getTrue();
+ return getRuntime().getFalse();
+ }
+
+ @JRubyMethod
+ public IRubyObject extensions() {
+ return getRuntime().newArray(extensions);
+ }
+
+ @SuppressWarnings("unchecked")
+ @JRubyMethod(name="extensions=")
+ public IRubyObject set_extensions(IRubyObject arg) {
+ extensions = new ArrayList(((RubyArray)arg).getList());
+ return arg;
+ }
+
+ @JRubyMethod
+ public IRubyObject add_extension(IRubyObject arg) {
+ changed = true;
+ DERObjectIdentifier oid = ((X509Extensions.Extension)arg).getRealOid();
+ if(oid.equals(new DERObjectIdentifier("2.5.29.17"))) {
+ boolean one = true;
+ for(Iterator iter = extensions.iterator();iter.hasNext();) {
+ X509Extensions.Extension ag = (X509Extensions.Extension)iter.next();
+ if(ag.getRealOid().equals(new DERObjectIdentifier("2.5.29.17"))) {
+ ASN1EncodableVector v1 = new ASN1EncodableVector();
+
+ try {
+ GeneralName[] n1 = GeneralNames.getInstance(new ASN1InputStream(ag.getRealValueBytes()).readObject()).getNames();
+ GeneralName[] n2 = GeneralNames.getInstance(new ASN1InputStream(((X509Extensions.Extension)arg).getRealValueBytes()).readObject()).getNames();
+
+ for(int i=0;i
+ *
+ * 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 CPL, 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 CPL, the GPL or the LGPL.
+ ***** END LICENSE BLOCK *****/
+package org.jruby.ext.openssl;
+
+
+import java.io.IOException;
+import java.security.GeneralSecurityException;
+import java.security.MessageDigest;
+import org.bouncycastle.asn1.ASN1Encodable;
+import org.bouncycastle.asn1.ASN1EncodableVector;
+import org.bouncycastle.asn1.ASN1InputStream;
+import org.bouncycastle.asn1.ASN1Sequence;
+import org.bouncycastle.asn1.DERBitString;
+import org.bouncycastle.asn1.DERBoolean;
+import org.bouncycastle.asn1.DEREncodable;
+import org.bouncycastle.asn1.DERIA5String;
+import org.bouncycastle.asn1.DERInteger;
+import org.bouncycastle.asn1.DERObject;
+import org.bouncycastle.asn1.DERObjectIdentifier;
+import org.bouncycastle.asn1.DEROctetString;
+import org.bouncycastle.asn1.DERSequence;
+import org.bouncycastle.asn1.DERString;
+import org.bouncycastle.asn1.DERUnknownTag;
+import org.bouncycastle.asn1.x509.GeneralName;
+import org.bouncycastle.asn1.x509.GeneralNames;
+import org.jruby.Ruby;
+import org.jruby.RubyArray;
+import org.jruby.RubyClass;
+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.ext.openssl.impl.ASN1Registry;
+import org.jruby.runtime.Block;
+import org.jruby.runtime.ObjectAllocator;
+import org.jruby.runtime.ThreadContext;
+import org.jruby.runtime.builtin.IRubyObject;
+import org.jruby.util.ByteList;
+
+/**
+ * @author Ola Bini
+ */
+public class X509Extensions {
+ public static void createX509Ext(Ruby runtime, RubyModule mX509) {
+ RubyClass cX509ExtFactory = mX509.defineClassUnder("ExtensionFactory",runtime.getObject(),ExtensionFactory.ALLOCATOR);
+ RubyClass openSSLError = runtime.getModule("OpenSSL").getClass("OpenSSLError");
+ mX509.defineClassUnder("ExtensionError",openSSLError,openSSLError.getAllocator());
+
+ cX509ExtFactory.attr_reader(runtime.getCurrentContext(), new IRubyObject[]{runtime.newString("issuer_certificate"),runtime.newString("subject_certificate"),
+ runtime.newString("subject_request"),runtime.newString("crl"),
+ runtime.newString("config")});
+
+ cX509ExtFactory.defineAnnotatedMethods(ExtensionFactory.class);
+
+ RubyClass cX509Ext = mX509.defineClassUnder("Extension",runtime.getObject(),Extension.ALLOCATOR);
+ cX509Ext.defineAnnotatedMethods(Extension.class);
+ }
+
+ public static class ExtensionFactory extends RubyObject {
+ private static final long serialVersionUID = 3180447029639456500L;
+
+ public static ObjectAllocator ALLOCATOR = new ObjectAllocator() {
+ public IRubyObject allocate(Ruby runtime, RubyClass klass) {
+ return new ExtensionFactory(runtime, klass);
+ }
+ };
+
+ public ExtensionFactory(Ruby runtime, RubyClass type) {
+ super(runtime,type);
+ }
+
+ @JRubyMethod(rest=true,frame=true)
+ public IRubyObject initialize(IRubyObject[] args, Block unusedBlock) {
+ org.jruby.runtime.Arity.checkArgumentCount(getRuntime(),args,0,4);
+ if(args.length > 0 && !args[0].isNil()) {
+ set_issuer_cert(args[0]);
+ }
+ if(args.length > 1 && !args[1].isNil()) {
+ set_subject_cert(args[1]);
+ }
+ if(args.length > 2 && !args[2].isNil()) {
+ set_subject_req(args[2]);
+ }
+ if(args.length > 3 && !args[3].isNil()) {
+ set_crl(args[3]);
+ }
+ return this;
+ }
+
+ @JRubyMethod(name="issuer_certificate=")
+ public IRubyObject set_issuer_cert(IRubyObject arg) {
+ setInstanceVariable("@issuer_certificate",arg);
+ return arg;
+ }
+
+ @JRubyMethod(name="subject_certificate=")
+ public IRubyObject set_subject_cert(IRubyObject arg) {
+ setInstanceVariable("@subject_certificate",arg);
+ return arg;
+ }
+
+ @JRubyMethod(name="subject_request=")
+ public IRubyObject set_subject_req(IRubyObject arg) {
+ setInstanceVariable("@subject_request",arg);
+ return arg;
+ }
+
+ @JRubyMethod(name="crl=")
+ public IRubyObject set_crl(IRubyObject arg) {
+ setInstanceVariable("@crl",arg);
+ return arg;
+ }
+
+ @JRubyMethod(name="config=")
+ public IRubyObject set_config(IRubyObject arg) {
+ setInstanceVariable("@config",arg);
+ return arg;
+ }
+
+ private DERObjectIdentifier getObjectIdentifier(String nameOrOid) {
+ Object val1 = ASN1.getOIDLookup(getRuntime()).get(nameOrOid.toLowerCase());
+ if(null != val1) {
+ return (DERObjectIdentifier)val1;
+ }
+ DERObjectIdentifier val2 = new DERObjectIdentifier(nameOrOid);
+ return val2;
+ }
+
+ private static boolean isHexDigit(char c) {
+ return ('0'<=c && c<='9') || ('A'<= c && c <= 'F') || ('a'<= c && c <= 'f');
+ }
+
+ private boolean isHexString(String str ){
+ for(int i = 0; i< str.length(); i++) {
+ if (!isHexDigit(str.charAt(i))) return false;
+ }
+ return true;
+ }
+
+ @JRubyMethod(rest=true)
+ public IRubyObject create_ext(IRubyObject[] args) {
+ IRubyObject critical = getRuntime().getFalse();
+ if(org.jruby.runtime.Arity.checkArgumentCount(getRuntime(),args,2,3) == 3 && !args[2].isNil()) {
+ critical = args[2];
+ }
+ String oid = args[0].toString();
+ String valuex = args[1].toString();
+ Object value = valuex;
+
+ DERObjectIdentifier r_oid = null;
+
+ try {
+ r_oid = getObjectIdentifier(oid);
+ } catch(IllegalArgumentException e) {
+ r_oid = null;
+ }
+ if (null == r_oid) {
+ throw newX509ExtError(getRuntime(), "unknown OID `" + oid + "'");
+ }
+
+ ThreadContext tc = getRuntime().getCurrentContext();
+ Extension ext = (Extension) Utils.newRubyInstance(getRuntime(), "OpenSSL::X509::Extension");
+
+ if(valuex.startsWith("critical,")) {
+ critical = getRuntime().getTrue();
+ valuex = valuex.substring(9).trim();
+ }
+
+ if(r_oid.equals(new DERObjectIdentifier("2.5.29.14"))) { //subjectKeyIdentifier
+ if("hash".equalsIgnoreCase(valuex)) {
+ IRubyObject pkey = getInstanceVariable("@subject_certificate").callMethod(tc,"public_key");
+ IRubyObject val = null;
+ if(pkey instanceof PKeyRSA) {
+ val = pkey.callMethod(tc,"to_der");
+ } else {
+ val = ASN1.decode(getRuntime().getClassFromPath("OpenSSL::ASN1"), pkey.callMethod(tc, "to_der")).callMethod(tc, "value")
+ .callMethod(tc, "[]", getRuntime().newFixnum(1)).callMethod(tc, "value");
+ }
+ byte[] b = getSHA1Digest(getRuntime(), val.convertToString().getBytes());
+ value = new String(ByteList.plain(new DEROctetString(b).getDEREncoded()));
+ } else if(valuex.length() == 20 || !isHexString(valuex)) {
+ value = new String(ByteList.plain(new DEROctetString(ByteList.plain(valuex)).getDEREncoded()));
+ } else {
+ StringBuffer nstr = new StringBuffer();
+ for(int i = 0; i < valuex.length(); i+=2) {
+ if(i+1 >= valuex.length()) {
+ throw newX509ExtError(getRuntime(), oid + " = " + value + ": odd number of digits");
+ }
+
+ char c1 = valuex.charAt(i);
+ char c2 = valuex.charAt(i+1);
+ if(isHexDigit(c1) && isHexDigit(c2)) {
+ nstr.append(Character.toUpperCase(c1)).append(Character.toUpperCase(c2));
+ } else {
+ throw newX509ExtError(getRuntime(), oid + " = " + value + ": illegal hex digit");
+ }
+ while((i+2) < valuex.length() && valuex.charAt(i+2) == ':') {
+ i++;
+ }
+ }
+ String v = nstr.toString();
+ byte[] arr = new byte[v.length()/2];
+ for(int i=0;i 3 && spl[i].substring(0,3).equalsIgnoreCase("CA:")) {
+ asnv.add(new DERBoolean("TRUE".equalsIgnoreCase(spl[i].substring(3).trim())));
+ }
+ }
+ for(int i=0;i 8 && spl[i].substring(0,8).equalsIgnoreCase("pathlen:")) {
+ asnv.add(new DERInteger(Integer.parseInt(spl[i].substring(8).trim())));
+ }
+ }
+ value = new String(ByteList.plain(new DERSequence(asnv).getDEREncoded()));
+ } else if(r_oid.equals(new DERObjectIdentifier("2.5.29.15"))) { //keyUsage
+ byte[] inp = null;
+ try {
+ String[] exx = valuex.split(":");
+ if(exx != null) {
+ inp = new byte[exx.length];
+ for(int i=0;i-1; i--) {
+ if(inp[i] == 0) {
+ unused += 8;
+ } else {
+ byte a2 = inp[i];
+ int x = 8;
+ while(a2 != 0) {
+ a2 <<= 1;
+ x--;
+ }
+ unused += x;
+ break;
+ }
+ }
+
+ value = new String(ByteList.plain(new DERBitString(inp,unused).getDEREncoded()));
+ } else if(r_oid.equals(new DERObjectIdentifier("2.16.840.1.113730.1.1"))) { //nsCertType
+ byte v = 0;
+ if (valuex.length() < 3) {
+ byte[] inp = ByteList.plain(valuex);
+ v = inp[0];
+ } else {
+ String[] spl = valuex.split(",");
+ for (int i = 0; i < spl.length; i++) {
+ spl[i] = spl[i].trim();
+ }
+ for (int i = 0; i < spl.length; i++) {
+ if ("SSL Client".equals(spl[i]) || "client".equals(spl[i])) {
+ v |= (byte) 128;
+ } else if ("SSL Server".equals(spl[i]) || "server".equals(spl[i])) {
+ v |= (byte) 64;
+ } else if ("S/MIME".equals(spl[i]) || "email".equals(spl[i])) {
+ v |= (byte) 32;
+ } else if ("Object Signing".equals(spl[i]) || "objsign".equals(spl[i])) {
+ v |= (byte) 16;
+ } else if ("Unused".equals(spl[i]) || "reserved".equals(spl[i])) {
+ v |= (byte) 8;
+ } else if ("SSL CA".equals(spl[i]) || "sslCA".equals(spl[i])) {
+ v |= (byte) 4;
+ } else if ("S/MIME CA".equals(spl[i]) || "emailCA".equals(spl[i])) {
+ v |= (byte) 2;
+ } else if ("Object Signing CA".equals(spl[i]) || "objCA".equals(spl[i])) {
+ v |= (byte) 1;
+ } else {
+ throw newX509ExtError(getRuntime(), oid + " = " + valuex + ": unknown bit string argument");
+ }
+ }
+ }
+ int unused = 0;
+ if (v == 0) {
+ unused += 8;
+ } else {
+ byte a2 = v;
+ int x = 8;
+ while (a2 != 0) {
+ a2 <<= 1;
+ x--;
+ }
+ unused += x;
+ }
+ value = new DERBitString(new byte[] { v }, unused);
+ } else if(r_oid.equals(new DERObjectIdentifier("2.5.29.17"))) { //subjectAltName
+ if(valuex.startsWith("DNS:")) {
+ value = new String(ByteList.plain(new GeneralNames(new GeneralName(GeneralName.dNSName,new DERIA5String(valuex.substring(4)))).getDEREncoded()));
+ } else if(valuex.startsWith("IP:")) {
+ String[] numbers = valuex.substring(3).split("\\.");
+ byte[] bs = new byte[4];
+ bs[0] = (byte) (Integer.parseInt(numbers[0]) & 0xff);
+ bs[1] = (byte) (Integer.parseInt(numbers[1]) & 0xff);
+ bs[2] = (byte) (Integer.parseInt(numbers[2]) & 0xff);
+ bs[3] = (byte) (Integer.parseInt(numbers[3]) & 0xff);
+ value = new String(ByteList.plain(new GeneralNames(new GeneralName(GeneralName.iPAddress,new DEROctetString(bs))).getDEREncoded()));
+ } else if(valuex.startsWith("IP Address:")) {
+ String[] numbers = valuex.substring(11).split("\\.");
+ byte[] bs = new byte[4];
+ bs[0] = (byte) (Integer.parseInt(numbers[0]) & 0xff);
+ bs[1] = (byte) (Integer.parseInt(numbers[1]) & 0xff);
+ bs[2] = (byte) (Integer.parseInt(numbers[2]) & 0xff);
+ bs[3] = (byte) (Integer.parseInt(numbers[3]) & 0xff);
+ value = new String(ByteList.plain(new GeneralNames(new GeneralName(GeneralName.iPAddress,new DEROctetString(bs))).getDEREncoded()));
+ }
+ } else if(r_oid.equals(new DERObjectIdentifier("2.5.29.37"))) { //extendedKeyUsage
+ String[] spl = valuex.split(", ?");
+ ASN1EncodableVector vector = new ASN1EncodableVector();
+ for(String name : spl) {
+ vector.add(ASN1Registry.sym2oid(name));
+ }
+ value = new DERSequence(vector);
+ } else {
+ value = new DEROctetString(new DEROctetString(ByteList.plain(valuex)).getDEREncoded());
+ }
+
+ ext.setRealOid(r_oid);
+ ext.setRealValue(value);
+ ext.setRealCritical(critical.isTrue());
+
+ return ext;
+ }
+ }
+
+ private static byte[] getSHA1Digest(Ruby runtime, byte[] bytes) {
+ try {
+ return MessageDigest.getInstance("SHA-1").digest(bytes);
+ } catch (GeneralSecurityException gse) {
+ throw newX509ExtError(runtime, gse.getMessage());
+ }
+ }
+
+ public static class Extension extends RubyObject {
+ private static final long serialVersionUID = -1160318458085651926L;
+
+ public static ObjectAllocator ALLOCATOR = new ObjectAllocator() {
+ public IRubyObject allocate(Ruby runtime, RubyClass klass) {
+ return new Extension(runtime, klass);
+ }
+ };
+
+ public Extension(Ruby runtime, RubyClass type) {
+ super(runtime,type);
+ }
+
+ private DERObjectIdentifier oid;
+ private Object value;
+ private boolean critical;
+
+ void setRealOid(DERObjectIdentifier oid) {
+ this.oid = oid;
+ }
+
+ void setRealValue(Object value) {
+ this.value = value;
+ }
+
+ void setRealCritical(boolean critical) {
+ this.critical = critical;
+ }
+
+ DERObjectIdentifier getRealOid() {
+ return oid;
+ }
+
+ Object getRealValue() {
+ return value;
+ }
+
+ byte[] getRealValueBytes() throws IOException {
+ if((value instanceof RubyString)) {
+ return ((RubyString) value).convertToString().getBytes();
+ } else if(value instanceof String) {
+ return ByteList.plain((String) value);
+ } else if(value instanceof DEROctetString) {
+ return ((DEROctetString)value).getOctets();
+ } else if(value instanceof DEREncodable) {
+ return ((ASN1Encodable)value).getEncoded();
+ } else {
+ return ((ASN1.ASN1Data)value).toASN1().getDEREncoded();
+ }
+ }
+
+ boolean getRealCritical() {
+ return critical;
+ }
+
+ DERObjectIdentifier getObjectIdentifier(String nameOrOid) {
+ Object val1 = ASN1.getOIDLookup(getRuntime()).get(nameOrOid.toLowerCase());
+ if(null != val1) {
+ return (DERObjectIdentifier)val1;
+ }
+ DERObjectIdentifier val2 = new DERObjectIdentifier(nameOrOid);
+ return val2;
+ }
+
+ @JRubyMethod(name = "initialize", rest = true)
+ public IRubyObject _initialize(IRubyObject[] args) {
+ byte[] octets = null;
+ if (args.length == 1) {
+ try {
+ ASN1InputStream is = new ASN1InputStream(OpenSSLImpl.to_der_if_possible(args[0]).convertToString().getBytes());
+ Object obj = is.readObject();
+ ASN1Sequence seq = (ASN1Sequence) obj;
+ setRealOid((DERObjectIdentifier) (seq.getObjectAt(0)));
+ setRealCritical(((DERBoolean) (seq.getObjectAt(1))).isTrue());
+ octets = ((DEROctetString) (seq.getObjectAt(2))).getOctets();
+ } catch (IOException ioe) {
+ throw newX509ExtError(getRuntime(), ioe.getMessage());
+ }
+ } else if (args.length > 1) {
+ setRealOid(getObjectIdentifier(args[0].toString()));
+ setRealValue(args[1]);
+ }
+ if (args.length > 2) {
+ setRealCritical(args[2].isTrue());
+ }
+ if (args.length > 0 && octets != null) {
+ setRealValue(new String(ByteList.plain(octets)));
+ }
+ return this;
+ }
+
+ @JRubyMethod(name="oid=")
+ public IRubyObject set_oid(IRubyObject arg) {
+ System.err.println("WARNING: calling ext#oid=");
+ return getRuntime().getNil();
+ }
+
+ @JRubyMethod(name="value=")
+ public IRubyObject set_value(IRubyObject arg) {
+ System.err.println("WARNING: calling ext#value=");
+ return getRuntime().getNil();
+ }
+
+ @JRubyMethod(name="critical=")
+ public IRubyObject set_critical(IRubyObject arg) {
+ System.err.println("WARNING: calling ext#critical=");
+ return getRuntime().getNil();
+ }
+
+ @JRubyMethod
+ public IRubyObject oid() {
+ Object val = ASN1.getSymLookup(getRuntime()).get(oid);
+ if(null == val) {
+ val = oid.toString();
+ }
+ return getRuntime().newString((String)(val));
+ }
+
+ @JRubyMethod
+ public IRubyObject value() {
+ Ruby runtime = getRuntime();
+ try {
+ if (getRealOid().equals(new DERObjectIdentifier("2.5.29.19"))) { //basicConstraints
+ ASN1Sequence seq2 = (ASN1Sequence) (new ASN1InputStream(getRealValueBytes()).readObject());
+ String c = "";
+ String path = "";
+ if (seq2.size() > 0) {
+ c = "CA:" + (((DERBoolean) (seq2.getObjectAt(0))).isTrue() ? "TRUE" : "FALSE");
+ }
+ if (seq2.size() > 1) {
+ path = ", pathlen:" + seq2.getObjectAt(1).toString();
+ }
+ return runtime.newString(c + path);
+ } else if (getRealOid().equals(new DERObjectIdentifier("2.5.29.15"))) { //keyUsage
+ byte[] bx = getRealValueBytes();
+ byte[] bs = new byte[bx.length - 2];
+ System.arraycopy(bx, 2, bs, 0, bs.length);
+ byte b1 = 0;
+ byte b2 = bs[0];
+ if (bs.length > 1) {
+ b1 = bs[1];
+ }
+ StringBuffer sbe = new StringBuffer();
+ String sep = "";
+ if ((b2 & (byte) 128) != 0) {
+ sbe.append(sep).append("Decipher Only");
+ sep = ", ";
+ }
+ if ((b1 & (byte) 128) != 0) {
+ sbe.append(sep).append("Digital Signature");
+ sep = ", ";
+ }
+ if ((b1 & (byte) 64) != 0) {
+ sbe.append(sep).append("Non Repudiation");
+ sep = ", ";
+ }
+ if ((b1 & (byte) 32) != 0) {
+ sbe.append(sep).append("Key Encipherment");
+ sep = ", ";
+ }
+ if ((b1 & (byte) 16) != 0) {
+ sbe.append(sep).append("Data Encipherment");
+ sep = ", ";
+ }
+ if ((b1 & (byte) 8) != 0) {
+ sbe.append(sep).append("Key Agreement");
+ sep = ", ";
+ }
+ if ((b1 & (byte) 4) != 0) {
+ sbe.append(sep).append("Key Cert Sign");
+ sep = ", ";
+ }
+ if ((b1 & (byte) 2) != 0) {
+ sbe.append(sep).append("cRLSign");
+ sep = ", ";
+ }
+ if ((b1 & (byte) 1) != 0) {
+ sbe.append(sep).append("Encipher Only");
+ }
+ return runtime.newString(sbe.toString());
+ } else if (getRealOid().equals(new DERObjectIdentifier("2.16.840.1.113730.1.1"))) { //nsCertType
+ byte[] bx = getRealValueBytes();
+ byte b = bx[0];
+ StringBuffer sbe = new StringBuffer();
+ String sep = "";
+ if ((b & (byte) 128) != 0) {
+ sbe.append(sep).append("SSL Client");
+ sep = ", ";
+ }
+ if ((b & (byte) 64) != 0) {
+ sbe.append(sep).append("SSL Servern");
+ sep = ", ";
+ }
+ if ((b & (byte) 32) != 0) {
+ sbe.append(sep).append("S/MIME");
+ sep = ", ";
+ }
+ if ((b & (byte) 16) != 0) {
+ sbe.append(sep).append("Object Signing");
+ sep = ", ";
+ }
+ if ((b & (byte) 8) != 0) {
+ sbe.append(sep).append("Unused");
+ sep = ", ";
+ }
+ if ((b & (byte) 4) != 0) {
+ sbe.append(sep).append("SSL CA");
+ sep = ", ";
+ }
+ if ((b & (byte) 2) != 0) {
+ sbe.append(sep).append("S/MIME CA");
+ sep = ", ";
+ }
+ if ((b & (byte) 1) != 0) {
+ sbe.append(sep).append("Object Signing CA");
+ }
+ return runtime.newString(sbe.toString());
+ } else if (getRealOid().equals(new DERObjectIdentifier("2.5.29.14"))) { //subjectKeyIdentifier
+ byte[] b1 = getRealValueBytes();
+ byte[] b2 = new byte[b1.length - 2];
+ System.arraycopy(b1, 2, b2, 0, b2.length);
+ return runtime.newString(Utils.toHex(b2, ':'));
+ } else if (getRealOid().equals(new DERObjectIdentifier("2.5.29.35"))) { // authorityKeyIdentifier
+ DERSequence seq = (DERSequence) (new ASN1InputStream(getRealValueBytes()).readObject());
+ StringBuffer out1 = new StringBuffer();
+ if (seq.size() > 0) {
+ out1.append("keyid:");
+ DERObject keyid = seq.getObjectAt(0).getDERObject();
+ if (keyid instanceof DEROctetString) {
+ out1.append(Utils.toHex(((DEROctetString) keyid).getOctets(), ':'));
+ } else {
+ out1.append(Utils.toHex(keyid.getDEREncoded(), ':'));
+ }
+ }
+ return runtime.newString(out1.toString());
+ } else if (getRealOid().equals(new DERObjectIdentifier("2.5.29.21"))) { // CRLReason
+ switch (RubyNumeric.fix2int(((IRubyObject) value).callMethod(runtime.getCurrentContext(), "value"))) {
+ case 0:
+ return runtime.newString("Unspecified");
+ case 1:
+ return runtime.newString("Key Compromise");
+ case 2:
+ return runtime.newString("CA Compromise");
+ case 3:
+ return runtime.newString("Affiliation Changed");
+ case 4:
+ return runtime.newString("Superseded");
+ case 5:
+ return runtime.newString("Cessation Of Operation");
+ case 6:
+ return runtime.newString("Certificate Hold");
+ case 8:
+ return runtime.newString("Remove From CRL");
+ case 9:
+ return runtime.newString("Privilege Withdrawn");
+ default:
+ return runtime.newString("Unspecified");
+ }
+ } else if (getRealOid().equals(new DERObjectIdentifier("2.5.29.17"))) { //subjectAltName
+ try {
+ DERObject seq = new ASN1InputStream(getRealValueBytes()).readObject();
+ GeneralName[] n1 = null;
+ if (seq instanceof DERUnknownTag) {
+ n1 = new GeneralName[]{GeneralName.getInstance(seq)};
+ } else if (seq instanceof org.bouncycastle.asn1.DERTaggedObject) {
+ n1 = new GeneralName[]{GeneralName.getInstance(seq)};
+ } else {
+ n1 = GeneralNames.getInstance(seq).getNames();
+ }
+ StringBuffer sbe = new StringBuffer();
+ String sep = "";
+ for (int i = 0; i < n1.length; i++) {
+ sbe.append(sep);
+ if (n1[i].getTagNo() == GeneralName.dNSName) {
+ sbe.append("DNS:");
+ sbe.append(((DERString) n1[i].getName()).getString());
+ } else if (n1[i].getTagNo() == GeneralName.iPAddress) {
+ sbe.append("IP Address:");
+ byte[] bs = ((DEROctetString) n1[i].getName()).getOctets();
+ String sep2 = "";
+ for (int j = 0; j < bs.length; j++) {
+ sbe.append(sep2);
+ sbe.append(((int) bs[j]) & 0xff);
+ sep2 = ".";
+ }
+ } else {
+ sbe.append(n1[i].toString());
+ }
+ sep = ", ";
+ }
+ return runtime.newString(sbe.toString());
+ } catch (Exception e) {
+ return runtime.newString(getRealValue().toString());
+ }
+ } else {
+ try {
+ return ASN1.decode(runtime.getClassFromPath("OpenSSL::ASN1"), RubyString.newString(runtime, getRealValueBytes()))
+ .callMethod(runtime.getCurrentContext(), "value").callMethod(runtime.getCurrentContext(), "to_s");
+ } catch (Exception e) {
+ return runtime.newString(getRealValue().toString());
+ }
+ }
+ } catch (IOException ioe) {
+ throw newX509ExtError(runtime, ioe.getMessage());
+ }
+ }
+
+ @JRubyMethod(name="critical?")
+ public IRubyObject critical_p() {
+ return critical ? getRuntime().getTrue() : getRuntime().getFalse();
+ }
+
+ @JRubyMethod
+ public IRubyObject to_der() {
+ ASN1EncodableVector all = new ASN1EncodableVector();
+ try {
+ all.add(getRealOid());
+ all.add(getRealCritical() ? DERBoolean.TRUE : DERBoolean.FALSE);
+ all.add(new DEROctetString(getRealValueBytes()));
+ return RubyString.newString(getRuntime(), new DERSequence(all).getDEREncoded());
+ } catch (IOException ioe) {
+ throw newX509ExtError(getRuntime(), ioe.getMessage());
+ }
+ }
+ }
+
+ private static RaiseException newX509ExtError(Ruby runtime, String message) {
+ return Utils.newError(runtime, "OpenSSL::X509::ExtensionError", message);
+ }
+}// X509Extensions
diff --git a/src/org/jruby/ext/openssl/X509Name.java b/src/org/jruby/ext/openssl/X509Name.java
new file mode 100644
index 00000000000..2b2d30e4e30
--- /dev/null
+++ b/src/org/jruby/ext/openssl/X509Name.java
@@ -0,0 +1,420 @@
+/***** BEGIN LICENSE BLOCK *****
+ * Version: CPL 1.0/GPL 2.0/LGPL 2.1
+ *
+ * The contents of this file are subject to the Common Public
+ * License Version 1.0 (the "License"); you may not use this file
+ * except in compliance with the License. You may obtain a copy of
+ * the License at http://www.eclipse.org/legal/cpl-v10.html
+ *
+ * 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, 2007 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 CPL, 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 CPL, the GPL or the LGPL.
+ ***** END LICENSE BLOCK *****/
+package org.jruby.ext.openssl;
+
+import java.util.ArrayList;
+import java.util.Enumeration;
+import java.util.Iterator;
+import java.util.List;
+import java.util.Map;
+import java.util.Vector;
+
+import org.bouncycastle.asn1.ASN1Encodable;
+import org.bouncycastle.asn1.ASN1EncodableVector;
+import org.bouncycastle.asn1.ASN1InputStream;
+import org.bouncycastle.asn1.ASN1Sequence;
+import org.bouncycastle.asn1.ASN1Set;
+import org.bouncycastle.asn1.DERObject;
+import org.bouncycastle.asn1.DERObjectIdentifier;
+import org.bouncycastle.asn1.DERSequence;
+import org.bouncycastle.asn1.DERSet;
+import org.bouncycastle.asn1.DERString;
+import org.bouncycastle.asn1.DERTags;
+import org.bouncycastle.asn1.x509.X509DefaultEntryConverter;
+import org.jruby.Ruby;
+import org.jruby.RubyArray;
+import org.jruby.RubyClass;
+import org.jruby.RubyFixnum;
+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.ext.openssl.x509store.Name;
+import org.jruby.runtime.Block;
+import org.jruby.runtime.ObjectAllocator;
+import org.jruby.runtime.builtin.IRubyObject;
+
+/**
+ * @author Ola Bini
+ */
+public class X509Name extends RubyObject {
+ private static final long serialVersionUID = -226196051911335103L;
+
+ private static ObjectAllocator X509NAME_ALLOCATOR = new ObjectAllocator() {
+ public IRubyObject allocate(Ruby runtime, RubyClass klass) {
+ return new X509Name(runtime, klass);
+ }
+ };
+
+ public static void createX509Name(Ruby runtime, RubyModule mX509) {
+ RubyClass cX509Name = mX509.defineClassUnder("Name",runtime.getObject(),X509NAME_ALLOCATOR);
+ RubyClass openSSLError = runtime.getModule("OpenSSL").getClass("OpenSSLError");
+ mX509.defineClassUnder("NameError",openSSLError,openSSLError.getAllocator());
+
+ cX509Name.defineAnnotatedMethods(X509Name.class);
+
+ cX509Name.setConstant("COMPAT",runtime.newFixnum(COMPAT));
+ cX509Name.setConstant("RFC2253",runtime.newFixnum(RFC2253));
+ cX509Name.setConstant("ONELINE",runtime.newFixnum(ONELINE));
+ cX509Name.setConstant("MULTILINE",runtime.newFixnum(MULTILINE));
+
+ cX509Name.setConstant("DEFAULT_OBJECT_TYPE",runtime.newFixnum(DERTags.UTF8_STRING));
+
+ RubyHash hash = new RubyHash(runtime, runtime.newFixnum(DERTags.UTF8_STRING));
+ hash.op_aset(runtime.getCurrentContext(), runtime.newString("C"),runtime.newFixnum(DERTags.PRINTABLE_STRING));
+ hash.op_aset(runtime.getCurrentContext(), runtime.newString("countryName"),runtime.newFixnum(DERTags.PRINTABLE_STRING));
+ hash.op_aset(runtime.getCurrentContext(), runtime.newString("serialNumber"),runtime.newFixnum(DERTags.PRINTABLE_STRING));
+ hash.op_aset(runtime.getCurrentContext(), runtime.newString("dnQualifier"),runtime.newFixnum(DERTags.PRINTABLE_STRING));
+ hash.op_aset(runtime.getCurrentContext(), runtime.newString("DC"),runtime.newFixnum(DERTags.IA5_STRING));
+ hash.op_aset(runtime.getCurrentContext(), runtime.newString("domainComponent"),runtime.newFixnum(DERTags.IA5_STRING));
+ hash.op_aset(runtime.getCurrentContext(), runtime.newString("emailAddress"),runtime.newFixnum(DERTags.IA5_STRING));
+ cX509Name.setConstant("OBJECT_TYPE_TEMPLATE", hash);
+ }
+
+ public static final int COMPAT = 0;
+ public static final int RFC2253 = 17892119;
+ public static final int ONELINE = 8520479;
+ public static final int MULTILINE = 44302342;
+
+ public X509Name(Ruby runtime, RubyClass type) {
+ super(runtime,type);
+ oids = new ArrayList