当前位置: 首页 > news >正文

实战 - AES对称加密算法加密和解密设备联动码

文章目录

      • 1. 项目背景
      • 2. xdr设备实例信息
        • 1. xdr实例信息-XdrInstanceDto
        • 2. xdr特定的配置信息-XdrConfig
        • 3. xdr接入方式-XdrConfigTypeEnum
      • 2. 解密联动码获取AkSk信息
        • 1. 生成联动码code的加密和解密密钥
        • 2. AES加密算法: 对联动码code加密
        • 3. AES解密算法: 对联动码code解密

1. 项目背景

① 概念:

  • openApi:所有对外对接提供的API接口定义为openApi,openApi走AkSk认证,无需 access_token。
  • AK(Access Key):设备的唯一标识;
  • SK(Secret Key): 秘钥,和AK成对出现;

② 原理:

  • 首先通信双方持有相同的 AKSK 信息;
  • 调用接口前,调用方通过AK找到SK信息,用SK对请求的接口的请求方式、地址、参数、请求头进行签名,得到签名串;
  • 调用方在请求头放入AK和签名串;
  • 被调用方从请求头取出AK,并通过AK找到SK信息,用SK以同样的方式进行签名;
  • 被调用方用签名结果和请求头中的签名串进行比对,签名一致,认证通过;

③ AkSk信息需要从xdr设备联动码code中解密获得:

  • 获取xdr设备实例中的设备名称(用户名username)和设备密码(密码password)
  • 生成对称加密算法AES-256中加密和解密的密钥;
  • 使用对称加密算法的密钥对xdr设备实例中的联动码code进行解密获取AkSk信息;

2. xdr设备实例信息

1. xdr实例信息-XdrInstanceDto

@Data
public class XdrInstanceDto {

    @ApiModelProperty("xdr实例名称")
    private String name;

    @ApiModelProperty("xdr实例id")
    private String instId;

    @ApiModelProperty("是否启用")
    private Boolean enable;

    @ApiModelProperty("xdr特定的配置信息")
    private XdrConfig config;
}

2. xdr特定的配置信息-XdrConfig

@Data
public class XdrConfig {

    @ApiModelProperty("接入方式")
    @Enum(clazz = XdrConfigTypeEnum.class, method = "getType", message = "app.instance.config.xdr.type.not.valid")
    private Integer type;

    @Size(min = 1, max = 1024, message = "app.instance.config.code.size.limit")
    @ApiModelProperty("用于获取设备唯一标识的联通码")
    private String code;

    @ApiModelProperty("解密AK所需的用户名")
    @Pattern(regexp = "^\\w{1,70}$", message = "app.instance.config.username.not.valid")
    private String username;

    @ApiModelProperty("解密AK所需的密码")
    @Size(min = 1, max = 90, message = "app.instance.config.password.not.valid")
    private String password;

    @ApiModelProperty("企业id")
    @Pattern(regexp = "^\\d{1,16}$", message = "app.instance.config.company.id.not.valid")
    private String companyId;

    @ApiModelProperty("云图地址")
    @Pattern(regexp = "^[A-Za-z\\d.]*$", message = "app.instance.config.cloudAddress.not.valid")
    private String cloudAddress;

    @ApiModelProperty("联动总线地址")
    @Pattern(regexp = "^(http://|https://)[A-Za-z\\d.:]*$", message = "app.instance.config.auth.address.not.valid")
    private String linkageBusAddress;

}

3. xdr接入方式-XdrConfigTypeEnum

@Getter
@AllArgsConstructor
public enum XdrConfigTypeEnum {

    /**
     * 分布式
     */
    LOCAL(1),

    /**
     * SAAS
     */
    SAAS(0);

    /**
     * 接入类型
     */
    final Integer type;
}

2. 解密联动码获取AkSk信息

  • 获取xdr设备实例中的设备名称(用户名username)和设备密码(密码password)
  • 生成对称加密算法AES-256中加密和解密的密钥;
  • 使用对称加密算法的密钥对xdr设备实例中的联动码code进行解密获取AkSk信息;
/**
 * 根据实例信息生成AkSk信息:解密联动码获取AKSK信息
 *
 * @param xdrInstanceDto 连接实例
 * @return AkSk信息
 */
