1# 2# ssl.rb -- SSL/TLS enhancement for GenericServer 3# 4# Copyright (c) 2003 GOTOU Yuuzou All rights reserved. 5# 6# $Id: ssl.rb 38945 2013-01-26 01:12:54Z drbrain $ 7 8require 'webrick' 9require 'openssl' 10 11module WEBrick 12 module Config 13 svrsoft = General[:ServerSoftware] 14 osslv = ::OpenSSL::OPENSSL_VERSION.split[1] 15 16 ## 17 # Default SSL server configuration. 18 # 19 # WEBrick can automatically create a self-signed certificate if 20 # <code>:SSLCertName</code> is set. For more information on the various 21 # SSL options see OpenSSL::SSL::SSLContext. 22 # 23 # :ServerSoftware :: 24 # The server software name used in the Server: header. 25 # :SSLEnable :: false, 26 # Enable SSL for this server. Defaults to false. 27 # :SSLCertificate :: 28 # The SSL certificate for the server. 29 # :SSLPrivateKey :: 30 # The SSL private key for the server certificate. 31 # :SSLClientCA :: nil, 32 # Array of certificates that will be sent to the client. 33 # :SSLExtraChainCert :: nil, 34 # Array of certificates that willbe added to the certificate chain 35 # :SSLCACertificateFile :: nil, 36 # Path to a CA certificate file 37 # :SSLCACertificatePath :: nil, 38 # Path to a directory containing CA certificates 39 # :SSLCertificateStore :: nil, 40 # OpenSSL::X509::Store used for certificate validation of the client 41 # :SSLTmpDhCallback :: nil, 42 # Callback invoked when DH parameters are required. 43 # :SSLVerifyClient :: 44 # Sets whether the client is verified. This defaults to VERIFY_NONE 45 # which is typical for an HTTPS server. 46 # :SSLVerifyDepth :: 47 # Number of CA certificates to walk when verifying a certificate chain 48 # :SSLVerifyCallback :: 49 # Custom certificate verification callback 50 # :SSLTimeout :: 51 # Maximum session lifetime 52 # :SSLOptions :: 53 # Various SSL options 54 # :SSLStartImmediately :: 55 # Immediately start SSL upon connection? Defaults to true 56 # :SSLCertName :: 57 # SSL certificate name. Must be set to enable automatic certificate 58 # creation. 59 # :SSLCertComment :: 60 # Comment used during automatic certificate creation. 61 62 SSL = { 63 :ServerSoftware => "#{svrsoft} OpenSSL/#{osslv}", 64 :SSLEnable => false, 65 :SSLCertificate => nil, 66 :SSLPrivateKey => nil, 67 :SSLClientCA => nil, 68 :SSLExtraChainCert => nil, 69 :SSLCACertificateFile => nil, 70 :SSLCACertificatePath => nil, 71 :SSLCertificateStore => nil, 72 :SSLTmpDhCallback => nil, 73 :SSLVerifyClient => ::OpenSSL::SSL::VERIFY_NONE, 74 :SSLVerifyDepth => nil, 75 :SSLVerifyCallback => nil, # custom verification 76 :SSLTimeout => nil, 77 :SSLOptions => nil, 78 :SSLStartImmediately => true, 79 # Must specify if you use auto generated certificate. 80 :SSLCertName => nil, 81 :SSLCertComment => "Generated by Ruby/OpenSSL" 82 } 83 General.update(SSL) 84 end 85 86 module Utils 87 ## 88 # Creates a self-signed certificate with the given number of +bits+, 89 # the issuer +cn+ and a +comment+ to be stored in the certificate. 90 91 def create_self_signed_cert(bits, cn, comment) 92 rsa = OpenSSL::PKey::RSA.new(bits){|p, n| 93 case p 94 when 0; $stderr.putc "." # BN_generate_prime 95 when 1; $stderr.putc "+" # BN_generate_prime 96 when 2; $stderr.putc "*" # searching good prime, 97 # n = #of try, 98 # but also data from BN_generate_prime 99 when 3; $stderr.putc "\n" # found good prime, n==0 - p, n==1 - q, 100 # but also data from BN_generate_prime 101 else; $stderr.putc "*" # BN_generate_prime 102 end 103 } 104 cert = OpenSSL::X509::Certificate.new 105 cert.version = 2 106 cert.serial = 1 107 name = OpenSSL::X509::Name.new(cn) 108 cert.subject = name 109 cert.issuer = name 110 cert.not_before = Time.now 111 cert.not_after = Time.now + (365*24*60*60) 112 cert.public_key = rsa.public_key 113 114 ef = OpenSSL::X509::ExtensionFactory.new(nil,cert) 115 ef.issuer_certificate = cert 116 cert.extensions = [ 117 ef.create_extension("basicConstraints","CA:FALSE"), 118 ef.create_extension("keyUsage", "keyEncipherment"), 119 ef.create_extension("subjectKeyIdentifier", "hash"), 120 ef.create_extension("extendedKeyUsage", "serverAuth"), 121 ef.create_extension("nsComment", comment), 122 ] 123 aki = ef.create_extension("authorityKeyIdentifier", 124 "keyid:always,issuer:always") 125 cert.add_extension(aki) 126 cert.sign(rsa, OpenSSL::Digest::SHA1.new) 127 128 return [ cert, rsa ] 129 end 130 module_function :create_self_signed_cert 131 end 132 133 ## 134 #-- 135 # Updates WEBrick::GenericServer with SSL functionality 136 137 class GenericServer 138 139 ## 140 # SSL context for the server when run in SSL mode 141 142 def ssl_context # :nodoc: 143 @ssl_context ||= nil 144 end 145 146 undef listen 147 148 ## 149 # Updates +listen+ to enable SSL when the SSL configuration is active. 150 151 def listen(address, port) # :nodoc: 152 listeners = Utils::create_listeners(address, port, @logger) 153 if @config[:SSLEnable] 154 unless ssl_context 155 @ssl_context = setup_ssl_context(@config) 156 @logger.info("\n" + @config[:SSLCertificate].to_text) 157 end 158 listeners.collect!{|svr| 159 ssvr = ::OpenSSL::SSL::SSLServer.new(svr, ssl_context) 160 ssvr.start_immediately = @config[:SSLStartImmediately] 161 ssvr 162 } 163 end 164 @listeners += listeners 165 end 166 167 ## 168 # Sets up an SSL context for +config+ 169 170 def setup_ssl_context(config) # :nodoc: 171 unless config[:SSLCertificate] 172 cn = config[:SSLCertName] 173 comment = config[:SSLCertComment] 174 cert, key = Utils::create_self_signed_cert(1024, cn, comment) 175 config[:SSLCertificate] = cert 176 config[:SSLPrivateKey] = key 177 end 178 ctx = OpenSSL::SSL::SSLContext.new 179 ctx.key = config[:SSLPrivateKey] 180 ctx.cert = config[:SSLCertificate] 181 ctx.client_ca = config[:SSLClientCA] 182 ctx.extra_chain_cert = config[:SSLExtraChainCert] 183 ctx.ca_file = config[:SSLCACertificateFile] 184 ctx.ca_path = config[:SSLCACertificatePath] 185 ctx.cert_store = config[:SSLCertificateStore] 186 ctx.tmp_dh_callback = config[:SSLTmpDhCallback] 187 ctx.verify_mode = config[:SSLVerifyClient] 188 ctx.verify_depth = config[:SSLVerifyDepth] 189 ctx.verify_callback = config[:SSLVerifyCallback] 190 ctx.timeout = config[:SSLTimeout] 191 ctx.options = config[:SSLOptions] 192 ctx 193 end 194 end 195end 196