本文简要介绍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 OpenSSL.fips_mode =用法及代码示例
- Ruby OpenSSL.print_mem_leaks用法及代码示例
- Ruby OpenSSL.Digest用法及代码示例
- Ruby OpenStruct.ostruct[name] =用法及代码示例
- Ruby OpenStruct类用法及代码示例
- Ruby OpenStruct.==用法及代码示例
- Ruby OpenStruct.each_pair用法及代码示例
- Ruby OpenStruct.delete_field用法及代码示例
- Ruby OpenStruct.dig用法及代码示例
- Ruby OpenStruct.new用法及代码示例
- Ruby OpenStruct.ostruct[name]用法及代码示例
- Ruby Open3.capture3用法及代码示例
- Ruby Open3.capture2用法及代码示例
- Ruby Open3.capture2e用法及代码示例
- Ruby Open3.popen2e用法及代码示例
- Ruby OpenRead.open用法及代码示例
- Ruby Open3.popen3用法及代码示例
- Ruby Open3.popen2用法及代码示例
- Ruby Open3.pipeline用法及代码示例
- Ruby OpenURI模块用法及代码示例
- Ruby Open3.pipeline_rw用法及代码示例
- Ruby Open3.pipeline_w用法及代码示例
- Ruby Open3.pipeline_r用法及代码示例
- Ruby Open3.pipeline_start用法及代码示例
- Ruby Option.level用法及代码示例
注:本文由纯净天空筛选整理自ruby-lang.org大神的英文原创作品 OpenSSL模块。非经特殊声明,原始代码版权归原作者所有,本译文未经允许或授权,请勿转载或复制。