• 非对称加密RSA在Android中的应用


    非对称加密RSA

    1. SSH利用非对称加密实现免密登陆
    • (1)本地客户端生成公私钥(密钥对):ssh-keygen,生成时所填写的用户名与密码不具效力,可有可无;
    • (2)执行命令1后,可在用户目录下的~/.ssh文件夹找到密钥对文件id_rsa和id_rsa.pub
    • (3)上传公钥到需要登陆的服务器用户目录下ssh-copy-id -i ~/.ssh/id_rsa.pub server_username@server_ip
    • (4)在cd ~/.ssh目录下,查看vim authorized_keys文件,是否存在之前拷贝的id_rsa.pub的内容;
    • (5)随后,在本地客户端命令行,输入ssh server_username@server_ip登陆远程服务器用户;
    • (6)RSA:非对称加密算法,其安全性基于极其困难的大整数的分解(两个素数的乘积)
    • (7)DSA:也是非对称加密算法,其安全性基于整数有限域离散对数难题;
    2. 服务器-客户端之间的RSA非对称加密技术
    2.1 工作原理
    • 服务器生成密钥对,并把公钥发送给客户端,客户端使用公钥加密登陆密码和敏感数据,服务器使用私钥解密;
    • 公钥加密后无法推算私钥,也无法解密得到数据;
    • 公私钥成对地生成,作为钥匙,加解密系统作为标准化锁实体,可在别处按照密钥长度、加密规则重构锁实体,即在客户端重构;
    2.2 实现流程

    (1)生成密钥对:

    public static Map<String, Object> initKey() throws Exception {
        //实例化密钥生成器
        KeyPairGenerator keyPairGenerator = KeyPairGenerator.getInstance(KEY_ALGORITHM);  //加密方式
        //初始化密钥生成器
        keyPairGenerator.initialize(KEY_SIZE);  //长度
        //生成密钥对
        KeyPair keyPair = keyPairGenerator.generateKeyPair();
        //甲方公钥
        RSAPublicKey publicKey = (RSAPublicKey) keyPair.getPublic();
        //甲方私钥
        RSAPrivateKey privateKey = (RSAPrivateKey) keyPair.getPrivate();
        //将密钥存储在map中
        Map<String, Object> keyMap = new HashMap<String, Object>();
        keyMap.put(PUBLIC_KEY, publicKey);
        keyMap.put(PRIVATE_KEY, privateKey);
        return keyMap;
    }
    

    (2)将密钥对转换为byte[]字节数组:

     public static byte[] getPrivateKey(Map<String, Object> keyMap) {
            Key key = (Key) keyMap.get(PRIVATE_KEY/PUBLIC_KEY);
            return key.getEncoded();
        }
    

    (3)将字节数组进行BASE64转换,得到字符串,以便于存储发送:

    Map<String, Object> map = ResUtil.initKey();
    byte[] PrivateKey = ResUtil.getPrivateKey(map);
    byte[] PublicKey = ResUtil.getPublicKey(map);
    // 将公私钥转为base64-法1
    String priEncBase64 = new String(Base64.encodeBase64(PrivateKey));
    String pubEncBase64 = new String(Base64.encodeBase64(PublicKey));
    // 将公私钥转为base64-法2
    String priEncBase64 = Base64.encodeBase64String(PrivateKey);
    String pubEncBase64 = Base64.encodeBase64String(PublicKey);
    

    (4)使用公钥加密

    /**
     * 公钥加密
     *
     * @param data 待加密数据
     * @param key       密钥
     * @return byte[] 加密数据
     */
    public static byte[] encryptByPublicKey(byte[] data, byte[] key) throws Exception {
        //实例化密钥工厂
        KeyFactory keyFactory = KeyFactory.getInstance(KEY_ALGORITHM);
        //初始化公钥
        //密钥材料转换
        X509EncodedKeySpec x509KeySpec = new X509EncodedKeySpec(key);
        //产生公钥
        PublicKey pubKey = keyFactory.generatePublic(x509KeySpec);
        //数据加密
        Cipher cipher = Cipher.getInstance(ECB_PKCS1_PADDING);
        cipher.init(Cipher.ENCRYPT_MODE, pubKey);
        return cipher.doFinal(data);
        }
    

    (5)使用私钥解密

     /**
     * 私钥解密
     *
     * @param data 待解密数据
     * @param key  密钥
     * @return byte[] 解密数据
     */
    public static byte[] decryptByPrivateKey(byte[] data, byte[] key) throws Exception {
        //取得私钥
        PKCS8EncodedKeySpec pkcs8KeySpec = new PKCS8EncodedKeySpec(key);
        KeyFactory keyFactory = KeyFactory.getInstance(KEY_ALGORITHM);
        //生成私钥
        PrivateKey privateKey = keyFactory.generatePrivate(pkcs8KeySpec);
        //数据解密
        Cipher cipher = Cipher.getInstance(ECB_PKCS1_PADDING);
        cipher.init(Cipher.DECRYPT_MODE, privateKey);
        return cipher.doFinal(data);
    }
    
    2.3 背景知识

    (1)byte[]转String: new String(byte[])Base64.encodeBase64String(byte[])String pubEncBase64 = new String(Base64.encodeBase64(PublicKey))
    (2)String转byte[]: string.getByte()Base64.decodeBase64()

    3. javax.crypto.BadPaddingException: Decryption error 非对称加密报错

    报错现象: 在服务端,相同公钥和报文,每次加密得到的密文都不一样;而在安卓端,相同情况下,得到的报文没有发生变化。

    • android 端使用的默认填充方式是“RSA/None/NoPadding”,即不使用填充机制;
    • 而服务端使用的是“RSA/None/PKCS1Padding”,使用随机填充机制;
    • 填充的目的是拓展密文的长度;
    • 原来android系统的RSA实现是"RSA/None/NoPadding",而标准JDK实现是"RSA/None/PKCS1Padding" ,这造成了在android机上加密后无法在服务器上解密的原因
    • 还需要注意字节数组与字符串的类型转换问题
    • 解决方法,在安卓端设置Cipher.getInstance("RSA/ECB/PKCS1Padding")
    • string to byte[]的方法:string.getByte()或者Base64.decodeBase64()
    • byte[] to string的方法:new String(byte[])或者Base64.encodeBase64String(byte[]);
    4. 参考资料

    参考资料
    优质参考资料
    Java 进行 RSA 加解密时不得不考虑到的那些事儿

  • 相关阅读:
    Zookeeper实现配置中心
    Springboot
    分布式事务框架
    Spring 事件Application Event
    了解“事务机制” 过程
    mysql : 行锁,表锁,共享锁,排他锁,悲观锁,乐观锁
    数据库隔离级别和实现原理
    aop用于日志
    exceptionHandler統一处理异常
    mysql 点总结
  • 原文地址:https://www.cnblogs.com/litchi99/p/13504310.html
Copyright © 2020-2023  润新知