简体   繁体   English

Java等同于带有密码保护密钥的PHP的openssl_open?

[英]What is the Java equivalent to PHP's openssl_open with passphrase protected key?

I have some encrypted data that was generated with PHP openssl_seal function. 我有一些使用PHP openssl_seal函数生成的加密数据。

I need to decrypt (open) that data with Java. 我需要使用Java解密(打开)该数据。

I've found an article that explains it ( http://blog.local.ch/en/2007/10/29/openssl-php-to-java/ ), but this does not cover the scenario when the key needed to decrypt the data is in turn passphrase-protected. 我发现有一篇文章对此进行了解释( http://blog.local.ch/zh/2007/10/29/openssl-php-to-java/ ),但是当需要使用解密数据反过来受密码短语保护。

How should I modify the solution mentioned in the article to use a passphrase-protected key? 我应该如何修改本文中提到的解决方案以使用受密码保护的密钥?

Code of the mentioned article: 所提到文章的代码:

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);

    }

}

Usage according to the article: 用法根据文章:

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

thanks! 谢谢!

You would need to modify the keyPairFromPEMFile method on the provided code to something like this: 您需要将提供的代码上的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();

}

Now you can specify a password to retrieve the PEM file that stores the key by creating a new PasswordFinder instance to return the given password as an array of char. 现在,您可以通过创建新的PasswordFinder实例以将给定密码作为char数组返回的方式,指定密码来检索存储密钥的PEM文件。

Note that I'm using bouncycastle version 1.46. 请注意,我使用的是Bouncycastle 1.46版。 If you move to newer versions you will need to adapt your code in several ways, specially the passwordFinder will be replaced with a new Password() instance. 如果您使用较新的版本,则需要以多种方式修改代码,特别是passwordFinder会替换为new Password()实例。 You can refer to this other questions regarding such adaptations: Bouncy Castle : PEMReader => PEMParser 您可以参考有关此类改编的其他问题: Bouncy Castle:PEMReader => PEMParser

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM