美文网首页
app与server通信加密

app与server通信加密

作者: 王小杰at2019 | 来源:发表于2018-09-23 17:18 被阅读293次

    [toc]

    使用的加密算法及作用

    1. RSA 非对称加密,做密钥分发
    2. AES/CBC/PKCS5Padding 对称加密接口传输

    交互流程

    image 实现流程

    java实现

    server

    1. 生成RSA密钥对
      在线生成

    公钥

    -----BEGIN PUBLIC KEY-----
    MIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEAsvYa1zk0RB+IrH7P5D9+
    DwKEZi4ai96wFUs7Iyef5PYC+SabuIBdHp2eVS44hnC6J92g3yBoxBlPp6neRcNY
    gcRvUbJ6xcpR25FuxiJ7SPe3PrbFow2hBe81kfU/GMdQvI3Eq0UUYSgY7M1W+ppI
    6jeAaoC8k379R7mhR619UFYxgRl2Gukj0+ihAgNDCshSqqNLqgqrk8jh0AyXjxtn
    IzfZg0E34FAidEl7txvrlP2PfUqYnxTksEqVGlk3orJ6ALZZx9CyaojEEPMsXXAU
    ftY09duJFt6jXI/qbXT/pTFsOxhJToYzq7NwDFhbtqlgrFQqp9/1/IQtOExD3IRd
    0QIDAQAB
    -----END PUBLIC KEY-----
    
    

    私钥

    -----BEGIN PRIVATE KEY-----
    MIIEwAIBADANBgkqhkiG9w0BAQEFAASCBKowggSmAgEAAoIBAQCy9hrXOTREH4is
    fs/kP34PAoRmLhqL3rAVSzsjJ5/k9gL5Jpu4gF0enZ5VLjiGcLon3aDfIGjEGU+n
    qd5Fw1iBxG9RsnrFylHbkW7GIntI97c+tsWjDaEF7zWR9T8Yx1C8jcSrRRRhKBjs
    zVb6mkjqN4BqgLyTfv1HuaFHrX1QVjGBGXYa6SPT6KECA0MKyFKqo0uqCquTyOHQ
    DJePG2cjN9mDQTfgUCJ0SXu3G+uU/Y99SpifFOSwSpUaWTeisnoAtlnH0LJqiMQQ
    8yxdcBR+1jT124kW3qNcj+ptdP+lMWw7GElOhjOrs3AMWFu2qWCsVCqn3/X8hC04
    TEPchF3RAgMBAAECggEBALIIbOlBSkKfEEtyGITbuR9bhLVWFiihT5DPd7RgXgvb
    xVdxVib05C5p5TKeObNc/4RbUgfG6RJ0j66fKbVDQZBPB+NBaCvOOyLwij/n27fa
    X1yvXOv5yf/qWeOrE6sl1abObiFoJn8E8c7Z9DZ4OCNxNXo+OduN0JCDFfQFno3E
    ljto9vDSbpKlOdC1Mputn2ISaVB6zzEUica1HGtsPa2XrBLnTKuVvqeviiRzoFn6
    DCJ9wteddfQpbhYrfaWvYQBgp9eI+YuY9c4b9q9tFKqdlQ+eBZJu8Qzu01U5CfeP
    JBLq/D299j0sy9+h3dTNrUlViFCbA4aC5P3B+onEpiUCgYEA6L7X/VxSBOZLDQR0
    9fE1V8t+GRu2OMDLyRLrRtPTnue27VmM2SmLbL7uWjpLiMNa3eAoXhl62WSYUbOj
    WkLMZIKy3AKEdLiKFNHwvH9FXShEOib4kBIEJ13KfgtSIRPinOck8lVTSDXMdIOy
    bdvfpYtIfFTkPXz3/m/iXI5l8tsCgYEAxNeSgLJVhKlBQUYWVexh59M6n4rCmQOr
    1UWLEaU05B2X9wJ+EjyciEO/fpATH2O9xVu9vR4pxAu5KkrcaJkVyPjtmY/oudXP
    m4DLyLNpVSneiYKn8amoNO7LZAUDLmpyPZzu7PgZQoAGWgZYpAtgkMbzjrsCJN+I
    3uT4wCuMc8MCgYEAlTBn0P8RkBRyfTijJFdmYw2Mmdmal4x11EDtUWxM1Sogpsnl
    L/qiZaWJsYp2iob2wwyBs7fPeHQz8wMcLapty+u/bKmscAkucaQVFS7brpg2C7SZ
    VfhGc1l6iAsHrS5K71p242NwS/Q4R2N3x1XOaRX7876SwxtM9+qOBdg9X0sCgYEA
    tFf9dcPt7hlUHAWmuRpVqRwx/bIYEDD44fFRNN1z0/v5GupBr1uw3neTntVJb9zm
    JUekUvyrr14+S61CuuJmvzayGZtr0bc++m3KRxt5SfmOVdZLIHIcFkMiPYUKISCN
    gj2h+aJlIjRBnYFq/QEffAWLaB2WHUpgEDcgYJCFohkCgYEAlslPd+4crwS7phQ+
    dUYkpTyZuf9Y58ltVHbCopqNWjB0CJMN3T0OMfNoqEIeN14pd+30BrclccvGT7B7
    I3uImbaMaOZnmL5miOcOzP0KTyPCLJOSTa4zBHbSA+23Xso1Iz7UXtJcS572VPs9
    dlUNmFd5BO9NKlSLq9E2LCDTx7M=
    -----END PRIVATE KEY-----
    
    
    1. 提供公钥访问地址
    
    @RestController
    public class PublicController {
        private static String publicKey;
    
        static {
            try (InputStream resourceAsStream =
                         ClassLoader.getSystemClassLoader().getResourceAsStream("public.key")) {
                publicKey = new String(FileCopyUtils.copyToByteArray(resourceAsStream));
            } catch (Exception e) {
                e.printStackTrace();
            }
        }
    
        @RequestMapping("/publicKey")
        public String getPublicKey() throws IOException {
            return publicKey;
        }
    }
    
    1. 解密并存储客户端AES密钥
    
    @RestController
    public class AESKeyController {
        private static String privateKey;
    
        @Autowired
        RedisTemplate<String, String> redisTemplate;
    
        static {
            try (InputStream resourceAsStream =
                         ClassLoader.getSystemClassLoader().getResourceAsStream("private.key")) {
                privateKey = new String(FileCopyUtils.copyToByteArray(resourceAsStream));
            } catch (Exception e) {
                e.printStackTrace();
            }
        }
    
        @RequestMapping("/register")
        public String test(@RequestBody String body) throws Exception {
            byte[] bytes = RSAUtil.decryptByPrivateKey(
                    new BASE64Decoder().decodeBuffer(body), new BASE64Decoder().decodeBuffer(privateKey));
            String txt = new String(bytes, "utf-8");
            String deviceId = JSON.parseObject(txt).getString("deviceId");
            redisTemplate.opsForValue().set(deviceId, txt);
            byte[] resp = RSAUtil.encryptByPrivateKey("ok".getBytes(), new BASE64Decoder().decodeBuffer(privateKey));
            return new BASE64Encoder().encode(resp);
        }
    }
    
    
    1. 解密客户端数据
    
    @RestController
    @RequestMapping("/user")
    public class UserInfoController {
    
       @Autowired
       RedisTemplate<String, String> redisTemplate;
    
       @RequestMapping("/login")
       public String login(@RequestBody String body) throws IOException {
           JSONObject jsonObject = JSON.parseObject(body);
           String deviceId = jsonObject.getString("deviceId");
           //取出密钥
           JSONObject info = JSON.parseObject(redisTemplate.opsForValue().get(deviceId));
           String key = info.getString("key");
           String vi = info.getString("vi");
           String data = AESUtil.decrypt(jsonObject.getString("data"), key, vi);
           System.out.println(data);
           data = AESUtil.encrypt("ok 我收到了", key, vi);
           return data;
       }
    }
    
    

    app

    1. 获取公钥
    String url = "http://localhost:8080/publicKey";
    OkHttpClient okHttpClient = new OkHttpClient();
    Request request = new Request.Builder()
            .url(url)
            .build();
    Call call = okHttpClient.newCall(request);
    String publicKey = call.execute().body().string();
    System.out.println(publicKey);
    
    
    1. 生成随机AES密钥
        //16 位key
        String key = UUID.randomUUID().toString().replace("-", "").substring(0, 16);
        // 16 为 vi
        String vi = UUID.randomUUID().toString().replace("-", "").substring(0, 16);
        System.out.println(key);
        System.out.println(vi);
    
    1. 使用公钥加密AES密钥发送到服务端
    @Test
        public void registKey() throws Exception {
            OkHttpClient okHttpClient = new OkHttpClient();
            Request request = new Request.Builder()
                    .url("http://localhost:8080/publicKey")
                    .build();
            Call call = okHttpClient.newCall(request);
            String publicKey = call.execute().body().string();
            System.out.println(publicKey);
            Map<String, String> map = new HashMap<>();
            map.put("key", key);
            map.put("vi", vi);
            map.put("deviceId", DEVICE_ID);
            byte[] keyByte = new BASE64Decoder().decodeBuffer(publicKey);
            byte[] reqData = RSAUtil.encryptByPublicKey(
                    JSON.toJSONString(map).getBytes(), keyByte);
    
            request = new Request.Builder()
                    .url("http://localhost:8080/register")
                    .post(RequestBody.create(null, new BASE64Encoder().encode(reqData)))
                    .build();
    
            call = okHttpClient.newCall(request);
            String respBase64 = call.execute().body().string();
            byte[] respData = RSAUtil.decryptByPublicKey(new BASE64Decoder().decodeBuffer(respBase64), keyByte);
            System.out.println(new String(respData, "utf-8"));
    
    
        }
    
    1. 使用AES密钥通信
     @Test
        public void test() throws IOException {
            OkHttpClient okHttpClient = new OkHttpClient();
            Map<String, Object> map = new HashMap<>();
            map.put("deviceId", DEVICE_ID);
            String encrypt = AESUtil.encrypt("wyj,test", key, vi);
            map.put("data", encrypt);
            Request request = new Request.Builder()
                    .url("http://localhost:8080/user/login")
                    .post(RequestBody.create(null, JSON.toJSONString(map)))
                    .build();
            Call call = okHttpClient.newCall(request);
            String response = call.execute().body().string();
            String txt = AESUtil.decrypt(response, key, vi);
            System.out.println(txt);
    
        }
    

    工具类

    1. AES
    package cn.wyj.appcrypto.util;
    
    import org.apache.commons.crypto.stream.CryptoInputStream;
    import org.apache.commons.crypto.stream.CryptoOutputStream;
    import sun.misc.BASE64Decoder;
    import sun.misc.BASE64Encoder;
    
    import javax.crypto.spec.IvParameterSpec;
    import javax.crypto.spec.SecretKeySpec;
    import java.io.ByteArrayInputStream;
    import java.io.ByteArrayOutputStream;
    import java.io.IOException;
    import java.io.InputStream;
    import java.nio.charset.StandardCharsets;
    import java.util.Properties;
    
    public class AESUtil {
        private final static String transform = "AES/CBC/PKCS5Padding";
        static Properties properties = new Properties();
    
        public static String decrypt(String content, String key, String iv) throws IOException {
            final SecretKeySpec keySpec = new SecretKeySpec(getUTF8Bytes(key), "AES");
            final IvParameterSpec ivParameterSpec = new IvParameterSpec(getUTF8Bytes(iv));
            InputStream inputStream = new ByteArrayInputStream(new BASE64Decoder().decodeBuffer(content));
            try (CryptoInputStream cis = new CryptoInputStream(transform, properties, inputStream, keySpec, ivParameterSpec)) {
                byte[] decryptedData = new byte[1024];
                int decryptedLen = 0;
                int i;
                while ((i = cis.read(decryptedData, decryptedLen, decryptedData.length - decryptedLen)) > -1) {
                    decryptedLen += i;
                }
                return new String(decryptedData, 0, decryptedLen, StandardCharsets.UTF_8);
            }
        }
    
        public static String encrypt(String content, String key, String iv) {
            final SecretKeySpec keySpec = new SecretKeySpec(getUTF8Bytes(key), "AES");
            final IvParameterSpec ivParameterSpec = new IvParameterSpec(getUTF8Bytes(iv));
            Properties properties = new Properties();
            ByteArrayOutputStream outputStream = new ByteArrayOutputStream();
            try (CryptoOutputStream cos = new CryptoOutputStream(transform, properties, outputStream, keySpec, ivParameterSpec)) {
                cos.write(getUTF8Bytes(content));
                cos.flush();
            } catch (IOException e) {
                e.printStackTrace();
            }
            return new BASE64Encoder().encode(outputStream.toByteArray());
        }
    
        /**
         * Converts String to UTF8 bytes
         *
         * @param input the input string
         * @return UTF8 bytes
         */
        private static byte[] getUTF8Bytes(String input) {
            return input.getBytes(StandardCharsets.UTF_8);
        }
    
    }
    
    
    1. RSA
    package cn.wyj.appcrypto.util;
    
    import javax.crypto.Cipher;
    import java.security.KeyFactory;
    import java.security.PrivateKey;
    import java.security.PublicKey;
    import java.security.spec.PKCS8EncodedKeySpec;
    import java.security.spec.X509EncodedKeySpec;
    
    public class RSAUtil {
        //非对称密钥算法
        private static final String KEY_ALGORITHM = "RSA";
    
        /**
         * 私钥加密
         *
         * @param data 待加密数据
         * @param key  密钥
         * @return byte[] 加密数据
         */
        public static byte[] encryptByPrivateKey(byte[] data, byte[] key) throws Exception {
    
            //取得私钥
            PKCS8EncodedKeySpec pkcs8KeySpec = new PKCS8EncodedKeySpec(key);
            KeyFactory keyFactory = KeyFactory.getInstance(KEY_ALGORITHM);
            //生成私钥
            PrivateKey privateKey = keyFactory.generatePrivate(pkcs8KeySpec);
            //数据加密
            Cipher cipher = Cipher.getInstance(keyFactory.getAlgorithm());
            cipher.init(Cipher.ENCRYPT_MODE, privateKey);
            return cipher.doFinal(data);
        }
    
        /**
         * 公钥加密
         *
         * @param data 待加密数据
         * @param key  密钥
         * @return byte[] 加密数据
         */
        public static byte[] encryptByPublicKey(byte[] data, byte[] key) throws Exception {
    
            //实例化密钥工厂
            KeyFactory keyFactory = KeyFactory.getInstance(KEY_ALGORITHM);
            //初始化公钥
            //密钥材料转换
            X509EncodedKeySpec x509KeySpec = new X509EncodedKeySpec(key);
            //产生公钥
            PublicKey pubKey = keyFactory.generatePublic(x509KeySpec);
    
            //数据加密
            Cipher cipher = Cipher.getInstance(keyFactory.getAlgorithm());
            cipher.init(Cipher.ENCRYPT_MODE, pubKey);
            return cipher.doFinal(data);
        }
    
        /**
         * 私钥解密
         *
         * @param data 待解密数据
         * @param key  密钥
         * @return byte[] 解密数据
         */
        public static byte[] decryptByPrivateKey(byte[] data, byte[] key) throws Exception {
            //取得私钥
            PKCS8EncodedKeySpec pkcs8KeySpec = new PKCS8EncodedKeySpec(key);
            KeyFactory keyFactory = KeyFactory.getInstance(KEY_ALGORITHM);
            //生成私钥
            PrivateKey privateKey = keyFactory.generatePrivate(pkcs8KeySpec);
            //数据解密
            Cipher cipher = Cipher.getInstance(keyFactory.getAlgorithm());
            cipher.init(Cipher.DECRYPT_MODE, privateKey);
            return cipher.doFinal(data);
        }
    
        /**
         * 公钥解密
         *
         * @param data 待解密数据
         * @param key  密钥
         * @return byte[] 解密数据
         */
        public static byte[] decryptByPublicKey(byte[] data, byte[] key) throws Exception {
    
            //实例化密钥工厂
            KeyFactory keyFactory = KeyFactory.getInstance(KEY_ALGORITHM);
            //初始化公钥
            //密钥材料转换
            X509EncodedKeySpec x509KeySpec = new X509EncodedKeySpec(key);
            //产生公钥
            PublicKey pubKey = keyFactory.generatePublic(x509KeySpec);
            //数据解密
            Cipher cipher = Cipher.getInstance(keyFactory.getAlgorithm());
            cipher.init(Cipher.DECRYPT_MODE, pubKey);
            return cipher.doFinal(data);
        }
    
    }
    

    相关文章

      网友评论

          本文标题:app与server通信加密

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