From ba6eb721e1db9bcc1befc171963aaf2768484425 Mon Sep 17 00:00:00 2001 From: mahongyin <1976222027@qq.com> Date: Wed, 24 Mar 2021 18:04:50 +0800 Subject: [PATCH] AES --- aes_rsa/.gitignore | 1 + aes_rsa/README.md | 166 +++++ aes_rsa/build.gradle | 33 + aes_rsa/consumer-rules.pro | 0 aes_rsa/proguard-rules.pro | 21 + aes_rsa/rsa测试密钥对.md | 98 +++ aes_rsa/src/main/AndroidManifest.xml | 17 + .../src/main/java/com/mhy/aesrsa/Main.java | 115 ++++ .../main/java/com/mhy/aesrsa/util/AES.java | 134 ++++ .../main/java/com/mhy/aesrsa/util/Base64.java | 604 ++++++++++++++++++ .../java/com/mhy/aesrsa/util/CheckUtils.java | 38 ++ .../util/ConfigureEncryptAndDecrypt.java | 8 + .../com/mhy/aesrsa/util/ConvertUtils.java | 335 ++++++++++ .../main/java/com/mhy/aesrsa/util/Digest.java | 167 +++++ .../java/com/mhy/aesrsa/util/EncryUtil.java | 106 +++ .../com/mhy/aesrsa/util/EncryptionUtil.java | 79 +++ .../main/java/com/mhy/aesrsa/util/RSA.java | 195 ++++++ .../com/mhy/aesrsa/util/SecureRandomUtil.java | 36 ++ .../java/cn/android/security/APISecurity.java | 73 +-- .../java/cn/android/security/AppSigning.java | 179 +++++- .../cn/android/security/InitProvider.java | 46 +- .../java/cn/android/security/NetProxy.java | 60 +- app/build.gradle | 18 +- app/src/main/cpp/apisecurity-lib.cpp | 4 + .../java/cn/android/sample/MyApplication.java | 25 +- settings.gradle | 1 + 26 files changed, 2401 insertions(+), 158 deletions(-) create mode 100644 aes_rsa/.gitignore create mode 100644 aes_rsa/README.md create mode 100644 aes_rsa/build.gradle create mode 100644 aes_rsa/consumer-rules.pro create mode 100644 aes_rsa/proguard-rules.pro create mode 100644 aes_rsa/rsa测试密钥对.md create mode 100644 aes_rsa/src/main/AndroidManifest.xml create mode 100644 aes_rsa/src/main/java/com/mhy/aesrsa/Main.java create mode 100644 aes_rsa/src/main/java/com/mhy/aesrsa/util/AES.java create mode 100644 aes_rsa/src/main/java/com/mhy/aesrsa/util/Base64.java create mode 100644 aes_rsa/src/main/java/com/mhy/aesrsa/util/CheckUtils.java create mode 100644 aes_rsa/src/main/java/com/mhy/aesrsa/util/ConfigureEncryptAndDecrypt.java create mode 100644 aes_rsa/src/main/java/com/mhy/aesrsa/util/ConvertUtils.java create mode 100644 aes_rsa/src/main/java/com/mhy/aesrsa/util/Digest.java create mode 100644 aes_rsa/src/main/java/com/mhy/aesrsa/util/EncryUtil.java create mode 100644 aes_rsa/src/main/java/com/mhy/aesrsa/util/EncryptionUtil.java create mode 100644 aes_rsa/src/main/java/com/mhy/aesrsa/util/RSA.java create mode 100644 aes_rsa/src/main/java/com/mhy/aesrsa/util/SecureRandomUtil.java diff --git a/aes_rsa/.gitignore b/aes_rsa/.gitignore new file mode 100644 index 0000000..42afabf --- /dev/null +++ b/aes_rsa/.gitignore @@ -0,0 +1 @@ +/build \ No newline at end of file diff --git a/aes_rsa/README.md b/aes_rsa/README.md new file mode 100644 index 0000000..901a744 --- /dev/null +++ b/aes_rsa/README.md @@ -0,0 +1,166 @@ +# aes-rsa-java AES+RSA结合应用java示例 + +## 介绍 + +随着Internet网的广泛应用,信息安全问题日益突出,以数据加密技术为核心的信息安全技术也得到了极大的发展。 +目前的数据加密技术根据加密密钥类型可分私钥加密(对称加密)系统和公钥加密(非对称加密)系统。对称加密算法是较传统的加密体制, +通信双方在加/解密过程中使用他们共享的单一密钥,鉴于其算法简单和加密速度快的优点,目前仍然是主流的密码体制之一。 +最常用的对称密码算法是数据加密标准(DES)算法,但是由于DES密钥长度较短,已经不适合当今分布式开放网络对数据加密安全性的要求。 +最后,一种新的基于Rijndael算法对称高级数据加密标准AES取代了数据加密标准DES。 +非对称加密由于加/解密钥不同(公钥加密,私钥解密),密钥管理简单,也得到广泛应用。RSA是非对称加密系统最著名的公钥密码算法。 + +## 加解密流程 + +1. 服务器端(server)和客户端(client)分别生成自己的密钥对 +2. server和client分别交换自己的公钥 +3. client生成AES密钥(aesKey) +4. client使用自己的RSA私钥(privateKey)对请求明文数据(params)进行数字签名 +5. 将签名加入到请求参数中,然后转换为json格式 +6. client使用aesKey对json数据进行加密得到密文(data) +7. client使用sever的RSA公钥对aesKey进行加密(encryptkey) +8. 分别将data和encryptkey作为参数传输给服务器端 + +服务器端进行请求响应时将上面流程反过来即可 + +## 文章详解 + +开放接口的安全验证方案(AES+RSA) + +随着密码分析技术的提高,新的数据加密标准AES取代了过时的DES。文章在阐述AES/RSA加密算法的基础上,分别给出了利用AES/RSA实现客户端/服务器端网络数据传输的加密流程。最后在比较AES算法和RSA算法基础上,将AES与RSA相结合提出一种新的数据加密方案。 + +### 基本需求及概念 + +随着Internet网的广泛应用,信息安全问题日益突出,以数据加密技术为核心的信息安全技术也得到了极大的发展。目前的数据加密技术根据加密密钥类型可分私钥加密(对称加密)系统和公钥加密(非对称加密)系统[1]。对称加密算法是较传统的加密体制,通信双方在加/解密过程中使用他们共享的单一密钥,鉴于其算法简单和加密速度快的优点,目前仍然是主流的密码体制之一。最常用的对称密码算法是数据加密标准(DES)算法,但是由于DES密钥长度较短,已经不适合当今分布式开放网络对数据加密安全性的要求。最后,一种新的基于Rijndael算法对称高级数据加密标准AES取代了数据加密标准DES。非对称加密由于加/解密钥不同(公钥加密,私钥解密),密钥管理简单,也得到广泛应用。RSA是非对称加密系统最著名的公钥密码算法。 + +### AES算法 + +#### 基本原理及算法流程 + +美国国家标准和技术研究所(NIST)经过三轮候选算法筛选,从众多的分组密码中选中Rijndael算法作为高级加密标准(AES)。Rijndael密码是一个迭代型分组密码,分组长度和密码长度都是可变的,分组长度和密码长度可以独立的指定为128比特,192比特或者256比特。AES的加密算法的数据处理单位是字节,128位的比特信息被分成16个字节,按顺序复制到一个4*4的矩阵中,称为状态(state),AES的所有变换都是基于状态矩阵的变换。 +用Nr表示对一个数据分组加密的轮数(加密轮数与密钥长度的关系如表1所示)。在轮函数的每一轮迭代中,包括四步变换,分别是字节代换运算(ByteSub())、行变换(ShiftRows())、列混合(MixColumns())以及轮密钥的添加变换AddRoundKey()[3],其作用就是通过重复简单的非线形变换、混合函数变换,将字节代换运算产生的非线性扩散,达到充分的混合,在每轮迭代中引入不同的密钥,从而实现加密的有效性。 +表1 是三种不同类型的AES加密密钥分组大小与相应的加密轮数的对照表。加密开始时,输入分组的各字节按表2 的方式装入矩阵state中。如输入ABCDEFGHIJKLMNOP,则输入块影射到如表2的状态矩阵中。 +表1: + +``` +|AES类型| 密钥长度 | 分组长度 | 加密轮数| +|AES-128| 4字 | 4字 | 10 | +|AES-192| 6字 | 4字 | 12 | +|AES-256| 8字 | 4字 | 14 | +``` + +表2: + +``` +| A | E | I | M | +| B | F | J | N | +| C | G | K | O | +| D | H | L | P | +``` + + + +1. 字节代换运算(ByteSub()) + 字节代换运算是一个可逆的非线形字节代换操作,对分组中的每个字节进行,对字节的操作遵循一个代换表,即S盒。S盒由有限域 GF(28)上的乘法取逆和GF(2)上的仿射变换两步组成。 +2. 行变换ShiftRows() + 行变换是一种线性变换,其目的就是使密码信息达到充分的混乱,提高非线形度。行变换对状态的每行以字节为单位进行循环右移,移动字节数根据行数来确定,第0行不发生偏移,第一行循环右移一个字节,第二行移两个,依次类推。 +3. 列混合变换MixColumns() + 列变换就是从状态中取出一列,表示成多项式的形式后,用它乘以一个固定的多项式a(x),然后将所得结果进行取模运算,模值为 x4+1。其中a(x)={03}x3+{02}x2+{01}x+{02}, + 这个多项式与x4+1互质,因此是可逆的。列混合变换的算术表达式为:s’(x)= a(x) s(x),其中, s(x)表示状态的列多项式。 +4. 轮密钥的添加变换AddRoundKey() + 在这个操作中,轮密钥被简单地异或到状态中,轮密钥根据密钥表获得,其长度等于数据块的长度Nb。 + +#### AES算法流程 + +对于发送方,它首先创建一个AES私钥,并用口令对这个私钥进行加密。然后把用口令加密后的AES密钥通过Internet发送到接收方。发送方解密这个私钥,并用此私钥加密明文得到密文,密文和加密后的AES密钥一起通过Internet发送到接收方。接收方收到后再用口令对加密密钥进行解密得到AES密钥,最后用解密后的密钥把收到的密文解密成明文。图1中是这个过程的实现流程。 +图1: +![img](http://7xifb5.com1.z0.glb.clouddn.com/wustrive-hexoAES%E6%B5%81%E7%A8%8B.png) + +### RSA算法 + +#### 基本原理及流程 + +RSA是在1977年发明RSA密码系统的三个人的名字的首字母的缩写,他们是:Ron Rivest、Adi Shamir和Leonard Adleman。它是第一个公钥加密算法,在很多密码协议中都有应用,如SSL和S/MIME。RSA算法是基于大质数的因数分解的公匙体系。简单的讲,就是两个很大的质数,一个作为公钥,另一个作为私钥,如用其中一个加密,则用另一个解密。密钥长度从40到2048位可变,密钥越长,加密效果越好,但加密解密的开销也大。RSA算法可简单描述如下: + +``` +公开密钥:n=pq,(p,q为两个不同的很大的质数,p和q必须保密) +将(p-1)和(q-1)相乘得到φ(n) +选择一个整数e (1 + + + + + \ No newline at end of file diff --git a/aes_rsa/src/main/java/com/mhy/aesrsa/Main.java b/aes_rsa/src/main/java/com/mhy/aesrsa/Main.java new file mode 100644 index 0000000..ff8378d --- /dev/null +++ b/aes_rsa/src/main/java/com/mhy/aesrsa/Main.java @@ -0,0 +1,115 @@ +package com.mhy.aesrsa; + +import java.util.TreeMap; +import com.alibaba.fastjson.JSON; +import com.alibaba.fastjson.JSONObject; +import com.mhy.aesrsa.util.AES; +import com.mhy.aesrsa.util.ConvertUtils; +import com.mhy.aesrsa.util.EncryUtil; +import com.mhy.aesrsa.util.RSA; +import com.mhy.aesrsa.util.SecureRandomUtil; + +/** + * Description: AES+RSA签名,加密 验签,解密 + */ +public class Main { + public static final String clientPrivateKey = "MIICdgIBADANBgkqhkiG9w0BAQEFAASCAmAwggJcAgEAAoGBAKbNojYr8KlqKD/y" + + "COd7QXu3e4TsrHd4sz3XgDYWEZZgYqIjVDcpcnlztwomgjMj9xSxdpyCc85GOGa0" + + "lva1fNZpG6KXYS1xuFa9G7FRbaACoCL31TRv8t4TNkfQhQ7e2S7ZktqyUePWYLlz" + + "u8hx5jXdriErRIx1jWK1q1NeEd3NAgMBAAECgYAws7Ob+4JeBLfRy9pbs/ovpCf1" + + "bKEClQRIlyZBJHpoHKZPzt7k6D4bRfT4irvTMLoQmawXEGO9o3UOT8YQLHdRLitW" + + "1CYKLy8k8ycyNpB/1L2vP+kHDzmM6Pr0IvkFgnbIFQmXeS5NBV+xOdlAYzuPFkCy" + + "fUSOKdmt3F/Pbf9EhQJBANrF5Uaxmk7qGXfRV7tCT+f27eAWtYi2h/gJenLrmtke" + + "Hg7SkgDiYHErJDns85va4cnhaAzAI1eSIHVaXh3JGXcCQQDDL9ns78LNDr/QuHN9" + + "pmeDdlQfikeDKzW8dMcUIqGVX4WQJMptviZuf3cMvgm9+hDTVLvSePdTlA9YSCF4" + + "VNPbAkEAvbe54XlpCKBIX7iiLRkPdGiV1qu614j7FqUZlAkvKrPMeywuQygNXHZ+" + + "HuGWTIUfItQfSFdjDrEBBuPMFGZtdwJAV5N3xyyIjfMJM4AfKYhpN333HrOvhHX1" + + "xVnsHOew8lGKnvMy9Gx11+xPISN/QYMa24dQQo5OAm0TOXwbsF73MwJAHzqaKZPs" + + "EN08JunWDOKs3ZS+92maJIm1YGdYf5ipB8/Bm3wElnJsCiAeRqYKmPpAMlCZ5x+Z" + + "AsuC1sjcp2r7xw=="; + + public static final String clientPublicKey = "MIGfMA0GCSqGSIb3DQEBAQUAA4GNADCBiQKBgQCmzaI2K/Cpaig/8gjne0F7t3uE" + + "7Kx3eLM914A2FhGWYGKiI1Q3KXJ5c7cKJoIzI/cUsXacgnPORjhmtJb2tXzWaRui" + + "l2EtcbhWvRuxUW2gAqAi99U0b/LeEzZH0IUO3tku2ZLaslHj1mC5c7vIceY13a4h" + + "K0SMdY1itatTXhHdzQIDAQAB"; + + public static final String serverPrivateKey = "MIICdgIBADANBgkqhkiG9w0BAQEFAASCAmAwggJcAgEAAoGBALIZ98KqgLW8IMt4" + + "G+N+4d3DiOiEa+5s6lCMSGE/NbU9stJEqw0EuCP54MY6JkT0HCYTCrLXqww6rSQy" + + "WF7BNCVGssk2XDcvSKiCz1ZMgabd6XVK5kvIycySydXQ0Ky6rnfxw8w2mllHABFv" + + "s1eamaHQozv18n/XGqemjW2BFy/jAgMBAAECgYAxT3FCi3SBXKnzy7hk/z9H6Bhi" + + "0C8V3z/stzpe+mJDYOa+wtZdD15wT4HFQFpSIwgcHo+Kvp2UEDbZ27qN2Y43AZbF" + + "9LOalWTRUzYtr8wL8MIbgtew/QQ9YFNWdkTZ6MxCItjD/mSz3Lrkcphvbsx4VoCV" + + "YIJ04r+Loi0t9g0guQJBANvkpfrq0bLVRYWfaigjkx47mr0trJkB7mjADe69Iqts" + + "M/2x5dHPpClDK78yzAWxU2BrYzOd31QIOm32iMIvRxUCQQDPWJPMOzcq8Jqs1PAM" + + "7D0hxnvF3tSJB0CJCQWdGFkJiuIYSbrWnCVF78jJyU2AK1H3RDi9BzGPL2Z3i2Si" + + "+9kXAkAPnKtAJl3fEY9PDmNuGCCA3AB/f/eqIV345/HVSm5kt1j1oSTNAa4JE/DO" + + "MWAU42MlDFrNtl69y5vCZOeOyeaFAkBOJieGmWcAozDZJWTYqg2cdk/eU08t2nLj" + + "c2gPPscIRrVSzC9EhhOyWV8HVv0D6s/471inPlfajNYFBp/Goj+/AkEAiejHX/58" + + "Vv8+ccW22RMZmyxiHcZpTw9hz7vHUCWv03+fyVGtGMhJ4xuPt8UaZm91yHSPWWar" + + "M8Xa7errKaXN9A=="; + public static final String serverPublicKey = "MIGfMA0GCSqGSIb3DQEBAQUAA4GNADCBiQKBgQCyGffCqoC1vCDLeBvjfuHdw4jo" + + "hGvubOpQjEhhPzW1PbLSRKsNBLgj+eDGOiZE9BwmEwqy16sMOq0kMlhewTQlRrLJ" + + "Nlw3L0iogs9WTIGm3el1SuZLyMnMksnV0NCsuq538cPMNppZRwARb7NXmpmh0KM7" + + "9fJ/1xqnpo1tgRcv4wIDAQAB"; + + public static void main(String[] args) throws Exception { + TreeMap params = new TreeMap(); + params.put("userid", "152255855"); + params.put("phone", "18965621420"); + + client(params); + + server(); + } + + public static void client(TreeMap params) throws Exception { + // 生成RSA签名 + String sign = EncryUtil.handleRSA(params, clientPrivateKey); + params.put("sign", sign); + + String info = JSON.toJSONString(params); + //随机生成AES密钥 + String aesKey = SecureRandomUtil.getRandom(16); + //AES加密数据 + String data = AES.encryptToBase64(ConvertUtils.stringToHexString(info), aesKey); + + // 使用RSA算法将商户自己随机生成的AESkey加密 + String encryptkey = RSA.encrypt(aesKey, serverPublicKey); + + Req.data = data; + Req.encryptkey = encryptkey; + + System.out.println("加密后的请求数据:\n" + new Req().toString()); + } + + public static void server() throws Exception { + + // 验签 + boolean passSign = EncryUtil.checkDecryptAndSign(Req.data, Req.encryptkey, clientPublicKey, serverPrivateKey); + + if (passSign) { + // 验签通过 + String aeskey = RSA.decrypt(Req.encryptkey, serverPrivateKey); + String data = ConvertUtils.hexStringToString(AES.decryptFromBase64(Req.data, aeskey)); + + JSONObject jsonObj = JSONObject.parseObject(data); + String userid = jsonObj.getString("userid"); + String phone = jsonObj.getString("phone"); + + System.out.println("解密后的明文:userid:" + userid + " phone:" + phone); + + } else { + System.out.println("验签失败"); + } + } + + static class Req { + public static String data; + public static String encryptkey; + + @Override + public String toString() { + return "data:" + data + "\nencryptkey:" + encryptkey; + } + } +} diff --git a/aes_rsa/src/main/java/com/mhy/aesrsa/util/AES.java b/aes_rsa/src/main/java/com/mhy/aesrsa/util/AES.java new file mode 100644 index 0000000..68cff97 --- /dev/null +++ b/aes_rsa/src/main/java/com/mhy/aesrsa/util/AES.java @@ -0,0 +1,134 @@ +package com.mhy.aesrsa.util; + +import java.io.UnsupportedEncodingException; +import java.security.Key; +import java.security.NoSuchAlgorithmException; +import java.security.SecureRandom; + +import javax.crypto.Cipher; +import javax.crypto.KeyGenerator; +import javax.crypto.spec.IvParameterSpec; +import javax.crypto.spec.SecretKeySpec; + +/** + * Description: + * + * @author: wubaoguo + * @email: wustrive2008@gmail.com + * @date: 2018/7/16 15:12 + */ +public class AES { + + /** + * 加密 + * + * @param data 需要加密的内容 + * @param key 加密密码 + * @return + */ + public static byte[] encrypt(byte[] data, byte[] key) { + CheckUtils.notEmpty(data, "data"); + CheckUtils.notEmpty(key, "key"); + if (key.length != 16) { + throw new RuntimeException("Invalid AES key length (must be 16 bytes)"); + } + try { + SecretKeySpec secretKey = new SecretKeySpec(key, "AES"); + byte[] enCodeFormat = secretKey.getEncoded(); + SecretKeySpec seckey = new SecretKeySpec(enCodeFormat, "AES"); + Cipher cipher = Cipher.getInstance(ConfigureEncryptAndDecrypt.AES_ALGORITHM);// 创建密码器 + IvParameterSpec iv = new IvParameterSpec(key);//使用CBC模式,需要一个向量iv,可增加加密算法的强度 + cipher.init(Cipher.ENCRYPT_MODE, seckey, iv);// 初始化 + byte[] result = cipher.doFinal(data); + return result; // 加密 + } catch (Exception e) { + e.printStackTrace(); + throw new RuntimeException("encrypt fail!", e); + } + } + + /** + * 解密 + * + * @param data 待解密内容 + * @param key 解密密钥 + * @return + */ + public static byte[] decrypt(byte[] data, byte[] key) { + CheckUtils.notEmpty(data, "data"); + CheckUtils.notEmpty(key, "key"); + if (key.length != 16) { + throw new RuntimeException("Invalid AES key length (must be 16 bytes)"); + } + try { + SecretKeySpec secretKey = new SecretKeySpec(key, "AES"); + byte[] enCodeFormat = secretKey.getEncoded(); + SecretKeySpec seckey = new SecretKeySpec(enCodeFormat, "AES"); + Cipher cipher = Cipher.getInstance(ConfigureEncryptAndDecrypt.AES_ALGORITHM);// 创建密码器 + IvParameterSpec iv = new IvParameterSpec(key);//使用CBC模式,需要一个向量iv,可增加加密算法的强度 + cipher.init(Cipher.DECRYPT_MODE, seckey, iv);// 初始化 + byte[] result = cipher.doFinal(data); + return result; // 解密 + } catch (Exception e) { + e.printStackTrace(); + throw new RuntimeException("decrypt fail!", e); + } + } + + public static String encryptToBase64(String data, String key) { + try { + byte[] valueByte = encrypt(data.getBytes(ConfigureEncryptAndDecrypt.CHAR_ENCODING), key.getBytes(ConfigureEncryptAndDecrypt.CHAR_ENCODING)); + return new String(Base64.encode(valueByte)); + } catch (UnsupportedEncodingException e) { + throw new RuntimeException("encrypt fail!", e); + } + + } + + public static String decryptFromBase64(String data, String key) { + try { + byte[] originalData = Base64.decode(data.getBytes()); + byte[] valueByte = decrypt(originalData, key.getBytes(ConfigureEncryptAndDecrypt.CHAR_ENCODING)); + return new String(valueByte, ConfigureEncryptAndDecrypt.CHAR_ENCODING); + } catch (UnsupportedEncodingException e) { + throw new RuntimeException("decrypt fail!", e); + } + } + + public static String encryptWithKeyBase64(String data, String key) { + try { + byte[] valueByte = encrypt(data.getBytes(ConfigureEncryptAndDecrypt.CHAR_ENCODING), Base64.decode(key.getBytes())); + return new String(Base64.encode(valueByte)); + } catch (UnsupportedEncodingException e) { + throw new RuntimeException("encrypt fail!", e); + } + } + + public static String decryptWithKeyBase64(String data, String key) { + try { + byte[] originalData = Base64.decode(data.getBytes()); + byte[] valueByte = decrypt(originalData, Base64.decode(key.getBytes())); + return new String(valueByte, ConfigureEncryptAndDecrypt.CHAR_ENCODING); + } catch (UnsupportedEncodingException e) { + throw new RuntimeException("decrypt fail!", e); + } + } + + public static byte[] genarateRandomKey() { + KeyGenerator keygen = null; + try { + keygen = KeyGenerator.getInstance(ConfigureEncryptAndDecrypt.AES_ALGORITHM); + } catch (NoSuchAlgorithmException e) { + throw new RuntimeException(" genarateRandomKey fail!", e); + } + SecureRandom random = new SecureRandom(); + keygen.init(random); + Key key = keygen.generateKey(); + return key.getEncoded(); + } + + public static String genarateRandomKeyWithBase64() { + return new String(Base64.encode(genarateRandomKey())); + } + +} diff --git a/aes_rsa/src/main/java/com/mhy/aesrsa/util/Base64.java b/aes_rsa/src/main/java/com/mhy/aesrsa/util/Base64.java new file mode 100644 index 0000000..3a87312 --- /dev/null +++ b/aes_rsa/src/main/java/com/mhy/aesrsa/util/Base64.java @@ -0,0 +1,604 @@ +package com.mhy.aesrsa.util; + +import java.io.UnsupportedEncodingException; + +public class Base64 { + /** + * Chunk size per RFC 2045 section 6.8. + * + *

