美文网首页
java android 对接接口加密

java android 对接接口加密

作者: 客观开发者 | 来源:发表于2021-11-04 12:48 被阅读0次

加密方式

方案1 使用Base64编码
最常用的就是Base64编码了,Base64不算是加密,只是把字符经过编码变成不可读一串文字。
如果你的项目对安全根本没有什么要求,你就可以使用编码的方式,简单省事。但是这个方式只是比“裸奔”好那么一点,可以防止大部分小白用户吧。
方案2 AES加密
后台和前台双方约定好key .进行加密和解密都是可以的。
方案3AES加密,密码随机生成
key 就公开出去了,就得吧key来回传递
方案4RSA 加密
公钥给服务器,私钥给客户端,这样就好很多了。 和aes 单个用一样
方案5 AES加密 + RSA结合

思考。

保障每次安全性。就相出了思路。

1552902841850-72dada79-acb1-4e99-bf81-56fad72ef6d9.png

思路大体是这样的

https://www.cnblogs.com/wuyanzu/p/11874333.html
app 数据用AES 加密,加密的key 用RSA 在加密。。
给服务器 加密后data 和 key .

后台用RSA 在解密得到key
然后数据用key 使用AES 解密 获取正确的数据

案例过程

例如登录

image.png

我把 登录的信息数据加密了。

{"aesKey":"FuQsmp2bKJZex02Ud2qx+uCkfA2Nef5KjCEC+O4sIwoD+ecxzUGk57plYdHLPdAYzt545A50hCfSxCRYy4KRcofzvhVV+JVjmIibeZUHozmvCdAqsSpi+Kd+W7FSFNvfZIoJAnTF6G8+UykDzSimZcfQALuAVc//q1d4dPT8Auc=","data":"A12B4B2AC0338865777FF7FD2F80D4CC768EBAE136F9B3B9C5AB06EDDD5D68AC7EB465D5872527EFE0520D8FC839010E"}

然后获取数据

{
    "code": 0,
    "msg": "操作成功!",
    "data": {
        "data": "C269985D917F9C09E5B054B850914802834ABBFE0974A452C5C5B8E3344A727559A72B8D75C45972626CF7F1BA4ACA7D29CE7DE161466784CC31C7780B37BF4C976D898F019FC3F8BDB54C556E4690AC6762B4152FC72F1EDAF128831A73F10E3E130332A87479938B414D6F65DF30567AF9FD51E1F27DED1A612749AE79537CBACDE0ED4C3015D07820E40D841D502E43B149DD8FDCA98F2DC47998720E360340555CE85AF28E10E38A6BF4F728D7D47BD14DFEF5F7ED9661B10702FDF0C0E0C0C8FDF7D06646BA1C021D819BE27F56DB3443FEB55F9C66AB215288694D0794AAB642A617186B0278E17507212F15582A024241C88F02EA12A27DC584C3FC556FA541144CD7D8FAD1BB39DA56EE4ED302930178F257498E48210E8EF81569EE90815CD13871C082BDA62A47124A59FCDB8682A45DB0B04BE45081C7E90AA6F2B2D1D28C0E2112B393A99776D5974CF4A24E97F1EBD5754692074D329FD44EBAC763920B923DEC97D809AE72ACF97A2E",
        "aesKey": "PV9eTaMQi3VJmjxkw9fdnAEJ+5UxHzGcc0SkIXiB6ShDnWcpZ/kTZjOzon2XHZYwKuMBE6QzaAJsMqHr40XMyJXU7uqITSAzY6fdyM/z35PFGV0W8uSMREaNc6qT22GyEJn5OFRcvL0HnLF/7hiHyTGqIwgUMxA2JxLW6swsONU="
    }
}

数据公开了就key 。key 用RSA 加密,我们解密一下。
说明这哥俩 参数,都是加密之后的。。。。
解密data 都得解析出来,才能继续解密
这个过程是不可逆的,因为aes 的key 是随机的。。。
我们的RSA私钥和公钥并没有 公开出去。

android 端的实现

android 用okhttp 来实现

binding.btLogin.setOnClickListener {
            val name = binding.etName.text.toString()
            val pass = binding.etPass.text.toString()
            val loginVo = LoginVo(name, pass)
//            val toJson = GsonUtils.toJson(loginVo)
//            val JSON = "application/json; charset=utf-8"
            val url = "http://192.168.1.138:8089/SysUser/loginAndroid"
//            val body = RequestBody.create(MediaType.parse(JSON), toJson)

            val toJson = GsonUtils.toJson(loginVo)
            println("加密前:$toJson")
            val encrypt = SecurityUtil.encrypt(loginVo, SecurityUtil.publicKey)
            println("加密后:$encrypt")
            val body = FormBody.Builder()
//                .add("name", name)
//                .add("pass", pass)
                .add("data", encrypt)
                .build();

            val okHttpClient = OkHttpClient()
            val request: Request = Request.Builder()
                .url(url)
                .post(body)
                .build()

            val call: Call = okHttpClient.newCall(request)
            call.enqueue(object : Callback {
                override fun onFailure(call: Call?, e: IOException?) {
                    Log.d(TAG, "onFailure: " + e?.message)
                }

                override fun onResponse(call: Call?, response: Response) {
                    val result = response.body()?.string()
                    Log.d(TAG, "onResponse: $result")
                    runOnUiThread {
                        if (!TextUtils.isEmpty(result)) {
                            println("获取的数据:$result")
                            val emptyBean =
                                GsonUtils.fromJson<EmptyBean>(result, EmptyBean::class.java)
                            if (emptyBean.code == 0) {
                                val encrypt1 =
                                    SecurityUtil.decrypt(emptyBean.data.data, emptyBean.data.aesKey)
                                println("解密后数据:$encrypt1")
                                val fromJson =
                                    GsonUtils.fromJson<UserInfo>(encrypt1, UserInfo::class.java)
                                println("解析后的数据:$fromJson")
                            }else{
                                println("错误信息" + emptyBean.msg)
                            }
                        }
                    }
                }
            })
        }

打印

2021-11-04 11:02:17.131 19744-19744/com.compose.empty I/System.out: 加密前:{"name":"admin","pass":"123456"}
2021-11-04 11:02:17.138 19744-19744/com.compose.empty I/System.out: aes_key:R1690Mlqe5Yp02Gc
2021-11-04 11:02:17.142 19744-19744/com.compose.empty I/System.out: 加密后:{"aesKey":"phmZ1eI4ahqexiJFNeomkuPeHXHbpsUwz18Nn8MaqHt8g27042T8xH76j8ZfCaUJ9//Y4cxJ9LdxFyeY2njpRxDcNsK7pxpnpgeDcVjflLWc4MUj+t+4qwilDpcxVpnvfPUSL5Q60aawZYENdn4GyBw64ZKecVc7Wnp7rk8gn8A=","data":"BCA1A469864B968EC82287DF6D75284B652B2830D03F853A9C9A010E8EC92DFC239D834ABA70382AF6ED47CDC114196B"}
2021-11-04 11:02:17.249 19744-19744/com.compose.empty I/System.out: 获取的数据:{"code":0,"msg":"操作成功!","data":{"data":"06849C74EACA36F649D9724C52308DF34D097CC998055F93BCA3F814C6D31EF810B13BBDC6368705E7FD86B5D43DA97D3BB893E0D599A60C221AC5179D14D318F87556031C8DB6E0A7C2D0C1078A025FFCC8D44A319D1E21DA83DB241B6D83090D8754ED26E9E56BD8EBC746F33F0529D324672ECDB10A3FF3EFC1D1D4116AF60F5BC772FC1A43F00DF53B7ECDDAB29829F70648AADA1486F6D276712D960931C249C006BADF1E18A27567CAB7862DE80120434A84728EE23FDAAD7862CD301F95A9C5FE2BE73E53690213A04E2D31F9B6AE162E677B89455BEFD95BFE73E2003D0AA782FF6CDEAEDF490B610ADE86D6C3B9D9314FCE88E3B6F7C9BF809807EEE84E29CBAC89816692DBA1FDDA2FDB27C15A559BFD3A6EA0C34C7B94E77B38AB1F76DDCC01AEB6660FF0B0AB3198F32E256189A66AFE71A2E7509FD4D634FCD8F90A7A1846D31BB0C36BF206AA4DE48E04DBD4D1B72A318A97E778BE41E34206D392E0B496012E50977893CF462F043B","aesKey":"CUrSmqrAimIpLNB+V4hJ9LYveD287iwk+NHYqEPYplRyCFx4wZWl6Ba79n0sHR7ookRUJ4fO5QASKQS1Nn/mbvp/AG8Bce5umUrJdgbOl7lIeAXmjOeLHk3vR2PJmx3Jv8veJ3IuZyBliW2MqbJz4euzJXoBtL0DEiXR/NAAUl0="}}
2021-11-04 11:02:17.262 19744-19744/com.compose.empty I/System.out: 解密后数据:{"id":1,"account":"admin","password":"123456","nickName":"管理员","name":"管理员","avatar":"http://localhost:8089/admin/images/avatar.png","birthday":null,"sex":0,"email":null,"phone":null,"tel":null,"lastLoginIp":null,"lastLoginTime":null,"adminType":0,"status":0,"createTime":null,"createUser":null,"updateTime":null,"updateUser":null,"remark":null}
2021-11-04 11:02:17.265 19744-19744/com.compose.empty I/System.out: 解析后的数据:com.compose.empty.bean.UserInfo@d221a8b

body 里面走的就是加密之后的数据。。。

 implementation 'com.squareup.okhttp3:okhttp:3.10.0'

加密过程就是


image.png

这个是真正的对数据加密

String decode = AESUtil.aesEncrypt(dataString, keyAES);

下面还有一个加密就是 对key 加密了

String aesKey = Base64Utils.encode(RSAUtils.encryptData(keyAES.getBytes(), public_Key));

Base64 给服务器就行了。。

服务上给的数据进行 解密。。。。


image.png

这样就搞定了。

java 后台实现

后台也是这样做的


image.png

