Last active
January 15, 2021 20:18
-
-
Save gamba/b93039e952f0e346b3e0e59430d0ba8d to your computer and use it in GitHub Desktop.
Example of a Ruby script to clone existing x.509 certificates with new keys. Used in forensic lab to test certificate pinning / verification implementation.
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
#!/usr/bin/ruby | |
require 'openssl' | |
out_path = "~/" | |
# Self Signed CA | |
input_ca = "~/ca_certificate.pem" | |
p = parse_input_path input_ca | |
ca = parse_input_certificate input_ca | |
banner ca | |
# Generate a new key of the same type | |
ca_key = ca[:c_algo_class].generate ca[:size], ca[:exponent] | |
# Replace existing key with the new one | |
ca[:cert].public_key = ca_key.public_key | |
# Self Sign the certificate | |
ca[:cert].sign ca_key, ca[:s_algo_class].new | |
#File.write("#{out_path}/#{name}.public_key.pem", new_key.public_key.to_pem) | |
File.write("#{out_path}/#{p[:name]}.private_key.pem", ca_key.to_pem) | |
File.write("#{out_path}/#{p[:name]}.certificate.pem", ca[:cert].to_s) | |
# CA Signed Leaf Certificate | |
input_cert = "~/leaf_certificate.pem" | |
p = parse_input_path input_cert | |
cert = parse_input_certificate input_cert | |
banner cert | |
# Generate a new key of the same type | |
cert_key = cert[:c_algo_class].generate cert[:size], cert[:exponent] | |
# Replace existing key with the new one | |
cert[:cert].public_key = cert_key.public_key | |
# Sign the certificate with CA private key | |
cert[:cert].sign ca_key, cert[:s_algo_class].new | |
File.write("#{out_path}/#{p[:name]}.private_key.pem", cert_key.to_pem) | |
File.write("#{out_path}/#{p[:name]}.certificate.pem", cert[:cert].to_s) | |
# ------------------------------------------------------------------------------------------------ | |
BEGIN { | |
def parse_input_path(arg) | |
ext = arg.split(".").last | |
name = arg.split("/").last.gsub(".#{ext}","") | |
path = arg.gsub( arg.split("/").last, "") | |
{ | |
name: name, | |
path: path, | |
ext: ext | |
} | |
end | |
def parse_input_certificate(arg) | |
cert = OpenSSL::X509::Certificate.new File.read(arg) | |
algo = cert.signature_algorithm | |
exponent = cert.public_key.e.to_i | |
size = cert.public_key.n.num_bytes * 8 | |
s_algo = algo.split("With").first.upcase | |
c_algo = algo.split("With").last.split("Encryption").first.upcase | |
# https://ruby-doc.org/stdlib-2.4.0/libdoc/openssl/rdoc/OpenSSL/PKey.html | |
# EC, RSA, DSA, DH | |
c_algo_class = Object.const_get "OpenSSL::PKey::#{c_algo}" | |
# https://ruby-doc.org/stdlib-2.4.0/libdoc/openssl/rdoc/OpenSSL/Digest.html | |
# MD2, MD4, MD5, SHA, SHA1, SHA224, SHA256, SHA384, SHA512 | |
s_algo_class = Object.const_get "OpenSSL::Digest::#{s_algo}" | |
{ | |
c_algo: c_algo, | |
c_algo_class: c_algo_class, | |
s_algo: s_algo, | |
s_algo_class: s_algo_class, | |
exponent: exponent, | |
size: size, | |
subject: cert.subject.to_s, | |
cert: cert | |
} | |
end | |
def banner(c) | |
puts "Cert: #{c[:subject]}" | |
puts "Algo: #{c[:c_algo]} #{c[:s_algo]} #{c[:size]} #{c[:exponent]}" | |
puts "" | |
end | |
} |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment