非常教程

Ruby 2.4参考手册

OpenSSL

OpenSSL

OpenSSL 提供 SSL,TLS 和通用加密。它包装了 OpenSSL 库。

所有的例子都假设你已经加载了 OpenSSL:

require 'openssl'

这些例子构建在彼此之上。例如,在这些例子中使用下一个创建的密钥。

按键

创建一个密钥

本示例创建一个2048位 RSA 密钥对并将其写入当前目录。

key = OpenSSL::PKey::RSA.new 2048

open 'private_key.pem', 'w' do |io| io.write key.to_pem end
open 'public_key.pem', 'w' do |io| io.write key.public_key.to_pem end

导出密钥

没有加密保存到磁盘的密钥是不安全的,因为任何获取密钥的人都可以使用它,除非它被加密。为了安全地导出密钥,您可以使用密码将其导出。

cipher = OpenSSL::Cipher.new 'AES-128-CBC'
pass_phrase = 'my secure pass phrase goes here'

key_secure = key.export cipher, pass_phrase

open 'private.secure.pem', 'w' do |io|
  io.write key_secure
end

OpenSSL :: Cipher.ciphers 返回可用密码的列表。

加载密钥

一个密钥也可以从文件中加载。

key2 = OpenSSL::PKey::RSA.new File.read 'private_key.pem'
key2.public? # => true
key2.private? # => true

或者

key3 = OpenSSL::PKey::RSA.new File.read 'public_key.pem'
key3.public? # => true
key3.private? # => false

加载加密密钥

加载加密密钥时,OpenSSL 会提示您输入密码。如果您无法输入密码,您可以在加载密钥时提供密码:

key4_pem = File.read 'private.secure.pem'
pass_phrase = 'my secure pass phrase goes here'
key4 = OpenSSL::PKey::RSA.new key4_pem, pass_phrase

RSA 加密

RSA 使用公钥和私钥提供加密和解密。根据加密数据的预期用途,您可以使用各种填充方法。

加密和解密

不对称的公钥/私钥加密速度较慢,并且在不使用填充的情况下使用它或直接加密大块数据时会受到攻击。RSA 加密的典型用例涉及用接收方的公钥“包装”对称密钥,该公钥使用其私钥再次“解开”该对称密钥。以下举例说明这种关键传输方案的简化示例。它不应该在实践中使用,但是,标准化的协议应该始终是首选。

wrapped_key = key.public_encrypt key

用公钥加密的对称密钥只能用接收方的相应私钥解密。

original_key = key.private_decrypt wrapped_key

默认情况下会使用 PKCS#1 填充,但也可以使用其他形式的填充,详情请参阅 PKey :: RSA。

签名

使用“private_encrypt”使用私钥加密某些数据相当于对数据应用数字签名。验证方可以通过将解密签名的结果与“public_decrypt”与原始数据进行比较来验证签名。但是,OpenSSL :: PKey 已经具有以标准方式处理数字签名的方法“签名”和“验证” - 实际上不应使用“private_encrypt”和“public_decrypt”。

为了签署文件,首先计算文件的密码安全散列,然后使用私钥对其进行签名。

digest = OpenSSL::Digest::SHA256.new
signature = key.sign digest, document

为了验证签名,再次计算文档的散列值,并使用公钥对签名进行解密。然后将结果与刚刚计算的散列进行比较,如果相等,则签名有效。

digest = OpenSSL::Digest::SHA256.new
if key.verify digest, signature, document
  puts 'Valid'
else
  puts 'Invalid'
end

PBKDF2 基于密码的加密

如果受使用的基础 OpenSSL 版本支持,基于密码的加密应使用 PKCS5 的功能。如果不支持或遗留应用程序需要,还支持 RFC 2898 中指定的较老的,不太安全的方法(见下文)。

PKCS5 支持PKCS#5 v2.0中指定的PBKDF2 。它仍然使用一个密码,一个盐,还有一些迭代,这会减缓密钥推导过程。这个速度越慢,它需要越多的工作就能暴力破解所产生的密钥。

加密

策略是首先实例化一个密码进行加密,然后使用 PBKDF2 生成一个随机的IV加密码。PKCS#5 v2.0 建议盐至少8个字节,迭代次数主要取决于正在使用的硬件。

cipher = OpenSSL::Cipher.new 'AES-128-CBC'
cipher.encrypt
iv = cipher.random_iv

pwd = 'some hopefully not to easily guessable password'
salt = OpenSSL::Random.random_bytes 16
iter = 20000
key_len = cipher.key_len
digest = OpenSSL::Digest::SHA256.new

key = OpenSSL::PKCS5.pbkdf2_hmac(pwd, salt, iter, key_len, digest)
cipher.key = key

Now encrypt the data:

encrypted = cipher.update document
encrypted << cipher.final

解密

使用与以前相同的步骤推导出对称 AES 密钥,这次将密码设置为解密。

cipher = OpenSSL::Cipher.new 'AES-128-CBC'
cipher.decrypt
cipher.iv = iv # the one generated with #random_iv

pwd = 'some hopefully not to easily guessable password'
salt = ... # the one generated above
iter = 20000
key_len = cipher.key_len
digest = OpenSSL::Digest::SHA256.new

key = OpenSSL::PKCS5.pbkdf2_hmac(pwd, salt, iter, key_len, digest)
cipher.key = key

Now decrypt the data:

decrypted = cipher.update encrypted
decrypted << cipher.final

PKCS#5 基于密码的加密

PKCS#5是RFC2898中记录的基于密码的加密标准。它允许使用短密码或密码来创建安全加密密钥。如果可能的话,如果情况允许,应使用上述的 PBKDF2。

PKCS#5 使用密码,密码短语和盐来生成加密密钥。

pass_phrase = 'my secure pass phrase goes here'
salt = '8 octets'

加密

首先设置加密密码

encryptor = OpenSSL::Cipher.new 'AES-128-CBC'
encryptor.encrypt
encryptor.pkcs5_keyivgen pass_phrase, salt

然后传递要加密的数据

encrypted = encryptor.update 'top secret document'
encrypted << encryptor.final

解密

使用设置用于解密的新密码实例

decryptor = OpenSSL::Cipher.new 'AES-128-CBC'
decryptor.decrypt
decryptor.pkcs5_keyivgen pass_phrase, salt

然后传递您想要解密的数据

plain = decryptor.update encrypted
plain << decryptor.final

证书

创建一个证书

本示例使用 RSA 密钥和 SHA1 签名创建自签名证书。

key = OpenSSL::PKey::RSA.new 2048
name = OpenSSL::X509::Name.parse 'CN=nobody/DC=example'

cert = OpenSSL::X509::Certificate.new
cert.version = 2
cert.serial = 0
cert.not_before = Time.now
cert.not_after = Time.now + 3600

cert.public_key = key.public_key
cert.subject = name

证书扩展

您可以使用 OpenSSL :: SSL :: ExtensionFactory 向证书添加扩展以指示证书的用途。

extension_factory = OpenSSL::X509::ExtensionFactory.new nil, cert

cert.add_extension    extension_factory.create_extension('basicConstraints', 'CA:FALSE', true)

cert.add_extension    extension_factory.create_extension(
    'keyUsage', 'keyEncipherment,dataEncipherment,digitalSignature')

cert.add_extension    extension_factory.create_extension('subjectKeyIdentifier', 'hash')

受支持的扩展名列表(在某些情况下,它们的可能值)可以从 OpenSSL 源代码中的“objects.h”文件派生。

签署证书

要签署证书,请设置颁发者并使用摘要算法使用 OpenSSL :: X509 :: Certificate#标记。这将创建一个自签名证书,因为我们使用相同的名称和密钥来签署证书,如同用于创建证书一样。

cert.issuer = name
cert.sign key, OpenSSL::Digest::SHA1.new

open 'certificate.pem', 'w' do |io| io.write cert.to_pem end

加载证书

像密钥一样,证书也可以从文件加载。

cert2 = OpenSSL::X509::Certificate.new File.read 'certificate.pem'

验证证书

当使用给定的公钥签名证书时,Certificate#verify将返回 true。

raise 'certificate can not be verified' unless cert2.verify key

证书颁发机构

证书颁发机构(CA)是可以信任的第三方,允许您验证未知证书的所有权。CA发布密钥签名,表明它信任该密钥的用户。遇到密钥的用户可以使用CA的公钥验证签名。

CA密钥

CA 密钥是有价值的,所以我们加密并将其保存到磁盘并确保它不可被其他用户读取。

ca_key = OpenSSL::PKey::RSA.new 2048
pass_phrase = 'my secure pass phrase goes here'

cipher = OpenSSL::Cipher.new 'AES-128-CBC'

open 'ca_key.pem', 'w', 0400 do |io|
  io.write ca_key.export(cipher, pass_phrase)
end

CA 证书

CA 证书的创建方式与我们在上面创建证书的方式相同,但具有不同的扩展名。

ca_name = OpenSSL::X509::Name.parse 'CN=ca/DC=example'

ca_cert = OpenSSL::X509::Certificate.new
ca_cert.serial = 0
ca_cert.version = 2
ca_cert.not_before = Time.now
ca_cert.not_after = Time.now + 86400

ca_cert.public_key = ca_key.public_key
ca_cert.subject = ca_name
ca_cert.issuer = ca_name

extension_factory = OpenSSL::X509::ExtensionFactory.new
extension_factory.subject_certificate = ca_cert
extension_factory.issuer_certificate = ca_cert

ca_cert.add_extension    extension_factory.create_extension('subjectKeyIdentifier', 'hash')

该扩展表示CA的密钥可以用作CA.

ca_cert.add_extension    extension_factory.create_extension('basicConstraints', 'CA:TRUE', true)

该扩展表明 CA 的密钥可用于验证证书和证书撤销中的签名。

ca_cert.add_extension    extension_factory.create_extension(
    'keyUsage', 'cRLSign,keyCertSign', true)

根 CA 证书是自签名的。

ca_cert.sign ca_key, OpenSSL::Digest::SHA1.new

CA证书保存到磁盘,以便分发给CA签名的所有密钥用户。

open 'ca_cert.pem', 'w' do |io|
  io.write ca_cert.to_pem
end

证书签名请求

CA通过证书签名请求(CSR)签署密钥。CSR包含识别密钥所需的信息。

csr = OpenSSL::X509::Request.new
csr.version = 0
csr.subject = name
csr.public_key = key.public_key
csr.sign key, OpenSSL::Digest::SHA1.new

CSR 将保存到磁盘并发送到 CA 进行签名。

open 'csr.pem', 'w' do |io|
  io.write csr.to_pem
end

从 CSR 创建证书

在收到CSR后,CA将在签署之前对其进行验证。最小的验证是检查CSR的签名。

csr = OpenSSL::X509::Request.new File.read 'csr.pem'

raise 'CSR can not be verified' unless csr.verify csr.public_key

验证后,创建一个证书,标记为各种用法,用 CA 密钥签名并返回给请求者。

csr_cert = OpenSSL::X509::Certificate.new
csr_cert.serial = 0
csr_cert.version = 2
csr_cert.not_before = Time.now
csr_cert.not_after = Time.now + 600

csr_cert.subject = csr.subject
csr_cert.public_key = csr.public_key
csr_cert.issuer = ca_cert.subject

extension_factory = OpenSSL::X509::ExtensionFactory.new
extension_factory.subject_certificate = csr_cert
extension_factory.issuer_certificate = ca_cert

csr_cert.add_extension    extension_factory.create_extension('basicConstraints', 'CA:FALSE')

csr_cert.add_extension    extension_factory.create_extension(
    'keyUsage', 'keyEncipherment,dataEncipherment,digitalSignature')

csr_cert.add_extension    extension_factory.create_extension('subjectKeyIdentifier', 'hash')

csr_cert.sign ca_key, OpenSSL::Digest::SHA1.new

open 'csr_cert.pem', 'w' do |io|
  io.write csr_cert.to_pem
end

SSL 和 TLS 连接

使用我们创建的密钥和证书,我们可以创建 SSL 或 TLS 连接。SSLContext 用于设置 SSL 会话。

context = OpenSSL::SSL::SSLContext.new

SSL 服务器

SSL 服务器要求证书和私钥与其客户端安全地进行通信:

context.cert = cert
context.key = key

然后用 TCP 服务器套接字和上下文创建一个 SSLServer。像普通的 TCP 服务器一样使用 SSLServer。

require 'socket'

tcp_server = TCPServer.new 5000
ssl_server = OpenSSL::SSL::SSLServer.new tcp_server, context

loop do
  ssl_connection = ssl_server.accept

  data = connection.gets

  response = "I got #{data.dump}"
  puts response

  connection.puts "I got #{data.dump}"
  connection.close
end

SSL 客户端

SSL 客户端使用 TCP 套接字和上下文创建。必须调用 SSLSocket#connect 来启动 SSL 握手并开始加密。客户端套接字不需要密钥和证书。

请注意,默认情况下,SSLSocket#关闭不会关闭底层套接字。如果需要,请将 SSLSocket#sync_close设置为true。

require 'socket'

tcp_socket = TCPSocket.new 'localhost', 5000
ssl_client = OpenSSL::SSL::SSLSocket.new tcp_socket, context
ssl_client.sync_close = true
ssl_client.connect

ssl_client.puts "hello server!"
puts ssl_client.gets

ssl_client.close # shutdown the TLS connection and close tcp_socket

同行验证

未经验证的 SSL 连接不能提供很高的安全性。为了增强安全性,客户端或服务器可以验证其对等体的证书。

可以修改客户端以根据证书颁发机构的证书验证服务器的证书:

context.ca_file = 'ca_cert.pem'
context.verify_mode = OpenSSL::SSL::VERIFY_PEER

require 'socket'

tcp_socket = TCPSocket.new 'localhost', 5000
ssl_client = OpenSSL::SSL::SSLSocket.new tcp_socket, context
ssl_client.connect

ssl_client.puts "hello server!"
puts ssl_client.gets

如果服务器证书无效或context.ca_file在验证对等体时未设置,则会引发 OpenSSL :: SSL :: SSLError。

常量

OPENSSL_FIPS

布尔值,指示 OpenSSL 是否启用 FIPS

OPENSSL_LIBRARY_VERSION

OpenSSL Ruby OpenSSL 扩展版本正在运行

OPENSSL_VERSION

OpenSSL OpenSSL Ruby OpenSSL 扩展的版本

OPENSSL_VERSION_NUMBER

OpenSSL 的版本号,OpenSSL 扩展是用(基数16)构建的,

VERSION

OpenSSL ruby 扩展版本

公共类方法

Digest(name) 显示源

返回 Digest 子类name

require 'openssl'

OpenSSL::Digest("MD5")
# => OpenSSL::Digest::MD5

Digest("Foo")
# => NameError: wrong constant name Foo
# File ext/openssl/lib/openssl/digest.rb, line 71
def Digest(name)
  OpenSSL::Digest.const_get(name)
end

debug → true | false 显示源

static VALUE
ossl_debug_get(VALUE self)
{
    return dOSSL;
}

debug = boolean → boolean 显示源

打开或关闭调试模式。在调试模式下,添加到 OpenSSL 错误队列的所有错误将被打印到 stderr。

static VALUE
ossl_debug_set(VALUE self, VALUE val)
{
    dOSSL = RTEST(val) ? Qtrue : Qfalse;

    return val;
}

errors → String...()

查看队列中剩余的任何错误。

你在这里看到的任何错误都可能是由于 ruby 的 OpenSSL 实现中的一个错误。

VALUE
ossl_get_errors(void)
{
    VALUE ary;
    long e;

    ary = rb_ary_new();
    while ((e = ERR_get_error()) != 0){
        rb_ary_push(ary, rb_str_new2(ERR_error_string(e, NULL)));
    }

    return ary;
}

fips_mode = boolean → boolean S显示源

打开或关闭 FIPS 模式。打开 FIPS 模式显然只会对支持 FIPS 的 OpenSSL 库的安装产生影响。试图这样做否则会导致错误。

示例

OpenSSL.fips_mode = true   # turn FIPS mode on
OpenSSL.fips_mode = false  # and off again
static VALUE
ossl_fips_mode_set(VALUE self, VALUE enabled)
{

#ifdef OPENSSL_FIPS
    if (RTEST(enabled)) {
        int mode = FIPS_mode();
        if(!mode && !FIPS_mode_set(1)) /* turning on twice leads to an error */
            ossl_raise(eOSSLError, "Turning on FIPS mode failed");
    } else {
        if(!FIPS_mode_set(0)) /* turning off twice is OK */
            ossl_raise(eOSSLError, "Turning off FIPS mode failed");
    }
    return enabled;
#else
    if (RTEST(enabled))
        ossl_raise(eOSSLError, "This version of OpenSSL does not support FIPS mode");
    return enabled;
#endif
}

私有实例方法

Digest(name) 显示源

返回 Digest 子类name

require 'openssl'

OpenSSL::Digest("MD5")
# => OpenSSL::Digest::MD5

Digest("Foo")
# => NameError: wrong constant name Foo
# File ext/openssl/lib/openssl/digest.rb, line 71
def Digest(name)
  OpenSSL::Digest.const_get(name)
end
OpenSSL
OpenSSL::ASN1 详细
OpenSSL::ASN1::ASN1Data 详细
OpenSSL::ASN1::ASN1Error 详细
OpenSSL::ASN1::Constructive 详细
OpenSSL::ASN1::ObjectId 详细
OpenSSL::ASN1::Primitive 详细
OpenSSL::BN 详细
OpenSSL::BNError 详细
OpenSSL::Buffering 详细
OpenSSL::Cipher 详细
OpenSSL::Cipher::Cipher 详细
OpenSSL::Config 详细
OpenSSL::ConfigError 详细
OpenSSL::Digest 详细
OpenSSL::Digest::DigestError 详细
OpenSSL::Engine 详细
OpenSSL::Engine::EngineError 详细
OpenSSL::ExtConfig 详细
OpenSSL::HMAC 详细
OpenSSL::HMACError 详细
OpenSSL::Netscape 详细
OpenSSL::Netscape::SPKI 详细
OpenSSL::Netscape::SPKIError 详细
OpenSSL::OCSP 详细
OpenSSL::OCSP::BasicResponse 详细
OpenSSL::OCSP::CertificateId 详细
OpenSSL::OCSP::OCSPError 详细
OpenSSL::OCSP::Request 详细
OpenSSL::OCSP::Response 详细
OpenSSL::OCSP::SingleResponse 详细
OpenSSL::OpenSSLError 详细
OpenSSL::PKCS12 详细
OpenSSL::PKCS5 详细
OpenSSL::PKCS5::PKCS5Error 详细
OpenSSL::PKCS7 详细
OpenSSL::PKCS7::RecipientInfo 详细
OpenSSL::PKCS7::SignerInfo 详细
OpenSSL::PKey 详细
OpenSSL::PKey::DH 详细
OpenSSL::PKey::DHError 详细
OpenSSL::PKey::DSA 详细
OpenSSL::PKey::DSAError 详细
OpenSSL::PKey::EC 详细
OpenSSL::PKey::EC::Group 详细
OpenSSL::PKey::EC::Point 详细
OpenSSL::PKey::PKey 详细
OpenSSL::PKey::PKeyError 详细
OpenSSL::PKey::RSA 详细
OpenSSL::PKey::RSAError 详细
OpenSSL::Random 详细
OpenSSL::SSL 详细
OpenSSL::SSL::Session 详细
OpenSSL::SSL::SocketForwarder 详细
OpenSSL::SSL::SSLContext 详细
OpenSSL::SSL::SSLError 详细
OpenSSL::SSL::SSLServer 详细
OpenSSL::SSL::SSLSocket 详细
OpenSSL::X509::Attribute 详细
OpenSSL::X509::Certificate 详细
OpenSSL::X509::CRL 详细
OpenSSL::X509::Extension 详细
OpenSSL::X509::ExtensionFactory 详细
OpenSSL::X509::Name 详细
OpenSSL::X509::Name::RFC2253DN 详细
OpenSSL::X509::Request 详细
OpenSSL::X509::Revoked 详细
OpenSSL::X509::Store 详细
OpenSSL::X509::StoreContext 详细
Ruby 2.4

