Last active
November 24, 2015 21:50
-
-
Save tasuten/9324404aaa0c9862affb to your computer and use it in GitHub Desktop.
Arranged https://github.com/ius/rsatool, written by Ruby
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 | |
# encoding : utf-8 | |
# LICENSE: Public Domain | |
# all of this libraries are Ruby's standard library | |
# no gem required | |
require 'base64' | |
require 'openssl' | |
require 'optparse' | |
# class definition | |
class RSAKeyGenerator | |
attr_reader :p, :q, :n, :d, :e | |
# e is hard-coded as 65537 in OpenSSL | |
def initialize(p: nil, q: nil, e: 65537) | |
@p = p | |
@q = q | |
@e = e | |
_calc | |
end | |
def to_pem(type = :private) | |
if type == :private | |
private_key = Base64.encode64(to_der(:private)) | |
return \ | |
"-----BEGIN RSA PRIVATE KEY-----\n" \ | |
"#{private_key}" \ | |
"-----END RSA PRIVATE KEY-----\n" | |
elsif type == :public | |
public_key = Base64.encode64(to_der(:public)) | |
return \ | |
"-----BEGIN PUBLIC KEY-----\n" \ | |
"#{public_key}" \ | |
"-----END PUBLIC KEY-----\n" | |
else | |
fail 'key type is :private or :public' | |
end | |
end | |
def to_der(type = :private) | |
seq = nil | |
if type == :private | |
# PKCS#1 RSAPrivateKey Format | |
seq = OpenSSL::ASN1::Sequence.new([ | |
OpenSSL::ASN1::Integer.new(0), | |
OpenSSL::ASN1::Integer.new(@n), | |
OpenSSL::ASN1::Integer.new(@e), | |
OpenSSL::ASN1::Integer.new(@d), | |
OpenSSL::ASN1::Integer.new(@p), | |
OpenSSL::ASN1::Integer.new(@q), | |
OpenSSL::ASN1::Integer.new(@d_mod_p_1), | |
OpenSSL::ASN1::Integer.new(@d_mod_q_1), | |
OpenSSL::ASN1::Integer.new(@q_inv) | |
]) | |
elsif type == :public | |
# OpenSSL uses X.509 SubjectPublicKeyInfo format | |
# for public key | |
# ref: https://www.openssl.org/docs/manmaster/apps/rsa.html | |
values = OpenSSL::ASN1::Sequence.new([ | |
OpenSSL::ASN1::Integer.new(@n), | |
OpenSSL::ASN1::Integer.new(@e) | |
]) | |
seq = OpenSSL::ASN1::Sequence.new([ | |
OpenSSL::ASN1::Sequence.new([ | |
OpenSSL::ASN1::ObjectId.new('rsaEncryption'), | |
OpenSSL::ASN1::Null.new(nil) | |
]), | |
OpenSSL::ASN1::BitString.new(values.to_der) | |
]) | |
else | |
fail 'key type is :private or :public' | |
end | |
seq.to_der | |
end | |
# under this line, all defined method is private | |
def _calc | |
@n = @p * @q | |
phi = (@p - 1) * (@q - 1) | |
fail 'e must be s.t. gcd(e, phi(n)) = 1' unless @e.gcd(phi) == 1 | |
@d = @e.mod_inverse(phi) | |
@d_mod_p_1 = @d % (@p - 1) | |
@d_mod_q_1 = @d % (@q - 1) | |
@q_inv = @q.mod_inverse(@p) | |
end | |
private :_calc | |
end | |
class Integer | |
# return r, such like (self * r) % m == 1 | |
def mod_inverse(m) | |
bn_self = OpenSSL::BN.new(self.to_s) | |
bn_m = OpenSSL::BN.new(m.to_s) | |
bn_r = OpenSSL::BN.new(bn_self).mod_inverse(bn_m) | |
bn_r.to_i | |
end | |
end | |
# entry point | |
# when this file is runned from command-line | |
if __FILE__ == $PROGRAM_NAME | |
# default values | |
args = { e: 65537, type: :private, format: :pem } | |
OptionParser.new do |options| | |
options.banner = "#{__FILE__}: Print OpenSSL RSA key from prime numbers\n" | |
options.banner += "Usage: #{__FILE__} -p prime1 -q prime2\n" | |
options.banner += " [-e exponent] [-t pubic|private] [-f pem|der]\n" | |
options.on('-p prime', /(?:0b|0o|0d|0x)?[1-9a-fA-F]+[0-9a-fA-F]*/, | |
'first prime numer') { |p| args[:p] = p.to_i(0) } | |
options.on('-q prime', /(?:0b|0o|0d|0x)?[1-9a-fA-F]+[0-9a-fA-F]*/, | |
'second prime number') { |q| args[:q] = q.to_i(0) } | |
options.on('-e positive_integer', | |
/(?:0b|0o|0d|0x)?[1-9a-fA-F]+[0-9a-fA-F]*/, | |
'exponent (default: 65537)') { |e| args[:e] = e.to_i(0) } | |
options.on('-t public|private', [:public, private], | |
'key type (default: private)') { |t| args[:type] = t } | |
options.on('-f pem|der', [:pem, :der], | |
'key format (default: pem)') { |f| args[:format] = f } | |
options.parse!(ARGV) | |
end | |
unless args.key?(:p) && args.key?(:q) | |
warn 'p and q are required.' | |
warn "see #{__FILE__} -h" | |
exit false | |
end | |
keys = RSAKeyGenerator.new(p: args[:p], q: args[:q], e: args[:e]) | |
output = '' | |
if args[:format] == :pem | |
output = keys.to_pem(args[:type]) | |
elsif args[:format] == :der | |
output = keys.to_der(args[:type]) | |
end | |
puts output | |
end |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
参考