@Nullable
private AkSkInfo generateAkSkInfo(XdrInstanceDto xdrInstanceDto) {
    try {
        // 1、生成对称加密和解密密钥
        String key = AesGcmEncrypt.generateLinkageCodeKey(
                xdrInstanceDto.getConfig().getUsername(),
                xdrInstanceDto.getConfig().getPassword()
        );
        // 2、使用密钥对联动码code解密获的AkSk信息
        String akskJsonStr = AesEcbEncrypt.decryptAes(
                xdrInstanceDto.getConfig().getCode(),
                key
        );
        // 3、将akskJsonStr反序列化为AkSkInfo对象
        @Nullable
        AkSkInfo akskInfo = JsonTranscodeUtil.transcode(
                akskJsonStr,
                AkSkInfo.class
        );
        return akskInfo;
    } catch (Exception e) {
        log.error("decrypt linkage code failed, code: {}", xdrInstanceDto.getConfig().getCode(), e);
        throw new DeviceConnectException("exception.xdr.device.connect.failed");
    }
}

1. 生成联动码code的加密和解密密钥

/**
 * AES 加解密联动码code
 */
public class AesGcmEncrypt {

    private static final int CUT_POINT = 2;

    /**
     * 生成联动码解密密钥
     * 
     * @param deviceName 设备名称
     * @param devicePwd  设备密码
     * @return 加密密钥
     */
    public static String generateLinkageCodeKey(String deviceName, String devicePwd) {
        if (StringUtils.isEmpty(deviceName) || deviceName.length() < CUT_POINT ||
                StringUtils.isEmpty(devicePwd) || devicePwd.length() < CUT_POINT) {
            return "";
        }
        // 根据deviceName和devicePwd生成一个key
        String key = deviceName.substring(0, CUT_POINT)
                .concat(devicePwd.substring(0, CUT_POINT))
                .concat(deviceName.substring(CUT_POINT))
                .concat(devicePwd.substring(CUT_POINT));
        // 生成密钥
        return DigestUtils.sha256Hex(key).substring(0, 32);
    }
}

2. AES加密算法: 对联动码code加密

/**
 * AES 加解密 解密联动码
 */
public class AesGcmEncrypt {

    static {
        Security.addProvider(new BouncyCastleProvider());
    }

    private static final String ALGORITHM = "AES";

    private static final String PADDING_TYPE = "AES/GCM/PKCS5Padding";

    private static final int GCM_IV_SIZE = 16;

    private static final int GCM_TAG_LENGTH = 16;

    private static final String CHARSET_NAME = "UTF-8";

    private static final int CUT_POINT = 2;

    /**
     * AES加密
     *
     * @param content 将要加密的内容
     * @param key     密钥
     * @return 已经加密的字节数组内容转为Base64之后的字符串
     */
    public static String encryptAes(String content, String key)
            throws UnsupportedEncodingException, NoSuchPaddingException, NoSuchAlgorithmException,
            IllegalBlockSizeException, BadPaddingException, InvalidKeyException, InvalidAlgorithmParameterException {
        byte[] keyByte = key.getBytes(CHARSET_NAME);
        SecretKeySpec keySpec = new SecretKeySpec(keyByte, ALGORITHM);
        Cipher cipher = Cipher.getInstance(PADDING_TYPE);
        byte[] iv = new byte[GCM_IV_SIZE];
        SecureRandom secureRandom = new SecureRandom();
        secureRandom.nextBytes(iv);
        GCMParameterSpec gcmParameterSpec = new GCMParameterSpec(GCM_TAG_LENGTH * Byte.SIZE, iv);
        cipher.init(Cipher.ENCRYPT_MODE, keySpec, gcmParameterSpec);
        byte[] resultByte = cipher.doFinal(content.getBytes(CHARSET_NAME));
        byte[] encrypted = new byte[iv.length + resultByte.length];
        System.arraycopy(iv, 0, encrypted, 0, iv.length);
        System.arraycopy(resultByte, 0, encrypted, iv.length, resultByte.length);
        return Base64Utils.encodeToString(encrypted);
    }
}

3. AES解密算法: 对联动码code解密

/**
 * AES 加解密 解密联动码
 */
public class AesGcmEncrypt {

    static {
        Security.addProvider(new BouncyCastleProvider());
    }

    private static final String ALGORITHM = "AES";

    private static final String PADDING_TYPE = "AES/GCM/PKCS5Padding";

    private static final int GCM_IV_SIZE = 16;

    private static final int GCM_TAG_LENGTH = 16;

    private static final String CHARSET_NAME = "UTF-8";

    private static final int CUT_POINT = 2;

