summaryrefslogtreecommitdiff
path: root/lib/hoe/signing.rb
blob: 36f2e0a2b8301efbbb85d91e0c5727b715769fac (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
##
# Signing plugin for hoe.
#
# === Tasks Provided:
#
# generate_key::       Generate a key for signing your gems.
#
# === Extra Configuration Options:
#
# signing_key_file::    Signs your gems with this private key.
# signing_cert_file::   Signs your gem with this certificate.
#
# === Usage:
#
# Run the 'generate_key' task.  This will:
#
# 1. Configure your ~/.hoerc.
# 2. Generate a signing key and certificate.
# 3. Install the private key and public certificate files into ~/.gem.
#
# Hoe will now generate signed gems when the package task is run.  If you have
# multiple machines you build gems on, be sure to install your key and
# certificate on each machine.
#
# Keep your private key secret!  Keep your private key safe!
#
# To make sure your gems are signed run:
#
#   rake package; tar tf pkg/yourproject-1.2.3.gem
#
# If your gem is signed you will see:
#
#   data.tar.gz
#   data.tar.gz.sig
#   metadata.gz
#   metadata.gz.sig

module Hoe::Signing
  Hoe::DEFAULT_CONFIG["signing_key_file"]  = "~/.gem/gem-private_key.pem"
  Hoe::DEFAULT_CONFIG["signing_cert_file"] = "~/.gem/gem-public_cert.pem"

  ##
  # Define tasks for plugin.

  def define_signing_tasks
    set_up_signing

    desc "Generate a key for signing your gems."
    task :generate_key do
      generate_key_task
    end

    desc "Check pubilc key for signing your gems."
    task :check_key do
      check_key_task
    end
  rescue NameError
    warn "Couldn't set up signing (openssl error?). Skipping."
  end

  def set_up_signing # :nodoc:
    signing_key = nil
    cert_chain = []

    with_config do |config, _path|
      break unless config["signing_key_file"] and config["signing_cert_file"]
      key_file = File.expand_path config["signing_key_file"].to_s
      signing_key = key_file if File.exist? key_file

      cert_file = File.expand_path config["signing_cert_file"].to_s
      cert_chain << cert_file if File.exist? cert_file
    end

    if signing_key and cert_chain then
      passphrase = ENV['GEM_PRIVATE_KEY_PASSPHRASE']
      spec.signing_key = OpenSSL::PKey::RSA.new(File.read(signing_key), passphrase)
      spec.cert_chain = cert_chain
    end
  end

  def check_key_task # :nodoc:
    with_config do |config, _path|
      break unless config["signing_cert_file"]
      pub_key = File.expand_path config["signing_cert_file"].to_s

      c = OpenSSL::X509::Certificate.new File.read pub_key
      t = c.not_after

      if t < Time.now then
        warn "Gem signing certificate has expired"
      else
        warn "Gem signing certificate has NOT expired. Carry on."
      end
    end
  end

  def generate_key_task # :nodoc:
    email = Array(spec.email)
    abort "No email in your gemspec" if email.nil? or email.empty?

    key_file = with_config { |config, _| config["signing_key_file"] }
    cert_file = with_config { |config, _| config["signing_cert_file"] }

    if key_file.nil? or cert_file.nil? then
      ENV["SHOW_EDITOR"] ||= "no"
      Rake::Task["config_hoe"].invoke

      key_file = with_config { |config, _| config["signing_key_file"] }
      cert_file = with_config { |config, _| config["signing_cert_file"] }
    end

    key_file = File.expand_path key_file
    cert_file = File.expand_path cert_file

    unless File.exist? key_file then
      puts "Generating certificate"

      if File.exist? key_file then
        abort "Have #{key_file} but no #{cert_file}, aborting as a precaution"
      end

      warn "NOTICE: using #{email.first} for certificate" if email.size > 1

      sh "gem cert --build #{email.first}"
      mv "gem-private_key.pem", key_file, :verbose => true
      mv "gem-public_cert.pem", cert_file, :verbose => true

      puts "Installed key and certificate."
    end

    puts "Key file = #{key_file}"
    puts "Cert file = #{cert_file}"
    puts
    puts "Until rubygems.org has a better strategy for signing, that's"
    puts "the best we can do at this point."
  end
end