项目场景:

        安卓开发中手机号一键登入,需要得到本机号码,号码有三大运营商,所以用的时极光平台,帮我们封装好了。客户端通过认证Android SDK 后获取loginToken给服务端,服务端拿到loginToken调用一键认证Api获取加密的手机号(基于RSA公钥),需要用对应的RSA私钥解密,但出现了java.security.InvalidKeyException: IOException : DER input, Integer tag error这个问题,意思是私钥格式不对,处理了半天,避免大家踩坑,总结出这篇文章。


问题描述

对获取到加密的手机号进行解密报的异常,先看官网的解密方案:

官方文档

官方是使用了RSA的私钥对加密的手机号进行解密,

什么是RSA?

  • 计算机中常用的加密技术分为两类:对称加密、非对称加密。

  • RSA属于非对称加密。加密、解密过程使用不同的秘钥,分为公钥、私钥。公钥可以公开,私钥不可以。

  • 对称加密:加密和解密使用相同的的秘钥Key,这个Key需要在网络上传输,不安全,因此需要非对称加密 

    我们服务端通过loginToken获取的手机号就是通过公钥来加密的,需要使用对应的私钥来进行解密,这里提供RSA相关的加解密工具网址 RSA加解密工具

        于是我应开发的需求,生成了一对带密码的RSA公私钥对(可以设置无密码,就是随机生成,但我设置了密码,就是这个行为让我报了半天的错误)

        为了验证生成的密钥对能否对症成功,我在RSA加解密工具验证的时候,发现需要带密码才能验证成功,可上面是说密码可写可不写,我当时没有在意,就任他发展,让我苦逼的在后面。


我的代码实现1

package com.lyj.biz.converter;

import cn.hutool.core.util.CharsetUtil;
import cn.hutool.core.util.StrUtil;
import cn.hutool.crypto.asymmetric.KeyType;
import cn.hutool.crypto.asymmetric.RSA;

/**
 * @Author liyingjie
 * @Date 2022/12/3 1:11
 * @Version 1.0
 */
public class RSATest {
    public static void main(String[] args) {

        //RSA私钥
        final String  JVERIFY_PRIVATE_KEY="MIIC3TBXBgkqhkiG9w0BBQ0wSjApBgkqhkiG9w0BBQwwHAQIM5nBmtpZeEQCAggA" +
                "MAwGCCqGSIb3DQIJBQAwHQYJYIZIAWUDBAEqBBBQ9OWtLaOumZtlrURZgNjiBIIC" +
                "gIK6eSC4EUaVGJPzOMTvDEnpsCvPIMpuIgPnpQldlF5z3qhoxVJ9fbYiuSeZuYz7" +
                "FM50OL2YdfcW2ajY4UcE0OlA+GVPR61fCcvr0PXYrQDjrXVQ24U52kfzxqsRlu4q" +
                "LnHPdNZwI8FHA5eBp" +
                "Q6f9xosqFmkhGXoENZV1cxseuT5hhcC6389V5GWG/gmtxy/ff01oHgzi1s5n6BdC" +
                "9jU9ju2VfwrQn2NxygNARtMajEtWL1/pk4vmtu694zPTv7SJe2bgMPIdh5uH6ZLT" +
                "dbrJSXLfwaNM39BWYJG4ZzcPl58yE8QqA6+mRkkCwvdV9n/Vs9uwXRHE3DfH1O11" +
                "ZtQax5TGpEHa+XUdG6p+3ktYJmm2NUoy3ED2bVYX54" +
                "fDXGh2/tV4wO+ohnpaLY6EaXf0vKWNrSzmUILahx0BcE/MY6Ea6hWemXuJFbxASD" +
                "Zb4EtYbMsa5/RDAueagGuDzWFOx957/868ZcYUhs9gEozCq8WQ8WCQ6wKHoI3L3W" +
                "RxQo7ROBDQ/fu0rqvT7Z5h2fMtXbiyybYkoV3TjxQP4RUCypTC2hgKK/owU4/FUG" +
                "GJzLPRe2jZBeWrlqgVmKo1zLdYu6EOQ/Z7pERUbmIOnXPDWeLx7fxJCCPlSLfVWb" +
                "5b37YOAZxP98kpR3tm/pXT/dDnZu3j7hmu+kldwajxwY+NNagVqwIfrUs5LOjq58" +
                "Eq7KcrwvSj0ymufRm13Z2hVFepn+y5W+pDIoTNLl8mBZNI0H3TlsV89NltTvp0TT" +
                "bUMXPCljfsaXNa2O7DGnVsk=";
        //RSA公钥
        final String  JVERIFY_PUBLIC_KEY="MIGfMA0GCSqGSIb3DQEBAQUAA4GNADC"+
                "BiQKBgQCS5mh332H7gtyJuwWtGk7DEG9J3xejIY23J"+
                "0KpGsAUuSV9WrBYasDf"+
                "jv6GIUeDCBIo+VxKfYJ2doRI53T3kQ9s"+
                "k8N8rvDpLerhYkyD0z0lIhc5pypsW1lgbSoEQOliS/BSdIp6MOFdwIDAQAB";
        //加密后的电话号码(RSA加密)
        String phoneToken = "fVPnF7Ux+s1NNFrJalt+nK4oOjS+CTt/5OUca5aIfBXcfeAouPJ4Bt96iIT91hjpMvAMshixCiuX5NK9he9OrtUfkRf"+
                "/mW3veLn75HJ2hQLZwwVAJ9R3AJVXgN1XMU1Kr1tWtEvdo1U1nCR9WNaYp0u4FSnrQijHjAyxMbITwfg=";

        //返回解密后的电话号码(通过私钥解密)

            RSA rsa = new RSA(JVERIFY_PRIVATE_KEY, JVERIFY_PUBLIC_KEY);
            byte[] decrypt = rsa.decrypt(phoneToken, KeyType.PrivateKey);

        System.out.println(StrUtil.str(decrypt, CharsetUtil.CHARSET_UTF_8));


    }
}

结果:

 我是通过公钥和私钥一起来对密文进行解密的,结果报错:意思是识别不到你的私钥,不自信的我开始以为是没有使用官方答案,于是我借鉴官网的方案

package com.lyj.biz.converter;

import javax.crypto.Cipher;
import java.security.KeyFactory;
import java.security.PrivateKey;
import java.security.spec.PKCS8EncodedKeySpec;
import java.util.Base64;

public class RSADecrypt {public static void main(String[] args) throws Exception {
    //加密手机号
    String encrypted = "fVPnF7Ux+s1NNFrJalt+nK4oOjS+CTt/5OUca5aIfBXcfeAouPJ4Bt96iIT91hjpMvAMshixCiuX5NK9he9OrtUfkRf/mW3veLn75HJ2hQLZwwVAJ9R3AJVXgN1XMU1Kr1tWtEvdo1U1nCR9WNaYp0u4FSnrQijHjAyxMbITwfg=";
    //RSA对应私钥
    String prikey =
            "MIIC3TBXBgkqhkiG9w0BBQ0wSjApBgkqhkiG9w0BBQwwHAQIM5nBmtpZeEQCAggA" +
                    "MAwGCCqGSIb3DQIJBQAwHQYJYIZIAWUDBAEqBBBQ9OWtLaOumZtlrURZgNjiBIIC" +
                    "gIK6sCvPIMpuIgPnpQldlF5z3qhoxVJ9fbYiuSeZuYz7" +
                    "FM50OL2YdfcW2ajY4UcE0OlA+GVPR61fCcvr0PXYrQDjrXVQ24U52kfzxqsRlu4q" +
                    "L5muocW0MVe5PIswm9tcwzbx6zIXQAiINyYgtFmNZaIichhMnHPdNZwI8FHA5eBp" +
                    "Q6f9xosqFmkhGXoENZV1cxseuT5hhcC6389V5GWG/gmtxy/ff01oHgzi1s5n6BdC" +
                    "9jU9jvmtu694zPTv7SJe2bgMPIdh5uH6ZLT" +
                    "dbrJSXLfwaNM39BWYJG4ZzcPl58yE8QqA6+mRkkCwvdV9n/Vs9uwXRHE3DfH1O11" +
                    "ZtQakSqY8PKPTMvfahwz9yWrVQx5TGpEHa+XUdG6p+3ktYJmm2NUoy3ED2bVYX54" +
                    "fDXGh2/tV4wO+ohnpaLY6EaXf0vKWNrSzmUILahx0BcE/MY6Ea6hWemXuJFbxASD" +
                    "Zb4EtYbMsa5/RWQ8WCQ6wKHoI3L3W" +
                    "RxQo7ROBDQ/fu0rqvT7Z5h2fMtXbiyybYkoV3TjxQP4RUCypTC2hgKK/owU4/FUG" +
                    "GJzLPRe2jZBeWrlqgVmKo1zLdYu6EOQ/Z7px7fxJCCPlSLfVWb" +
                    "5b37YOAZ+kldwajxwY+NNagVqwIfrUs5LOjq58" +
                    "Eq7KcrwvSj0ymufRm13Z2hVFepn+y5W+pDIoTNLl8mBZNI0H3TlsV89NltTvp0TT" +
                    "bUMXPCljfsaXNa2O7DGnVsk=";

    String result = decrypt(encrypted, prikey);
    System.out.println(result);
}

    public static String decrypt(String cryptograph, String prikey) throws Exception {
        PKCS8EncodedKeySpec keySpec = new PKCS8EncodedKeySpec(Base64.getDecoder().decode(prikey));
        PrivateKey privateKey = KeyFactory.getInstance("RSA").generatePrivate(keySpec);

        Cipher cipher=Cipher.getInstance("RSA");
        cipher.init(Cipher.DECRYPT_MODE, privateKey);

        byte [] b = Base64.getDecoder().decode(cryptograph);
        return new String(cipher.doFinal(b));
    }
}

 结果还是报错

         一开始我是纠结是不是我的密钥格式不正确,接着去查又说什么jdk的security版本不兼容了,后面我就研究那个生成工具,意识到可能是设置了密码问题,设置密码后的每次使用几乎都要带上密码,否则就是失败。

于是我抱着尝试的心态,把之前私钥的密码给清除了

 运用生成好后的私钥,替换代码中先前的私钥,果然,十分顺畅,完美解决

 无论是官方的还是自己的都行的通,成功解密出对应的手机号

 如果需要清除RSA密码的可以通过私钥在在线RSA私钥密码添加、修改、清除工具-ME2在线工具 (metools.info)修改删除,公钥主要用于加密,私钥用于解密,所以公钥可以公开,私钥记得妥善保管。


总结解决方案:

        如果碰见和我同样的问题,建议拿到私钥后去RSA在线工具修改清楚掉密码,RSA公私钥对请妥善保管,

Logo

旨在为数千万中国开发者提供一个无缝且高效的云端环境,以支持学习、使用和贡献开源项目。

更多推荐