当前位置: 首页 > 知识库问答 >
问题:

在android上解密php加密数据

林涵映
2023-03-14

Android客户端(4.2.1)应用程序通过HTTPPOST请求向PHP(5.6)API发送公钥。此API使用符合AESRijndael_128加密数据,然后使用OpenSSL public encryption和RSA_PKCS1_OAEP_Padding的客户端公钥加密AES加密的密钥。它将通过XML编码的数据base64发送回客户端android应用程序,客户端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
<?php
$randomBytes = openssl_random_pseudo_bytes(32, $safe);
$randomKey = bin2hex($randomBytes);
$randomKeyPacked = pack('H*', $randomKey);
// test with fixed key:
// $randomKeyPacked = "12345678901234567890123456789012";
$iv_size = mcrypt_get_iv_size(MCRYPT_RIJNDAEL_128, MCRYPT_MODE_CBC);
$iv = mcrypt_create_iv($iv_size, MCRYPT_RAND);
$dataCrypted = mcrypt_encrypt(MCRYPT_RIJNDAEL_128, $randomKeyPacked, $data, MCRYPT_MODE_CBC, $iv);

由此产生的AES密钥使用openssl_public_encrypt和填充设置openssl_pkcs1_oaep_padding进行编码。读取源代码(PHP OpenSSL实现的源代码)这相当于rsa_pkcs1_oaep_padding所描述的

openssl_public_encrypt($randomKeyPacked, $cryptionKeyCrypted, $clientPublicKey, OPENSSL_PKCS1_OAEP_PADDING);
$content = array(
    'cryptionKeyCryptedBase64' => base64_encode($cryptionKeyCrypted),
    'cryptionIVBase64' => base64_encode($iv),
    'dataCryptedBase64' => base64_encode($dataCrypted)
);
// $content gets parsed to a valid xml element here
public class Content {

    @Element
    private String cryptionKeyCryptedBase64;

    @Element
    private String cryptionIVBase64;

    @Element
    private String dataCryptedBase64;

    @SuppressLint("TrulyRandom")
    public String getData() {
        String dataDecrypted = null;
        try {
            PRNGFixes.apply(); // fix TrulyRandom
            KeyPairGenerator keygen = KeyPairGenerator.getInstance("RSA");
            keygen.initialize(2048);
            KeyPair keypair = keygen.generateKeyPair();
            PrivateKey privateKey = keypair.getPrivate();

            byte[] cryptionKeyCrypted = Base64.decode(cryptionKeyCryptedBase64, Base64.DEFAULT);
            //byte[] cryptionIV = Base64.decode(cryptionIVBase64, Base64.DEFAULT);

            Cipher cipherRSA = Cipher.getInstance("RSA/ECB/OAEPWithSHA-1AndMGF1Padding");
            cipherRSA.init(Cipher.DECRYPT_MODE, privateKey);
            byte[] key = cipherRSA.doFinal(cryptionKeyCrypted);

            byte[] dataCrytped = Base64.decode(dataCryptedBase64, Base64.DEFAULT);
            SecretKeySpec skeySpec = new SecretKeySpec(key, "AES");
            Cipher cipherAES = Cipher.getInstance("AES");
            cipherAES.init(Cipher.DECRYPT_MODE, skeySpec);
            byte[] decryptedAESBytes = cipherAES.doFinal(dataCrytped);
            dataDecrypted = new String(decryptedAESBytes, "UTF-8");
        } catch (Exception e) {
            e.printStackTrace();
        }
        return dataDecrypted;
    }
}

执行此操作时,我当前在行失败

byte[] key = cipherRSA.doFinal(cryptionKeyCrypted);

对于几乎所有PHPopenssl_public_encrypt填充参数-Android密码转换字符串组合,我尝试了错误填充异常。通过省略openssl_public_encrypt中的padding参数(默认为openssl_pkcs1_padding)和密码转换字符串(仅Cipher.getInstance(“rsa”))使用标准PHP填充参数,我不会得到错误填充异常。但加密的密钥似乎无效,因为AES解密失败

java.security.InvalidKeyException: Key length not 128/192/256 bits.

