如何使用 CloudHSM 在 Ruby 中实现双向 TLS(客户端)

5 ruby ssl openssl ruby-on-rails hsm

我在阅读了 StackOverflow、密码学、信息安全和 CloudHSM 论坛上的所有 CloudHSM 主题后提出这个问题,但找不到任何有用的内容。任何想法或代码片段都有帮助。

\n\n

我们有一个 Ruby 应用程序,它通过 X.509 证书向 Web 服务器请求,我们应该在 CloudHSM 内生成/托管私钥。

\n\n

我逐步按照CloudHSM 文档进行操作,并通过 NGINX 和 Apache HTTPD 配置了 TLS 卸载,以了解其工作原理,现在我正在使用 CloudHSM 进行双向 TLS 工作。

\n\n

我的网络服务器需要客户端证书,我可以通过 cURL 进行验证:

\n\n
curl --cert app-selfsigned.crt --key app-selfsigned.key  -k https://127.0.0.1/index.html\n
Run Code Online (Sandbox Code Playgroud)\n\n

我还可以使用此 Ruby 代码通过磁盘上的认证进行身份验证:

\n\n
require \'faraday\'\nrequire \'openssl\'\n\ndef ssl_options\n  cert_file = File.read "app-selfsigned.crt"\n  key_file = File.read "app-selfsigned.key"\n  ssl_options = {\n    verify: false,\n    client_cert: OpenSSL::X509::Certificate.new(cert_file),\n    client_key: OpenSSL::PKey::RSA.new(key_file)\n  }\nend\n\ndef connection\n  dest = "https://127.0.0.1/"\n  connection = Faraday::Connection.new(dest, ssl: ssl_options)\n  connection.get\nend\n
Run Code Online (Sandbox Code Playgroud)\n\n
irb -I . -r rubytest.rb\nconnection\n
Run Code Online (Sandbox Code Playgroud)\n\n

cURL 和 Ruby 通过磁盘上的认证进行测试:

\n\n

通过磁盘上的认证检查这张照片、cURL 和 Ruby 测试

\n\n

我需要app-selfsigned.key在 CloudHSM 内托管密钥,该怎么做?

\n\n

1) 我可以通过CloudHSM OpenSSL 动态引擎执行此操作吗?如果是,即使在安装了cloudhsm引擎(/opt/cloudhsm/lib/libcloudhsm_openssl.so)之后,我是否应该每次在我的代码中加载并安装引擎?

\n\n

2) 或者我应该通过p11-kit or pkcs11-openssl包和p11tool命令使用 PKCS#11 还是 Ruby PKCS#11?

\n\n

3) 我应该在 Ruby 应用程序中添加与我的 Ruby 应用程序相关的任何内容吗n3fips_password

\n\n

以下是我尝试使用 CloudHSM 的 Ruby 代码(我使用 FAKE PEM 密钥而不是真正的私钥来指向带有nginx-selfsigned_imported_keyCloudHSM 内标签的真实私钥):

\n\n
require \'faraday\'\nrequire \'openssl\'\n\ndef initialize_openssl\n  key_label = "nginx-selfsigned_imported_key"\n  # OpenSSL Engine:\n  OpenSSL::Engine.load\n  e = OpenSSL::Engine.by_id(\'cloudhsm\')\n  e.ctrl_cmd("SO_PATH", "/opt/cloudhsm/lib/libcloudhsm_openssl.so")\n  e.ctrl_cmd("ID", "cloudhsm")\n  e.ctrl_cmd("LOAD")\n  e.load_private_key("CKA_LABEL=#{ key_label }")\nend\n\ndef ssl_options\n  cert_file = File.read "app-selfsigned.crt"\n  key_file = File.read "app-selfsigned_fake_PEM.key"\n  {\n    verify: false,\n    client_cert: OpenSSL::X509::Certificate.new(cert_file),\n    client_key: OpenSSL::PKey::RSA.new(key_file)\n  }\nend\n\ndef connection\n  dest = "https://127.0.0.1/"\n  Faraday::Connection.new(dest, ssl: ssl_options)\nend\n\ndef connect\n  initialize_openssl\n  c = connection\n  c.get\nend\n
Run Code Online (Sandbox Code Playgroud)\n\n
irb -I . -r rubytest_cloudhsm.rb\ninitialize_openssl\n
Run Code Online (Sandbox Code Playgroud)\n\n

