Java 在android上解密php加密数据
Java 在android上解密php加密数据,java,php,android,security,encryption,Java,Php,Android,Security,Encryption,Android客户端(4.2.1)应用程序通过HttpPost请求向PHP(5.6)API发送公钥。此API使用符合AES的RIJNDAEL_128对数据进行加密,然后使用OpenSSL公共加密和RSA_PKCS1_OAEP_填充对AES加密的密钥进行加密。它将通过XML编码的数据base64发送回客户端android应用程序,该应用程序将加密数据。我已经设置了一个基本的PHP测试脚本,它测试了整个过程,正如预期的那样 目前,我正在客户端Android应用程序中实现解密,但解密AES密钥已经失败
Android
客户端(4.2.1)应用程序通过HttpPost
请求向PHP
(5.6)API发送公钥。此API使用符合AES
的RIJNDAEL_128
对数据进行加密,然后使用OpenSSL公共加密和RSA_PKCS1_OAEP_填充对AES加密的密钥进行加密。它将通过XML
编码的数据base64
发送回客户端android应用程序,该应用程序将加密数据。我已经设置了一个基本的PHP测试脚本,它测试了整个过程,正如预期的那样
目前,我正在客户端Android应用程序中实现解密,但解密AES密钥已经失败。除了当前的问题之外,我还有其他问题(见结尾)
以下是正在发生的事情的文本图形摘要:
client -> public key -> API -> data -> AESencrypt(data), RSAencrypt(AES-key) -> base64encode[AES(data)], base64encode[RSA(AES-key)] -> <xml>base64[AES(data)], base64[RSA(AES-key)]</xml> -> client -> base64[AES(data)], base64[RSA(AES-key)] -> base64decode[AES(data)], base64decode[RSA(AES-key)] -> AESdecrypt(data), RSAdecrypt(AES-key) -> data
由此产生的AES密钥使用和填充设置OPENSSL\u PKCS1\u OAEP\u padding
进行编码。阅读源代码()这相当于RSA\u PKCS1\u OAEP\u PADDING
描述为
PKCS#1 v2.0中定义的EME-OAEP,带有SHA-1、MGF1和空编码参数
在OpenSSL文档中找到。之后,我将创建数据,以便能够通过XML字符串将其传输到客户端。代码如下所示:
openssl\u public\u encrypt($randomKeyPacked、$cryptionKeyCrypted、$clientPublicKey、openssl\u PKCS1\u OAEP\u PADDING);
$content=array(
'cryptionKeyCryptedBase64'=>base64_编码($cryptionKeyCrypted),
'cryptionIVBase64'=>base64_编码($iv),
'dataCryptedBase64'=>base64_编码($dataCrypted)
);
//$content在此处被解析为有效的xml元素
客户端Android应用程序通过HttpPost
请求通过BasicResponseHandler
获取返回数据。此返回的XML字符串有效,并通过解析到相应的java对象。在保存传输数据的实际内容的类中,我当前尝试解密数据。我使用转换RSA/ECB/OAEPWithSHA-1和mgf1 padding
解密AES密钥,因为(只有我能找到)是一个有效字符串,似乎与我在PHP中使用的padding等效。我包括了生成私钥的方法,因为它与生成发送到PHPAPI的公钥的方法相同。这是一节课:
公共类内容{
@元素
私有字符串cryptionKeyCryptedBase64;
@元素
私有字符串cryptionIVBase64;
@元素
私有字符串dataCryptedBase64;
@SuppressLint(“TrulyRandom”)
公共字符串getData(){
字符串dataDecrypted=null;
试一试{
PRNGFixes.apply();//修复TrulyRandom
KeyPairGenerator keygen=KeyPairGenerator.getInstance(“RSA”);
密钥初始化(2048);
KeyPair KeyPair=keygen.generateKeyPair();
PrivateKey PrivateKey=keypair.getPrivate();
字节[]cryptionKeyCrypted=Base64.decode(cryptionKeyCryptedBase64,Base64.DEFAULT);
//字节[]cryptionIV=Base64.decode(cryptionIVBase64,Base64.DEFAULT);
Cipher cipherRSA=Cipher.getInstance(“RSA/ECB/OAEPWithSHA-1和MGF1padding”);
cipherRSA.init(Cipher.DECRYPT_模式,privateKey);
byte[]key=cipherra.doFinal(cryptionKeyCrypted);
字节[]dataCrytped=Base64.decode(dataCryptedBase64,Base64.DEFAULT);
SecretKeySpec skeySpec=新SecretKeySpec(键,“AES”);
Cipher cipherAES=Cipher.getInstance(“AES”);
init(Cipher.DECRYPT_模式,skeySpec);
byte[]decryptedaasbytes=cipherAES.doFinal(dataCrytped);
dataDecrypted=新字符串(decryptedAESBytes,“UTF-8”);
}捕获(例外e){
e、 printStackTrace();
}
返回解密后的数据;
}
}
这样做,我目前在第二行失败
byte[] key = cipherRSA.doFinal(cryptionKeyCrypted);
对于几乎所有的PHPopenssl\u public\u encrypt
padding参数,我都尝试过使用Bad padding异常
。通过省略openssl_public_encrypt中的padding参数(默认为openssl_PKCS1_padding
和密码转换字符串justCipher.getInstance(“RSA”)
使用标准PHP padding参数,我不会得到错误的填充异常。但加密密钥似乎无效,因为AES解密失败
java.security.InvalidKeyException: Key length not 128/192/256 bits.
我试着用一个固定的密钥来验证它(参见上面PHP代码中的代码注释),但在解密并将其转换为字符串后,我没有得到相同的密钥。如果我正确读取EclipseADT调试器,它看起来只是一个被篡改的数据,尽管它的长度是256位
对于PHP的OPENSSL\u PKCS1\u OAEP\u PADDING
,可以使用什么正确的密码转换字符串作为等价物。在阅读我需要格式为“algorithm/mode/padding”
的转换字符串时,我猜测algorithm=RSA,但我无法找到如何将OpenSSL(上面)文档中关于填充的内容转换为有效的密码转换字符串。例如,什么是模式
?
不幸的是,这个被接受的答案并没有解决我的问题
不管怎样,这可能解决我的问题,还是我的问题源自其他地方
我将如何进一步调试它?将base64解码、解密的密钥转换为人类可读的形式以便与用于加密的密钥进行比较的正确方法是什么?
我试过:
String keyString = new String(keyBytes, "UTF-8");
但是这并没有返回任何人类可读的文本,所以我假设要么是键错了,要么是我转换它的方法错了
解密函数中还需要在PHP中解密AES加密数据。正如你在我发送的代码中看到的,但在Android中似乎不需要这样做?为什么会这样
PS:我希望我提供了所有需要的信息,我可以在评论中进一步补充
PPS:完整性h
@SuppressLint("TrulyRandom")
protected String doInBackground(URI... urls) {
try {
System.setProperty("jsse.enableSNIExtension", "false");
HttpClient httpClient = createHttpClient();
HttpPost httpPost = new HttpPost(urls[0]);
PRNGFixes.apply(); // fix TrulyRandom
KeyPairGenerator keygen = KeyPairGenerator.getInstance("RSA");
keygen.initialize(2048);
KeyPair keypair = keygen.generateKeyPair();
PublicKey publickey = keypair.getPublic();
byte[] publicKeyBytes = publickey.getEncoded();
String pubkeystr = "-----BEGIN PUBLIC KEY-----\n"+Base64.encodeToString(publicKeyBytes,
Base64.DEFAULT)+"-----END PUBLIC KEY-----";
List<NameValuePair> nameValuePairs = new ArrayList<NameValuePair>(2);
nameValuePairs.add(new BasicNameValuePair("publickey", pubkeystr));
httpPost.setEntity(new UrlEncodedFormEntity(nameValuePairs));
// Execute HTTP Post Request
HttpResponse response = httpClient.execute(httpPost);
return new BasicResponseHandler().handleResponse(response);
} catch (Exception e) {
Toast toast = Toast.makeText(asyncResult.getContext(),
"unknown exception occured: " + e.getMessage(),
Toast.LENGTH_SHORT);
toast.show();
return "error";
}
}
public String getData() {
String dataDecrypted = null;
try {
byte[] cryptionKeyCrypted = Base64.decode(cryptionKeyCryptedBase64, Base64.NO_WRAP);
byte[] cryptionIV = Base64.decode(cryptionIVBase64, Base64.NO_WRAP);
Cipher cipherRSA = Cipher.getInstance("RSA/ECB/OAEPWithSHA-1AndMGF1Padding");
// get private key from the pair used to grab the public key to send to the api
cipherRSA.init(Cipher.DECRYPT_MODE, rsaKeyPair.getPrivateKey());
byte[] key = cipherRSA.doFinal(cryptionKeyCrypted);
byte[] dataCrytped = Base64.decode(dataCryptedBase64, Base64.NO_WRAP);
IvParameterSpec ivSpec = new IvParameterSpec(cryptionIV);
SecretKeySpec skeySpec = new SecretKeySpec(key, "AES");
Cipher cipherAES = Cipher.getInstance("AES/CBC/ZeroBytePadding");
cipherAES.init(Cipher.DECRYPT_MODE, skeySpec, ivSpec);
byte[] decryptedAESBytes = cipherAES.doFinal(dataCrytped);
dataDecrypted = new String(decryptedAESBytes, "UTF-8");
} catch (Exception e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
return dataDecrypted;
}