在Android项目中使用AES、RSA加密

2018-08-23遇到的一点小问题
公司最近和银联合作,要求接口请求必须加密。网上搜一下有很多相关内容,这边贴几个有参考到的。
Android数据加密
AES加密CBC模式兼容互通四种编程语言平台
Java进行 RSA 加解密时不得不考虑到的那些事儿

AES加密

    /**
     * AES加密
     */
    public static String aesEncrypt(String content, String encryptKey, String encryptIv) {
        try {
            Cipher cipher = Cipher.getInstance("AES/CBC/NoPadding");
            int blockSize = cipher.getBlockSize();
            byte[] dataBytes = content.getBytes();
            int plaintextLength = dataBytes.length;
            if (plaintextLength % blockSize != 0) {
                plaintextLength = plaintextLength + (blockSize - (plaintextLength % blockSize));
            }
            byte[] plaintext = new byte[plaintextLength];
            System.arraycopy(dataBytes, 0, plaintext, 0, dataBytes.length);
            SecretKeySpec keyspec = new SecretKeySpec(encryptKey.getBytes(), "AES");
            IvParameterSpec ivspec = new IvParameterSpec(encryptIv.getBytes());
            cipher.init(Cipher.ENCRYPT_MODE, keyspec, ivspec);
            byte[] encrypted = cipher.doFinal(plaintext);
            return Base64Coder.encodeLines(encrypted);
        } catch (Exception e) {
            e.printStackTrace();
            Log.e("wannoo", "ASE加密出错:", e);
            return null;
        }
    }

公司使用的AES选择的CBC模式,还要有偏移量,服务端给的,长度9位,因为PHP和IOS竟然都没有长度限制,能正常测试。然而我是需要16位(128 bits),不然会报错,这个网上在线加密,有的会提示,有的不会。然后和服务端协商后统一给的16位。

java.security.InvalidAlgorithmParameterException: expected IV length of 16 but was 9
at com.android.org.conscrypt.OpenSSLCipher$EVP_CIPHER.engineInitInternal(OpenSSLCipher.java:512)
at com.android.org.conscrypt.OpenSSLCipher.engineInit(OpenSSLCipher.java:274)
at javax.crypto.Cipher.tryTransformWithProvider(Cipher.java:2659)
at javax.crypto.Cipher.tryCombinations(Cipher.java:2570)
at javax.crypto.Cipher$SpiAndProviderUpdater.updateAndGetSpiAndProvider(Cipher.java:2475)
at javax.crypto.Cipher.chooseProvider(Cipher.java:566)
at javax.crypto.Cipher.init(Cipher.java:973)
at javax.crypto.Cipher.init(Cipher.java:908)

然后服务端给的秘钥是9位,我使用的这个方法会出错。没办法,网上又找方法解决。有的方法出来的文本竟然不一样,试了几次才解决。不过因为后面服务端又给了长度16位的秘钥,所以方法删掉了,下次有需要还要网上找。

java.security.InvalidKeyException: Unsupported key size: 9 bytes
at com.android.org.conscrypt.OpenSSLCipher$EVP_CIPHER$AES.checkSupportedKeySize(OpenSSLCipher.java:733)
at com.android.org.conscrypt.OpenSSLCipher.checkAndSetEncodedKey(OpenSSLCipher.java:443)
at com.android.org.conscrypt.OpenSSLCipher.engineInit(OpenSSLCipher.java:273)
at javax.crypto.Cipher.tryTransformWithProvider(Cipher.java:2659)
at javax.crypto.Cipher.tryCombinations(Cipher.java:2570)
at javax.crypto.Cipher$SpiAndProviderUpdater.updateAndGetSpiAndProvider(Cipher.java:2475)
at javax.crypto.Cipher.chooseProvider(Cipher.java:566)
at javax.crypto.Cipher.init(Cipher.java:973)
at javax.crypto.Cipher.init(Cipher.java:908)

然后说一下获取的数据byte[] encrypted = cipher.doFinal(plaintext);不能直接new String()获取,需要base64HEX转换,具体看服务端需求。这一点包括生成AES加密秘钥时的处理也一样。

    /**
     * 随机生成秘钥
     */
    public static String getAesKey() {
        try {
            KeyGenerator kg = KeyGenerator.getInstance("AES");
            kg.init(128);
            SecretKey sk = kg.generateKey();
            byte[] b = sk.getEncoded();
            return byteToHexString(b);
        } catch (Exception e) {
            e.printStackTrace();
            Log.e("wannoo", "生成ASE秘钥出错:", e);
        }
        return null;
    }

MD5加密

