PageRenderTime 56ms CodeModel.GetById 22ms RepoModel.GetById 0ms app.codeStats 1ms

/Languages/Ruby/StdLib/ruby/1.9.1/rubygems/security.rb

http://github.com/IronLanguages/main
Ruby | 786 lines | 312 code | 60 blank | 414 comment | 19 complexity | c61a779f0dd460775ba699515b135eef MD5 | raw file
Possible License(s): CPL-1.0, BSD-3-Clause, ISC, GPL-2.0, MPL-2.0-no-copyleft-exception
  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. require 'rubygems'
  7. require 'rubygems/gem_openssl'
  8. # = Signed Gems README
  9. #
  10. # == Table of Contents
  11. # * Overview
  12. # * Walkthrough
  13. # * Command-Line Options
  14. # * OpenSSL Reference
  15. # * Bugs/TODO
  16. # * About the Author
  17. #
  18. # == Overview
  19. #
  20. # Gem::Security implements cryptographic signatures in RubyGems. The section
  21. # below is a step-by-step guide to using signed gems and generating your own.
  22. #
  23. # == Walkthrough
  24. #
  25. # In order to start signing your gems, you'll need to build a private key and
  26. # a self-signed certificate. Here's how:
  27. #
  28. # # build a private key and certificate for gemmaster@example.com
  29. # $ gem cert --build gemmaster@example.com
  30. #
  31. # This could take anywhere from 5 seconds to 10 minutes, depending on the
  32. # speed of your computer (public key algorithms aren't exactly the speediest
  33. # crypto algorithms in the world). When it's finished, you'll see the files
  34. # "gem-private_key.pem" and "gem-public_cert.pem" in the current directory.
  35. #
  36. # First things first: take the "gem-private_key.pem" file and move it
  37. # somewhere private, preferably a directory only you have access to, a floppy
  38. # (yuck!), a CD-ROM, or something comparably secure. Keep your private key
  39. # hidden; if it's compromised, someone can sign packages as you (note: PKI has
  40. # ways of mitigating the risk of stolen keys; more on that later).
  41. #
  42. # Now, let's sign an existing gem. I'll be using my Imlib2-Ruby bindings, but
  43. # you can use whatever gem you'd like. Open up your existing gemspec file and
  44. # add the following lines:
  45. #
  46. # # signing key and certificate chain
  47. # s.signing_key = '/mnt/floppy/gem-private_key.pem'
  48. # s.cert_chain = ['gem-public_cert.pem']
  49. #
  50. # (Be sure to replace "/mnt/floppy" with the ultra-secret path to your private
  51. # key).
  52. #
  53. # After that, go ahead and build your gem as usual. Congratulations, you've
  54. # just built your first signed gem! If you peek inside your gem file, you'll
  55. # see a couple of new files have been added:
  56. #
  57. # $ tar tf tar tf Imlib2-Ruby-0.5.0.gem
  58. # data.tar.gz
  59. # data.tar.gz.sig
  60. # metadata.gz
  61. # metadata.gz.sig
  62. #
  63. # Now let's verify the signature. Go ahead and install the gem, but add the
  64. # following options: "-P HighSecurity", like this:
  65. #
  66. # # install the gem with using the security policy "HighSecurity"
  67. # $ sudo gem install Imlib2-Ruby-0.5.0.gem -P HighSecurity
  68. #
  69. # The -P option sets your security policy -- we'll talk about that in just a
  70. # minute. Eh, what's this?
  71. #
  72. # Attempting local installation of 'Imlib2-Ruby-0.5.0.gem'
  73. # ERROR: Error installing gem Imlib2-Ruby-0.5.0.gem[.gem]: Couldn't
  74. # verify data signature: Untrusted Signing Chain Root: cert =
  75. # '/CN=gemmaster/DC=example/DC=com', error = 'path
  76. # "/root/.rubygems/trust/cert-15dbb43a6edf6a70a85d4e784e2e45312cff7030.pem"
  77. # does not exist'
  78. #
  79. # The culprit here is the security policy. RubyGems has several different
  80. # security policies. Let's take a short break and go over the security
  81. # policies. Here's a list of the available security policies, and a brief
  82. # description of each one:
  83. #
  84. # * NoSecurity - Well, no security at all. Signed packages are treated like
  85. # unsigned packages.
  86. # * LowSecurity - Pretty much no security. If a package is signed then
  87. # RubyGems will make sure the signature matches the signing
  88. # certificate, and that the signing certificate hasn't expired, but
  89. # that's it. A malicious user could easily circumvent this kind of
  90. # security.
  91. # * MediumSecurity - Better than LowSecurity and NoSecurity, but still
  92. # fallible. Package contents are verified against the signing
  93. # certificate, and the signing certificate is checked for validity,
  94. # and checked against the rest of the certificate chain (if you don't
  95. # know what a certificate chain is, stay tuned, we'll get to that).
  96. # The biggest improvement over LowSecurity is that MediumSecurity
  97. # won't install packages that are signed by untrusted sources.
  98. # Unfortunately, MediumSecurity still isn't totally secure -- a
  99. # malicious user can still unpack the gem, strip the signatures, and
  100. # distribute the gem unsigned.
  101. # * HighSecurity - Here's the bugger that got us into this mess.
  102. # The HighSecurity policy is identical to the MediumSecurity policy,
  103. # except that it does not allow unsigned gems. A malicious user
  104. # doesn't have a whole lot of options here; he can't modify the
  105. # package contents without invalidating the signature, and he can't
  106. # modify or remove signature or the signing certificate chain, or
  107. # RubyGems will simply refuse to install the package. Oh well, maybe
  108. # he'll have better luck causing problems for CPAN users instead :).
  109. #
  110. # So, the reason RubyGems refused to install our shiny new signed gem was
  111. # because it was from an untrusted source. Well, my code is infallible
  112. # (hah!), so I'm going to add myself as a trusted source.
  113. #
  114. # Here's how:
  115. #
  116. # # add trusted certificate
  117. # gem cert --add gem-public_cert.pem
  118. #
  119. # I've added my public certificate as a trusted source. Now I can install
  120. # packages signed my private key without any hassle. Let's try the install
  121. # command above again:
  122. #
  123. # # install the gem with using the HighSecurity policy (and this time
  124. # # without any shenanigans)
  125. # $ sudo gem install Imlib2-Ruby-0.5.0.gem -P HighSecurity
  126. #
  127. # This time RubyGems should accept your signed package and begin installing.
  128. # While you're waiting for RubyGems to work it's magic, have a look at some of
  129. # the other security commands:
  130. #
  131. # Usage: gem cert [options]
  132. #
  133. # Options:
  134. # -a, --add CERT Add a trusted certificate.
  135. # -l, --list List trusted certificates.
  136. # -r, --remove STRING Remove trusted certificates containing STRING.
  137. # -b, --build EMAIL_ADDR Build private key and self-signed certificate
  138. # for EMAIL_ADDR.
  139. # -C, --certificate CERT Certificate for --sign command.
  140. # -K, --private-key KEY Private key for --sign command.
  141. # -s, --sign NEWCERT Sign a certificate with my key and certificate.
  142. #
  143. # (By the way, you can pull up this list any time you'd like by typing "gem
  144. # cert --help")
  145. #
  146. # Hmm. We've already covered the "--build" option, and the "--add", "--list",
  147. # and "--remove" commands seem fairly straightforward; they allow you to add,
  148. # list, and remove the certificates in your trusted certificate list. But
  149. # what's with this "--sign" option?
  150. #
  151. # To answer that question, let's take a look at "certificate chains", a
  152. # concept I mentioned earlier. There are a couple of problems with
  153. # self-signed certificates: first of all, self-signed certificates don't offer
  154. # a whole lot of security. Sure, the certificate says Yukihiro Matsumoto, but
  155. # how do I know it was actually generated and signed by matz himself unless he
  156. # gave me the certificate in person?
  157. #
  158. # The second problem is scalability. Sure, if there are 50 gem authors, then
  159. # I have 50 trusted certificates, no problem. What if there are 500 gem
  160. # authors? 1000? Having to constantly add new trusted certificates is a
  161. # pain, and it actually makes the trust system less secure by encouraging
  162. # RubyGems users to blindly trust new certificates.
  163. #
  164. # Here's where certificate chains come in. A certificate chain establishes an
  165. # arbitrarily long chain of trust between an issuing certificate and a child
  166. # certificate. So instead of trusting certificates on a per-developer basis,
  167. # we use the PKI concept of certificate chains to build a logical hierarchy of
  168. # trust. Here's a hypothetical example of a trust hierarchy based (roughly)
  169. # on geography:
  170. #
  171. #
  172. # --------------------------
  173. # | rubygems@rubyforge.org |
  174. # --------------------------
  175. # |
  176. # -----------------------------------
  177. # | |
  178. # ---------------------------- -----------------------------
  179. # | seattle.rb@zenspider.com | | dcrubyists@richkilmer.com |
  180. # ---------------------------- -----------------------------
  181. # | | | |
  182. # --------------- ---------------- ----------- --------------
  183. # | alf@seattle | | bob@portland | | pabs@dc | | tomcope@dc |
  184. # --------------- ---------------- ----------- --------------
  185. #
  186. #
  187. # Now, rather than having 4 trusted certificates (one for alf@seattle,
  188. # bob@portland, pabs@dc, and tomecope@dc), a user could actually get by with 1
  189. # certificate: the "rubygems@rubyforge.org" certificate. Here's how it works:
  190. #
  191. # I install "Alf2000-Ruby-0.1.0.gem", a package signed by "alf@seattle". I've
  192. # never heard of "alf@seattle", but his certificate has a valid signature from
  193. # the "seattle.rb@zenspider.com" certificate, which in turn has a valid
  194. # signature from the "rubygems@rubyforge.org" certificate. Voila! At this
  195. # point, it's much more reasonable for me to trust a package signed by
  196. # "alf@seattle", because I can establish a chain to "rubygems@rubyforge.org",
  197. # which I do trust.
  198. #
  199. # And the "--sign" option allows all this to happen. A developer creates
  200. # their build certificate with the "--build" option, then has their
  201. # certificate signed by taking it with them to their next regional Ruby meetup
  202. # (in our hypothetical example), and it's signed there by the person holding
  203. # the regional RubyGems signing certificate, which is signed at the next
  204. # RubyConf by the holder of the top-level RubyGems certificate. At each point
  205. # the issuer runs the same command:
  206. #
  207. # # sign a certificate with the specified key and certificate
  208. # # (note that this modifies client_cert.pem!)
  209. # $ gem cert -K /mnt/floppy/issuer-priv_key.pem -C issuer-pub_cert.pem
  210. # --sign client_cert.pem
  211. #
  212. # Then the holder of issued certificate (in this case, our buddy
  213. # "alf@seattle"), can start using this signed certificate to sign RubyGems.
  214. # By the way, in order to let everyone else know about his new fancy signed
  215. # certificate, "alf@seattle" would change his gemspec file to look like this:
  216. #
  217. # # signing key (still kept in an undisclosed location!)
  218. # s.signing_key = '/mnt/floppy/alf-private_key.pem'
  219. #
  220. # # certificate chain (includes the issuer certificate now too)
  221. # s.cert_chain = ['/home/alf/doc/seattlerb-public_cert.pem',
  222. # '/home/alf/doc/alf_at_seattle-public_cert.pem']
  223. #
  224. # Obviously, this RubyGems trust infrastructure doesn't exist yet. Also, in
  225. # the "real world" issuers actually generate the child certificate from a
  226. # certificate request, rather than sign an existing certificate. And our
  227. # hypothetical infrastructure is missing a certificate revocation system.
  228. # These are that can be fixed in the future...
  229. #
  230. # I'm sure your new signed gem has finished installing by now (unless you're
  231. # installing rails and all it's dependencies, that is ;D). At this point you
  232. # should know how to do all of these new and interesting things:
  233. #
  234. # * build a gem signing key and certificate
  235. # * modify your existing gems to support signing
  236. # * adjust your security policy
  237. # * modify your trusted certificate list
  238. # * sign a certificate
  239. #
  240. # If you've got any questions, feel free to contact me at the email address
  241. # below. The next couple of sections
  242. #
  243. #
  244. # == Command-Line Options
  245. #
  246. # Here's a brief summary of the certificate-related command line options:
  247. #
  248. # gem install
  249. # -P, --trust-policy POLICY Specify gem trust policy.
  250. #
  251. # gem cert
  252. # -a, --add CERT Add a trusted certificate.
  253. # -l, --list List trusted certificates.
  254. # -r, --remove STRING Remove trusted certificates containing
  255. # STRING.
  256. # -b, --build EMAIL_ADDR Build private key and self-signed
  257. # certificate for EMAIL_ADDR.
  258. # -C, --certificate CERT Certificate for --sign command.
  259. # -K, --private-key KEY Private key for --sign command.
  260. # -s, --sign NEWCERT Sign a certificate with my key and
  261. # certificate.
  262. #
  263. # A more detailed description of each options is available in the walkthrough
  264. # above.
  265. #
  266. #
  267. # == OpenSSL Reference
  268. #
  269. # The .pem files generated by --build and --sign are just basic OpenSSL PEM
  270. # files. Here's a couple of useful commands for manipulating them:
  271. #
  272. # # convert a PEM format X509 certificate into DER format:
  273. # # (note: Windows .cer files are X509 certificates in DER format)
  274. # $ openssl x509 -in input.pem -outform der -out output.der
  275. #
  276. # # print out the certificate in a human-readable format:
  277. # $ openssl x509 -in input.pem -noout -text
  278. #
  279. # And you can do the same thing with the private key file as well:
  280. #
  281. # # convert a PEM format RSA key into DER format:
  282. # $ openssl rsa -in input_key.pem -outform der -out output_key.der
  283. #
  284. # # print out the key in a human readable format:
  285. # $ openssl rsa -in input_key.pem -noout -text
  286. #
  287. # == Bugs/TODO
  288. #
  289. # * There's no way to define a system-wide trust list.
  290. # * custom security policies (from a YAML file, etc)
  291. # * Simple method to generate a signed certificate request
  292. # * Support for OCSP, SCVP, CRLs, or some other form of cert
  293. # status check (list is in order of preference)
  294. # * Support for encrypted private keys
  295. # * Some sort of semi-formal trust hierarchy (see long-winded explanation
  296. # above)
  297. # * Path discovery (for gem certificate chains that don't have a self-signed
  298. # root) -- by the way, since we don't have this, THE ROOT OF THE CERTIFICATE
  299. # CHAIN MUST BE SELF SIGNED if Policy#verify_root is true (and it is for the
  300. # MediumSecurity and HighSecurity policies)
  301. # * Better explanation of X509 naming (ie, we don't have to use email
  302. # addresses)
  303. # * Possible alternate signing mechanisms (eg, via PGP). this could be done
  304. # pretty easily by adding a :signing_type attribute to the gemspec, then add
  305. # the necessary support in other places
  306. # * Honor AIA field (see note about OCSP above)
  307. # * Maybe honor restriction extensions?
  308. # * Might be better to store the certificate chain as a PKCS#7 or PKCS#12
  309. # file, instead of an array embedded in the metadata. ideas?
  310. # * Possibly embed signature and key algorithms into metadata (right now
  311. # they're assumed to be the same as what's set in Gem::Security::OPT)
  312. #
  313. # == About the Author
  314. #
  315. # Paul Duncan <pabs@pablotron.org>
  316. # http://pablotron.org/
  317. module Gem::Security
  318. class Exception < Gem::Exception; end
  319. #
  320. # default options for most of the methods below
  321. #
  322. OPT = {
  323. # private key options
  324. :key_algo => Gem::SSL::PKEY_RSA,
  325. :key_size => 2048,
  326. # public cert options
  327. :cert_age => 365 * 24 * 3600, # 1 year
  328. :dgst_algo => Gem::SSL::DIGEST_SHA1,
  329. # x509 certificate extensions
  330. :cert_exts => {
  331. 'basicConstraints' => 'CA:FALSE',
  332. 'subjectKeyIdentifier' => 'hash',
  333. 'keyUsage' => 'keyEncipherment,dataEncipherment,digitalSignature',
  334. },
  335. # save the key and cert to a file in build_self_signed_cert()?
  336. :save_key => true,
  337. :save_cert => true,
  338. # if you define either of these, then they'll be used instead of
  339. # the output_fmt macro below
  340. :save_key_path => nil,
  341. :save_cert_path => nil,
  342. # output name format for self-signed certs
  343. :output_fmt => 'gem-%s.pem',
  344. :munge_re => Regexp.new(/[^a-z0-9_.-]+/),
  345. # output directory for trusted certificate checksums
  346. :trust_dir => File::join(Gem.user_home, '.gem', 'trust'),
  347. # default permissions for trust directory and certs
  348. :perms => {
  349. :trust_dir => 0700,
  350. :trusted_cert => 0600,
  351. :signing_cert => 0600,
  352. :signing_key => 0600,
  353. },
  354. }
  355. #
  356. # A Gem::Security::Policy object encapsulates the settings for verifying
  357. # signed gem files. This is the base class. You can either declare an
  358. # instance of this or use one of the preset security policies below.
  359. #
  360. class Policy
  361. attr_accessor :verify_data, :verify_signer, :verify_chain,
  362. :verify_root, :only_trusted, :only_signed
  363. #
  364. # Create a new Gem::Security::Policy object with the given mode and
  365. # options.
  366. #
  367. def initialize(policy = {}, opt = {})
  368. # set options
  369. @opt = Gem::Security::OPT.merge(opt)
  370. # build policy
  371. policy.each_pair do |key, val|
  372. case key
  373. when :verify_data then @verify_data = val
  374. when :verify_signer then @verify_signer = val
  375. when :verify_chain then @verify_chain = val
  376. when :verify_root then @verify_root = val
  377. when :only_trusted then @only_trusted = val
  378. when :only_signed then @only_signed = val
  379. end
  380. end
  381. end
  382. #
  383. # Get the path to the file for this cert.
  384. #
  385. def self.trusted_cert_path(cert, opt = {})
  386. opt = Gem::Security::OPT.merge(opt)
  387. # get digest algorithm, calculate checksum of root.subject
  388. algo = opt[:dgst_algo]
  389. dgst = algo.hexdigest(cert.subject.to_s)
  390. # build path to trusted cert file
  391. name = "cert-#{dgst}.pem"
  392. # join and return path components
  393. File::join(opt[:trust_dir], name)
  394. end
  395. #
  396. # Verify that the gem data with the given signature and signing chain
  397. # matched this security policy at the specified time.
  398. #
  399. def verify_gem(signature, data, chain, time = Time.now)
  400. Gem.ensure_ssl_available
  401. cert_class = OpenSSL::X509::Certificate
  402. exc = Gem::Security::Exception
  403. chain ||= []
  404. chain = chain.map{ |str| cert_class.new(str) }
  405. signer, ch_len = chain[-1], chain.size
  406. # make sure signature is valid
  407. if @verify_data
  408. # get digest algorithm (TODO: this should be configurable)
  409. dgst = @opt[:dgst_algo]
  410. # verify the data signature (this is the most important part, so don't
  411. # screw it up :D)
  412. v = signer.public_key.verify(dgst.new, signature, data)
  413. raise exc, "Invalid Gem Signature" unless v
  414. # make sure the signer is valid
  415. if @verify_signer
  416. # make sure the signing cert is valid right now
  417. v = signer.check_validity(nil, time)
  418. raise exc, "Invalid Signature: #{v[:desc]}" unless v[:is_valid]
  419. end
  420. end
  421. # make sure the certificate chain is valid
  422. if @verify_chain
  423. # iterate down over the chain and verify each certificate against it's
  424. # issuer
  425. (ch_len - 1).downto(1) do |i|
  426. issuer, cert = chain[i - 1, 2]
  427. v = cert.check_validity(issuer, time)
  428. raise exc, "%s: cert = '%s', error = '%s'" % [
  429. 'Invalid Signing Chain', cert.subject, v[:desc]
  430. ] unless v[:is_valid]
  431. end
  432. # verify root of chain
  433. if @verify_root
  434. # make sure root is self-signed
  435. root = chain[0]
  436. raise exc, "%s: %s (subject = '%s', issuer = '%s')" % [
  437. 'Invalid Signing Chain Root',
  438. 'Subject does not match Issuer for Gem Signing Chain',
  439. root.subject.to_s,
  440. root.issuer.to_s,
  441. ] unless root.issuer.to_s == root.subject.to_s
  442. # make sure root is valid
  443. v = root.check_validity(root, time)
  444. raise exc, "%s: cert = '%s', error = '%s'" % [
  445. 'Invalid Signing Chain Root', root.subject, v[:desc]
  446. ] unless v[:is_valid]
  447. # verify that the chain root is trusted
  448. if @only_trusted
  449. # get digest algorithm, calculate checksum of root.subject
  450. algo = @opt[:dgst_algo]
  451. path = Gem::Security::Policy.trusted_cert_path(root, @opt)
  452. # check to make sure trusted path exists
  453. raise exc, "%s: cert = '%s', error = '%s'" % [
  454. 'Untrusted Signing Chain Root',
  455. root.subject.to_s,
  456. "path \"#{path}\" does not exist",
  457. ] unless File.exist?(path)
  458. # load calculate digest from saved cert file
  459. save_cert = OpenSSL::X509::Certificate.new(File.read(path))
  460. save_dgst = algo.digest(save_cert.public_key.to_s)
  461. # create digest of public key
  462. pkey_str = root.public_key.to_s
  463. cert_dgst = algo.digest(pkey_str)
  464. # now compare the two digests, raise exception
  465. # if they don't match
  466. raise exc, "%s: %s (saved = '%s', root = '%s')" % [
  467. 'Invalid Signing Chain Root',
  468. "Saved checksum doesn't match root checksum",
  469. save_dgst, cert_dgst,
  470. ] unless save_dgst == cert_dgst
  471. end
  472. end
  473. # return the signing chain
  474. chain.map { |cert| cert.subject }
  475. end
  476. end
  477. end
  478. #
  479. # No security policy: all package signature checks are disabled.
  480. #
  481. NoSecurity = Policy.new(
  482. :verify_data => false,
  483. :verify_signer => false,
  484. :verify_chain => false,
  485. :verify_root => false,
  486. :only_trusted => false,
  487. :only_signed => false
  488. )
  489. #
  490. # AlmostNo security policy: only verify that the signing certificate is the
  491. # one that actually signed the data. Make no attempt to verify the signing
  492. # certificate chain.
  493. #
  494. # This policy is basically useless. better than nothing, but can still be
  495. # easily spoofed, and is not recommended.
  496. #
  497. AlmostNoSecurity = Policy.new(
  498. :verify_data => true,
  499. :verify_signer => false,
  500. :verify_chain => false,
  501. :verify_root => false,
  502. :only_trusted => false,
  503. :only_signed => false
  504. )
  505. #
  506. # Low security policy: only verify that the signing certificate is actually
  507. # the gem signer, and that the signing certificate is valid.
  508. #
  509. # This policy is better than nothing, but can still be easily spoofed, and
  510. # is not recommended.
  511. #
  512. LowSecurity = Policy.new(
  513. :verify_data => true,
  514. :verify_signer => true,
  515. :verify_chain => false,
  516. :verify_root => false,
  517. :only_trusted => false,
  518. :only_signed => false
  519. )
  520. #
  521. # Medium security policy: verify the signing certificate, verify the signing
  522. # certificate chain all the way to the root certificate, and only trust root
  523. # certificates that we have explicitly allowed trust for.
  524. #
  525. # This security policy is reasonable, but it allows unsigned packages, so a
  526. # malicious person could simply delete the package signature and pass the
  527. # gem off as unsigned.
  528. #
  529. MediumSecurity = Policy.new(
  530. :verify_data => true,
  531. :verify_signer => true,
  532. :verify_chain => true,
  533. :verify_root => true,
  534. :only_trusted => true,
  535. :only_signed => false
  536. )
  537. #
  538. # High security policy: only allow signed gems to be installed, verify the
  539. # signing certificate, verify the signing certificate chain all the way to
  540. # the root certificate, and only trust root certificates that we have
  541. # explicitly allowed trust for.
  542. #
  543. # This security policy is significantly more difficult to bypass, and offers
  544. # a reasonable guarantee that the contents of the gem have not been altered.
  545. #
  546. HighSecurity = Policy.new(
  547. :verify_data => true,
  548. :verify_signer => true,
  549. :verify_chain => true,
  550. :verify_root => true,
  551. :only_trusted => true,
  552. :only_signed => true
  553. )
  554. #
  555. # Hash of configured security policies
  556. #
  557. Policies = {
  558. 'NoSecurity' => NoSecurity,
  559. 'AlmostNoSecurity' => AlmostNoSecurity,
  560. 'LowSecurity' => LowSecurity,
  561. 'MediumSecurity' => MediumSecurity,
  562. 'HighSecurity' => HighSecurity,
  563. }
  564. #
  565. # Sign the cert cert with @signing_key and @signing_cert, using the digest
  566. # algorithm opt[:dgst_algo]. Returns the newly signed certificate.
  567. #
  568. def self.sign_cert(cert, signing_key, signing_cert, opt = {})
  569. opt = OPT.merge(opt)
  570. # set up issuer information
  571. cert.issuer = signing_cert.subject
  572. cert.sign(signing_key, opt[:dgst_algo].new)
  573. cert
  574. end
  575. #
  576. # Make sure the trust directory exists. If it does exist, make sure it's
  577. # actually a directory. If not, then create it with the appropriate
  578. # permissions.
  579. #
  580. def self.verify_trust_dir(path, perms)
  581. # if the directory exists, then make sure it is in fact a directory. if
  582. # it doesn't exist, then create it with the appropriate permissions
  583. if File.exist?(path)
  584. # verify that the trust directory is actually a directory
  585. unless File.directory?(path)
  586. err = "trust directory #{path} isn't a directory"
  587. raise Gem::Security::Exception, err
  588. end
  589. else
  590. # trust directory doesn't exist, so create it with permissions
  591. FileUtils.mkdir_p(path)
  592. FileUtils.chmod(perms, path)
  593. end
  594. end
  595. #
  596. # Build a certificate from the given DN and private key.
  597. #
  598. def self.build_cert(name, key, opt = {})
  599. Gem.ensure_ssl_available
  600. opt = OPT.merge(opt)
  601. # create new cert
  602. ret = OpenSSL::X509::Certificate.new
  603. # populate cert attributes
  604. ret.version = 2
  605. ret.serial = 0
  606. ret.public_key = key.public_key
  607. ret.not_before = Time.now
  608. ret.not_after = Time.now + opt[:cert_age]
  609. ret.subject = name
  610. # add certificate extensions
  611. ef = OpenSSL::X509::ExtensionFactory.new(nil, ret)
  612. ret.extensions = opt[:cert_exts].map { |k, v| ef.create_extension(k, v) }
  613. # sign cert
  614. i_key, i_cert = opt[:issuer_key] || key, opt[:issuer_cert] || ret
  615. ret = sign_cert(ret, i_key, i_cert, opt)
  616. # return cert
  617. ret
  618. end
  619. #
  620. # Build a self-signed certificate for the given email address.
  621. #
  622. def self.build_self_signed_cert(email_addr, opt = {})
  623. Gem.ensure_ssl_available
  624. opt = OPT.merge(opt)
  625. path = { :key => nil, :cert => nil }
  626. # split email address up
  627. cn, dcs = email_addr.split('@')
  628. dcs = dcs.split('.')
  629. # munge email CN and DCs
  630. cn = cn.gsub(opt[:munge_re], '_')
  631. dcs = dcs.map { |dc| dc.gsub(opt[:munge_re], '_') }
  632. # create DN
  633. name = "CN=#{cn}/" << dcs.map { |dc| "DC=#{dc}" }.join('/')
  634. name = OpenSSL::X509::Name::parse(name)
  635. # build private key
  636. key = opt[:key_algo].new(opt[:key_size])
  637. # method name pretty much says it all :)
  638. verify_trust_dir(opt[:trust_dir], opt[:perms][:trust_dir])
  639. # if we're saving the key, then write it out
  640. if opt[:save_key]
  641. path[:key] = opt[:save_key_path] || (opt[:output_fmt] % 'private_key')
  642. File.open(path[:key], 'wb') do |file|
  643. file.chmod(opt[:perms][:signing_key])
  644. file.write(key.to_pem)
  645. end
  646. end
  647. # build self-signed public cert from key
  648. cert = build_cert(name, key, opt)
  649. # if we're saving the cert, then write it out
  650. if opt[:save_cert]
  651. path[:cert] = opt[:save_cert_path] || (opt[:output_fmt] % 'public_cert')
  652. File.open(path[:cert], 'wb') do |file|
  653. file.chmod(opt[:perms][:signing_cert])
  654. file.write(cert.to_pem)
  655. end
  656. end
  657. # return key, cert, and paths (if applicable)
  658. { :key => key, :cert => cert,
  659. :key_path => path[:key], :cert_path => path[:cert] }
  660. end
  661. #
  662. # Add certificate to trusted cert list.
  663. #
  664. # Note: At the moment these are stored in OPT[:trust_dir], although that
  665. # directory may change in the future.
  666. #
  667. def self.add_trusted_cert(cert, opt = {})
  668. opt = OPT.merge(opt)
  669. # get destination path
  670. path = Gem::Security::Policy.trusted_cert_path(cert, opt)
  671. # verify trust directory (can't write to nowhere, you know)
  672. verify_trust_dir(opt[:trust_dir], opt[:perms][:trust_dir])
  673. # write cert to output file
  674. File.open(path, 'wb') do |file|
  675. file.chmod(opt[:perms][:trusted_cert])
  676. file.write(cert.to_pem)
  677. end
  678. # return nil
  679. nil
  680. end
  681. #
  682. # Basic OpenSSL-based package signing class.
  683. #
  684. class Signer
  685. attr_accessor :key, :cert_chain
  686. def initialize(key, cert_chain)
  687. Gem.ensure_ssl_available
  688. @algo = Gem::Security::OPT[:dgst_algo]
  689. @key, @cert_chain = key, cert_chain
  690. # check key, if it's a file, and if it's key, leave it alone
  691. if @key && !@key.kind_of?(OpenSSL::PKey::PKey)
  692. @key = OpenSSL::PKey::RSA.new(File.read(@key))
  693. end
  694. # check cert chain, if it's a file, load it, if it's cert data, convert
  695. # it into a cert object, and if it's a cert object, leave it alone
  696. if @cert_chain
  697. @cert_chain = @cert_chain.map do |cert|
  698. # check cert, if it's a file, load it, if it's cert data, convert it
  699. # into a cert object, and if it's a cert object, leave it alone
  700. if cert && !cert.kind_of?(OpenSSL::X509::Certificate)
  701. cert = File.read(cert) if File::exist?(cert)
  702. cert = OpenSSL::X509::Certificate.new(cert)
  703. end
  704. cert
  705. end
  706. end
  707. end
  708. #
  709. # Sign data with given digest algorithm
  710. #
  711. def sign(data)
  712. @key.sign(@algo.new, data)
  713. end
  714. end
  715. end