簡體   English   中英

Java等同於帶有密碼保護密鑰的PHP的openssl_open?

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

我有一些使用PHP openssl_seal函數生成的加密數據。

我需要使用Java解密(打開)該數據。

我發現有一篇文章對此進行了解釋( http://blog.local.ch/zh/2007/10/29/openssl-php-to-java/ ),但是當需要使用解密數據反過來受密碼短語保護。

我應該如何修改本文中提到的解決方案以使用受密碼保護的密鑰?

所提到文章的代碼:

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

}

現在,您可以通過創建新的PasswordFinder實例以將給定密碼作為char數組返回的方式,指定密碼來檢索存儲密鑰的PEM文件。

請注意,我使用的是Bouncycastle 1.46版。 如果您使用較新的版本,則需要以多種方式修改代碼,特別是passwordFinder會替換為new Password()實例。 您可以參考有關此類改編的其他問題: Bouncy Castle:PEMReader => PEMParser

暫無
暫無

聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.

 
粵ICP備18138465號  © 2020-2024 STACKOOM.COM