    /**
     * AES解密
     *
     * @param content 将要解密的字节数组内容的base64字符串
     * @param key     密钥
     * @return 已经解密的内容
     */
    public static String decryptAes(String content, String key)
            throws UnsupportedEncodingException, NoSuchPaddingException, NoSuchAlgorithmException,
            InvalidAlgorithmParameterException, IllegalBlockSizeException, BadPaddingException, InvalidKeyException {
        byte[] data = Base64Utils.decodeFromString(content);
        byte[] keyByte = key.getBytes(CHARSET_NAME);
        SecretKeySpec keySpec = new SecretKeySpec(keyByte, ALGORITHM);
        byte[] iv = Arrays.copyOfRange(data, 0, GCM_IV_SIZE);
        GCMParameterSpec gcmParameterSpec = new GCMParameterSpec(GCM_TAG_LENGTH * Byte.SIZE, iv);
        Cipher cipher = Cipher.getInstance(PADDING_TYPE);
        cipher.init(Cipher.DECRYPT_MODE, keySpec, gcmParameterSpec);
        byte[] decryptData = cipher.doFinal(data, GCM_IV_SIZE, data.length - GCM_IV_SIZE);
        String resultStr = new String(decryptData);
        return resultStr.trim();
    }
}

相关文章:

  • IDEA统计项目代码量
  • 图像处理:推导五种滤波算法(均值、中值、高斯、双边、引导)
  • 【光学】基于matlab GUI双缝干涉和牛顿环【含Matlab源码 2165期】
  • 合宙AIR32F103CBT6刷回CMSIS-DAP固件以及刷ST-LINK V2-1固件方法
  • 【操作系统】volatile、wait和notify以及“单例模式”基础知识
  • java自定义注解防重提交
  • 4-Arm PEG-Aldehyde,4ARM-PEG-CHO,四臂-聚乙二醇-醛基修饰蛋白质用试剂
  • C语言预处理、宏定义
  • Flink 成长之路专栏 - 导读目录
  • 软考高级系统架构设计师系列论文五十:论SOA在企业集成架构设计中的应用
  • spring boot企业网站设计与实现毕业设计源码211750
  • springboot基于JavaWeb的疫苗接种管理系统-JAVA.JSP【数据库设计、毕业设计、源码、开题报告】
  • vue组件间传值的六种方法
  • 2022牛客杭电多校dp题汇总
  • 记一次内网靶场渗透测试
  • [笔记] php常见简单功能及函数
  • 「面试题」如何实现一个圣杯布局?
  • 【许晓笛】 EOS 智能合约案例解析(3)
  • ➹使用webpack配置多页面应用(MPA)
  • axios 和 cookie 的那些事
  • ES6, React, Redux, Webpack写的一个爬 GitHub 的网页
  • iBatis和MyBatis在使用ResultMap对应关系时的区别
  • interface和setter,getter
  • IOS评论框不贴底(ios12新bug)
  • PHP的Ev教程三(Periodic watcher)
  • React中的“虫洞”——Context
  • Vue组件定义
  • 包装类对象
  • 给新手的新浪微博 SDK 集成教程【一】
  • 力扣(LeetCode)56
  • 前端设计模式
  • 如何实现 font-size 的响应式
  • 掌握面试——弹出框的实现(一道题中包含布局/js设计模式)
  • ​configparser --- 配置文件解析器​
  • ​创新驱动,边缘计算领袖:亚马逊云科技海外服务器服务再进化
  • #Z0458. 树的中心2
  • #我与Java虚拟机的故事#连载15:完整阅读的第一本技术书籍
  • #预处理和函数的对比以及条件编译
  • $().each和$.each的区别
  • (rabbitmq的高级特性)消息可靠性
  • (论文阅读30/100)Convolutional Pose Machines
  • (三)Hyperledger Fabric 1.1安装部署-chaincode测试
  • (算法)Game
  • (转)IOS中获取各种文件的目录路径的方法
  • (最完美)小米手机6X的Usb调试模式在哪里打开的流程
  • **PHP二维数组遍历时同时赋值
  • .cfg\.dat\.mak(持续补充)
  • .helper勒索病毒的最新威胁:如何恢复您的数据?
  • .htaccess 强制https 单独排除某个目录
  • .net 后台导出excel ,word
  • .NET连接数据库方式
  • [ACM] hdu 1201 18岁生日
  • [BZOJ2281][SDOI2011]黑白棋(K-Nim博弈)
  • [Excel VBA]单元格区域引用方式的小结
  • [javascript]Tab menu实现