From 382c3e1a5942cdd57675dba594f5bc5a7c1e8468 Mon Sep 17 00:00:00 2001 From: AI-Mozi Date: Thu, 30 Nov 2023 10:46:48 -0800 Subject: [PATCH] Add `subject_alt_names` kwarg to `Cert.generate` (#445). --- lib/ronin/support/crypto/cert.rb | 18 ++++++++++++++++++ spec/crypto/cert_spec.rb | 15 +++++++++++++++ 2 files changed, 33 insertions(+) diff --git a/lib/ronin/support/crypto/cert.rb b/lib/ronin/support/crypto/cert.rb index 7cb2faec2..1bfcaf3fe 100644 --- a/lib/ronin/support/crypto/cert.rb +++ b/lib/ronin/support/crypto/cert.rb @@ -19,6 +19,7 @@ require 'ronin/support/crypto/openssl' require 'ronin/support/crypto/key/rsa' require 'ronin/support/crypto/key/ec' +require 'ronin/support/network/ip' module Ronin module Support @@ -279,6 +280,9 @@ def self.load_file(path) # @param [Boolean] ca # Indicates whether to add the basicConstraints extension. # + # @param [Array, nil] subject_alt_names + # List of subject alt names to add into subjectAltName extension. + # # @param [Symbol] signing_hash # The hashing algorithm to use to sign the new certificate. # @@ -356,6 +360,7 @@ def self.generate(version: 2, ca_cert: nil, ca_key: nil, ca: false, + subject_alt_names: nil, signing_hash: :sha256) cert = new @@ -375,6 +380,19 @@ def self.generate(version: 2, else cert.subject end + if subject_alt_names + subject_alt_name = subject_alt_names.map { |alt_name| + if alt_name.match?(Network::IP::REGEX) + "IP:#{alt_name}" + else + "DNS:#{alt_name}" + end + }.join(', ') + + extensions ||= {} + extensions = extensions.merge('subjectAltName' => subject_alt_name) + end + if ca extensions ||= {} extensions = extensions.merge('basicConstraints' => ['CA:TRUE', true]) diff --git a/spec/crypto/cert_spec.rb b/spec/crypto/cert_spec.rb index a695dcef5..c11ed7d76 100644 --- a/spec/crypto/cert_spec.rb +++ b/spec/crypto/cert_spec.rb @@ -494,6 +494,21 @@ expect(subject.extension_names).to match_array(["subjectAltName", "basicConstraints"]) end end + + context "when subject_alt_names kwarg is given" do + subject do + Ronin::Support::Crypto::Cert.generate( + key: rsa_key, + extensions: { 'basicConstraints' => ['CA:TRUE', true] }, + subject_alt_names: ["localhost", "127.0.0.1"] + ) + end + + it "must not override extensions from extensions kwarg" do + expect(subject.extension_names).to match_array(["subjectAltName", "basicConstraints"]) + expect(subject.extension_value("subjectAltName")).to eq("DNS:localhost, IP Address:127.0.0.1") + end + end end it "must default #not_before to Time.now" do