Skip to content

Instantly share code, notes, and snippets.

@espresso3389
Created December 10, 2024 18:42
Show Gist options
  • Save espresso3389/392be0335b376a9ce79a5bdc7f5ec3f7 to your computer and use it in GitHub Desktop.
Save espresso3389/392be0335b376a9ce79a5bdc7f5ec3f7 to your computer and use it in GitHub Desktop.
Find a signing certificate of serial number from keychain
#!/usr/bin/env ruby
require 'openssl'
def print_usage
puts "Usage: #{File.basename($0)} [-s|-S|-C component] <serial_number> [keychain]"
puts "Options:"
puts " -s Show only certificate subject (single line)"
puts " -S Show only certificate subject (one parameter per line)"
puts " -C component Show only specified component (e.g., -C CN for Common Name)"
exit 1
end
# Parse command line options
show_subject_inline = ARGV.delete("-s")
show_subject_multiline = ARGV.delete("-S")
component_index = ARGV.index("-C")
component_name = nil
if component_index
ARGV.delete_at(component_index)
component_name = ARGV.delete_at(component_index)
print_usage unless component_name
end
# Check remaining arguments
print_usage if ARGV.empty? || ARGV.length > 2
print_usage if [show_subject_inline, show_subject_multiline, component_name].count { |x| x } > 1
# Extract command line arguments
target_serial = ARGV[0].upcase
keychain = ARGV[1]
# Get certificates using security command
cmd = ["security", "find-certificate", "-p", "-a"]
cmd << keychain if keychain
certs_data = `#{cmd.map { |arg| arg.include?(' ') ? %Q("#{arg}") : arg }.join(' ')}`
if $?.success?
# Split the output into individual certificates
certs = certs_data.split(/(?=-----BEGIN CERTIFICATE-----\n)/)
# Search through each certificate
found = false
certs.each do |cert_str|
begin
cert = OpenSSL::X509::Certificate.new(cert_str)
serial = cert.serial.to_s(16).upcase
if serial == target_serial
if show_subject_inline
puts cert.subject.to_s.sub(/^\//, '').gsub('/', ', ')
elsif show_subject_multiline
cert.subject.to_s.sub(/^\//, '').split('/').each do |param|
puts param
end
elsif component_name
subject_hash = cert.subject.to_s.sub(/^\//, '').split('/').map { |pair|
k, v = pair.split('=', 2)
[k, v]
}.to_h
if value = subject_hash[component_name]
puts value
else
exit 1
end
else
puts cert_str
end
found = true
break
end
rescue OpenSSL::X509::CertificateError
# Skip invalid certificates
next
end
end
exit(1) unless found
else
puts "Error: Failed to get certificates"
exit 1
end
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment