lib/r509/csr.rb in r509-0.10.0 vs lib/r509/csr.rb in r509-1.0

- old
+ new

@@ -16,11 +16,11 @@ # @option opts [String,OpenSSL::X509::Request] :csr a csr # @option opts [String] :type Required if not providing existing :csr. Defaults to R509::PrivateKey::DEFAULT_TYPE. Allows R509::PrivateKey::KNOWN_TYPES. # @option opts [String] :curve_name ("secp384r1") Only used if :type is EC # @option opts [Integer] :bit_length (2048) Only used if :type is RSA or DSA # @option opts [Integer] :bit_strength (2048) Deprecated, identical to bit_length. - # @option opts [String] :message_digest Optional digest. sha1, sha224, sha256, sha384, sha512, md5. Defaults to sha1 + # @option opts [String] :message_digest Optional digest. sha1, sha224, sha256, sha384, sha512, md5. Defaults to sha256 # @option opts [Array,R509::ASN1::GeneralNames] :san_names List of domains, IPs, email addresses, or URIs to encode as subjectAltNames. The type is determined from the structure of the strings via the R509::ASN1.general_name_parser method. You can also pass an explicit R509::ASN1::GeneralNames object. Parsed names will be uniqued, but a GeneralNames object will not be touched. # @option opts [R509::Subject,Array,OpenSSL::X509::Name] :subject array of subject items # @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) # @example Generate a 4096-bit RSA key + CSR # :type => "RSA", @@ -41,96 +41,93 @@ # :curve_name => 'secp384r1', # :message_digest => 'sha512', # :subject => [ # ['CN','somedomain.com'], # ] - def initialize(opts={}) - if not opts.kind_of?(Hash) + def initialize(opts = {}) + unless opts.is_a?(Hash) raise ArgumentError, 'Must provide a hash of options' end - if opts.has_key?(:subject) and opts.has_key?(:csr) + if opts.key?(:subject) && opts.key?(:csr) raise ArgumentError, "You must provide :subject or :csr, not both" end @bit_length = opts[:bit_length] || opts[:bit_strength] || R509::PrivateKey::DEFAULT_STRENGTH @curve_name = opts[:curve_name] || R509::PrivateKey::DEFAULT_CURVE @key = load_private_key(opts) - @type = opts[:type] || R509::PrivateKey::DEFAULT_TYPE - if not R509::PrivateKey::KNOWN_TYPES.include?(@type.upcase) and @key.nil? + if !R509::PrivateKey::KNOWN_TYPES.include?(@type.upcase) && @key.nil? raise ArgumentError, "Must provide #{R509::PrivateKey::KNOWN_TYPES.join(", ")} as type when key is nil" end - if opts.has_key?(:subject) + if opts.key?(:subject) san_names = R509::ASN1.general_name_parser(opts[:san_names]) - create_request(opts[:subject], san_names) #sets @req - elsif opts.has_key?(:csr) - if opts.has_key?(:san_names) + create_request(opts[:subject], san_names) # sets @req + elsif opts.key?(:csr) + if opts.key?(:san_names) raise ArgumentError, "You can't add domains to an existing CSR" end parse_csr(opts[:csr]) else raise ArgumentError, "You must provide :subject or :csr" end if 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 + # 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 @message_digest = R509::MessageDigest.new(opts[:message_digest]) end - if not opts.has_key?(:csr) + unless opts.key?(:csr) @req.sign(@key.key, @message_digest.digest) end - if not @key.nil? and not @req.verify(@key.public_key) then + if @key && !@req.verify(@key.public_key) raise R509Error, 'Key does not match request.' end - end # Helper method to quickly load a CSR from the filesystem # # @param [String] filename Path to file you want to load # @return [R509::CSR] CSR object - def self.load_from_file( filename ) - return R509::CSR.new(:csr => IOHelpers.read_data(filename) ) + def self.load_from_file(filename) + R509::CSR.new(:csr => IOHelpers.read_data(filename)) end # @return [OpenSSL::PKey::RSA,OpenSSL::PKey::DSA,OpenSSL::PKey::EC] public key def public_key - if(@req.kind_of?(OpenSSL::X509::Request)) then - @req.public_key - end + @req.public_key end # Verifies the integrity of the signature on the request # @return [Boolean] def verify_signature @req.verify(public_key) end # @return [Boolean] Boolean of whether the object contains a private key def has_private_key? - if not @key.nil? + if @key true else false end end - alias :to_s :to_pem + alias_method :to_s, :to_pem # Returns subject component + # @param [String] short_name short name subject component (e.g. CN, O, ST) # # @return [String] value of the subject component requested - def subject_component short_name + def subject_component(short_name) @req.subject.to_a.each do |element| - if element[0].downcase == short_name.downcase then + if element[0].downcase == short_name.downcase return element[1] end end nil end @@ -144,49 +141,49 @@ # Returns key algorithm (RSA/DSA/EC) # # @return [String] value of the key algorithm. RSA, DSA, or EC def key_algorithm - if @req.public_key.kind_of? OpenSSL::PKey::RSA then + if @req.public_key.is_a? OpenSSL::PKey::RSA "RSA" - elsif @req.public_key.kind_of? OpenSSL::PKey::DSA then + elsif @req.public_key.is_a? OpenSSL::PKey::DSA "DSA" - elsif @req.public_key.kind_of? OpenSSL::PKey::EC then + elsif @req.public_key.is_a? OpenSSL::PKey::EC "EC" end end private def parse_csr(csr) begin @req = OpenSSL::X509::Request.new csr rescue OpenSSL::X509::RequestError - #let's try to load this thing by handling a few - #common error cases - if csr.kind_of?(String) - #normalize line endings (really just for the next replace) + # let's try to load this thing by handling a few + # common error cases + if csr.is_a?(String) + # normalize line endings (really just for the next replace) csr.gsub!(/\r\n?/, "\n") - #remove extraneous newlines - csr.gsub!(/^\s*\n/,'') - #and leading/trailing whitespace - csr.gsub!(/^\s*|\s*$/,'') - if not csr.match(/-----BEGIN.+-----/) and csr.match(/MII/) - #if csr is probably PEM (MII is the beginning of every base64 - #encoded DER) then add the wrapping lines if they aren't provided. - #tools like Microsoft's xenroll do this. - csr = "-----BEGIN CERTIFICATE REQUEST-----\n"+csr+"\n-----END CERTIFICATE REQUEST-----" + # remove extraneous newlines + csr.gsub!(/^\s*\n/, '') + # and leading/trailing whitespace + csr.gsub!(/^\s*|\s*$/, '') + if !csr.match(/-----BEGIN.+-----/) && csr.match(/MII/) + # if csr is probably PEM (MII is the beginning of every base64 + # encoded DER) then add the wrapping lines if they aren't provided. + # tools like Microsoft's xenroll do this. + csr = "-----BEGIN CERTIFICATE REQUEST-----\n" + csr + "\n-----END CERTIFICATE REQUEST-----" end end - #and now we try again... + # and now we try again... @req = OpenSSL::X509::Request.new csr end @subject = R509::Subject.new(@req.subject) parse_san_attribute_from_csr(@req) end - def create_request(subject,san_names) + def create_request(subject, san_names) @req = OpenSSL::X509::Request.new @req.version = 0 @subject = R509::Subject.new(subject) @req.subject = @subject.name if @key.nil? @@ -199,34 +196,31 @@ # @return [Array] array of GeneralName objects def parse_san_attribute_from_csr(req) req.attributes.each do |attribute| if attribute.oid == 'extReq' set = OpenSSL::ASN1.decode attribute.value - extensions = set.value[0].value.collect{|asn1ext| OpenSSL::X509::Extension.new(asn1ext) } - r509_extensions = R509::Cert::Extensions.wrap_openssl_extensions( extensions ) - if not r509_extensions[R509::Cert::Extensions::SubjectAlternativeName].nil? + extensions = set.value[0].value.map { |asn1ext| OpenSSL::X509::Extension.new(asn1ext) } + r509_extensions = R509::Cert::Extensions.wrap_openssl_extensions(extensions) + unless r509_extensions[R509::Cert::Extensions::SubjectAlternativeName].nil? @san = r509_extensions[R509::Cert::Extensions::SubjectAlternativeName].general_names end break end end end def add_san_extension(san_names) - if san_names.kind_of?(R509::ASN1::GeneralNames) and not san_names.names.empty? - ef = OpenSSL::X509::ExtensionFactory.new - serialized = san_names.serialize_names - ef.config = OpenSSL::Config.parse(serialized[:conf]) - ex = [] - ex << ef.create_extension("subjectAltName", serialized[:extension_string]) - request_extension_set = OpenSSL::ASN1::Set([OpenSSL::ASN1::Sequence(ex)]) - @req.add_attribute(OpenSSL::X509::Attribute.new("extReq", request_extension_set)) - parse_san_attribute_from_csr(@req) - end + return unless san_names.is_a?(R509::ASN1::GeneralNames) && !san_names.names.empty? + ef = OpenSSL::X509::ExtensionFactory.new + serialized = san_names.serialize_names + ef.config = OpenSSL::Config.parse(serialized[:conf]) + ex = [] + ex << ef.create_extension("subjectAltName", serialized[:extension_string]) + request_extension_set = OpenSSL::ASN1::Set([OpenSSL::ASN1::Sequence(ex)]) + @req.add_attribute(OpenSSL::X509::Attribute.new("extReq", request_extension_set)) + parse_san_attribute_from_csr(@req) end - def internal_obj - @req - end - + # Returns the proper instance variable + alias_method :internal_obj, :req end end