java - 获取 ruby​​ 的 "RSA/ECB/OAEPWithSHA-256AndMGF1Padding"组合

标签 java ruby ruby-on-rails-3 cryptography public-key-encryption

我有这个用于加密和解密的java代码,我想将其更改/转换为Ruby代码。我在 OpenSSL gem 中查找,但没有找到可用于 ruby​​ 的“RSA/ECB/OAEPWithSHA-256AndMGF1Padding”组合。我该如何实现它?

public class EncryptDecryptService {
    
    public String encryptRequestObject(RequestObject requestObject) throws UnsupportedEncodingException, FileNotFoundException, CertificateException, InvalidKeyException, NoSuchAlgorithmException, NoSuchPaddingException, IllegalBlockSizeException, BadPaddingException {
        PublicKey publicKey = getPublicKey(requestObject.getKeyFilename());
        byte[] message = requestObject.getString().getBytes("UTF-8");
        byte[] secret = encrypt(publicKey, message);
        return Base64.encodeBase64String(secret);
    }
    
    public String decryptRequestObject(RequestObject requestObject) throws UnrecoverableKeyException, KeyStoreException, NoSuchAlgorithmException, CertificateException, IOException, InvalidKeyException, NoSuchPaddingException, IllegalBlockSizeException, BadPaddingException {
        PrivateKey privateKey = getPrivateKey(requestObject.getKeyFilename(), requestObject.getKeyPassword());
        byte[] cipherText = Base64.decodeBase64(requestObject.getString());
        byte[] decrypted = decrypt(privateKey, cipherText);
        return new String(decrypted, "UTF-8");
    }
    
    private PublicKey getPublicKey(String filename) throws FileNotFoundException, CertificateException {
        FileInputStream fin = new FileInputStream(filename);
        CertificateFactory factory = CertificateFactory.getInstance("X.509");
        X509Certificate certificate = (X509Certificate) factory.generateCertificate(fin);
        PublicKey publicKey = certificate.getPublicKey();
        return publicKey;
    }
    
    private PrivateKey getPrivateKey(String filename, String password) throws KeyStoreException, NoSuchAlgorithmException, CertificateException, IOException, UnrecoverableKeyException {
        FileInputStream fin = new FileInputStream(filename);
        KeyStore ks = KeyStore.getInstance("pkcs12");
        ks.load(fin, password.toCharArray());
        String str = ks.aliases().nextElement();
        PrivateKey privateKey = (PrivateKey) ks.getKey(str, password.toCharArray());
        return privateKey;
    }
    
    private byte[] encrypt(PublicKey key, byte[] plainText) throws NoSuchAlgorithmException, NoSuchPaddingException, InvalidKeyException, IllegalBlockSizeException, BadPaddingException {
        Cipher cipher = Cipher.getInstance("RSA/ECB/OAEPWithSHA-256AndMGF1Padding");
        cipher.init(Cipher.ENCRYPT_MODE, key);
        return cipher.doFinal(plainText);
    }
    
    private byte[] decrypt(PrivateKey key, byte[] cipherText) throws NoSuchAlgorithmException, NoSuchPaddingException, InvalidKeyException, IllegalBlockSizeException, BadPaddingException {
        Cipher cipher = Cipher.getInstance("RSA/ECB/OAEPWithSHA-256AndMGF1Padding");
        cipher.init(Cipher.DECRYPT_MODE, key);
        return cipher.doFinal(cipherText);
    }
}

最佳答案

OAEP 使用多个参数,包括两个摘要,一个用于 OAEP(即用于散列 OAEP 标签),一个用于掩码生成函数 (MGF1),请参阅 RFC8017, sec. 7.1 .

标识符 RSA/ECB/OAEPWithSHA-256AndMGF1Padding 不明确,取决于提供商。例如,SunJCE 提供程序使用 SHA-256 作为 OAEP 摘要,使用 SHA-1 作为 MGF1 摘要,BouncyCaSTLe 提供程序对这两个摘要都使用 SHA-256。

下面是用Java代码加密、用Ruby代码解密的例子(反方向模拟)。


在 Java 端,SunJCE 提供程序使用 WLOG 并确定涉及的摘要:

String pubKey = "MIGfMA0GCSqGSIb3DQEBAQUAA4GNADCBiQKBgQDNvs/qUMjkfq2E9o0qn03+KJE7" + 
                "ASczEbn6q+kkthNBdmTsskikWsykpDPnLWhAVkmjz4alQyqw+mHYP9xhx8qUC4A3" + 
                "tXY0ObxANUUKhUvR7zNj4vk4t8F2nP3erWvaG8J+sN3Ubr40ZYIYLS6UHYRFrqRD" + 
                "CDhUtyjwERlz8KhLyQIDAQAB";

KeyFactory keyFactory = KeyFactory.getInstance("RSA");
X509EncodedKeySpec keySpec = new X509EncodedKeySpec(Base64.getDecoder().decode(pubKey));
RSAPublicKey publicKey = (RSAPublicKey) keyFactory.generatePublic(keySpec);

byte[] plaintext = "The quick brown fox jumps over the lazy dog".getBytes(StandardCharsets.UTF_8);
byte[] ciphertext = encrypt(publicKey, plaintext);

System.out.println("Ciphertext: " + Base64.getEncoder().encodeToString(ciphertext));

private static byte[] encrypt(PublicKey key, byte[] plainText) throws Exception {
    Cipher cipher = Cipher.getInstance("RSA/ECB/OAEPWithSHA-256AndMGF1Padding"); 
    cipher.init(Cipher.ENCRYPT_MODE, key);
    OAEPParameterSpec oaepParameterSpec = cipher.getParameters().getParameterSpec(OAEPParameterSpec.class);
    MGF1ParameterSpec mgf1ParameterSpec = (MGF1ParameterSpec)oaepParameterSpec.getMGFParameters();
    System.out.println("Provider  : " + cipher.getProvider().getName());
    System.out.println("OAEP-Hash : " + oaepParameterSpec.getDigestAlgorithm());
    System.out.println("MGF1-Hash : " + mgf1ParameterSpec.getDigestAlgorithm());
    return cipher.doFinal(plainText);
}

