lib/r509/spki.rb in r509-0.9.2 vs lib/r509/spki.rb in r509-0.10.0

- old
+ new

@@ -1,13 +1,15 @@ require 'openssl' require 'r509/exceptions' require 'r509/io_helpers' +require 'r509/helpers' module R509 # class for loading/generating SPKAC/SPKI requests (typically generated by the <keygen> tag class SPKI include R509::IOHelpers + include R509::Helpers attr_reader :spki, :key # @option opts [String,OpenSSL::Netscape::SPKI] :spki the spki you want to parse # @option opts [R509::PrivateKey,String] :key optional private key to supply. either an unencrypted PEM/DER string or an R509::PrivateKey object (use the latter if you need password/hardware support). if supplied you do not need to pass an spki. # @option opts [String] :message_digest Optional digest. sha1, sha224, sha256, sha384, sha512, md5. Defaults to sha1. Only used if you supply a :key and no :spki @@ -16,49 +18,17 @@ raise ArgumentError, 'Must provide a hash of options' elsif not opts.has_key?(:spki) and not opts.has_key?(:key) raise ArgumentError, 'Must provide either :spki or :key' end - if opts.has_key?(:key) - if opts[:key].kind_of?(R509::PrivateKey) - @key = opts[:key] - else - @key = R509::PrivateKey.new(:key => opts[:key]) - end - end + @key = load_private_key(opts) + if opts.has_key?(:spki) - spki = opts[:spki] - # first let's try cleaning up the input a bit so OpenSSL is happy with it - # OpenSSL hates SPKAC= - spki.sub!("SPKAC=","") - # it really hates newlines (Firefox loves 'em) - # so let's normalize line endings - spki.gsub!(/\r\n?/, "\n") - # and nuke 'em - spki.gsub!("\n", "") - # ...and leading/trailing whitespace - spki.strip! - @spki = OpenSSL::Netscape::SPKI.new(spki) - if not @key.nil? and not @spki.verify(@key.public_key) then - raise R509Error, 'Key does not match SPKI.' - end - end + @spki = parse_spki(opts[:spki]) + else # create the SPKI from the private key if it wasn't passed in - if @spki.nil? - @spki = OpenSSL::Netscape::SPKI.new - @spki.public_key = @key.public_key - if @key.dsa? - #only DSS1 is acceptable for DSA signing in OpenSSL < 1.0 - #post-1.0 you can sign with anything, but let's be conservative - #see: http://www.ruby-doc.org/stdlib-1.9.3/libdoc/openssl/rdoc/OpenSSL/PKey/DSA.html - message_digest = R509::MessageDigest.new('dss1') - elsif opts.has_key?(:message_digest) - message_digest = R509::MessageDigest.new(opts[:message_digest]) - else - message_digest = R509::MessageDigest.new('sha1') - end - @spki.sign(@key.key,message_digest.digest) + @spki = build_spki(opts[:message_digest]) end end # @return [OpenSSL::PKey::RSA] public key def public_key @@ -69,96 +39,63 @@ # @return [Boolean] def verify_signature @spki.verify(public_key) end - # Converts the SPKI into the PEM format - # - # @return [String] the SPKI converted into PEM format. - def to_pem - @spki.to_pem - end - alias :to_s :to_pem - # Converts the SPKI into the DER format + # Returns the signature algorithm (e.g., RSA-SHA1, ecdsa-with-SHA256) # - # @return [String] the SPKI converted into DER format. - def to_der - @spki.to_der + # @return [String] signature algorithm string + def signature_algorithm + data = OpenSSL::ASN1.decode(self.to_der) + return data.entries[1].value.entries[0].value end - # Writes the SPKI into the PEM format - # - # @param [String, #write] filename_or_io Either a string of the path for - # the file that you'd like to write, or an IO-like object. - def write_pem(filename_or_io) - write_data(filename_or_io, @spki.to_pem) - end + private - # Writes the SPKI into the DER format - # - # @param [String, #write] filename_or_io Either a string of the path for - # the file that you'd like to write, or an IO-like object. - def write_der(filename_or_io) - write_data(filename_or_io, @spki.to_der) - end - - # Returns whether the public key is RSA - # - # @return [Boolean] true if the public key is RSA, false otherwise - def rsa? - @spki.public_key.kind_of?(OpenSSL::PKey::RSA) - end - - # Returns whether the public key is DSA - # - # @return [Boolean] true if the public key is DSA, false otherwise - def dsa? - @spki.public_key.kind_of?(OpenSSL::PKey::DSA) - end - - # Returns whether the public key is EC - # - # @return [Boolean] true if the public key is EC, false otherwise - def ec? - @spki.public_key.kind_of?(OpenSSL::PKey::EC) - end - - # Returns the bit strength of the key used to create the SPKI - # @return [Integer] the integer bit strength. - def bit_strength - if self.rsa? - return @spki.public_key.n.num_bits - elsif self.dsa? - return @spki.public_key.p.num_bits - elsif self.ec? - raise R509::R509Error, 'Bit strength is not available for EC at this time.' + # Tries to clean and parse an inbound SPKI + # @param [String] spki string + # @return [OpenSSL::Netscape::SPKI] spki object + def parse_spki(spki) + # first let's try cleaning up the input a bit so OpenSSL is happy with it + # OpenSSL hates SPKAC= + spki.sub!("SPKAC=","") + # it really hates newlines (Firefox loves 'em) + # so let's normalize line endings + spki.gsub!(/\r\n?/, "\n") + # and nuke 'em + spki.gsub!("\n", "") + # ...and leading/trailing whitespace + spki.strip! + spki = OpenSSL::Netscape::SPKI.new(spki) + if not @key.nil? and not spki.verify(@key.public_key) then + raise R509Error, 'Key does not match SPKI.' end + return spki end - # Returns the short name of the elliptic curve used to generate the public key - # if the key is EC. If not, raises an error. - # - # @return [String] elliptic curve name - def curve_name - if self.ec? - @spki.public_key.group.curve_name + # Tries to build an SPKI using an existing private key + # @param [String] md optional message digest + # @return [OpenSSL::Netscape::SPKI] spki object + def build_spki(md) + spki = OpenSSL::Netscape::SPKI.new + spki.public_key = @key.public_key + if @key.dsa? + #only DSS1 is acceptable for DSA signing in OpenSSL < 1.0 + #post-1.0 you can sign with anything, but let's be conservative + #see: http://www.ruby-doc.org/stdlib-1.9.3/libdoc/openssl/rdoc/OpenSSL/PKey/DSA.html + message_digest = R509::MessageDigest.new('dss1') else - raise R509::R509Error, 'Curve name is only available with EC SPKIs' + message_digest = R509::MessageDigest.new(md) end + spki.sign(@key.key,message_digest.digest) + return spki end - # Returns key algorithm (RSA/DSA) - # - # @return [String] value of the key algorithm. RSA or DSA - def key_algorithm - if self.rsa? - :rsa - elsif self.dsa? - :dsa - elsif self.ec? - :ec - end + # Returns the proper instance variable + def internal_obj + @spki end + end end