遇到问题是,加密之后app 加密后后台解析不了。
Base64 的问题
java 后台使用的是org.apache.commons.codec.binary.Base64 app 使用的是android.util.Base64;

两种方式简单的demo


/**
     * AES
     */
    @Test
    fun AESAppContext() {
        val secretKey: String = AESUtils.generateKey()
        val name = "admin"
        val pass = "123456"
        val loginVo = LoginVo(name, pass)
        val dataString = GsonUtils.toJson(loginVo)
        println("AES加密前:$dataString")
        Log.d("xxx", "AES加密前:$dataString")

        val encrypt = AESUtils.encrypt(secretKey, dataString)
        println("AES加密后:$encrypt")
        Log.d("xxx", "AES加密后:$encrypt")

        val decode = AESUtils.decrypt(secretKey, encrypt)
        println("AES解密后:$decode")
        Log.d("xxx", "AES解密后:$decode")
    }

    /**
     * Rsa
     */
    @Test
    fun RSAAppContext() {
        val PublicKey: String = ApiSecurityUtil.publicKey
//        val dataString: String = "1111111"
        val name = "admin"
        val pass = "123456"
        val loginVo = LoginVo(name, pass)
        val dataString: String = GsonUtils.toJson(loginVo)
        println("RSA加密前:$dataString")
        Log.d("xxx", "RSA加密前:$dataString")

        // 从字符串中得到公钥
        val publicKey: PublicKey = RSAUtils.loadPublicKey(PublicKey);
        // 从文件中得到公钥
//        val inPublic: InputStream = getResources().getAssets().open("rsa_public_key.pem")
//        val publicKey: PublicKey = RSAUtils2.loadPublicKey(inPublic)
        // 加密
        val encryptByte: ByteArray = RSAUtils.encryptData(dataString.toByteArray(), publicKey)
        // 为了方便观察吧加密后的数据用base64加密转一下,要不然看起来是乱码,所以解密是也是要用Base64先转换
        // 为了方便观察吧加密后的数据用base64加密转一下,要不然看起来是乱码,所以解密是也是要用Base64先转换
        val encrypt = Base64Utils.encode(encryptByte)

        println("RSA加密后:$encrypt")
        Log.d("xxx", "RSA加密后:$encrypt")

        val privateKey: PrivateKey = RSAUtils.loadPrivateKey(ApiSecurityUtil.privateKey)

        val decryptByte: ByteArray =
            RSAUtils.decryptData(Base64Utils.decode(encrypt), privateKey)
        val decode = String(decryptByte)
        println("RSA解密后:$decode")
        Log.d("xxx", "RSA解密后:$decode")
    }

这样为了能明白,rsa 和 AES 里面需要什么key 和 解决的具体问题了。

后台思路

之前写好了登录界面

@PostMapping("/login")
    public R login(@ModelAttribute LoginVo loginVo) {
        QueryWrapper<SysUser> queryWrapper = new QueryWrapper<>();
        queryWrapper.in("account", loginVo.getName());
        SysUser one = sysUserService.getOne(queryWrapper);
        if (one == null) {
            return R.error("用户名不存在");
        }
        if (one.getPassword().equals(loginVo.getPass())) {
            return R.ok();
        } else {
            return R.error("密码错误");
        }
    }

改造之后

@PostMapping("/loginAndroid")
    public R loginAndroid(@ModelAttribute R r) {
        Object data = r.getData();
        if (data == null) {
            return R.error("请输入内容");
        }

        String encrypt = JSONUtil.toJsonStr(r.getData());
        AesVo aesVo = JSONUtil.toBean(encrypt, AesVo.class);

        if (StrUtil.isEmpty(aesVo.getData()) && StrUtil.isEmpty(aesVo.getAesKey())) {
            return R.error("请求产生错误");
        }
        String decrypt = SecurityUtil.decrypt(aesVo.getData(), aesVo.getAesKey());
        if (StrUtil.isEmpty(decrypt)) {
            return R.error();
        }
        LoginVo loginVo = JSONUtil.toBean(decrypt, LoginVo.class);
        QueryWrapper<SysUser> queryWrapper = new QueryWrapper<>();
        queryWrapper.in("account", loginVo.getName());
        SysUser one = sysUserService.getOne(queryWrapper);
        if (one == null) {
            return R.error("用户名不存在");
        }
        if (one.getPassword().equals(loginVo.getPass())) {
            R r1 = SecurityUtil.encrypt(one, SecurityUtil.publicKey);
            return r1;
//            return R.ok(one);
        } else {
            return R.error("密码错误");
        }
    }

这样不能每个要加加密的接口都得处理一下吧。

所以用aop 把获取的参数处理一些。。。。然后在对原来方法进行放行的思路。
下次分享啦。。。。
R.ok 对object 进行加密处理 可以写公共里面了。

参考blog

https://juejin.cn/post/6919760673052311559
https://www.cnblogs.com/huanzi-qch/p/10913636.html

相关文章

网友评论

      本文标题:java android 对接接口加密

      本文链接:https://www.haomeiwen.com/subject/ufzhzltx.html