对应于发布的 encrypt() 方法(附加输出除外)。该代码产生(例如)以下输出:

Provider  : SunJCE
OAEP-Hash : SHA-256
MGF1-Hash : SHA-1
Ciphertext: WlozD9ojNRQafip41dpuuhBMe7ruH2FBWnMhbAaSuAtPDpHOUyKaAm6mO15BbvL3eTXyqfEQx29dYPJEbUr5T/WXs846PQN6g7Yv25EXGVbPCzc4aIbms76C1jP92wXNEGWMnu624Fq5W9MVXX75mfaY0Fjvrh5k/TFuO4AIxMk=

为了完整起见,应该提到的是,也可以通过以下方式明确指定参数:

Cipher cipher = Cipher.getInstance("RSA/ECB/OAEPPadding"); 
OAEPParameterSpec oaepParameterSpec = new OAEPParameterSpec("SHA-256", "MGF1", new MGF1ParameterSpec("SHA-1"), PSource.PSpecified.DEFAULT);
cipher.init(Cipher.ENCRYPT_MODE, key, oaepParameterSpec);

由于上述的矛盾性,这种明确的规范是更可靠的替代方案。


确定摘要后(因为提供者已知或明确使用上述输出),可以进行 Ruby 实现。

Ruby 的可能 OAEP 实现是 openssl-oaep .

这样,解密的Ruby代码就可以实现如下:

require 'openssl'
require 'openssl/oaep'
require 'base64'

private_key = 
'-----BEGIN PRIVATE KEY-----
MIICeAIBADANBgkqhkiG9w0BAQEFAASCAmIwggJeAgEAAoGBAM2+z+pQyOR+rYT2
jSqfTf4okTsBJzMRufqr6SS2E0F2ZOyySKRazKSkM+ctaEBWSaPPhqVDKrD6Ydg/
3GHHypQLgDe1djQ5vEA1RQqFS9HvM2Pi+Ti3wXac/d6ta9obwn6w3dRuvjRlghgt
LpQdhEWupEMIOFS3KPARGXPwqEvJAgMBAAECgYADxGqqL7B9/pPOy3TqQuB6tuNx
4SOGm9x76onqUisoF7LhYqJR4Be/LAKHSR2PkATpKvOcMw6lDvCbtQ+j+rSK2PkN
4iDi1RYqbLUbZBS8vhrgU0CPlmgSSp1NBsqMK9265CaJox3frxmBK1yuf22RboIK
pqOzcluuA4aqLegmwQJBAP0+gM/tePzx+53DrxpYQvlfi9UJo7KeqIFL8TjMziKt
EaRGeOZ6UX/r6CQHojYKnNti7pjAwonsdwCTcv1yy7sCQQDP+/ww49VFHErON/MO
w5iYCsrM5Lx+Yc2JAjetCDpkMrRT92cgQ0nxR5+jNeh+gE2AmB9iKlNxsHJoRaPQ
lBRLAkEAl9hiZEp/wStXM8GhvKovfldMAPFGtlNrthtTCDvFXgVoDpgy5f9x3sIU
74WkPcMfSmyHpA/wlcKzmCTRTicHAQJBALUjq7MQ2tAEIgqUo/W52I6i55mnpZsU
pyOqcL8cqW5W0sNGd+SbdizTym8lJkX2jIlw8/RVFLOxjxLNhCzGqx0CQQDeUMnw
7KGP3F7BnbsXCp64YDdihzSO5X/Mfwxw6+S/pyKZ0/X4uwt24kZuoDnFzGWJYlea
sDQC6enIru+ne5es
-----END PRIVATE KEY-----'

key = OpenSSL::PKey::RSA.new(private_key) 
label = ''
md_oaep = OpenSSL::Digest::SHA256
md_mgf1 = OpenSSL::Digest::SHA1
cipher_text_B64 = 'WlozD9ojNRQafip41dpuuhBMe7ruH2FBWnMhbAaSuAtPDpHOUyKaAm6mO15BbvL3eTXyqfEQx29dYPJEbUr5T/WXs846PQN6g7Yv25EXGVbPCzc4aIbms76C1jP92wXNEGWMnu624Fq5W9MVXX75mfaY0Fjvrh5k/TFuO4AIxMk='
cipher_text = Base64.decode64(cipher_text_B64)
plain_text = key.private_decrypt_oaep(cipher_text, label, md_oaep, md_mgf1)
print(plain_text) # The quick brown fox jumps over the lazy dog

以原始明文作为输出。

关于java - 获取 ruby​​ 的 "RSA/ECB/OAEPWithSHA-256AndMGF1Padding"组合,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/65018514/

相关文章:

java - 如何访问默认包中的 java 类?

java - 调用 cloudant db 服务,出现错误 javax.net.ssl.SSLException : Received fatal alert: protocol_version

java - 如何在树 Java 中查找节点

sql - 优化查询,使 Order by random 运行得更快

ruby-on-rails - Rails 3 身份验证插件建议?

java - 当存在带有集合的属性时反序列化 JSON

ruby - 替换 Ruby 中的 to_s 方法。没有打印出想要的字符串

javascript - 如何使用 ROR 从 Controller 操作调用 javascript 函数

ruby-on-rails - 如何在没有 CRUD 操作的情况下路由 Controller ?

ruby-on-rails - 在 Rails 中呈现 Action 时不显示警报