我试着用一个固定的密钥来验证它(参见上面PHP代码中的代码注释),但是在解密它并将它转换为字符串之后,我没有得到相同的密钥。如果我正确地读取了Eclipse ADT调试器,虽然它有256位长,但它似乎只是乱码数据。

String keyString =  new String(keyBytes, "UTF-8");
@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";
    }
}

共有1个答案

乐正远航
2023-03-14

您将在doinbackground中生成一个RSA小键盘,并告诉主机使用该小键盘的公共部分来加密DEK(数据加密密钥)。然后在getdata中生成一个完全不同的RSA小键盘,并尝试使用该小键盘的私有部分来解密加密的DEK。公钥加密的工作方式是用一个小键盘的公共部分加密,用同一个小键盘的私有部分解密;公共和私人的一半在数学上是相关的。您至少需要保存和使用键区的私有部分(可选的是包含两个部分的键区),而您发送的则是其公共部分。

正确获得DEK后,为了解密CBC模式的数据,是的,您确实需要使用与加密相同的IV进行解密。接收者需要将其放入IVParameterSpec中,并在Cipher.init(direction,key[,params])调用中传递。或者,如果您可以更改PHP,因为您为每个消息使用一个新的DEK,所以使用固定的IV是安全的;最简单的方法是使用'\0'x16加密,并允许Java解密默认为全零。

此外,您需要使用参数base64.no_wrap设置base64.decode,因为PHP只会输出由\0分隔的base64。为此,您还需要使用“AES/cbc/zerobytepadding”转换密码来解密AES数据,因为PHP函数mycrypt_encrypt将用零填充数据。下面是getdata函数的外观:

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;
}
 类似资料:
  • 问题内容: 我有一个正在下载xml并将其解析为sql数据库的应用程序。我的问题是,一旦部署了应用程序,xml中的数据就很容易被抓取,其他人可能将我辛苦赚来的数据用于自己的邪恶目的/应用程序。基本上我需要使用php加密xml,然后使用android解密。我已经看过几个php类,它们可以很容易地加密,但是我不完全确定哪种加密方法与android兼容。 解决后编辑: 起初,我认为出于某种原因我不得不忽略

  • 我试图在Android和PHP端使用AES加密/解密数据,并累犯空答案。 首先,我在Android中生成了对称密钥: 在服务器端,我试图解密数据。我可以解密(从RSA)秘密的AES密钥,并得到它的字符串表示。在客户端(Android)和服务器端(PHP)上是一样的。但是如何使用这个字符串AES密钥来解密数据呢?我尝试了这个(PHP): PHP中的结果: 怎么啦?

  • 我用java加密一个单词,但用php解密时遇到了问题。 以下是我如何在android中创建密钥: 下面是我如何在android中使用生成的公钥加密单词: 然后我在android中将加密字符串转换为Bas64: 在php中,我解码base64字符串: 获取私钥: 最后,我尝试用php解密这个字符串: 我得到的错误是: 警告:openssl_private_decrypt():密钥参数不是有效的私钥.

  • 本文向大家介绍PHP加密解密函数详解,包括了PHP加密解密函数详解的使用技巧和注意事项,需要的朋友参考一下 分享一个PHP加密解密的函数,此函数实现了对部分变量值的加密的功能。 加密代码如下: 解密代码如下: 辅助函数: 使用如下所示: 以上就是为大家分享的php加密解密函数,希望大家喜欢,可以应用到自己的学习中。

  • 问题内容: 我目前是一名学生,并且正在学习PHP,我正在尝试对PHP中的数据进行简单的加密/解密。我进行了一些在线研究,其中一些非常令人困惑(至少对我而言)。 这是我想做的事情: 我有一个包含这些字段 (用户ID,Fname,Lname,Email,Password)的表 我要拥有的是先将所有字段加密,然后再解密(如果没有任何加密算法,是否可以用于加密/解密) 我想学习的另一件事是如何创建一种与优

  • 我有一个由以下字段组成的表(UserID、Fname、Lname、Email、Password) 我想要的是加密所有字段,然后解密(如果没有任何加密算法,是否可以使用进行加密/解密) 我想学的另一件事是如何创建一个单向的并结合一个好的“salt”。(基本上我只想有一个加密/解密的简单实现,先生/女士,您的回答将会有很大的帮助,非常感谢。谢谢++