但我收到此错误:

\n\n
OpenSSL::Engine::EngineError: invalid cmd name\nfrom /root/self-signed/app-selfsigned/rubytest_cloudhsm.rb:9:in `ctrl_cmd\'\nfrom /root/self-signed/app-selfsigned/rubytest_cloudhsm.rb:9:in `initialize_openssl\'\nfrom (irb):1\nfrom /bin/irb:12:in `<main>\'\n
Run Code Online (Sandbox Code Playgroud)\n\n

逐行添加它们:

\n\n

在此输入图像描述

\n\n

CloudHSM 的 Ruby 错误:

\n\n

在此输入图像描述

\n\n

调试日志

\n\n

OpenSSL动态引擎已成功安装:

\n\n
OpenSSL::Engine::EngineError: invalid cmd name\nfrom /root/self-signed/app-selfsigned/rubytest_cloudhsm.rb:9:in `ctrl_cmd\'\nfrom /root/self-signed/app-selfsigned/rubytest_cloudhsm.rb:9:in `initialize_openssl\'\nfrom (irb):1\nfrom /bin/irb:12:in `<main>\'\n
Run Code Online (Sandbox Code Playgroud)\n\n

OpenSSL CloudHSM 动态引擎共享对象位于正确的位置:

\n\n
export n3fips_password=<Crypto User Username>:<CU Password>\nopenssl engine -tt cloudhsm\n# (cloudhsm) CloudHSM hardware engine support\n#       SDK Version: 2.03\n# [ available ]\nopenssl engine -vvvv dynamic -pre SO_PATH:/opt/cloudhsm/lib/libcloudhsm_openssl.so -pre ID:cloudhsm -pre LOAD\n# (dynamic) Dynamic engine loading support\n# [Success]: SO_PATH:/opt/cloudhsm/lib/libcloudhsm_openssl.so\n# [Success]: ID:cloudhsm\n# [Success]: LOAD\n# Loaded: (cloudhsm) CloudHSM hardware engine support\nopenssl speed -engine cloudhsm\n# SDK Version: 2.03\n# engine "cloudhsm" set.\n# Doing md2 for 3s on 16 size blocks:\n# 557992 md2\'s in 2.99s\nopenssl version\n# OpenSSL 1.0.2k-fips  26 Jan 2017\nrpm -qa | grep -i openssl\n# openssl-1.0.2k-16.amzn2.1.1.x86_64\n# openssl-libs-1.0.2k-16.amzn2.1.1.x86_64\n
Run Code Online (Sandbox Code Playgroud)\n\n

libcloudhsm_openssl.so:

\n\n

在此输入图像描述

\n\n

操作系统:

\n\n
cat /etc/os-release\n# NAME="Amazon Linux"\n# VERSION="2"\n# ID="amzn"\n# ID_LIKE="centos rhel fedora"\n# VERSION_ID="2"\n# PRETTY_NAME="Amazon Linux 2"\n# ANSI_COLOR="0;33"\n# CPE_NAME="cpe:2.3:o:amazon:amazon_linux:2"\n# HOME_URL="https://amazonlinux.com/"\nuname -a\n# Linux hsm.example.net 4.14.133-113.112.amzn2.x86_64 #1 SMP Tue Jul 30 18:29:50 UTC 2019 x86_64 x86_64 x86_64 GNU/Linux\n
Run Code Online (Sandbox Code Playgroud)\n\n
rpm -qa | grep -i cloudhsm-client\n# cloudhsm-client-2.0.3-3.el7.x86_64\n# cloudhsm-client-dyn-2.0.3-3.el6.x86_64\n
Run Code Online (Sandbox Code Playgroud)\n\n

我可以在有或没有 CloudHSM 引擎的情况下检查私钥和公钥:

\n\n
app-selfsigned.crt: Public Key\napp-selfsigned.key: Private key has been exported from CloudHSM \napp-selfsigned_fake_PEM.key: Fake private key pointing to real private key inside CloudHSM generated by getCaviumPrivKey -k 14 -out app-selfsigned_fake_PEM.key\n
Run Code Online (Sandbox Code Playgroud)\n\n
ls -ltrha /usr/lib64/openssl/engines/libcloudhsm.so\nlrwxrwxrwx 1 root root 40 Aug  7 09:56 /usr/lib64/openssl/engines/libcloudhsm.so -> /opt/cloudhsm/lib/libcloudhsm_openssl.so\n
Run Code Online (Sandbox Code Playgroud)\n\n