The {@value} character limit does not count the trailing CRLF, but counts + * all other characters, including any equal signs.

+ * + * @see RFC 2045 section 6.8 + */ + static final int CHUNK_SIZE = 76; + + /** + * Chunk separator per RFC 2045 section 2.1. + * + * @see RFC 2045 section 2.1 + */ + static final byte[] CHUNK_SEPARATOR = "\r\n".getBytes(); + + /** + * The base length. + */ + static final int BASELENGTH = 255; + + /** + * Lookup length. + */ + static final int LOOKUPLENGTH = 64; + + /** + * Used to calculate the number of bits in a byte. + */ + static final int EIGHTBIT = 8; + + /** + * Used when encoding something which has fewer than 24 bits. + */ + static final int SIXTEENBIT = 16; + + /** + * Used to determine how many bits data contains. + */ + static final int TWENTYFOURBITGROUP = 24; + + /** + * Used to get the number of Quadruples. + */ + static final int FOURBYTE = 4; + + /** + * Used to test the sign of a byte. + */ + static final int SIGN = -128; + + /** + * Byte used to pad output. + */ + static final byte PAD = (byte) '='; + + // Create arrays to hold the base64 characters and a + // lookup for base64 chars + private static byte[] base64Alphabet = new byte[BASELENGTH]; + private static byte[] lookUpBase64Alphabet = new byte[LOOKUPLENGTH]; + + // Populating the lookup and character arrays + static { + for (int i = 0; i < BASELENGTH; i++) { + base64Alphabet[i] = (byte) -1; + } + for (int i = 'Z'; i >= 'A'; i--) { + base64Alphabet[i] = (byte) (i - 'A'); + } + for (int i = 'z'; i >= 'a'; i--) { + base64Alphabet[i] = (byte) (i - 'a' + 26); + } + for (int i = '9'; i >= '0'; i--) { + base64Alphabet[i] = (byte) (i - '0' + 52); + } + + base64Alphabet['+'] = 62; + base64Alphabet['/'] = 63; + + for (int i = 0; i <= 25; i++) { + lookUpBase64Alphabet[i] = (byte) ('A' + i); + } + + for (int i = 26, j = 0; i <= 51; i++, j++) { + lookUpBase64Alphabet[i] = (byte) ('a' + j); + } + + for (int i = 52, j = 0; i <= 61; i++, j++) { + lookUpBase64Alphabet[i] = (byte) ('0' + j); + } + + lookUpBase64Alphabet[62] = (byte) '+'; + lookUpBase64Alphabet[63] = (byte) '/'; + } + + private static boolean isBase64(byte octect) { + if (octect == PAD) { + return true; + } else if (base64Alphabet[octect] == -1) { + return false; + } else { + return true; + } + } + + /** + * Tests a given byte array to see if it contains + * only valid characters within the Base64 alphabet. + * + * @param arrayOctect byte array to test + * @return true if all bytes are valid characters in the Base64 + * alphabet or if the byte array is empty; false, otherwise + */ + public static boolean isArrayByteBase64(byte[] arrayOctect) { + + arrayOctect = discardWhitespace(arrayOctect); + + int length = arrayOctect.length; + if (length == 0) { + // shouldn't a 0 length array be valid base64 data? + // return false; + return true; + } + for (int i = 0; i < length; i++) { + if (!isBase64(arrayOctect[i])) { + return false; + } + } + return true; + } + + /** + * Encodes binary data using the base64 algorithm but + * does not chunk the output. + * + * @param binaryData binary data to encode + * @return Base64 characters + */ + public static byte[] encodeBase64(byte[] binaryData) { + return encodeBase64(binaryData, false); + } + + /** + * Encodes binary data using the base64 algorithm and chunks + * the encoded output into 76 character blocks + * + * @param binaryData binary data to encode + * @return Base64 characters chunked in 76 character blocks + */ + public static byte[] encodeBase64Chunked(byte[] binaryData) { + return encodeBase64(binaryData, true); + } + + /** + * Decodes a byte[] containing containing + * characters in the Base64 alphabet. + * + * @param pArray A byte array containing Base64 character data + * @return a byte array containing binary data + */ + public static byte[] decode(byte[] pArray) { + return decodeBase64(pArray); + } + + /** + * Encodes binary data using the base64 algorithm, optionally + * chunking the output into 76 character blocks. + * + * @param binaryData Array containing binary data to encode. + * @param isChunked if isChunked is true this encoder will chunk + * the base64 output into 76 character blocks + * @return Base64-encoded data. + */ + public static byte[] encodeBase64(byte[] binaryData, boolean isChunked) { + int lengthDataBits = binaryData.length * EIGHTBIT; + int fewerThan24bits = lengthDataBits % TWENTYFOURBITGROUP; + int numberTriplets = lengthDataBits / TWENTYFOURBITGROUP; + byte encodedData[] = null; + int encodedDataLength = 0; + int nbrChunks = 0; + + if (fewerThan24bits != 0) { + //data not divisible by 24 bit + encodedDataLength = (numberTriplets + 1) * 4; + } else { + // 16 or 8 bit + encodedDataLength = numberTriplets * 4; + } + + // If the output is to be "chunked" into 76 character sections, + // for compliance with RFC 2045 MIME, then it is important to + // allow for extra length to account for the separator(s) + if (isChunked) { + + nbrChunks = + (CHUNK_SEPARATOR.length == 0 ? 0 : (int) Math.ceil((float) encodedDataLength / CHUNK_SIZE)); + encodedDataLength += nbrChunks * CHUNK_SEPARATOR.length; + } + + encodedData = new byte[encodedDataLength]; + + byte k = 0, l = 0, b1 = 0, b2 = 0, b3 = 0; + + int encodedIndex = 0; + int dataIndex = 0; + int i = 0; + int nextSeparatorIndex = CHUNK_SIZE; + int chunksSoFar = 0; + + //log.debug("number of triplets = " + numberTriplets); + for (i = 0; i < numberTriplets; i++) { + dataIndex = i * 3; + b1 = binaryData[dataIndex]; + b2 = binaryData[dataIndex + 1]; + b3 = binaryData[dataIndex + 2]; + + //log.debug("b1= " + b1 +", b2= " + b2 + ", b3= " + b3); + + l = (byte) (b2 & 0x0f); + k = (byte) (b1 & 0x03); + + byte val1 = + ((b1 & SIGN) == 0) ? (byte) (b1 >> 2) : (byte) ((b1) >> 2 ^ 0xc0); + byte val2 = + ((b2 & SIGN) == 0) ? (byte) (b2 >> 4) : (byte) ((b2) >> 4 ^ 0xf0); + byte val3 = + ((b3 & SIGN) == 0) ? (byte) (b3 >> 6) : (byte) ((b3) >> 6 ^ 0xfc); + + encodedData[encodedIndex] = lookUpBase64Alphabet[val1]; + //log.debug( "val2 = " + val2 ); + //log.debug( "k4 = " + (k<<4) ); + //log.debug( "vak = " + (val2 | (k<<4)) ); + encodedData[encodedIndex + 1] = + lookUpBase64Alphabet[val2 | (k << 4)]; + encodedData[encodedIndex + 2] = + lookUpBase64Alphabet[(l << 2) | val3]; + encodedData[encodedIndex + 3] = lookUpBase64Alphabet[b3 & 0x3f]; + + encodedIndex += 4; + + // If we are chunking, let's put a chunk separator down. + if (isChunked) { + // this assumes that CHUNK_SIZE % 4 == 0 + if (encodedIndex == nextSeparatorIndex) { + System.arraycopy( + CHUNK_SEPARATOR, + 0, + encodedData, + encodedIndex, + CHUNK_SEPARATOR.length); + chunksSoFar++; + nextSeparatorIndex = + (CHUNK_SIZE * (chunksSoFar + 1)) + + (chunksSoFar * CHUNK_SEPARATOR.length); + encodedIndex += CHUNK_SEPARATOR.length; + } + } + } + + // form integral number of 6-bit groups + dataIndex = i * 3; + + if (fewerThan24bits == EIGHTBIT) { + b1 = binaryData[dataIndex]; + k = (byte) (b1 & 0x03); + //log.debug("b1=" + b1); + //log.debug("b1<<2 = " + (b1>>2) ); + byte val1 = + ((b1 & SIGN) == 0) ? (byte) (b1 >> 2) : (byte) ((b1) >> 2 ^ 0xc0); + encodedData[encodedIndex] = lookUpBase64Alphabet[val1]; + encodedData[encodedIndex + 1] = lookUpBase64Alphabet[k << 4]; + encodedData[encodedIndex + 2] = PAD; + encodedData[encodedIndex + 3] = PAD; + } else if (fewerThan24bits == SIXTEENBIT) { + + b1 = binaryData[dataIndex]; + b2 = binaryData[dataIndex + 1]; + l = (byte) (b2 & 0x0f); + k = (byte) (b1 & 0x03); + + byte val1 = + ((b1 & SIGN) == 0) ? (byte) (b1 >> 2) : (byte) ((b1) >> 2 ^ 0xc0); + byte val2 = + ((b2 & SIGN) == 0) ? (byte) (b2 >> 4) : (byte) ((b2) >> 4 ^ 0xf0); + + encodedData[encodedIndex] = lookUpBase64Alphabet[val1]; + encodedData[encodedIndex + 1] = + lookUpBase64Alphabet[val2 | (k << 4)]; + encodedData[encodedIndex + 2] = lookUpBase64Alphabet[l << 2]; + encodedData[encodedIndex + 3] = PAD; + } + + if (isChunked) { + // we also add a separator to the end of the final chunk. + if (chunksSoFar < nbrChunks) { + System.arraycopy( + CHUNK_SEPARATOR, + 0, + encodedData, + encodedDataLength - CHUNK_SEPARATOR.length, + CHUNK_SEPARATOR.length); + } + } + + return encodedData; + } + + /** + * Decodes Base64 data into octects + * + * @param base64Data Byte array containing Base64 data + * @return Array containing decoded data. + */ + public static byte[] decodeBase64(byte[] base64Data) { + // RFC 2045 requires that we discard ALL non-Base64 characters + base64Data = discardNonBase64(base64Data); + + // handle the edge case, so we don't have to worry about it later + if (base64Data.length == 0) { + return new byte[0]; + } + + int numberQuadruple = base64Data.length / FOURBYTE; + byte decodedData[] = null; + byte b1 = 0, b2 = 0, b3 = 0, b4 = 0, marker0 = 0, marker1 = 0; + + // Throw away anything not in base64Data + + int encodedIndex = 0; + int dataIndex = 0; + { + // this sizes the output array properly - rlw + int lastData = base64Data.length; + // ignore the '=' padding + while (base64Data[lastData - 1] == PAD) { + if (--lastData == 0) { + return new byte[0]; + } + } + decodedData = new byte[lastData - numberQuadruple]; + } + + for (int i = 0; i < numberQuadruple; i++) { + dataIndex = i * 4; + marker0 = base64Data[dataIndex + 2]; + marker1 = base64Data[dataIndex + 3]; + + b1 = base64Alphabet[base64Data[dataIndex]]; + b2 = base64Alphabet[base64Data[dataIndex + 1]]; + + if (marker0 != PAD && marker1 != PAD) { + //No PAD e.g 3cQl + b3 = base64Alphabet[marker0]; + b4 = base64Alphabet[marker1]; + + decodedData[encodedIndex] = (byte) (b1 << 2 | b2 >> 4); + decodedData[encodedIndex + 1] = + (byte) (((b2 & 0xf) << 4) | ((b3 >> 2) & 0xf)); + decodedData[encodedIndex + 2] = (byte) (b3 << 6 | b4); + } else if (marker0 == PAD) { + //Two PAD e.g. 3c[Pad][Pad] + decodedData[encodedIndex] = (byte) (b1 << 2 | b2 >> 4); + } else if (marker1 == PAD) { + //One PAD e.g. 3cQ[Pad] + b3 = base64Alphabet[marker0]; + + decodedData[encodedIndex] = (byte) (b1 << 2 | b2 >> 4); + decodedData[encodedIndex + 1] = + (byte) (((b2 & 0xf) << 4) | ((b3 >> 2) & 0xf)); + } + encodedIndex += 3; + } + return decodedData; + } + + /** + * Discards any whitespace from a base-64 encoded block. + * + * @param data The base-64 encoded data to discard the whitespace + * from. + * @return The data, less whitespace (see RFC 2045). + */ + static byte[] discardWhitespace(byte[] data) { + byte groomedData[] = new byte[data.length]; + int bytesCopied = 0; + + for (int i = 0; i < data.length; i++) { + switch (data[i]) { + case (byte) ' ' : + case (byte) '\n' : + case (byte) '\r' : + case (byte) '\t' : + break; + default: + groomedData[bytesCopied++] = data[i]; + } + } + + byte packedData[] = new byte[bytesCopied]; + + System.arraycopy(groomedData, 0, packedData, 0, bytesCopied); + + return packedData; + } + + /** + * Discards any characters outside of the base64 alphabet, per + * the requirements on page 25 of RFC 2045 - "Any characters + * outside of the base64 alphabet are to be ignored in base64 + * encoded data." + * + * @param data The base-64 encoded data to groom + * @return The data, less non-base64 characters (see RFC 2045). + */ + static byte[] discardNonBase64(byte[] data) { + byte groomedData[] = new byte[data.length]; + int bytesCopied = 0; + + for (int i = 0; i < data.length; i++) { + if (isBase64(data[i])) { + groomedData[bytesCopied++] = data[i]; + } + } + + byte packedData[] = new byte[bytesCopied]; + + System.arraycopy(groomedData, 0, packedData, 0, bytesCopied); + + return packedData; + } + + /** + * Encodes a byte[] containing binary data, into a byte[] containing + * characters in the Base64 alphabet. + * + * @param pArray a byte array containing binary data + * @return A byte array containing only Base64 character data + */ + public static byte[] encode(byte[] pArray) { + return encodeBase64(pArray, false); + } + + public static String encode(String str) throws UnsupportedEncodingException + { + String baseStr = new String(encode(str.getBytes("UTF-8"))); + String tempStr = Digest.digest(str).toUpperCase(); + String result = tempStr+baseStr; + return new String(encode(result.getBytes("UTF-8"))); + } + + public static String decode(String cryptoStr) throws + UnsupportedEncodingException { + if(cryptoStr.length()<40) + return ""; + try + { + String tempStr = new String(decode(cryptoStr.getBytes("UTF-8"))); + String result = tempStr.substring(40, tempStr.length()); + return new String(decode(result.getBytes("UTF-8"))); + } + catch(ArrayIndexOutOfBoundsException ex) + { + return ""; + } + } + + /** + * Decodes Base64 data into octects + * + * @param encoded string containing Base64 data + * @return Array containind decoded data. + */ + public static byte[] decode2(String encoded) { + + if (encoded == null) { + return null; + } + + char[] base64Data = encoded.toCharArray(); + // remove white spaces + int len = removeWhiteSpace(base64Data); + + if (len % FOURBYTE != 0) { + return null;//should be divisible by four + } + + int numberQuadruple = (len / FOURBYTE); + + if (numberQuadruple == 0) { + return new byte[0]; + } + + byte decodedData[] = null; + byte b1 = 0, b2 = 0, b3 = 0, b4 = 0; + char d1 = 0, d2 = 0, d3 = 0, d4 = 0; + + int i = 0; + int encodedIndex = 0; + int dataIndex = 0; + decodedData = new byte[(numberQuadruple) * 3]; + + for (; i < numberQuadruple - 1; i++) { + + if (!isData((d1 = base64Data[dataIndex++])) || !isData((d2 = base64Data[dataIndex++])) + || !isData((d3 = base64Data[dataIndex++])) + || !isData((d4 = base64Data[dataIndex++]))) { + return null; + }//if found "no data" just return null + + b1 = base64Alphabet[d1]; + b2 = base64Alphabet[d2]; + b3 = base64Alphabet[d3]; + b4 = base64Alphabet[d4]; + + decodedData[encodedIndex++] = (byte) (b1 << 2 | b2 >> 4); + decodedData[encodedIndex++] = (byte) (((b2 & 0xf) << 4) | ((b3 >> 2) & 0xf)); + decodedData[encodedIndex++] = (byte) (b3 << 6 | b4); + } + + if (!isData((d1 = base64Data[dataIndex++])) || !isData((d2 = base64Data[dataIndex++]))) { + return null;//if found "no data" just return null + } + + b1 = base64Alphabet[d1]; + b2 = base64Alphabet[d2]; + + d3 = base64Data[dataIndex++]; + d4 = base64Data[dataIndex++]; + if (!isData((d3)) || !isData((d4))) {//Check if they are PAD characters + if (isPad(d3) && isPad(d4)) { + if ((b2 & 0xf) != 0)//last 4 bits should be zero + { + return null; + } + byte[] tmp = new byte[i * 3 + 1]; + System.arraycopy(decodedData, 0, tmp, 0, i * 3); + tmp[encodedIndex] = (byte) (b1 << 2 | b2 >> 4); + return tmp; + } else if (!isPad(d3) && isPad(d4)) { + b3 = base64Alphabet[d3]; + if ((b3 & 0x3) != 0)//last 2 bits should be zero + { + return null; + } + byte[] tmp = new byte[i * 3 + 2]; + System.arraycopy(decodedData, 0, tmp, 0, i * 3); + tmp[encodedIndex++] = (byte) (b1 << 2 | b2 >> 4); + tmp[encodedIndex] = (byte) (((b2 & 0xf) << 4) | ((b3 >> 2) & 0xf)); + return tmp; + } else { + return null; + } + } else { //No PAD e.g 3cQl + b3 = base64Alphabet[d3]; + b4 = base64Alphabet[d4]; + decodedData[encodedIndex++] = (byte) (b1 << 2 | b2 >> 4); + decodedData[encodedIndex++] = (byte) (((b2 & 0xf) << 4) | ((b3 >> 2) & 0xf)); + decodedData[encodedIndex++] = (byte) (b3 << 6 | b4); + + } + + return decodedData; + } + + private static boolean isWhiteSpace(char octect) { + return (octect == 0x20 || octect == 0xd || octect == 0xa || octect == 0x9); + } + + private static boolean isData(char octect) { + return (octect < BASELENGTH && base64Alphabet[octect] != -1); + } + + private static boolean isPad(char octect) { + return (octect == PAD); + } + + /** + * remove WhiteSpace from MIME containing encoded Base64 data. + * + * @param data the byte array of base64 data (with WS) + * @return the new length + */ + private static int removeWhiteSpace(char[] data) { + if (data == null) { + return 0; + } + + // count characters that's not whitespace + int newSize = 0; + int len = data.length; + for (int i = 0; i < len; i++) { + if (!isWhiteSpace(data[i])) { + data[newSize++] = data[i]; + } + } + return newSize; + } +} diff --git a/aes_rsa/src/main/java/com/mhy/aesrsa/util/CheckUtils.java b/aes_rsa/src/main/java/com/mhy/aesrsa/util/CheckUtils.java new file mode 100644 index 0000000..4cb806b --- /dev/null +++ b/aes_rsa/src/main/java/com/mhy/aesrsa/util/CheckUtils.java @@ -0,0 +1,38 @@ +package com.mhy.aesrsa.util; + +import java.lang.reflect.Array; +import java.util.Collection; +import java.util.Map; + + +public class CheckUtils { + + public static final String COMMON_FIELD = "flowID,initiator,"; + + + /** + * 验证对象是否为NULL,空字符串,空数组,空的Collection或Map(只有空格的字符串也认为是空串) + * @param obj 被验证的对象 + * @param message 异常信息 + */ + @SuppressWarnings("rawtypes") + public static void notEmpty(Object obj, String message) { + if (obj == null){ + throw new IllegalArgumentException(message + " must be specified"); + } + if (obj instanceof String && obj.toString().trim().length()==0){ + throw new IllegalArgumentException(message + " must be specified"); + } + if (obj.getClass().isArray() && Array.getLength(obj)==0){ + throw new IllegalArgumentException(message + " must be specified"); + } + if (obj instanceof Collection && ((Collection)obj).isEmpty()){ + throw new IllegalArgumentException(message + " must be specified"); + } + if (obj instanceof Map && ((Map)obj).isEmpty()){ + throw new IllegalArgumentException(message + " must be specified"); + } + } + + +} diff --git a/aes_rsa/src/main/java/com/mhy/aesrsa/util/ConfigureEncryptAndDecrypt.java b/aes_rsa/src/main/java/com/mhy/aesrsa/util/ConfigureEncryptAndDecrypt.java new file mode 100644 index 0000000..01a5487 --- /dev/null +++ b/aes_rsa/src/main/java/com/mhy/aesrsa/util/ConfigureEncryptAndDecrypt.java @@ -0,0 +1,8 @@ +package com.mhy.aesrsa.util; + +public class ConfigureEncryptAndDecrypt { + public static final String CHAR_ENCODING = "UTF-8"; + public static final String AES_ALGORITHM = "AES/CBC/PKCS5Padding"; + public static final String RSA_ALGORITHM = "RSA/ECB/PKCS1Padding"; +} + diff --git a/aes_rsa/src/main/java/com/mhy/aesrsa/util/ConvertUtils.java b/aes_rsa/src/main/java/com/mhy/aesrsa/util/ConvertUtils.java new file mode 100644 index 0000000..2158993 --- /dev/null +++ b/aes_rsa/src/main/java/com/mhy/aesrsa/util/ConvertUtils.java @@ -0,0 +1,335 @@ +package com.mhy.aesrsa.util; + +import java.io.UnsupportedEncodingException; +import java.sql.Date; +import java.sql.Time; +import java.sql.Timestamp; +import java.text.DecimalFormat; +import java.util.Calendar; +import java.util.GregorianCalendar; +import java.util.Locale; +import java.util.TimeZone; + +public abstract class ConvertUtils { + + private static final DecimalFormat simpleFormat = new DecimalFormat("####"); + + public static final boolean objectToBoolean(Object o){ + return o != null ? Boolean.valueOf(o.toString()).booleanValue() : false; + } + + public static final int objectToInt(Object o){ + if(o instanceof Number) + return ((Number)o).intValue(); + try{ + if(o == null) + return -1; + else + return Integer.parseInt(o.toString()); + }catch(NumberFormatException e){ + return -1; + } + } + + public static final short objectToShort(Object o){ + if(o instanceof Number) + return ((Number)o).shortValue(); + try{ + if(o == null) + return -1; + else + return Short.parseShort(o.toString()); + }catch(NumberFormatException e){ + return -1; + } + } + + public static final double objectToDouble(Object o){ + if(o instanceof Number) + return ((Number)o).doubleValue(); + try{ + if(o == null) + return -1D; + else + return Double.parseDouble(o.toString()); + }catch(NumberFormatException e){ + return -1D; + } + } + + public static final long objectToLong(Object o) + { + if(o instanceof Number) + return ((Number)o).longValue(); + try{ + if(o == null) + return -1L; + else + return Long.parseLong(o.toString()); + }catch(NumberFormatException e){ + return -1L; + } + } + + public static final String objectToString(Object obj, DecimalFormat fmt) + { + fmt.setDecimalSeparatorAlwaysShown(false); + if(obj instanceof Double) + return fmt.format(((Double)obj).doubleValue()); + if(obj instanceof Long) + return fmt.format(((Long)obj).longValue()); + else + return obj.toString(); + } + + public static final Object getObjectValue(String value) + { + try{ + return Long.valueOf(value); + }catch(NumberFormatException e) {} + + try{ + return Double.valueOf(value); + }catch(NumberFormatException e){ + return value; + } + } + + public static String longToSimpleString(long value){ + return simpleFormat.format(value); + } + + public static String asHex(byte hash[]){ + return toHex(hash); + } + + public static String toHex(byte input[]){ + if(input == null) + return null; + StringBuffer output = new StringBuffer(input.length * 2); + for(int i = 0; i < input.length; i++){ + int current = input[i] & 0xff; + if(current < 16) + output.append("0"); + output.append(Integer.toString(current, 16)); + } + + return output.toString(); + } + + public static byte[] fromHex(String input){ + if(input == null) + return null; + byte output[] = new byte[input.length() / 2]; + for(int i = 0; i < output.length; i++) + output[i] = (byte)Integer.parseInt(input.substring(i * 2, (i + 1) * 2), 16); + + return output; + } + + public static String stringToHexString(String input, String encoding) + throws UnsupportedEncodingException{ + return input != null ? toHex(input.getBytes(encoding)) : null; + } + + public static String stringToHexString(String input){ + try{ + return stringToHexString(input, "UTF-8"); + }catch(UnsupportedEncodingException e){ + throw new IllegalStateException("UTF-8 encoding is not supported by JVM"); + } + } + + public static String hexStringToString(String input, String encoding) + throws UnsupportedEncodingException{ + return input != null ? new String(fromHex(input), encoding) : null; + } + + public static String hexStringToString(String input){ + try{ + return hexStringToString(input, "UTF-8"); + }catch(UnsupportedEncodingException e){ + throw new IllegalStateException("UTF-8 encoding is not supported by JVM"); + } + } + + public static String timeZoneToCode(TimeZone tz){ + + return timeZoneToString(tz); + } + + public static TimeZone codeToTimeZone(String tzString){ + + return stringToTimeZone(tzString); + } + + public static String timeZoneToString(TimeZone tz){ + + return tz != null ? tz.getID() : ""; + } + + public static TimeZone stringToTimeZone(String tzString){ + + return TimeZone.getTimeZone(tzString != null ? tzString : ""); + } + + public static String localeToCode(Locale aLocale){ + + return localeToString(aLocale); + } + + public static Locale codeToLocale(String locString){ + + return stringToLocale(locString); + } + + public static String localeToString(Locale loc){ + + return loc != null ? loc.toString() : ""; + } + + public static Locale stringToLocale(String locString){ + + locString = locString != null ? locString.trim() : ""; + if(locString.equals("")) + return new Locale("", "", ""); + int pos = locString.indexOf(95); + if(pos == -1) + return new Locale(locString, "", ""); + String language = locString.substring(0, pos); + locString = locString.substring(pos + 1); + pos = locString.indexOf(95); + if(pos == -1){ + return new Locale(language, locString, ""); + }else{ + String country = locString.substring(0, pos); + locString = locString.substring(pos + 1); + return new Locale(language, country, locString); + } + } + + public static Date dateToSQLDate(java.util.Date d){ + + return d != null ? new Date(d.getTime()) : null; + } + + public static Time dateToSQLTime(java.util.Date d){ + + return d != null ? new Time(d.getTime()) : null; + } + + public static Timestamp dateToSQLTimestamp(java.util.Date d){ + + return d != null ? new Timestamp(d.getTime()) : null; + } + + public static java.util.Date sqlTimestampToDate(Timestamp t){ + + return t != null ? new java.util.Date(Math.round((double)t.getTime() + (double)t.getNanos() / 1000000D)) : null; + } + + public static Timestamp getCurrentDate(){ + + Calendar c = Calendar.getInstance(); + c.set(c.get(1), c.get(2), c.get(5), 0, 0, 0); + Timestamp t = new Timestamp(c.getTime().getTime()); + t.setNanos(0); + return t; + } + + public static java.util.Date getDate(int y, int m, int d, boolean inclusive) + { + java.util.Date dt = null; + Calendar c = Calendar.getInstance(); + c.clear(); + if(c.getActualMinimum(1) <= y && y <= c.getActualMaximum(1)) + { + c.set(1, y); + if(c.getActualMinimum(2) <= m && m <= c.getActualMaximum(2)) + { + c.set(2, m); + if(c.getActualMinimum(5) <= d && d <= c.getActualMaximum(5)) + c.set(5, d); + } + if(inclusive) + { + c.add(5, 1); + c.add(14, -1); + } + dt = c.getTime(); + } + return dt; + } + + public static java.util.Date getDateStart(java.util.Date d) + { + + Calendar c = new GregorianCalendar(); + c.clear(); + Calendar co = new GregorianCalendar(); + co.setTime(d); + c.set(Calendar.DAY_OF_MONTH,co.get(Calendar.DAY_OF_MONTH)); + c.set(Calendar.MONTH,co.get(Calendar.MONTH)); + c.set(Calendar.YEAR,co.get(Calendar.YEAR)); + //c.add(Calendar.DAY_OF_MONTH,1); + //c.add(Calendar.MILLISECOND,-1); + return c.getTime(); + } + + public static java.util.Date getDateEnd(java.util.Date d) + { + Calendar c = Calendar.getInstance(); + c.clear(); + Calendar co = Calendar.getInstance(); + co.setTime(d); + c.set(Calendar.DAY_OF_MONTH,co.get(Calendar.DAY_OF_MONTH)); + c.set(Calendar.MONTH,co.get(Calendar.MONTH)); + c.set(Calendar.YEAR,co.get(Calendar.YEAR)); + c.add(Calendar.DAY_OF_MONTH,1); + c.add(Calendar.MILLISECOND,-1); + return c.getTime(); + } + + public static double roundNumber(double rowNumber, int roundingPoint) + { + double base = Math.pow(10D, roundingPoint); + return (double)Math.round(rowNumber * base) / base; + } + public static Object getObject(String type,String value) throws Exception{ + + type=type.toLowerCase(); + if("boolean".equals(type)) + return Boolean.valueOf(value); + if("byte".equals(type)) + return Byte.valueOf(value); + if("short".equals(type)) + return Short.valueOf(value); + if("char".equals(type)) + if(value.length() != 1) + throw new NumberFormatException("Argument is not a character!"); + else + return Character.valueOf(value.toCharArray()[0]); + if("int".equals(type)) + return Integer.valueOf(value); + if("long".equals(type)) + return Long.valueOf(value); + if("float".equals(type)) + return Float.valueOf(value); + if("double".equals(type)) + return Double.valueOf(value); + if("string".equals(type)) + return value; + else{ + Object objs[]=new String[]{value}; + return Class.forName(type).getConstructor(new Class[] { + String.class + }).newInstance(objs); + } + } + private ConvertUtils(){} + +// public static void main(String[] args) +// { +// System.out.println(getDateStart(new java.util.Date())); +// } +} diff --git a/aes_rsa/src/main/java/com/mhy/aesrsa/util/Digest.java b/aes_rsa/src/main/java/com/mhy/aesrsa/util/Digest.java new file mode 100644 index 0000000..14a70fc --- /dev/null +++ b/aes_rsa/src/main/java/com/mhy/aesrsa/util/Digest.java @@ -0,0 +1,167 @@ +package com.mhy.aesrsa.util; + +import java.io.UnsupportedEncodingException; +import java.security.MessageDigest; +import java.security.NoSuchAlgorithmException; +import java.util.Arrays; + +import org.apache.log4j.Logger; + +public class Digest { + private static final Logger log = Logger.getLogger(Digest.class); + public static final String ENCODE = "UTF-8"; + + public static String signMD5(String aValue, String encoding) { + try { + byte[] input = aValue.getBytes(encoding); + MessageDigest md = MessageDigest.getInstance("MD5"); + return ConvertUtils.toHex(md.digest(input)); + } catch (NoSuchAlgorithmException e) { + log.error(e.getMessage(), e); + return null; + } catch (UnsupportedEncodingException e) { + log.error(e.getMessage(), e); + return null; + } + } + + public static String hmacSign(String aValue) { + try { + byte[] input = aValue.getBytes(); + MessageDigest md = MessageDigest.getInstance("MD5"); + return ConvertUtils.toHex(md.digest(input)); + } catch (NoSuchAlgorithmException e) { + log.error(e.getMessage(), e); + return null; + } + } + + public static String hmacSign(String aValue, String aKey) { + return hmacSign(aValue, aKey, ENCODE); + } + + public static String hmacSign(String aValue, String aKey, String encoding) { + byte k_ipad[] = new byte[64]; + byte k_opad[] = new byte[64]; + byte keyb[]; + byte value[]; + try { + keyb = aKey.getBytes(encoding); + value = aValue.getBytes(encoding); + } catch (UnsupportedEncodingException e) { + keyb = aKey.getBytes(); + value = aValue.getBytes(); + } + Arrays.fill(k_ipad, keyb.length, 64, (byte) 54); + Arrays.fill(k_opad, keyb.length, 64, (byte) 92); + for (int i = 0; i < keyb.length; i++) { + k_ipad[i] = (byte) (keyb[i] ^ 0x36); + k_opad[i] = (byte) (keyb[i] ^ 0x5c); + } + + MessageDigest md = null; + try { + md = MessageDigest.getInstance("MD5"); + } catch (NoSuchAlgorithmException e) { + log.error(e.getMessage(), e); + return null; + } + md.update(k_ipad); + md.update(value); + byte dg[] = md.digest(); + md.reset(); + md.update(k_opad); + md.update(dg, 0, 16); + dg = md.digest(); + return ConvertUtils.toHex(dg); + } + + public static String hmacSHASign(String aValue, String aKey, String encoding) { + byte k_ipad[] = new byte[64]; + byte k_opad[] = new byte[64]; + byte keyb[]; + byte value[]; + try { + keyb = aKey.getBytes(encoding); + value = aValue.getBytes(encoding); + } catch (UnsupportedEncodingException e) { + keyb = aKey.getBytes(); + value = aValue.getBytes(); + } + Arrays.fill(k_ipad, keyb.length, 64, (byte) 54); + Arrays.fill(k_opad, keyb.length, 64, (byte) 92); + for (int i = 0; i < keyb.length; i++) { + k_ipad[i] = (byte) (keyb[i] ^ 0x36); + k_opad[i] = (byte) (keyb[i] ^ 0x5c); + } + + MessageDigest md = null; + try { + md = MessageDigest.getInstance("SHA"); + } catch (NoSuchAlgorithmException e) { + log.error(e.getMessage(), e); + return null; + } + md.update(k_ipad); + md.update(value); + byte dg[] = md.digest(); + md.reset(); + md.update(k_opad); + md.update(dg, 0, 20); + dg = md.digest(); + return ConvertUtils.toHex(dg); + } + + public static String digest(String aValue) { + return digest(aValue, ENCODE); + + } + + public static String digest(String aValue, String encoding) { + aValue = aValue.trim(); + byte value[]; + try { + value = aValue.getBytes(encoding); + } catch (UnsupportedEncodingException e) { + value = aValue.getBytes(); + } + MessageDigest md = null; + try { + md = MessageDigest.getInstance("SHA"); + } catch (NoSuchAlgorithmException e) { + log.error(e.getMessage(), e); + return null; + } + return ConvertUtils.toHex(md.digest(value)); + } + + + public static String digest(String aValue, String alg, String encoding) { + aValue = aValue.trim(); + byte value[]; + try { + value = aValue.getBytes(encoding); + } catch (UnsupportedEncodingException e) { + value = aValue.getBytes(); + } + MessageDigest md = null; + try { + md = MessageDigest.getInstance(alg); + } catch (NoSuchAlgorithmException e) { + log.error(e.getMessage(), e); + return null; + } + return ConvertUtils.toHex(md.digest(value)); + } + + public static String udpSign(String aValue) { + try { + byte[] input = aValue.getBytes("UTF-8"); + MessageDigest md = MessageDigest.getInstance("SHA1"); + return new String(Base64.encode(md.digest(input)), ENCODE); + } catch (Exception e) { + return null; + } + } + +} diff --git a/aes_rsa/src/main/java/com/mhy/aesrsa/util/EncryUtil.java b/aes_rsa/src/main/java/com/mhy/aesrsa/util/EncryUtil.java new file mode 100644 index 0000000..9e31332 --- /dev/null +++ b/aes_rsa/src/main/java/com/mhy/aesrsa/util/EncryUtil.java @@ -0,0 +1,106 @@ +package com.mhy.aesrsa.util; + +import android.text.TextUtils; +import android.util.Log; + +import java.util.Iterator; +import java.util.Map.Entry; +import java.util.TreeMap; + +import com.alibaba.fastjson.JSON; +import com.alibaba.fastjson.TypeReference; + +public class EncryUtil { + + /** + * 生成RSA签名 + */ + public static String handleRSA(TreeMap map, + String privateKey) { + StringBuffer sbuffer = new StringBuffer(); + for (Entry entry : map.entrySet()) { + sbuffer.append(entry.getValue()); + } + String signTemp = sbuffer.toString(); + + String sign = ""; + if (!TextUtils.isEmpty(privateKey)) { + sign = RSA.sign(signTemp, privateKey); + } + return sign; + } + + /** + * 返回的结果进行验签 + * + * @param data + * 业务数据密文 + * @param encrypt_key + * 对ybAesKey加密后的密文 + * @param clientPublicKey + * 客户端公钥 + * @param serverPrivateKey + * 服务器私钥 + * @return 验签是否通过 + * @throws Exception + */ + public static boolean checkDecryptAndSign(String data, String encrypt_key, + String clientPublicKey, String serverPrivateKey) throws Exception { + + /** 1.使用serverPrivateKey解开aesEncrypt。 */ + String AESKey = ""; + try { + AESKey = RSA.decrypt(encrypt_key, serverPrivateKey); + } catch (Exception e) { + e.printStackTrace(); + /** AES密钥解密失败 */ + Log.e(EncryUtil.class.getSimpleName(), e.getMessage()); + return false; + } + + /** 2.用aeskey解开data。取得data明文 */ + String realData = ConvertUtils.hexStringToString(AES.decryptFromBase64(data, AESKey)); + + TreeMap map = JSON.parseObject(realData, + new TypeReference>() {}); + + /** 3.取得data明文sign。 */ + String sign = map.get("sign") == null ? "" : map.get("sign").trim(); + + /** 4.对map中的值进行验证 */ + StringBuffer signData = new StringBuffer(); + Iterator> iter = map.entrySet().iterator(); + while (iter.hasNext()) { + Entry entry = iter.next(); + + /** 把sign参数隔过去 */ + if (TextUtils.equals((String) entry.getKey(), "sign")) { + continue; + } + signData.append(entry.getValue() == null ? "" : entry.getValue()); + } + + /** 5. result为true时表明验签通过 */ + boolean result = RSA.checkSign(signData.toString(), sign, + clientPublicKey); + + return result; + } + + /** + * 生成hmac + */ + public static String handleHmac(TreeMap map, String hmacKey) { + StringBuffer sbuffer = new StringBuffer(); + for (Entry entry : map.entrySet()) { + sbuffer.append(entry.getValue()); + } + String hmacTemp = sbuffer.toString(); + + String hmac = ""; + if (!TextUtils.isEmpty(hmacKey)) { + hmac = Digest.hmacSHASign(hmacTemp, hmacKey, Digest.ENCODE); + } + return hmac; + } +} diff --git a/aes_rsa/src/main/java/com/mhy/aesrsa/util/EncryptionUtil.java b/aes_rsa/src/main/java/com/mhy/aesrsa/util/EncryptionUtil.java new file mode 100644 index 0000000..61a2022 --- /dev/null +++ b/aes_rsa/src/main/java/com/mhy/aesrsa/util/EncryptionUtil.java @@ -0,0 +1,79 @@ +package com.mhy.aesrsa.util; + +import java.security.MessageDigest; + +/** + * Description: 加密工具类 + * + * @author: wubaoguo + * @email: wustrive2008@gmail.com + * @date: 2018/9/20 14:28 + * @Copyright: 2017-2018 dgztc Inc. All rights reserved. + */ +public class EncryptionUtil { + + /** + * 对字符串进行md5加密 + * + * @param str + * @return + */ + public static String md5(String str) { + try { + MessageDigest md = MessageDigest.getInstance("MD5"); + md.update(str.getBytes()); + return byteToHex(md.digest()); + } catch (Exception e) { + e.printStackTrace(); + } + return ""; + } + + /** + * 对字符串进行sha256加密 + * + * @param str + * @return + */ + public static String sha256(String str) { + try { + MessageDigest md = MessageDigest.getInstance("SHA-256"); + md.update(str.getBytes()); + return byteToHex(md.digest()); + } catch (Exception e) { + e.printStackTrace(); + } + return ""; + } + + /** + * 对字符串进行sha1加密 + * + * @param str + * @return + */ + public static String sha1(String str) { + try { + MessageDigest md = MessageDigest.getInstance("SHA-1"); + md.update(str.getBytes()); + return byteToHex(md.digest()); + } catch (Exception e) { + e.printStackTrace(); + } + return ""; + } + + /** + * 字节数组转16进制字符串 + * + * @param data + * @return + */ + public static String byteToHex(byte[] data) { + final StringBuilder builder = new StringBuilder(); + for(byte b : data) { + builder.append(String.format("%02x", b)); + } + return builder.toString(); + } +} diff --git a/aes_rsa/src/main/java/com/mhy/aesrsa/util/RSA.java b/aes_rsa/src/main/java/com/mhy/aesrsa/util/RSA.java new file mode 100644 index 0000000..98ec297 --- /dev/null +++ b/aes_rsa/src/main/java/com/mhy/aesrsa/util/RSA.java @@ -0,0 +1,195 @@ +package com.mhy.aesrsa.util; + +/* + --------------------------------------------**********-------------------------------------------- + + 该算法于1977年由美国麻省理工学院MIT(Massachusetts Institute of Technology)的Ronal Rivest,Adi Shamir和Len Adleman三位年轻教授提出,并以三人的姓氏Rivest,Shamir和Adlernan命名为RSA算法,是一个支持变长密钥的公共密钥算法,需要加密的文件快的长度也是可变的! + + 所谓RSA加密算法,是世界上第一个非对称加密算法,也是数论的第一个实际应用。它的算法如下: + + 1.找两个非常大的质数p和q(通常p和q都有155十进制位或都有512十进制位)并计算n=pq,k=(p-1)(q-1)。 + + 2.将明文编码成整数M,保证M不小于0但是小于n。 + + 3.任取一个整数e,保证e和k互质,而且e不小于0但是小于k。加密钥匙(称作公钥)是(e, n)。 + + 4.找到一个整数d,使得ed除以k的余数是1(只要e和n满足上面条件,d肯定存在)。解密钥匙(称作密钥)是(d, n)。 + + 加密过程: 加密后的编码C等于M的e次方除以n所得的余数。 + + 解密过程: 解密后的编码N等于C的d次方除以n所得的余数。 + + 只要e、d和n满足上面给定的条件。M等于N。 + + --------------------------------------------**********-------------------------------------------- + */ +import android.util.Log; + +import java.math.BigInteger; +import java.security.Key; +import java.security.KeyFactory; +import java.security.KeyPair; +import java.security.KeyPairGenerator; +import java.security.PrivateKey; +import java.security.PublicKey; +import java.security.SecureRandom; +import java.security.Signature; +import java.security.interfaces.RSAPublicKey; +import java.security.spec.PKCS8EncodedKeySpec; +import java.security.spec.X509EncodedKeySpec; +import java.util.HashMap; +import java.util.Map; + +import javax.crypto.Cipher; + +public class RSA { + + /** 指定key的大小 */ + private static int KEYSIZE = 2048; + /** + * 生成密钥对 + */ + public static Map generateKeyPair() throws Exception { + /** RSA算法要求有一个可信任的随机数源 */ + SecureRandom sr = new SecureRandom(); + /** 为RSA算法创建一个KeyPairGenerator对象 */ + KeyPairGenerator kpg = KeyPairGenerator.getInstance("RSA"); + /** 利用上面的随机数据源初始化这个KeyPairGenerator对象 */ + kpg.initialize(KEYSIZE, sr); + /** 生成密匙对 */ + KeyPair kp = kpg.generateKeyPair(); + /** 得到公钥 */ + Key publicKey = kp.getPublic(); + byte[] publicKeyBytes = publicKey.getEncoded(); + String pub = new String(Base64.encodeBase64(publicKeyBytes), + ConfigureEncryptAndDecrypt.CHAR_ENCODING); + /** 得到私钥 */ + Key privateKey = kp.getPrivate(); + byte[] privateKeyBytes = privateKey.getEncoded(); + String pri = new String(Base64.encodeBase64(privateKeyBytes), + ConfigureEncryptAndDecrypt.CHAR_ENCODING); + + Map map = new HashMap(); + map.put("publicKey", pub); + map.put("privateKey", pri); + RSAPublicKey rsp = (RSAPublicKey) kp.getPublic(); + BigInteger bint = rsp.getModulus(); + byte[] b = bint.toByteArray(); + byte[] deBase64Value = Base64.encodeBase64(b); + String retValue = new String(deBase64Value); + map.put("modulus", retValue); + return map; + } + + /** + * 加密方法 source: 源数据 + */ + public static String encrypt(String source, String publicKey) + throws Exception { + Key key = getPublicKey(publicKey); + /** 得到Cipher对象来实现对源数据的RSA加密 */ + Cipher cipher = Cipher.getInstance(ConfigureEncryptAndDecrypt.RSA_ALGORITHM); + cipher.init(Cipher.ENCRYPT_MODE, key); + byte[] b = source.getBytes(); + /** 执行加密操作 */ + byte[] b1 = cipher.doFinal(b); + return new String(Base64.encodeBase64(b1), + ConfigureEncryptAndDecrypt.CHAR_ENCODING); + } + + /** + * 解密算法 cryptograph:密文 + */ + public static String decrypt(String cryptograph, String privateKey) + throws Exception { + Key key = getPrivateKey(privateKey); + /** 得到Cipher对象对已用公钥加密的数据进行RSA解密 */ + Cipher cipher = Cipher.getInstance(ConfigureEncryptAndDecrypt.RSA_ALGORITHM); + cipher.init(Cipher.DECRYPT_MODE, key); + byte[] b1 = Base64.decodeBase64(cryptograph.getBytes()); + /** 执行解密操作 */ + byte[] b = cipher.doFinal(b1); + return new String(b); + } + + /** + * 得到公钥 + * + * @param key + * 密钥字符串(经过base64编码) + * @throws Exception + */ + public static PublicKey getPublicKey(String key) throws Exception { + X509EncodedKeySpec keySpec = new X509EncodedKeySpec( + Base64.decodeBase64(key.getBytes())); + KeyFactory keyFactory = KeyFactory.getInstance("RSA"); + PublicKey publicKey = keyFactory.generatePublic(keySpec); + return publicKey; + } + + /** + * 得到私钥 + * + * @param key + * 密钥字符串(经过base64编码) + * @throws Exception + */ + public static PrivateKey getPrivateKey(String key) throws Exception { + PKCS8EncodedKeySpec keySpec = new PKCS8EncodedKeySpec( + Base64.decodeBase64(key.getBytes())); + KeyFactory keyFactory = KeyFactory.getInstance("RSA"); + PrivateKey privateKey = keyFactory.generatePrivate(keySpec); + return privateKey; + } + + public static String sign(String content, String privateKey) { + String charset = ConfigureEncryptAndDecrypt.CHAR_ENCODING; + try { + PKCS8EncodedKeySpec priPKCS8 = new PKCS8EncodedKeySpec( + Base64.decodeBase64(privateKey.getBytes())); + KeyFactory keyf = KeyFactory.getInstance("RSA"); + PrivateKey priKey = keyf.generatePrivate(priPKCS8); + + Signature signature = Signature.getInstance("SHA256WithRSA"); + + signature.initSign(priKey); + signature.update(content.getBytes(charset)); + + byte[] signed = signature.sign(); + + return new String(Base64.encodeBase64(signed)); + } catch (Exception e) { + + } + + return null; + } + + public static boolean checkSign(String content, String sign, String publicKey) + { + try + { + KeyFactory keyFactory = KeyFactory.getInstance("RSA"); + byte[] encodedKey = Base64.decode2(publicKey); + PublicKey pubKey = keyFactory.generatePublic(new X509EncodedKeySpec(encodedKey)); + + + Signature signature = Signature + .getInstance("SHA256WithRSA"); + + signature.initVerify(pubKey); + signature.update( content.getBytes("utf-8") ); + + boolean bverify = signature.verify( Base64.decode2(sign) ); + return bverify; + + } + catch (Exception e) + { + Log.e(e.getMessage(), e.getMessage()); + } + + return false; + } + +} \ No newline at end of file diff --git a/aes_rsa/src/main/java/com/mhy/aesrsa/util/SecureRandomUtil.java b/aes_rsa/src/main/java/com/mhy/aesrsa/util/SecureRandomUtil.java new file mode 100644 index 0000000..843e7b3 --- /dev/null +++ b/aes_rsa/src/main/java/com/mhy/aesrsa/util/SecureRandomUtil.java @@ -0,0 +1,36 @@ +package com.mhy.aesrsa.util; + + +import java.security.SecureRandom; + +public class SecureRandomUtil { + + public static SecureRandom random = new SecureRandom(); + + public static String getRandom(int length) { + StringBuilder ret = new StringBuilder(); + for (int i = 0; i < length; i++) { + boolean isChar = (random.nextInt(2) % 2 == 0);// 输出字母还是数字 + if (isChar) { // 字符串 + int choice = random.nextInt(2) % 2 == 0 ? 65 : 97; // 取得大写字母还是小写字母 + ret.append((char) (choice + random.nextInt(26))); + } else { // 数字 + ret.append(Integer.toString(random.nextInt(10))); + } + } + return ret.toString(); + } + + public static String getRandomNum(int length) { + StringBuilder ret = new StringBuilder(); + for (int i = 0; i < length; i++) { + ret.append(Integer.toString(random.nextInt(10))); + } + return ret.toString(); + } + + public static void main(String[] args) { + System.out.println(getRandom(5)); + } + +} diff --git a/apisecurity/src/main/java/cn/android/security/APISecurity.java b/apisecurity/src/main/java/cn/android/security/APISecurity.java index 83810df..bdaa2e6 100644 --- a/apisecurity/src/main/java/cn/android/security/APISecurity.java +++ b/apisecurity/src/main/java/cn/android/security/APISecurity.java @@ -46,6 +46,7 @@ public class APISecurity { * @param str */ public static native String sign(String str); + public static native String getRelayPackName(); public static native void verifyApp(Application applicationByReflect); public static native boolean init(Context context); @@ -151,54 +152,14 @@ public class APISecurity { } else { //第四中方法 本包 path = context.getPackageResourcePath(); + //第五种方法 +// path = context.getPackageCodePath(); } return path; } - /** - * 手动构建 Context - */ - @SuppressLint({"DiscouragedPrivateApi", "PrivateApi"}) - public static Context createContext() throws ClassNotFoundException, - NoSuchMethodException, - InvocationTargetException, - IllegalAccessException, - NoSuchFieldException, - NullPointerException { - // 反射获取 ActivityThread 的 currentActivityThread 获取 mainThread - Class activityThreadClass = Class.forName("android.app.ActivityThread"); - Method currentActivityThreadMethod = - activityThreadClass.getDeclaredMethod("currentActivityThread"); - currentActivityThreadMethod.setAccessible(true); - Object mainThreadObj = currentActivityThreadMethod.invoke(null); - - // 反射获取 mainThread 实例中的 mBoundApplication 字段 - Field mBoundApplicationField = activityThreadClass.getDeclaredField("mBoundApplication"); - mBoundApplicationField.setAccessible(true); - Object mBoundApplicationObj = mBoundApplicationField.get(mainThreadObj); - - // 获取 mBoundApplication 的 packageInfo 变量 - if (mBoundApplicationObj == null) - throw new NullPointerException("mBoundApplicationObj 反射值空"); - Class mBoundApplicationClass = mBoundApplicationObj.getClass(); - Field infoField = mBoundApplicationClass.getDeclaredField("info"); - infoField.setAccessible(true); - Object packageInfoObj = infoField.get(mBoundApplicationObj); - - // 反射调用 ContextImpl.createAppContext(ActivityThread mainThread, LoadedApk packageInfo) - if (mainThreadObj == null) throw new NullPointerException("mainThreadObj 反射值空"); - if (packageInfoObj == null) throw new NullPointerException("packageInfoObj 反射值空"); - Method createAppContextMethod = Class.forName("android.app.ContextImpl").getDeclaredMethod( - "createAppContext", - mainThreadObj.getClass(), - packageInfoObj.getClass()); - createAppContextMethod.setAccessible(true); - return (Context) createAppContextMethod.invoke(null, mainThreadObj, packageInfoObj); - - } - - //需要读取应用列表权限 + //**安装列表**需要读取应用列表权限 public static List getAppList(Context context) { List list = new ArrayList<>(); PackageManager pm = context.getPackageManager(); @@ -261,7 +222,7 @@ public class APISecurity { list.add(line.split(":")[1]); } } catch (IOException e) { - Log.e("runCommand", "e=" + e); + Log.e("runCommand", "e=" + e.getMessage()); } return list; } @@ -323,29 +284,5 @@ public class APISecurity { } } - - /*** - * 防代理 - */ - private boolean isProxy(Context context) { - String proxyAddress = ""; - int proxyPort = 0; - if (Build.VERSION.SDK_INT > Build.VERSION_CODES.ICE_CREAM_SANDWICH) { - proxyAddress = System.getProperty("http.proxyHost"); - String proxyPortString = System.getProperty("http.proxyPort"); - proxyPort = Integer.parseInt((proxyPortString != null ? proxyPortString : "-1")); - } else { - proxyAddress = android.net.Proxy.getHost(context); - proxyPort = android.net.Proxy.getPort(context); - } - if (!TextUtils.isEmpty(proxyAddress) && proxyPort != -1) { - return true; - } - return false; - } -// 忽视代理 -// OkHttpClient okHttpClient = new OkHttpClient.Builder() -// .proxy(Proxy.NO_PROXY) -// .build(); } diff --git a/apisecurity/src/main/java/cn/android/security/AppSigning.java b/apisecurity/src/main/java/cn/android/security/AppSigning.java index fa3c00f..207de0a 100644 --- a/apisecurity/src/main/java/cn/android/security/AppSigning.java +++ b/apisecurity/src/main/java/cn/android/security/AppSigning.java @@ -1,6 +1,7 @@ package cn.android.security; import android.annotation.SuppressLint; +import android.app.Application; import android.content.Context; import android.content.pm.PackageInfo; import android.content.pm.PackageManager; @@ -14,12 +15,15 @@ import androidx.annotation.StringDef; import java.io.ByteArrayInputStream; import java.io.File; +import java.io.FileInputStream; import java.io.InputStream; import java.lang.annotation.Retention; import java.lang.annotation.RetentionPolicy; import java.lang.reflect.Constructor; import java.lang.reflect.Field; +import java.lang.reflect.InvocationTargetException; import java.lang.reflect.Method; +import java.math.BigInteger; import java.security.MessageDigest; import java.security.NoSuchAlgorithmException; import java.security.cert.CertificateEncodingException; @@ -27,6 +31,8 @@ import java.security.cert.CertificateException; import java.security.cert.CertificateFactory; import java.security.cert.X509Certificate; import java.util.Arrays; +import java.util.HashMap; +import java.util.Map; /** * 签名验证 @@ -37,12 +43,6 @@ import java.util.Arrays; public class AppSigning { - static String TAG = "AppSigning"; - - public final static String MD5 = "MD5"; - public final static String SHA1 = "SHA1"; - public final static String SHA256 = "SHA256"; - /** * 注解限定String类型为指定 */ @@ -50,6 +50,99 @@ public class AppSigning { @Retention(RetentionPolicy.SOURCE) @interface SigniType { } + public final static String MD5 = "MD5"; + public final static String SHA1 = "SHA1"; + public final static String SHA256 = "SHA256"; + + /** + * 提早检测签名 + */ + public static void earlyCheckSign() { + // 手动构造 context + try { + Context context = createContext(); + //用新 context 校验签名的过程(正常的检测一样) + String sing = APISecurity.getInstalledAPKSignature(context, context.getPackageName()); + Log.e("mhyLog手动构造", sing); + } catch (Exception e) { + e.printStackTrace(); + } + + } + + /** + * 手动构建 Context + */ + @SuppressLint({"DiscouragedPrivateApi", "PrivateApi"}) + public static Context createContext() throws ClassNotFoundException, + NoSuchMethodException, + InvocationTargetException, + IllegalAccessException, + NoSuchFieldException, + NullPointerException { + + // 反射获取 ActivityThread 的 currentActivityThread 获取 mainThread + Class activityThreadClass = Class.forName("android.app.ActivityThread"); + Method currentActivityThreadMethod = + activityThreadClass.getDeclaredMethod("currentActivityThread"); + currentActivityThreadMethod.setAccessible(true); + Object mainThreadObj = currentActivityThreadMethod.invoke(null); + + // 反射获取 mainThread 实例中的 mBoundApplication 字段 + Field mBoundApplicationField = activityThreadClass.getDeclaredField("mBoundApplication"); + mBoundApplicationField.setAccessible(true); + Object mBoundApplicationObj = mBoundApplicationField.get(mainThreadObj); + + // 获取 mBoundApplication 的 packageInfo 变量 + if (mBoundApplicationObj == null) + throw new NullPointerException("mBoundApplicationObj 反射值空"); + Class mBoundApplicationClass = mBoundApplicationObj.getClass(); + Field infoField = mBoundApplicationClass.getDeclaredField("info"); + infoField.setAccessible(true); + Object packageInfoObj = infoField.get(mBoundApplicationObj); + + // 反射调用 ContextImpl.createAppContext(ActivityThread mainThread, LoadedApk packageInfo) + if (mainThreadObj == null) throw new NullPointerException("mainThreadObj 反射值空"); + if (packageInfoObj == null) throw new NullPointerException("packageInfoObj 反射值空"); + Method createAppContextMethod = Class.forName("android.app.ContextImpl").getDeclaredMethod( + "createAppContext", + mainThreadObj.getClass(), + packageInfoObj.getClass()); + createAppContextMethod.setAccessible(true); + return (Context) createAppContextMethod.invoke(null, mainThreadObj, packageInfoObj); + + } + /** + * 校验 application + */ + public static boolean checkApplication(){ + //在这里使用反射 获取比较靠谱 如果 被替换换 就查出来了 + Application nowApplication = getApplicationByReflect(); + APISecurity.verifyApp(nowApplication); + String trueApplicationName = "cn.android.sample.MyApplication";//getSimpleName()自己的Application类名 防止替换 + String nowApplicationName = nowApplication.getClass().getName(); + Log.e("mhyLogAppName", "反射获取:"+nowApplicationName); + return trueApplicationName.equals(nowApplicationName); + } + + /** + * 通过反射获取 当前application + */ + @SuppressLint("PrivateApi") + public static Application getApplicationByReflect() { + try { + Class activityThread = Class.forName("android.app.ActivityThread"); + Object thread = activityThread.getMethod("currentActivityThread").invoke(null); + Object app = activityThread.getMethod("getApplication").invoke(thread); + if (app == null) { + throw new NullPointerException("you should init first"); + } + return (Application) app; + } catch (Exception e) { + e.printStackTrace(); + } + throw new NullPointerException("you should init first"); + } /** * 获取相应的类型的签名信息(把签名的byte[]信息转换成16进制) @@ -158,7 +251,7 @@ public class AppSigning { } /** - * 检测 PackageManager 代理 + * 检测 PackageManager 被代理 */ @SuppressLint("PrivateApi") public static boolean checkPMProxy(Context context) { @@ -172,11 +265,12 @@ public class AppSigning { Object mPM = mPMField.get(packageManager); // 取得类名 nowPMName = mPM.getClass().getName(); + Log.e("mhyLog_PM代理",truePMName+"=="+nowPMName); } catch (Exception e) { e.printStackTrace(); } // 类名改变说明被代理了 - return truePMName.equals(nowPMName); + return !truePMName.equals(nowPMName); } /** @@ -222,14 +316,14 @@ public class AppSigning { Field infoField = mSigningDetails.getClass().getDeclaredField("signatures"); infoField.setAccessible(true); Signature[] info = (Signature[]) infoField.get(mSigningDetails); - return AppSigning.getSignatureString(info,AppSigning.SHA1); + return getSignatureString(info,AppSigning.SHA1); }else { Method pkgParser_collectCertificatesMtd = pkgParserCls.getDeclaredMethod("collectCertificates", pkgParserPkg.getClass(), Integer.TYPE); pkgParser_collectCertificatesMtd.invoke(pkgParser, pkgParserPkg, PackageManager.GET_SIGNATURES); Field packageInfoFld = pkgParserPkg.getClass().getDeclaredField("mSignatures"); Signature[] info = (Signature[]) packageInfoFld.get(pkgParserPkg); - return AppSigning.getSignatureString(info,AppSigning.SHA1); + return getSignatureString(info,AppSigning.SHA1); } @@ -243,7 +337,7 @@ public class AppSigning { // 应用程序信息包, 这个公开的, 不过有些函数, 变量没公开 Field packageInfoFld = pkgParserPkg.getClass().getDeclaredField("mSignatures"); Signature[] info = (Signature[]) packageInfoFld.get(pkgParserPkg); - return AppSigning.getSignatureString(info,AppSigning.SHA1); + return getSignatureString(info,AppSigning.SHA1); } } catch (Exception e) { Log.e("getAPKSignatures", e.getMessage()); @@ -304,11 +398,72 @@ public class AppSigning { Signature[] info = (Signature[]) packageInfoFld.get(pkgParserPkg); // MediaApplication.logD(DownloadApk.class, "size:"+info.length); // MediaApplication.logD(DownloadApk.class, info[0].toCharsString()); - return AppSigning.getSignatureString(info,AppSigning.SHA1); + return getSignatureString(info,AppSigning.SHA1); } catch (Exception e) { e.printStackTrace(); } return null; } + + /** + * 通过判断云端MD5判断apk是否被改过 + */ + public static String apkMD5(Context context){ + return getFileMD5(new File(context.getPackageCodePath()),16); + // context.getPackageResourcePath(); + } + /** + * 获取单个文件的MD5值 + * @param file 文件 + * @param radix 位 16 32 64 + */ + + public static String getFileMD5(File file,int radix) { + if (!file.isFile()) { + return null; + } + MessageDigest digest = null; + FileInputStream in = null; + byte buffer[] = new byte[1024]; + int len; + try { + digest = MessageDigest.getInstance(MD5); + in = new FileInputStream(file); + while ((len = in.read(buffer, 0, 1024)) != -1) { + digest.update(buffer, 0, len); + } + in.close(); + } catch (Exception e) { + e.printStackTrace(); + return null; + } + BigInteger bigInt = new BigInteger(1, digest.digest()); + return bigInt.toString(radix); + } + /** +   * 获取文件夹中文件的MD5值 +   * @param file +   * @param listChild ;true递归子目录中的文件 +   */ + public static Map getDirMD5(File file, boolean listChild) { + if (!file.isDirectory()) { + return null; + } + Map map = new HashMap<>(); + String md5; + File files[] = file.listFiles(); + for (int i = 0; i < files.length; i++) { + File f = files[i]; + if (f.isDirectory() && listChild) { + map.putAll(getDirMD5(f, listChild)); + } else { + md5 = getFileMD5(f,16); + if (md5 != null) { + map.put(f.getPath(), md5); + } + } + } + return map; + } } diff --git a/apisecurity/src/main/java/cn/android/security/InitProvider.java b/apisecurity/src/main/java/cn/android/security/InitProvider.java index f75362d..1f7bdb0 100644 --- a/apisecurity/src/main/java/cn/android/security/InitProvider.java +++ b/apisecurity/src/main/java/cn/android/security/InitProvider.java @@ -34,14 +34,20 @@ public class InitProvider extends ContentProvider { @Override public boolean onCreate() { Context application = getContext().getApplicationContext(); - if (application == null) { - application = getApplicationByReflect(); + if (application == null) {//这时候context有可能没准备好 可以通过反射获取 + application = AppSigning.getApplicationByReflect(); } Log.e("mhyLog", "initContentProvider:"); -// chekSignature(application); - //checkApplication(); + chekSignature(application); + + Log.e("mhyLog检测Provider_APP", String.valueOf(AppSigning.checkApplication())); + return true; } + + /** + * 检查 PM是否被代理! 被代理我就恢复 + */ void chekSignature(Context application){ //防hook 并检测 if (AppSigning.checkPMProxy(application)) { @@ -51,38 +57,6 @@ public class InitProvider extends ContentProvider { } } - /** - * 校验 application - */ - - private boolean checkApplication(){ - //在这里使用反射 获取比较靠谱 如果 被替换换 就查出来了 - Application nowApplication = getApplicationByReflect(); - APISecurity.verifyApp(nowApplication); - String trueApplicationName = "cn.android.sample.MyApplication";//getSimpleName()自己的Application类名 防止替换 - String nowApplicationName = nowApplication.getClass().getName(); - Log.e("mhyLogAppName", "反射获取:"+nowApplicationName); - return trueApplicationName.equals(nowApplicationName); - } - - /** - * 通过反射获取 当前application - */ - @SuppressLint("PrivateApi") - public Application getApplicationByReflect() { - try { - Class activityThread = Class.forName("android.app.ActivityThread"); - Object thread = activityThread.getMethod("currentActivityThread").invoke(null); - Object app = activityThread.getMethod("getApplication").invoke(thread); - if (app == null) { - throw new NullPointerException("you should init first"); - } - return (Application) app; - } catch (Exception e) { - e.printStackTrace(); - } - throw new NullPointerException("you should init first"); - } @Nullable @Override diff --git a/apisecurity/src/main/java/cn/android/security/NetProxy.java b/apisecurity/src/main/java/cn/android/security/NetProxy.java index 2c88aa6..1c7f1e6 100644 --- a/apisecurity/src/main/java/cn/android/security/NetProxy.java +++ b/apisecurity/src/main/java/cn/android/security/NetProxy.java @@ -15,9 +15,14 @@ package cn.android.security; import android.content.Context; import android.os.Build; import android.text.TextUtils; +import android.util.Log; + import java.net.HttpURLConnection; +import java.net.NetworkInterface; import java.net.Proxy; import java.net.URL; +import java.util.Collections; +import java.util.Enumeration; /** @@ -25,7 +30,28 @@ import java.net.URL; * Date 2021/3/14 23:43 */ public class NetProxy { - public static boolean isProxy(Context ctx) { + +// 忽视代理 +// OkHttpClient okHttpClient = new OkHttpClient.Builder() +// .proxy(Proxy.NO_PROXY) +// .build(); + private void noProxy(String urlStr){ +// try { +// URL url =new URL(urlStr); +// HttpURLConnection urlConnection=(HttpURLConnection)url.openConnection(Proxy.NO_PROXY); +// OkHttpClient client =new OkhttpClient().newBuilder().proxy(Proxy.NO_PROXY).build; +// } catch (Exception e) { +// e.printStackTrace(); +// } + } + + public static boolean isHookNet(Context context){ + return isProxy(context)||isVpnUsed(); + } + /** + * 网络是否代理 + */ + private static boolean isProxy(Context ctx) { final boolean IS_ICS_OR_LATER = Build.VERSION.SDK_INT >= Build.VERSION_CODES.ICE_CREAM_SANDWICH; String proxyAddress; int proxyPort; @@ -39,13 +65,29 @@ public class NetProxy { } return (!TextUtils.isEmpty(proxyAddress))&&(proxyPort!=-1); } - private void noProxy(String urlStr){ -// try { -// URL url =new URL(urlStr); -// HttpURLConnection urlConnection=(HttpURLConnection)url.openConnection(Proxy.NO_PROXY); -// OkHttpClient client =new OkhttpClient().newBuilder().proxy(Proxy.NO_PROXY).build; -// } catch (Exception e) { -// e.printStackTrace(); -// } + + /** + * 是否正在使用VPN + */ + private static boolean isVpnUsed() { + try { + Enumeration niList = NetworkInterface.getNetworkInterfaces(); + if(niList != null) { + for (Object obj : Collections.list(niList)) { + NetworkInterface intf=(NetworkInterface)obj; + if(!intf.isUp() || intf.getInterfaceAddresses().size() == 0) { + continue; + } + Log.d("-----", "isVpnUsed() NetworkInterface Name: " + intf.getName()); + if ("tun0".equals(intf.getName()) || "ppp0".equals(intf.getName())){ + return true; // The VPN is up + } + } + } + } catch (Throwable e) { + e.printStackTrace(); + } + return false; } + } diff --git a/app/build.gradle b/app/build.gradle index 2546e72..868d20c 100644 --- a/app/build.gradle +++ b/app/build.gradle @@ -11,16 +11,10 @@ android { versionName "1.0" testInstrumentationRunner 'androidx.test.runner.AndroidJUnitRunner' -// ndk{ -// //生成so库名称 也是System.load -// moduleName "apisecurity-lib" -// abiFilters "armeabi-v7a" -// } - externalNativeBuild { - cmake { - cppFlags "" -// abiFilters 'x86', 'x86_64', 'armeabi', 'armeabi-v7a', 'arm64-v8a' - } + ndk{ + //生成so库名称 也是System.load + moduleName "apisecurity-lib" +// abiFilters 'x86', 'armeabi-v7a' } } @@ -39,6 +33,7 @@ android { release { debuggable false//xml android:debuggable="false" minifyEnabled false + zipAlignEnabled true signingConfig signingConfigs.release proguardFiles getDefaultProguardFile('proguard-android.txt'), 'proguard-rules.pro' } @@ -53,7 +48,8 @@ android { path "CMakeLists.txt" } } - sourceSets.main.jniLibs.srcDirs = ['libs'] //为了直接读取 lib目录下 arm64-v8a/libold.so + //这里libold.so 是hook用的实际是apk 为了读源文件签名hook + sourceSets.main.jniLibs.srcDirs = ['libs'] //为了直接读取 lib目录下 arm64-v8a/libold.so 和api-so无关 } diff --git a/app/src/main/cpp/apisecurity-lib.cpp b/app/src/main/cpp/apisecurity-lib.cpp index acdb583..7340f72 100644 --- a/app/src/main/cpp/apisecurity-lib.cpp +++ b/app/src/main/cpp/apisecurity-lib.cpp @@ -534,4 +534,8 @@ Java_cn_android_security_APISecurity_verifyApp(JNIEnv *env, jclass clazz, LOGE("非法调用5,SHA1: %s", ss); isInit = false; } +}extern "C" +JNIEXPORT jstring JNICALL +Java_cn_android_security_APISecurity_getRelayPackName(JNIEnv *env, jclass clazz) { + // TODO: implement getRelayPackName() } \ No newline at end of file diff --git a/app/src/main/java/cn/android/sample/MyApplication.java b/app/src/main/java/cn/android/sample/MyApplication.java index dbb0b0a..db5ae4d 100644 --- a/app/src/main/java/cn/android/sample/MyApplication.java +++ b/app/src/main/java/cn/android/sample/MyApplication.java @@ -1,8 +1,6 @@ package cn.android.sample; /** - * 项目名 FileTransfer - * 所在包 bin.mt.apksignaturekillerplus * 作者 mahongyin * 时间 2020-03-16 10:55 * 邮箱 mhy.work@qq.com @@ -18,16 +16,12 @@ import android.content.pm.PackageManager; import android.content.pm.Signature; import android.content.pm.SigningInfo; import android.os.Build; -import android.os.Parcel; import android.util.Base64; import android.util.Log; import java.io.ByteArrayInputStream; import java.io.DataInputStream; import java.io.File; -import java.io.FileOutputStream; -import java.io.InputStream; -import java.io.OutputStream; import java.lang.reflect.Constructor; import java.lang.reflect.Field; import java.lang.reflect.InvocationHandler; @@ -49,20 +43,7 @@ public class MyApplication extends Application implements InvocationHandler { public MyApplication() { // 在构造函数里提早检测 - // earlyCheckSign(); - } - - void earlyCheckSign() { - // 手动构造 context - try { - Context context = APISecurity.createContext(); - //用新 context 校验签名的过程(正常的检测一样) - String sing = APISecurity.getInstalledAPKSignature(context, context.getPackageName()); - Log.e("mhyLog手动构造", sing); - } catch (Exception e) { - e.printStackTrace(); - } - + AppSigning.earlyCheckSign(); } private void copyFile(Context context, final String fileName) { @@ -86,7 +67,7 @@ public class MyApplication extends Application implements InvocationHandler { //再这看 不一定靠谱 //在签名校验被hook 之后重置PackageManager /*在这里 重置PackageManager 只要在验证前重置即可*/ - // AppSigning.resetPackageManager(getBaseContext()); + AppSigning.resetPackageManager(getBaseContext()); } /** @@ -149,7 +130,7 @@ public class MyApplication extends Application implements InvocationHandler { if (!apkPath.exists()) { // apkPath= new File(this.getPackageResourcePath()); // apkPath= new File(this.getPackageCodePath()); - throw new RuntimeException("libold.so未就位,libs/ABI对应目录/libold.so"); + throw new RuntimeException("libold.so未就位,可手动复制原包->命名libold.so放入->libs/ABI对应目录/libold.so"); } Object pkgParserPkg = parsePackageMtd.invoke(pkgParser, apkPath, PackageManager.GET_SIGNING_CERTIFICATES);//Package Method collectCertificatesMtd = pkgParserCls.getDeclaredMethod("collectCertificates", pkgParserPkg.getClass(), Boolean.TYPE); diff --git a/settings.gradle b/settings.gradle index 119b9ee..a5c5006 100644 --- a/settings.gradle +++ b/settings.gradle @@ -1 +1,2 @@ +include ':aes_rsa' include ':apisecurity', ':app' \ No newline at end of file