Created
December 10, 2024 18:42
-
-
Save espresso3389/392be0335b376a9ce79a5bdc7f5ec3f7 to your computer and use it in GitHub Desktop.
Find a signing certificate of serial number from keychain
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/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