Created
November 24, 2015 04:46
-
-
Save pzb/eef7702530e0b08a804d to your computer and use it in GitHub Desktop.
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' | |
require 'base64' | |
require 'optparse' | |
options = { | |
:outform => "PEM" | |
} | |
OptionParser.new do |opts| | |
opts.on("-n", "--name NAME", "Trust Anchor Name") do |v| | |
options[:name] = v | |
end | |
opts.on("--outform FORM", ["DER", "PEM"], "Select output encoding", " (DER, PEM)") do |v| | |
options[:outform] = v | |
end | |
end.parse! | |
# This takes a certificate as input and outputs trust anchor info | |
# RFC 5914 defines the TrustAnchorInfo structure | |
c = OpenSSL::X509::Certificate.new(File.read(ARGV[0])) | |
key_id = nil | |
eku = nil | |
policy_set = nil | |
policy_flags = nil | |
name_constr = nil | |
pathlen = nil | |
c.extensions.each do |ext| | |
e = OpenSSL::ASN1.decode(ext.to_der).value | |
oid = e.first.oid | |
content = OpenSSL::ASN1.decode(e.last.value) | |
case oid | |
when "2.5.29.14" # id-ce-subjectKeyIdentifier | |
key_id = e.last | |
when "2.5.29.19" # id-ce-basicConstraints | |
bc = content.value | |
if bc.length == 0 || !bc.first.value | |
raise "Only able to create Trust Anchor Info from CA-certificate" | |
end | |
if bc.length == 2 | |
$stderr.puts "Copying path length constraint of #{bc.last.value.to_s}" | |
pathlen = bc.last | |
end | |
when "2.5.29.30" # id-ce-nameConstraints | |
$stderr.puts "Copying name constraints:" | |
$stderr.puts ext.value | |
name_constr = content | |
when "2.5.29.32" # id-ce-certificatePolicies | |
policies = content.value.map{|pi|pi.value.first.oid}.uniq | |
if policies.length > 1 || policies[0] != "2.5.29.32.0" | |
# mulitple policies or a single non-anyPolicy policy | |
$stderr.puts "Adding policies #{policies.join(",")}" | |
policy_infos = policies.map do |p| | |
OpenSSL::ASN1::Sequence.new([OpenSSL::ASN1::ObjectId.new(p)]) | |
end | |
policy_set = OpenSSL::ASN1::Sequence.new(policy_infos) | |
end | |
when "2.5.29.36" # id-ce-policyConstraints | |
raise "PolicyConstraints not yet implemented" | |
when "2.5.29.37" # id-ce-extKeyUsage | |
$stderr.puts "Copying extended key usages" | |
$stderr.puts ext.value | |
eku = ext | |
when "2.5.29.54" # id-ce-inhibitAnyPolicy | |
raise "InhibitAnyPolicy not yet implemented" | |
end | |
end | |
if key_id.nil? | |
raise "Certificate must have subject key identifier" | |
end | |
cert_path_data = [ | |
c.subject.to_der, # taName | |
# Never including certificate (our purpose is to convert cerificate to tai) | |
] | |
if !policy_set.nil? | |
policy_set.tag_class = :CONTEXT_SPECIFIC | |
policy_set.tag = 1 | |
cert_path_data << policy_set | |
end | |
if !name_constr.nil? | |
name_constr.tag_class = :CONTEXT_SPECIFIC | |
name_constr.tag = 3 | |
cert_path_data << name_constr | |
end | |
if !pathlen.nil? | |
pathlen.tag_class = :CONTEXT_SPECIFIC | |
pathlen.tag = 4 | |
cert_path_data << pathlen | |
end | |
cert_path = OpenSSL::ASN1::Sequence.new(cert_path_data) | |
tai_data = [ | |
# version DEFAULT v1 (not encoded), | |
c.public_key.to_der, | |
key_id | |
] | |
if !options[:name].nil? | |
tai_data << OpenSSL::ASN1::UTF8String.new(options[:name]) | |
end | |
tai_data << cert_path | |
if !eku.nil? | |
tai_data << OpenSSL::ASN1::Sequence.new([eku], 1, :EXPLICIT) | |
#tai_data << OpenSSL::ASN1::ASN1Data.new(ext_data, 1, :CONTEXT_SPECIFIC) | |
end | |
tai = OpenSSL::ASN1::Sequence.new(tai_data).to_der | |
if options[:outform] == "PEM" | |
puts "-----BEGIN TRUST ANCHOR-----" | |
puts Base64.encode64(tai) | |
puts "-----END TRUST ANCHOR-----" | |
else | |
print tai | |
end |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment