当前位置: 首页>>代码示例 >>用法及示例精选 >>正文


Ruby OpenSSL模块用法及代码示例


本文简要介绍ruby语言中 OpenSSL模块 的用法。

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

例子

所有示例都假定您已加载 OpenSSL

require 'openssl'

这些示例相互叠加。例如,接下来创建的 key 将在这些示例中使用。

钥匙

创建 key

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

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

导出 key

未经加密保存到磁盘的 key 是不安全的,因为任何获得 key 的人都可以使用它,除非它被加密。为了安全地导出 key ,您可以使用密码短语将其导出。

cipher = OpenSSL::Cipher.new 'aes-256-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 返回可用密码列表。

加载 key

也可以从文件中加载 key 。

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

或者

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

加载加密 key

OpenSSL 将在加载加密 key 时提示您输入密码。如果您无法输入密码短语,您可以在加载 key 时提供它:

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

RSA 加密

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

加解密

非对称公钥/私钥加密速度很慢,并且在不使用填充或直接加密较大数据块的情况下成为攻击的受害者。 RSA 加密的典型用例包括 “wrapping” 对称 key 和接收者的公钥,接收者将再次使用他们的私钥 “unwrap” 该对称 key 。以下说明了这种 key 传输方案的简化示例。它不应该在实践中使用,但应该始终首选标准化协议。

wrapped_key = key.public_encrypt key

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

original_key = key.private_decrypt wrapped_key

默认情况下,将使用 PKCS#1 填充,但也可以使用其他形式的填充,有关详细信息,请参阅 PKey::RSA

签名

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

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

signature = key.sign 'SHA256', document

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

if key.verify 'SHA256', signature, document
  puts 'Valid'
else
  puts 'Invalid'
end

PBKDF2 基于密码的加密

如果使用的底层 OpenSSL 版本支持,基于密码的加密应该使用 PKCS5 的函数。如果不支持或旧应用程序需要,则还支持 RFC 2898 中指定的较旧、安全性较低的方法(见下文)。

PKCS5 支持 PBKDF2,因为它在 PKCS#5 v2.0 中指定。它仍然使用密码、盐,以及额外的一些迭代,这些迭代会减慢 key 派生过程。速度越慢,暴力破解生成的 key 所需的工作就越多。

加密

该策略是首先实例化一个 Cipher 进行加密,然后使用 PBKDF2 生成一个随机 IV 和一个从密码派生的 key 。 PKCS #5 v2.0 建议盐至少使用 8 个字节,迭代次数很大程度上取决于所使用的硬件。

cipher = OpenSSL::Cipher.new 'aes-256-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.new('SHA256')

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 key ,这次设置 Cipher 进行解密。

cipher = OpenSSL::Cipher.new 'aes-256-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.new('SHA256')

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 中。它允许使用短密码或密码来创建安全加密 key 。如果可能,如果情况允许,应使用上述 PBKDF2。

PKCS #5 使用 Cipher 、密码短语和 salt 来生成加密 key 。

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

加密

首先设置密码进行加密

encryptor = OpenSSL::Cipher.new 'aes-256-cbc'
encryptor.encrypt
encryptor.pkcs5_keyivgen pass_phrase, salt

然后传递你要加密的数据

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

解密

使用为解密设置的新 Cipher 实例

decryptor = OpenSSL::Cipher.new 'aes-256-cbc'
decryptor.decrypt
decryptor.pkcs5_keyivgen pass_phrase, salt

然后传递你要解密的数据

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

X509 证书

创建证书

此示例使用 RSA key 和 SHA1 签名创建 self-signed 证书。

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#sign 。这将创建 self-signed 证书,因为我们使用与创建证书相同的名称和 key 来签署证书。

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

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

加载证书

与 key 一样,证书也可以从文件中加载。

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

验证证书

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

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

证书颁发机构

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

CA key

CA key 很有价值,因此我们将其加密并保存到磁盘,并确保其他用户无法读取它。

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

cipher = OpenSSL::Cipher.new 'aes-256-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 的 key 可以用作 CA。

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

此扩展表明 CA 的 key 可用于验证证书和证书吊销上的签名。

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

根 CA 证书是self-signed。

ca_cert.sign ca_key, OpenSSL::Digest.new('SHA1')

CA 证书保存在磁盘上,因此它可以分发给该 CA 将签署的 key 的所有用户。

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

证书签名请求

CA 通过证书签名请求 (CSR) 对 key 进行签名。 CSR 包含识别 key 所需的信息。

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

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 key 签名并返回给请求者。

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.new('SHA1')

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

SSL 和 TLS 连接

使用我们创建的 key 和证书,我们可以创建 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 = ssl_connection.gets

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

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

SSL 客户端

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

请注意,默认情况下 SSLSocket#close 不会关闭底层套接字。如果需要, Set SSLSocket#sync_close 为真。

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

相关用法


注:本文由纯净天空筛选整理自ruby-lang.org大神的英文原创作品 OpenSSL模块。非经特殊声明,原始代码版权归原作者所有,本译文未经允许或授权,请勿转载或复制。