为了确保我在 CloudHSM 中请求正确的密钥,我通过 NGINX 配置了app-selfsigned.crtTLSapp-selfsigned_fake_PEM.key卸载:

\n\n

/etc/nginx/nginx.conf:

\n\n
ssl_engine cloudhsm;\nssl_certificate "/etc/pki/nginx/app-selfsigned.crt";\nssl_certificate_key "/etc/pki/nginx/private/app-selfsigned_fake_PEM.key";\n
Run Code Online (Sandbox Code Playgroud)\n\n
nginx -t\n# SDK Version: 2.03\n# nginx: the configuration file /etc/nginx/nginx.conf syntax is ok\n# nginx: configuration file /etc/nginx/nginx.conf test is successful\nsystemctl start nginx && systemctl status nginx\n# Aug 13 11:21:33 hsm.example.net nginx[13046]: SDK Version: 2.03\n
Run Code Online (Sandbox Code Playgroud)\n\n

通过 OpenSSL 检查证书文件:

\n\n
openssl x509 -in app-selfsigned.crt -text -noout\n# Serial Number: c7:c4:07:a6:78:22:2e:ff\n# Subject: C=AA, ST=AA, L=AA, O=AA, OU=AA, CN=a.com/emailAddress=a@a.com\n
Run Code Online (Sandbox Code Playgroud)\n\n

通过 Firefox 检查证书:

\n\n

在此输入图像描述

\n\n

通过 key_mgmt_util 和 cloudhsm_mgmt_util 检查 CloudHSM 内的私钥:

\n\n
cat /etc/os-release\n# NAME="Amazon Linux"\n# VERSION="2"\n# ID="amzn"\n# ID_LIKE="centos rhel fedora"\n# VERSION_ID="2"\n# PRETTY_NAME="Amazon Linux 2"\n# ANSI_COLOR="0;33"\n# CPE_NAME="cpe:2.3:o:amazon:amazon_linux:2"\n# HOME_URL="https://amazonlinux.com/"\nuname -a\n# Linux hsm.example.net 4.14.133-113.112.amzn2.x86_64 #1 SMP Tue Jul 30 18:29:50 UTC 2019 x86_64 x86_64 x86_64 GNU/Linux\n
Run Code Online (Sandbox Code Playgroud)\n\n
rpm -qa | grep -i cloudhsm-client\n# cloudhsm-client-2.0.3-3.el7.x86_64\n# cloudhsm-client-dyn-2.0.3-3.el6.x86_64\n
Run Code Online (Sandbox Code Playgroud)\n\n

如果您使用其他语言的 CloudHSM 双向 TLS,请将您的代码粘贴到此处,以便我了解想法并在 Ruby 中实现它。

\n\n

提前致谢。

\n

小智 3

对于稍后看到此问题的任何人,以下是与 Amazon CloudHSM 配合使用的示例 Ruby 代码:

require 'openssl'
require 'base64'

FAKE_KEY = "/root/ruby/ruby_key_inside_hsm/ruby_hsm_fake_private.key"
REAL_KEY = "/root/ruby/ruby_key_inside_hsm/ruby_hsm_real_private_exported.key"
PUB_KEY = "/root/ruby/ruby_key_inside_hsm/pubkey.pem"

STR = "test string"

def encrypt(str)
  pubkey = OpenSSL::PKey::RSA.new(File.read(PUB_KEY))
  Base64.encode64(pubkey.public_encrypt(str))
end

def decrypt(str, key)
  OpenSSL::Engine.load
  privkey = OpenSSL::PKey::RSA.new(File.read(key))
  privkey.private_decrypt(Base64.decode64(str))
end


def estr
  encrypt(STR)
end

def real_dec
  decrypt(estr, REAL_KEY)
end

def hsm_dec
  OpenSSL::Engine.load
  OpenSSL::Engine.by_id('cloudhsm')
  decrypt(estr, FAKE_KEY)
end

Run Code Online (Sandbox Code Playgroud)

目前我们正在努力将其添加到生产环境中。