java - 与带有密码保护 key 的 PHP openssl_open 等效的 Java 是什么?

标签 java php encryption openssl passphrase

我有一些用 PHP 生成的加密数据 openssl_seal功能。

我需要用 Java 解密(打开)该数据。

我找到了一篇对其进行解释的文章 ( http://blog.local.ch/en/2007/10/29/openssl-php-to-java/ ),但这并不涵盖解密数据所需的 key 又受密码保护的情况。

我应该如何修改文章中提到的解决方案以使用受密码保护的 key ?

上述文章的代码:

package ch.local.common.util.crypto;

import java.io.File;
import java.io.FileInputStream;
import java.io.FileReader;
import java.security.Key;
import java.security.KeyPair;
import java.security.KeyStore;
import java.security.PrivateKey;
import java.security.PublicKey;
import java.security.Security;
import java.security.cert.Certificate;

import javax.crypto.Cipher;
import javax.crypto.SecretKey;
import javax.crypto.spec.SecretKeySpec;

import org.bouncycastle.jce.provider.BouncyCastleProvider;
import org.bouncycastle.openssl.PEMReader;
import org.bouncycastle.util.encoders.Base64;

/**
 * For decrypting data encrypted with PHP's openssl_seal()
 * 
 * Example - String envKey is the Base64 encoded, RSA encrypted envelop key
 * and String sealedData is the Base64 encoded, RC4 encrypted payload, which
 * you got from PHP's openssl_seal() then;
 * 
 * <pre>
 * KeyPair keyPair = OpenSSLInterop.keyPairPEMFile(
 *          "/path/to/openssl/privkey_rsa.pem"
 *      );
 * 
 * PrivateKey privateKey = keyPair.getPrivate();
 * String plainText = OpenSSLInterop.decrypt(sealedData, envKey, privateKey);
 * </pre>
 * 
 * @see http://www.php.net/openssl_seal
 * @author Harry Fuecks
 * @since Oct 25, 2007
 * @version $Id$
 */
public class OpenSSLInterop {

    private static boolean bcInitialized = false;

    /**
     * @param cipherKey
     * @param cipherText
     * @param privateKey
     * @return
     * @throws Exception
     */
    public static String decrypt(String cipherText, String cipherKey, PrivateKey privateKey)
    throws Exception {

        return decrypt(cipherText, cipherKey, privateKey, "UTF-8");

    }

    /**
     * @param cipherKey Base64 encoded, RSA encrypted key for RC4 decryption
     * @param cipherText Base64 encoded, RC4 encrypted payload
     * @param privateKey
     * @param charsetName
     * @return decrypted payload
     * @throws Exception
     */
    public static String decrypt(String cipherText, String cipherKey, PrivateKey privateKey, String charsetName)
    throws Exception {

        byte[] plainKey = decryptRSA(Base64.decode(cipherKey), privateKey);
        byte[] plaintext = decryptRC4(plainKey, Base64.decode(cipherText));
        return new String(plaintext, charsetName);

    }

    /**
     * Loads a KeyPair object from an OpenSSL private key file
     * (Just wrapper around Bouncycastles PEMReader)
     * @param filename
     * @return
     * @throws Exception
     */
    public static KeyPair keyPairFromPEMFile(String filename)
    throws Exception {

        if ( !bcInitialized ) {
            Security.addProvider(new BouncyCastleProvider());
            bcInitialized = true;
        }

        FileReader keyFile = new FileReader(filename);
        PEMReader pemReader = new PEMReader(keyFile);
        return (KeyPair)pemReader.readObject();

    }

    /**
     * Returns a KeyPair from a Java keystore file.
     * 
     * Note that you can convert OpenSSL keys into Java Keystore using the
     * "Not yet commons-ssl" KeyStoreBuilder
     * See - http://juliusdavies.ca/commons-ssl/utilities/
     * e.g.
     * 
     * $ java -cp not-yet-commons-ssl-0.3.8.jar org.apache.commons.ssl.KeyStoreBuilder \
     *     "privkey password" ./privkey.pem ./pubkey.pem 
     *   
     * @param filename
     * @param alias
     * @param keystorePassword
     * @return
     */
    public static KeyPair keyPairFromKeyStore(String filename, String alias, String keystorePassword)
    throws Exception {

        KeyStore keystore = KeyStore.getInstance("JKS");
        File keystoreFile = new File(filename);

        FileInputStream in = new FileInputStream(keystoreFile);
        keystore.load(in, keystorePassword.toCharArray());
        in.close();

        Key key = keystore.getKey(alias, keystorePassword.toCharArray());
        Certificate cert = keystore.getCertificate(alias);
        PublicKey publicKey = cert.getPublicKey();

        return new KeyPair(publicKey, (PrivateKey)key);

    }

    /**
     * @param cipherKey
     * @param privateKey
     * @return
     * @throws Exception
     */
    private static byte[] decryptRSA(byte[] cipherKey, PrivateKey privateKey) throws Exception {

        Cipher cipher = Cipher.getInstance("RSA");
        cipher.init(Cipher.DECRYPT_MODE, privateKey);
        return cipher.doFinal(cipherKey);

    }

    /**
     * Defaults to UTF-8 as the output encoding
     * @param plainKey Base64 encoded, RSA encrypted key for RC4 decryption
     * @param cipherText Base64 encoded, RC4 encrypted payload
     * @return decrypted payload 
     * @throws Exception
     */
    private static byte[] decryptRC4(byte[] plainKey, byte[] cipherText)
    throws Exception {

        SecretKey skeySpec = new SecretKeySpec(plainKey, "RC4");
        Cipher cipher = Cipher.getInstance("RC4");
        cipher.init(Cipher.DECRYPT_MODE, skeySpec);
        return cipher.doFinal(cipherText);

    }

}

根据文章使用:

KeyPair keyPair = OpenSSLInterop. keyPairFromPEMFile("/path/to/openssl/privkey_rsa.pem");
PrivateKey privateKey = keyPair.getPrivate();
String plainText = OpenSSLInterop.decrypt(sealedData, envKey, privateKey);

谢谢!

最佳答案

您需要将提供的代码上的 keyPairFromPEMFile 方法修改为如下所示:

public static KeyPair keyPairFromPEMFile(String filename, final String password) throws Exception {

    if ( !bcInitialized ) {
        Security.addProvider(new BouncyCastleProvider());
        bcInitialized = true;
    }

    FileReader keyFile = new FileReader(filename);
    PEMReader pemReader = new PEMReader(keyFile,  new PasswordFinder() {

        public char[] getPassword() {
            return password.toCharArray();
        }});

    return (KeyPair)pemReader.readObject();

}

现在,您可以指定密码来检索存储 key 的 PEM 文件,方法是创建一个新的 PasswordFinder 实例,以字符数组形式返回给定密码。

请注意,我使用的是 bouncycaSTLe 版本 1.46。如果您迁移到较新的版本,您将需要以多种方式调整您的代码,特别是passwordFinder将被替换为new Password()实例。您可以引用有关此类调整的其他问题:Bouncy Castle : PEMReader => PEMParser

关于java - 与带有密码保护 key 的 PHP openssl_open 等效的 Java 是什么?,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/33817374/

相关文章:

java - 无法存储长度超过 32766 的关键字文本字段

java - 如何开始使用 Chainsaw for Log4j?

php - 如何在 Laravel 4 中使用 RESTful 实现到 Controller 的命名路由?

php - Laravel Facebook 登录 - 无回调(本地主机)

python - 使用 AES 在 C 中加密并在 Python 中解密

java - 在 Java 中使用公共(public)键合并两个列表

java - Callable 应该优于 Runnable 吗?

java - Java 中带有三位数字 key 的凯撒密码

php - 更新数据库类中的删除函数,以排除两组条件

java - 使用 PHP OpenSSL 将 Java AES/ECB/PKCS7Padding/代码转换为 PHP