Ruby on rails 使用Ruby验证Web加密子脚本密钥签名

Ruby on rails 使用Ruby验证Web加密子脚本密钥签名,ruby-on-rails,openssl,rsa,private-key,webcrypto-api,Ruby On Rails,Openssl,Rsa,Private Key,Webcrypto Api,我正在努力验证Ruby中使用RSASSA-PKCS1-v1_5私钥加密的消息 密钥对是使用SubTelcrypto(Web加密)API生成的。然后将公钥发送到服务器 const usage = ["sign", "verify"]; const exponent = new Uint8Array([0x01, 0x00, 0x01]); const encryption = {name: 'RSASSA-PKCS1-v1_5', modulusLength: 2048, publicExpone

我正在努力验证Ruby中使用RSASSA-PKCS1-v1_5私钥加密的消息

密钥对是使用SubTelcrypto(Web加密)API生成的。然后将公钥发送到服务器

const usage = ["sign", "verify"];
const exponent = new Uint8Array([0x01, 0x00, 0x01]);
const encryption = {name: 'RSASSA-PKCS1-v1_5', modulusLength: 2048, publicExponent: exponent, hash: {name: "SHA-256"}};
const keys = await window.crypto.subtle.generateKey(encryption, true, usage);
const exportedKey = await window.crypto.subtle.exportKey("spki", keys.publicKey);
let body = window.btoa(String.fromCharCode(...new Uint8Array(exportedKey)));
body = body.match(/.{1,64}/g).join('\n');
const publicKey `-----BEGIN PUBLIC KEY-----\n${body}\n-----END PUBLIC KEY-----`;

uploadToServer(publicKey);
我使用私钥加密消息

const message = "My Message";
const messageData = message.toArrayBuffer();
const signature = await window.crypto.subtle.sign({name: 'RSASSA-PKCS1-v1_5'}, keys.privateKey, messageData);

sendSignatureToServer(signature.toString());
我可以使用JS成功地验证签名,但是,我的Ruby逻辑总是返回false

publicKeyString = "-----BEGIN PUBLIC KEY-----\n...\n-----END PUBLIC KEY-----"; # Sent to server in a previous request
signature = params[:signature]
message = "My Message"

publicKey = OpenSSL::PKey::RSA.new(publicKeyString)
verified = publicKey.verify(OpenSSL::Digest::SHA256.new, signature, message)

非常感谢您的指导。

我无法重现这个问题

我使用了您发布的代码(对所需输出和某些缺失部分进行了一些更改)来生成RSA密钥对和签名:

const usage = ["sign", "verify"];
const exponent = new Uint8Array([0x01, 0x00, 0x01]);
const encryption = {name: 'RSASSA-PKCS1-v1_5', modulusLength: 2048, publicExponent: exponent, hash: {name: "SHA-256"}};
window.crypto.subtle.generateKey(encryption, true, usage).then(function (key){
    const message = "My Message";
    const messageData = new TextEncoder().encode(message);
    window.crypto.subtle.sign({name: 'RSASSA-PKCS1-v1_5'}, key.privateKey, messageData).then(function (signature){
        console.log("Signature: " + buf2hex(signature));                    
        window.crypto.subtle.exportKey("spki", key.publicKey).then(function(publicKey){
            console.log("Public key: " + buf2hex(publicKey));
            let publicKeyBase64 = window.btoa(String.fromCharCode.apply(null, new Uint8Array(publicKey)));
            publicKeyBase64 = publicKeyBase64.match(/.{1,64}/g).join('\n');
            publicKeyBase64 = `-----BEGIN PUBLIC KEY-----\n${publicKeyBase64}\n-----END PUBLIC KEY-----`;
            console.log("Public key (base64): " + publicKeyBase64);
        });             
    });
});

// https://stackoverflow.com/a/40031979/9014097
function buf2hex(buffer) { // buffer is an ArrayBuffer
    return Array.prototype.map.call(new Uint8Array(buffer), x => ('00' + x.toString(16)).slice(-2)).join('');
}
由于JavaScript端的验证对您有效,因此问题不应出现在本部分中,因此更改应该是无问题的。公钥(Base64)的JavaScript代码的典型输出为:

对于相应的签名(十六进制):

现在来看有趣的部分:Ruby端的代码包含以下数据:

require 'openssl'

def hex2bin(s)
    s.scan(/../).map { |x| x.hex }.pack('c*')
end

publicKeyString = '-----BEGIN PUBLIC KEY-----
MIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEA2txHYgsCGfS9eZfys/m1
P1rhi7x1frM02QbgdR1oXjP6qyarvPHJSbOnpZR7FkuZ4Y1q+DLb98iNDw+AlP0z
HxE6Q2qsRHIfjeperJTtKXLNfrBtdVrp+UbtpiyU6HOXyXt/8joIE1s4+AMNIK47
XghXB1RXKzt/WO4ykldSYAcx00Hfjf5i1ABc16jF0dSfSDDEmWj6WCYn64K9yBOO
I1s8ijmwDyadK5Mgs1YPaPGHtuRMatHn02jXUyhyYWMRdd6evlan1MhHYv1z517M
Wq+Blx/+5vJw58SYn4/i9Pn5VbscQhhwJVxLW8BWQUEl9zeTXmMSWMxGT8+yP69f
hwIDAQAB
-----END PUBLIC KEY-----' 

signature = hex2bin('10461ea36a99495d4e1b1ec066a79cb5270c6dca3c0b2e700d2d5d6eed111328841611135d46b1d09e82906a93d8cb423d5daf4bfd8be4d0b90314103d2bbb89e506a4b84d8a36ac39b2f50e576d7cdbd1d50ec0f71c47ccfbf3309b7cbf0b2f9f0b1eef65b0848de1a1801b5cc5e41438487d9a32c0428de3acfdab41dd83bffdccc5ebfc7e03ae35343139eb7faaab3493b33e9d585e64e677e8d5f6afd37a190a3a86dc0059131c31d8881c58ef2572a13609e0e7884d65b12fe83dfc16d446eca4df4913b94d78103ed90b779d75ded5993e0fd1fac5f11455ae97d11a4d92e8868087f44ee337e522455b452907456ca54d600df97d09df6839e61c8952')

message = "My Message"

publicKey = OpenSSL::PKey::RSA.new(publicKeyString)
puts publicKey.verify(OpenSSL::Digest::SHA256.new, signature, message) # provides true
验证成功,可以很容易地进行检查

由于Ruby端的验证主要是有效的,所以我只能猜测您的数据(密钥和/或签名)可能在发送过程中被更改 或者在Ruby端进行重构,从而导致验证失败。公钥和双方签名的字节比较 我可以澄清这一点

10461ea36a99495d4e1b1ec066a79cb5270c6dca3c0b2e700d2d5d6eed111328841611135d46b1d09e82906a93d8cb423d5daf4bfd8be4d0b90314103d2bbb89e506a4b84d8a36ac39b2f50e576d7cdbd1d50ec0f71c47ccfbf3309b7cbf0b2f9f0b1eef65b0848de1a1801b5cc5e41438487d9a32c0428de3acfdab41dd83bffdccc5ebfc7e03ae35343139eb7faaab3493b33e9d585e64e677e8d5f6afd37a190a3a86dc0059131c31d8881c58ef2572a13609e0e7884d65b12fe83dfc16d446eca4df4913b94d78103ed90b779d75ded5993e0fd1fac5f11455ae97d11a4d92e8868087f44ee337e522455b452907456ca54d600df97d09df6839e61c8952
require 'openssl'

def hex2bin(s)
    s.scan(/../).map { |x| x.hex }.pack('c*')
end

publicKeyString = '-----BEGIN PUBLIC KEY-----
MIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEA2txHYgsCGfS9eZfys/m1
P1rhi7x1frM02QbgdR1oXjP6qyarvPHJSbOnpZR7FkuZ4Y1q+DLb98iNDw+AlP0z
HxE6Q2qsRHIfjeperJTtKXLNfrBtdVrp+UbtpiyU6HOXyXt/8joIE1s4+AMNIK47
XghXB1RXKzt/WO4ykldSYAcx00Hfjf5i1ABc16jF0dSfSDDEmWj6WCYn64K9yBOO
I1s8ijmwDyadK5Mgs1YPaPGHtuRMatHn02jXUyhyYWMRdd6evlan1MhHYv1z517M
Wq+Blx/+5vJw58SYn4/i9Pn5VbscQhhwJVxLW8BWQUEl9zeTXmMSWMxGT8+yP69f
hwIDAQAB
-----END PUBLIC KEY-----' 

signature = hex2bin('10461ea36a99495d4e1b1ec066a79cb5270c6dca3c0b2e700d2d5d6eed111328841611135d46b1d09e82906a93d8cb423d5daf4bfd8be4d0b90314103d2bbb89e506a4b84d8a36ac39b2f50e576d7cdbd1d50ec0f71c47ccfbf3309b7cbf0b2f9f0b1eef65b0848de1a1801b5cc5e41438487d9a32c0428de3acfdab41dd83bffdccc5ebfc7e03ae35343139eb7faaab3493b33e9d585e64e677e8d5f6afd37a190a3a86dc0059131c31d8881c58ef2572a13609e0e7884d65b12fe83dfc16d446eca4df4913b94d78103ed90b779d75ded5993e0fd1fac5f11455ae97d11a4d92e8868087f44ee337e522455b452907456ca54d600df97d09df6839e61c8952')

message = "My Message"

publicKey = OpenSSL::PKey::RSA.new(publicKeyString)
puts publicKey.verify(OpenSSL::Digest::SHA256.new, signature, message) # provides true