我有一些用 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/