这里顺便贴一下MD5加密的代码,至于其他的下次再说。

    /**
     * MD5加密
     */
    public static String md5Encrypt(String str) {
        if (str != null && !str.equals("")) {
            try {
                MessageDigest md5 = MessageDigest.getInstance("MD5");
                char[] HEX = {'0', '1', '2', '3', '4', '5', '6', '7', '8', '9', 'a', 'b', 'c', 'd', 'e', 'f'};
                byte[] md5Byte = md5.digest(str.getBytes("UTF8"));
                StringBuilder sb = new StringBuilder();
                for (byte aMd5Byte : md5Byte) {
                    sb.append(HEX[(int) (aMd5Byte & 0xff) / 16]);
                    sb.append(HEX[(int) (aMd5Byte & 0xff) % 16]);
                }
                str = sb.toString();
            } catch (Exception e) {
                Log.e("wannoo", "MD5加密出错", e);
            }
        }
        return str;
    }

RSA加密

我们进行的RSA加密是服务端给公钥,我们加密后发送数据,服务端用私钥解密。
收到的公钥是类似这样子的,很长一串。

 -----BEGIN PUBLIC KEY-----
QWERTYUIOPpoiuytrewq
asdfghjklAYLsU50c7gK5OxTrfdqe
ZXCVBNMEulERs6a2rVCo5xCVoCuJjq
1234567890mqv6CwIDAQAB
-----END PUBLIC KEY-----

然后加密时就一堆问题了,现在随便列几个:

java.security.spec.InvalidKeySpecException: key spec not recognized

这个错误是使用PKCS8EncodedKeySpec获取公钥对象,参考这个

java.lang.NullPointerException: Attempt to invoke virtual method 'java.lang.Object java.lang.Object.clone()' on a null object reference

这个错误是使用X509EncodedKeySpec获取公钥对象时,没去掉 -----BEGIN PUBLIC KEY----------END PUBLIC KEY-----

 java.security.spec.InvalidKeySpecException: java.lang.RuntimeException: error:0c0000be:ASN.1 encoding routines:OPENSSL_internal:WRONG_TAG

这个错误说是因为Android版本问题,具体是不是我就不懂了,参考这个

java.security.spec.InvalidKeySpecException: encoded key spec not recognized: failed to construct sequence from byte[]: unknown tag 13 encountered

这个错误是直接将服务端给的公钥用.getBytes();获取byte[]时出错的

java.security.spec.InvalidKeySpecException: encoded key spec not recognized: failed to construct sequence from byte[]: Extra data detected in stream
java.security.spec.InvalidKeySpecException: encoded key spec not recognized: failed to construct sequence from byte[]: unexpected end-of-contents marker

上面那两个是将公钥用Base64转byte[]时出错的。找了很久原因才发现我之前用的Base64库不适用这个。还找了个包含sun.misc.BASE64Encoder的jar包,可惜不行。最后上Github找了一个,没问题了。
贴一下里的客户端获取公钥和私钥的方法,测试时可以用。

SecureRandom sr = new SecureRandom();//RSA算法要求有一个可信任的随机数源
KeyPairGenerator kpg = KeyPairGenerator.getInstance("RSA");//为RSA算法创建一个KeyPairGenerator对象
kpg.initialize(KEYSIZE, sr);//利用上面的随机数据源初始化这个KeyPairGenerator对象
KeyPair kp = kpg.generateKeyPair();//生成密匙对

Key publicKey = kp.getPublic();//得到公钥
byte[] publicKeyBytes = publicKey.getEncoded();
String pub = new String(Base64.encodeBase64(publicKeyBytes),"UTF-8");

Key privateKey = kp.getPrivate();// 得到私钥
byte[] privateKeyBytes = privateKey.getEncoded();
String pri = new String(Base64.encodeBase64(privateKeyBytes),"UTF-8");

然后贴一下用公钥进行加密的步骤

encryptKey = encryptKey.replaceAll("-----BEGIN PUBLIC KEY-----", "");
encryptKey = encryptKey.replaceAll("-----END PUBLIC KEY-----", "");
/**
 * RSA加密
 */
public static String rsaEncrypt(String content, String encryptKey) {
   try {
        byte[] buffer = Base64Coder2.decode2(encryptKey);
        KeyFactory keyFactory = KeyFactory.getInstance("RSA", "BC");
        X509EncodedKeySpec keySpec = new X509EncodedKeySpec(buffer);
        PublicKey pubKey = keyFactory.generatePublic(keySpec);

        Cipher cipher = Cipher.getInstance("RSA/ECB/PKCS1Padding");
        cipher.init(Cipher.ENCRYPT_MODE, pubKey);
        byte[] dataBytes = content.getBytes();
        byte[] encrypted = cipher.doFinal(dataBytes);
            
        return Base64Coder.encodeLines(encrypted);
    } catch (Exception e) {
        e.printStackTrace();
        Log.e("wannoo", "RSA加密出错:", e);
       return null;
   }
}
最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 219,366评论 6 508
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 93,521评论 3 395
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 165,689评论 0 356
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 58,925评论 1 295
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 67,942评论 6 392
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 51,727评论 1 305
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 40,447评论 3 420
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 39,349评论 0 276
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 45,820评论 1 317
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 37,990评论 3 337
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 40,127评论 1 351
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 35,812评论 5 346
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 41,471评论 3 331
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 32,017评论 0 22
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 33,142评论 1 272
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 48,388评论 3 373
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 45,066评论 2 355

推荐阅读更多精彩内容