當前位置: 首頁>>代碼示例 >>用法及示例精選 >>正文


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