1#-- 2# Copyright 2006 by Chad Fowler, Rich Kilmer, Jim Weirich and others. 3# All rights reserved. 4# See LICENSE.txt for permissions. 5#++ 6 7require 'rubygems/exceptions' 8require 'fileutils' 9 10begin 11 require 'openssl' 12rescue LoadError => e 13 raise unless (e.respond_to?(:path) && e.path == 'openssl') || 14 e.message =~ / -- openssl$/ 15 16 module OpenSSL # :nodoc: 17 class Digest # :nodoc: 18 class SHA1 # :nodoc: 19 def name 20 'SHA1' 21 end 22 end 23 end 24 module PKey # :nodoc: 25 class RSA # :nodoc: 26 end 27 end 28 end 29end 30 31## 32# = Signing gems 33# 34# The Gem::Security implements cryptographic signatures for gems. The section 35# below is a step-by-step guide to using signed gems and generating your own. 36# 37# == Walkthrough 38# 39# === Building your certificate 40# 41# In order to start signing your gems, you'll need to build a private key and 42# a self-signed certificate. Here's how: 43# 44# # build a private key and certificate for yourself: 45# $ gem cert --build you@example.com 46# 47# This could take anywhere from a few seconds to a minute or two, depending on 48# the speed of your computer (public key algorithms aren't exactly the 49# speediest crypto algorithms in the world). When it's finished, you'll see 50# the files "gem-private_key.pem" and "gem-public_cert.pem" in the current 51# directory. 52# 53# First things first: Move both files to ~/.gem if you don't already have a 54# key and certificate in that directory. Ensure the file permissions make the 55# key unreadable by others (by default the file is saved securely). 56# 57# Keep your private key hidden; if it's compromised, someone can sign packages 58# as you (note: PKI has ways of mitigating the risk of stolen keys; more on 59# that later). 60# 61# === Signing Gems 62# 63# In RubyGems 2 and newer there is no extra work to sign a gem. RubyGems will 64# automatically find your key and certificate in your home directory and use 65# them to sign newly packaged gems. 66# 67# If your certificate is not self-signed (signed by a third party) RubyGems 68# will attempt to load the certificate chain from the trusted certificates. 69# Use <code>gem cert --add signing_cert.pem</code> to add your signers as 70# trusted certificates. See below for further information on certificate 71# chains. 72# 73# If you build your gem it will automatically be signed. If you peek inside 74# your gem file, you'll see a couple of new files have been added: 75# 76# $ tar tf your-gem-1.0.gem 77# metadata.gz 78# metadata.gz.sum 79# metadata.gz.sig # metadata signature 80# data.tar.gz 81# data.tar.gz.sum 82# data.tar.gz.sig # data signature 83# 84# === Manually signing gems 85# 86# If you wish to store your key in a separate secure location you'll need to 87# set your gems up for signing by hand. To do this, set the 88# <code>signing_key</code> and <code>cert_chain</code> in the gemspec before 89# packaging your gem: 90# 91# s.signing_key = '/secure/path/to/gem-private_key.pem' 92# s.cert_chain = %w[/secure/path/to/gem-public_cert.pem] 93# 94# When you package your gem with these options set RubyGems will automatically 95# load your key and certificate from the secure paths. 96# 97# === Signed gems and security policies 98# 99# Now let's verify the signature. Go ahead and install the gem, but add the 100# following options: <code>-P HighSecurity</code>, like this: 101# 102# # install the gem with using the security policy "HighSecurity" 103# $ sudo gem install your.gem -P HighSecurity 104# 105# The <code>-P</code> option sets your security policy -- we'll talk about 106# that in just a minute. Eh, what's this? 107# 108# $ gem install -P HighSecurity your-gem-1.0.gem 109# ERROR: While executing gem ... (Gem::Security::Exception) 110# root cert /CN=you/DC=example is not trusted 111# 112# The culprit here is the security policy. RubyGems has several different 113# security policies. Let's take a short break and go over the security 114# policies. Here's a list of the available security policies, and a brief 115# description of each one: 116# 117# * NoSecurity - Well, no security at all. Signed packages are treated like 118# unsigned packages. 119# * LowSecurity - Pretty much no security. If a package is signed then 120# RubyGems will make sure the signature matches the signing 121# certificate, and that the signing certificate hasn't expired, but 122# that's it. A malicious user could easily circumvent this kind of 123# security. 124# * MediumSecurity - Better than LowSecurity and NoSecurity, but still 125# fallible. Package contents are verified against the signing 126# certificate, and the signing certificate is checked for validity, 127# and checked against the rest of the certificate chain (if you don't 128# know what a certificate chain is, stay tuned, we'll get to that). 129# The biggest improvement over LowSecurity is that MediumSecurity 130# won't install packages that are signed by untrusted sources. 131# Unfortunately, MediumSecurity still isn't totally secure -- a 132# malicious user can still unpack the gem, strip the signatures, and 133# distribute the gem unsigned. 134# * HighSecurity - Here's the bugger that got us into this mess. 135# The HighSecurity policy is identical to the MediumSecurity policy, 136# except that it does not allow unsigned gems. A malicious user 137# doesn't have a whole lot of options here; he can't modify the 138# package contents without invalidating the signature, and he can't 139# modify or remove signature or the signing certificate chain, or 140# RubyGems will simply refuse to install the package. Oh well, maybe 141# he'll have better luck causing problems for CPAN users instead :). 142# 143# The reason RubyGems refused to install your shiny new signed gem was because 144# it was from an untrusted source. Well, your code is infallible (naturally), 145# so you need to add yourself as a trusted source: 146# 147# # add trusted certificate 148# gem cert --add ~/.gem/gem-public_cert.pem 149# 150# You've now added your public certificate as a trusted source. Now you can 151# install packages signed by your private key without any hassle. Let's try 152# the install command above again: 153# 154# # install the gem with using the HighSecurity policy (and this time 155# # without any shenanigans) 156# $ gem install -P HighSecurity your-gem-1.0.gem 157# Successfully installed your-gem-1.0 158# 1 gem installed 159# 160# This time RubyGems will accept your signed package and begin installing. 161# 162# While you're waiting for RubyGems to work it's magic, have a look at some of 163# the other security commands by running <code>gem help cert</code>: 164# 165# Options: 166# -a, --add CERT Add a trusted certificate. 167# -l, --list [FILTER] List trusted certificates where the 168# subject contains FILTER 169# -r, --remove FILTER Remove trusted certificates where the 170# subject contains FILTER 171# -b, --build EMAIL_ADDR Build private key and self-signed 172# certificate for EMAIL_ADDR 173# -C, --certificate CERT Signing certificate for --sign 174# -K, --private-key KEY Key for --sign or --build 175# -s, --sign CERT Signs CERT with the key from -K 176# and the certificate from -C 177# 178# We've already covered the <code>--build</code> option, and the 179# <code>--add</code>, <code>--list</code>, and <code>--remove</code> commands 180# seem fairly straightforward; they allow you to add, list, and remove the 181# certificates in your trusted certificate list. But what's with this 182# <code>--sign</code> option? 183# 184# === Certificate chains 185# 186# To answer that question, let's take a look at "certificate chains", a 187# concept I mentioned earlier. There are a couple of problems with 188# self-signed certificates: first of all, self-signed certificates don't offer 189# a whole lot of security. Sure, the certificate says Yukihiro Matsumoto, but 190# how do I know it was actually generated and signed by matz himself unless he 191# gave me the certificate in person? 192# 193# The second problem is scalability. Sure, if there are 50 gem authors, then 194# I have 50 trusted certificates, no problem. What if there are 500 gem 195# authors? 1000? Having to constantly add new trusted certificates is a 196# pain, and it actually makes the trust system less secure by encouraging 197# RubyGems users to blindly trust new certificates. 198# 199# Here's where certificate chains come in. A certificate chain establishes an 200# arbitrarily long chain of trust between an issuing certificate and a child 201# certificate. So instead of trusting certificates on a per-developer basis, 202# we use the PKI concept of certificate chains to build a logical hierarchy of 203# trust. Here's a hypothetical example of a trust hierarchy based (roughly) 204# on geography: 205# 206# -------------------------- 207# | rubygems@rubygems.org | 208# -------------------------- 209# | 210# ----------------------------------- 211# | | 212# ---------------------------- ----------------------------- 213# | seattlerb@seattlerb.org | | dcrubyists@richkilmer.com | 214# ---------------------------- ----------------------------- 215# | | | | 216# --------------- ---------------- ----------- -------------- 217# | drbrain | | zenspider | | pabs@dc | | tomcope@dc | 218# --------------- ---------------- ----------- -------------- 219# 220# 221# Now, rather than having 4 trusted certificates (one for drbrain, zenspider, 222# pabs@dc, and tomecope@dc), a user could actually get by with one 223# certificate, the "rubygems@rubygems.org" certificate. 224# 225# Here's how it works: 226# 227# I install "rdoc-3.12.gem", a package signed by "drbrain". I've never heard 228# of "drbrain", but his certificate has a valid signature from the 229# "seattle.rb@seattlerb.org" certificate, which in turn has a valid signature 230# from the "rubygems@rubygems.org" certificate. Voila! At this point, it's 231# much more reasonable for me to trust a package signed by "drbrain", because 232# I can establish a chain to "rubygems@rubygems.org", which I do trust. 233# 234# === Signing certificates 235# 236# The <code>--sign</code> option allows all this to happen. A developer 237# creates their build certificate with the <code>--build</code> option, then 238# has their certificate signed by taking it with them to their next regional 239# Ruby meetup (in our hypothetical example), and it's signed there by the 240# person holding the regional RubyGems signing certificate, which is signed at 241# the next RubyConf by the holder of the top-level RubyGems certificate. At 242# each point the issuer runs the same command: 243# 244# # sign a certificate with the specified key and certificate 245# # (note that this modifies client_cert.pem!) 246# $ gem cert -K /mnt/floppy/issuer-priv_key.pem -C issuer-pub_cert.pem 247# --sign client_cert.pem 248# 249# Then the holder of issued certificate (in this case, your buddy "drbrain"), 250# can start using this signed certificate to sign RubyGems. By the way, in 251# order to let everyone else know about his new fancy signed certificate, 252# "drbrain" would save his newly signed certificate as 253# <code>~/.gem/gem-public_cert.pem</code> 254# 255# Obviously this RubyGems trust infrastructure doesn't exist yet. Also, in 256# the "real world", issuers actually generate the child certificate from a 257# certificate request, rather than sign an existing certificate. And our 258# hypothetical infrastructure is missing a certificate revocation system. 259# These are that can be fixed in the future... 260# 261# At this point you should know how to do all of these new and interesting 262# things: 263# 264# * build a gem signing key and certificate 265# * adjust your security policy 266# * modify your trusted certificate list 267# * sign a certificate 268# 269# == Manually verifying signatures 270# 271# In case you don't trust RubyGems you can verify gem signatures manually: 272# 273# 1. Fetch and unpack the gem 274# 275# gem fetch some_signed_gem 276# tar -xf some_signed_gem-1.0.gem 277# 278# 2. Grab the public key from the gemspec 279# 280# gem spec some_signed_gem-1.0.gem cert_chain | \ 281# ruby -ryaml -e 'puts YAML.load_documents($stdin)' > public_key.crt 282# 283# 3. Generate a SHA1 hash of the data.tar.gz 284# 285# openssl dgst -sha1 < data.tar.gz > my.hash 286# 287# 4. Verify the signature 288# 289# openssl rsautl -verify -inkey public_key.crt -certin \ 290# -in data.tar.gz.sig > verified.hash 291# 292# 5. Compare your hash to the verified hash 293# 294# diff -s verified.hash my.hash 295# 296# 6. Repeat 5 and 6 with metadata.gz 297# 298# == OpenSSL Reference 299# 300# The .pem files generated by --build and --sign are PEM files. Here's a 301# couple of useful OpenSSL commands for manipulating them: 302# 303# # convert a PEM format X509 certificate into DER format: 304# # (note: Windows .cer files are X509 certificates in DER format) 305# $ openssl x509 -in input.pem -outform der -out output.der 306# 307# # print out the certificate in a human-readable format: 308# $ openssl x509 -in input.pem -noout -text 309# 310# And you can do the same thing with the private key file as well: 311# 312# # convert a PEM format RSA key into DER format: 313# $ openssl rsa -in input_key.pem -outform der -out output_key.der 314# 315# # print out the key in a human readable format: 316# $ openssl rsa -in input_key.pem -noout -text 317# 318# == Bugs/TODO 319# 320# * There's no way to define a system-wide trust list. 321# * custom security policies (from a YAML file, etc) 322# * Simple method to generate a signed certificate request 323# * Support for OCSP, SCVP, CRLs, or some other form of cert status check 324# (list is in order of preference) 325# * Support for encrypted private keys 326# * Some sort of semi-formal trust hierarchy (see long-winded explanation 327# above) 328# * Path discovery (for gem certificate chains that don't have a self-signed 329# root) -- by the way, since we don't have this, THE ROOT OF THE CERTIFICATE 330# CHAIN MUST BE SELF SIGNED if Policy#verify_root is true (and it is for the 331# MediumSecurity and HighSecurity policies) 332# * Better explanation of X509 naming (ie, we don't have to use email 333# addresses) 334# * Honor AIA field (see note about OCSP above) 335# * Honor extension restrictions 336# * Might be better to store the certificate chain as a PKCS#7 or PKCS#12 337# file, instead of an array embedded in the metadata. 338# * Flexible signature and key algorithms, not hard-coded to RSA and SHA1. 339# 340# == Original author 341# 342# Paul Duncan <pabs@pablotron.org> 343# http://pablotron.org/ 344 345module Gem::Security 346 347 ## 348 # Gem::Security default exception type 349 350 class Exception < Gem::Exception; end 351 352 ## 353 # Digest algorithm used to sign gems 354 355 DIGEST_ALGORITHM = OpenSSL::Digest::SHA1 356 357 ## 358 # Used internally to select the signing digest from all computed digests 359 360 DIGEST_NAME = DIGEST_ALGORITHM.new.name # :nodoc: 361 362 ## 363 # Algorithm for creating the key pair used to sign gems 364 365 KEY_ALGORITHM = OpenSSL::PKey::RSA 366 367 ## 368 # Length of keys created by KEY_ALGORITHM 369 370 KEY_LENGTH = 2048 371 372 ## 373 # One year in seconds 374 375 ONE_YEAR = 86400 * 365 376 377 ## 378 # The default set of extensions are: 379 # 380 # * The certificate is not a certificate authority 381 # * The key for the certificate may be used for key and data encipherment 382 # and digital signatures 383 # * The certificate contains a subject key identifier 384 385 EXTENSIONS = { 386 'basicConstraints' => 'CA:FALSE', 387 'keyUsage' => 388 'keyEncipherment,dataEncipherment,digitalSignature', 389 'subjectKeyIdentifier' => 'hash', 390 } 391 392 def self.alt_name_or_x509_entry certificate, x509_entry 393 alt_name = certificate.extensions.find do |extension| 394 extension.oid == "#{x509_entry}AltName" 395 end 396 397 return alt_name.value if alt_name 398 399 certificate.send x509_entry 400 end 401 402 ## 403 # Creates an unsigned certificate for +subject+ and +key+. The lifetime of 404 # the key is from the current time to +age+ which defaults to one year. 405 # 406 # The +extensions+ restrict the key to the indicated uses. 407 408 def self.create_cert subject, key, age = ONE_YEAR, extensions = EXTENSIONS, 409 serial = 1 410 cert = OpenSSL::X509::Certificate.new 411 412 cert.public_key = key.public_key 413 cert.version = 2 414 cert.serial = serial 415 416 cert.not_before = Time.now 417 cert.not_after = Time.now + age 418 419 cert.subject = subject 420 421 ef = OpenSSL::X509::ExtensionFactory.new nil, cert 422 423 cert.extensions = extensions.map do |ext_name, value| 424 ef.create_extension ext_name, value 425 end 426 427 cert 428 end 429 430 ## 431 # Creates a self-signed certificate with an issuer and subject from +email+, 432 # a subject alternative name of +email+ and the given +extensions+ for the 433 # +key+. 434 435 def self.create_cert_email email, key, age = ONE_YEAR, extensions = EXTENSIONS 436 subject = email_to_name email 437 438 extensions = extensions.merge "subjectAltName" => "email:#{email}" 439 440 create_cert_self_signed subject, key, age, extensions 441 end 442 443 ## 444 # Creates a self-signed certificate with an issuer and subject of +subject+ 445 # and the given +extensions+ for the +key+. 446 447 def self.create_cert_self_signed subject, key, age = ONE_YEAR, 448 extensions = EXTENSIONS, serial = 1 449 certificate = create_cert subject, key, age, extensions 450 451 sign certificate, key, certificate, age, extensions, serial 452 end 453 454 ## 455 # Creates a new key pair of the specified +length+ and +algorithm+. The 456 # default is a 2048 bit RSA key. 457 458 def self.create_key length = KEY_LENGTH, algorithm = KEY_ALGORITHM 459 algorithm.new length 460 end 461 462 ## 463 # Turns +email_address+ into an OpenSSL::X509::Name 464 465 def self.email_to_name email_address 466 email_address = email_address.gsub(/[^\w@.-]+/i, '_') 467 468 cn, dcs = email_address.split '@' 469 470 dcs = dcs.split '.' 471 472 name = "CN=#{cn}/#{dcs.map { |dc| "DC=#{dc}" }.join '/'}" 473 474 OpenSSL::X509::Name.parse name 475 end 476 477 ## 478 # Signs +expired_certificate+ with +private_key+ if the keys match and the 479 # expired certificate was self-signed. 480 #-- 481 # TODO increment serial 482 483 def self.re_sign expired_certificate, private_key, age = ONE_YEAR, 484 extensions = EXTENSIONS 485 raise Gem::Security::Exception, 486 "incorrect signing key for re-signing " + 487 "#{expired_certificate.subject}" unless 488 expired_certificate.public_key.to_pem == private_key.public_key.to_pem 489 490 unless expired_certificate.subject.to_s == 491 expired_certificate.issuer.to_s then 492 subject = alt_name_or_x509_entry expired_certificate, :subject 493 issuer = alt_name_or_x509_entry expired_certificate, :issuer 494 495 raise Gem::Security::Exception, 496 "#{subject} is not self-signed, contact #{issuer} " + 497 "to obtain a valid certificate" 498 end 499 500 serial = expired_certificate.serial + 1 501 502 create_cert_self_signed(expired_certificate.subject, private_key, age, 503 extensions, serial) 504 end 505 506 ## 507 # Resets the trust directory for verifying gems. 508 509 def self.reset 510 @trust_dir = nil 511 end 512 513 ## 514 # Sign the public key from +certificate+ with the +signing_key+ and 515 # +signing_cert+, using the Gem::Security::DIGEST_ALGORITHM. Uses the 516 # default certificate validity range and extensions. 517 # 518 # Returns the newly signed certificate. 519 520 def self.sign certificate, signing_key, signing_cert, 521 age = ONE_YEAR, extensions = EXTENSIONS, serial = 1 522 signee_subject = certificate.subject 523 signee_key = certificate.public_key 524 525 alt_name = certificate.extensions.find do |extension| 526 extension.oid == 'subjectAltName' 527 end 528 529 extensions = extensions.merge 'subjectAltName' => alt_name.value if 530 alt_name 531 532 issuer_alt_name = signing_cert.extensions.find do |extension| 533 extension.oid == 'subjectAltName' 534 end 535 536 extensions = extensions.merge 'issuerAltName' => issuer_alt_name.value if 537 issuer_alt_name 538 539 signed = create_cert signee_subject, signee_key, age, extensions, serial 540 signed.issuer = signing_cert.subject 541 542 signed.sign signing_key, Gem::Security::DIGEST_ALGORITHM.new 543 end 544 545 ## 546 # Returns a Gem::Security::TrustDir which wraps the directory where trusted 547 # certificates live. 548 549 def self.trust_dir 550 return @trust_dir if @trust_dir 551 552 dir = File.join Gem.user_home, '.gem', 'trust' 553 554 @trust_dir ||= Gem::Security::TrustDir.new dir 555 end 556 557 ## 558 # Enumerates the trusted certificates via Gem::Security::TrustDir. 559 560 def self.trusted_certificates &block 561 trust_dir.each_certificate(&block) 562 end 563 564 ## 565 # Writes +pemmable+, which must respond to +to_pem+ to +path+ with the given 566 # +permissions+. 567 568 def self.write pemmable, path, permissions = 0600 569 path = File.expand_path path 570 571 open path, 'wb', permissions do |io| 572 io.write pemmable.to_pem 573 end 574 575 path 576 end 577 578 reset 579 580end 581 582require 'rubygems/security/policy' 583require 'rubygems/security/policies' 584require 'rubygems/security/signer' 585require 'rubygems/security/trust_dir' 586 587