Ruby 是一种面向对象、命令式、函数式、动态的通用编程语言,是世界上最优美而巧妙的语言。

主页 https://www.ruby-lang.org/
源码 https://github.com/ruby/ruby
版本 2.4
发布版本 2.4.1

Ruby 2.4目录

1.缩略 | Abbrev
2.ARGF
3.数组 | Array
4.Base64
5.基本对象 | BasicObject
6.基准测试 | Benchmark
7.BigDecimal
8.绑定 | Binding
9.CGI
10.类 | Class
11.比较 | Comparable
12.负责 | Complex
13.计算续体 | Continuation
14.覆盖 | Coverage
15.CSV
16.日期 | Date
17.日期时间 | DateTime
18.DBM
19.代理 | Delegator
20.摘要 | Digest
21.Dir
22.DRb
23.编码 | Encoding
24.枚举 | Enumerable
25.枚举 | Enumerator
26.ENV
27.ERB
28.错误 | Errors
29.Etc
30.期望值 | Exception
31.错误类 | FalseClass
32.Fiber
33.Fiddle
34.文件 | File
35.文件实用程序 | FileUtils
36.查找 | Find
37.浮点 | Float
38.Forwardable
39.GC
40.GDBM
41.GetoptLong
42.Hash
43.Integer
44.IO
45.IPAddr
46.JSON
47.Kernel
48.语言 | 3Language
49.记录 | Logger
50.编排 | Marshal
51.MatchData
52.数学 | Math
53.矩阵 | Matrix
54.方法 | Method
55.模型 | Module
56.监控 | Monitor
57. 互斥 | Mutex
58.Net
59.Net::FTP
60.Net::HTTP
61.Net::IMAP
62.Net::SMTP
63.NilClass
64.数字 | Numeric
65.对象 | Object
66.ObjectSpace
67.Observable
68.Open3
69.OpenSSL
70.OpenStruct
71.OpenURI
72.OptionParser
73.路径名 | Pathname
74.完整输出 | PrettyPrint
75.Prime
76.Proc
77.过程 | Process
78.PStore
79.PTY
80.队列 | Queue
81.随机 | Random
82.范围 | Range
83.合理的 | Rational
84.Readline
85.Regexp
86.Resolv
87.Ripper
88.RubyVM
89.Scanf
90.SDBM
91.SecureRandom
92.Set
93.Shell
94.信号 | Signal
95.Singleton
96.套接字 | Socket
97.字符串 | String
98.StringIO
99.StringScanner
100.结构 | Struct