本文簡要介紹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模塊。非經特殊聲明,原始代碼版權歸原作者所有,本譯文未經允許或授權,請勿轉載或複製。