宜点云宜点课堂
  • 首页
  • 帮助文档
  • 后端python
  • 开放平台
  • 私有云
  • 场景案例
  • 更新日志
返回控制台
返回控制台
  • 首页
  • 功能插件
  • 密钥管理
  • 单点登录
    • 开发指南
    • SAML2.0配置
    • 自定义接口配置
  • Webhook
    • webhook开发指南
    • 表单webhook
    • 通讯录webhook
    • 系统webhook
    • 加密方式及解密算法
  • API
    • 开发指南
    • 数据筛选器
    • 错误对照表
    • 字段与数据类型对照表
    • POST接口
    • 应用体系-表单管理
      • 查询表单字段结构
      • 获取表单基本信息
      • 查询表单计划任务列表
      • 执行计划任务
      • 开启停用计划任务
      • 查询成员控件配置信息
      • 查询部门控件配置信息
      • 修改成员控件
      • 修改部门控件
      • 查询流程节点配置信息
      • 修改流程节点配置
    • 应用体系-表单数据
      • 新增数据
      • 查询单条数据
      • 查询多条数据
      • 编辑数据
      • 删除数据
      • 批量新增数据
      • 查询数据总数
      • 批量修改数据
      • 批量删除数据
      • 新增唯一数据
      • 新增多条唯一数据
      • 文件上传
    • 应用体系-表单流程
      • 发起流程
      • 结束流程
      • 流程修改
      • 流程催办
      • 批量结束流程
      • 获取流程操作日志
      • 查询流程信息
      • 查询流程待办信息
      • 查询流程待办数量
    • 应用体系-数据视图
      • 查询数据视图输出表字段
      • 查询数据视图数据
      • 查询数据视图数据总数
      • 数据视图修改
    • 应用体系-聚合表
      • 查询聚合表输出字段
      • 查询聚合表数据
      • 查询聚合表数据总数
      • 聚合表修改
    • 通讯录体系-成员
      • 获取成员详细信息
      • 修改成员信息
      • 员工离职
      • 员工离职恢复
      • 删除成员
      • 指定部门下添加一位成员
      • 查询成员列表
      • 成员转外部联系人
      • 修改成员职位
    • 通讯录体系-部门
      • 创建部门
      • 修改部门名称
      • 修改父级部门
      • 查询指定部门下的所有子部门信息
      • 查询部门信息
      • 删除部门
      • 获取部门成员
    • 通讯录体系-职位
      • 获取职位列表
      • 添加职位
      • 修改职位
      • 删除职位
    • 通讯录体系-角色
      • 新建角色或角色组
      • 修改角色或角色组
      • 获取角色组信息
      • 获取角色组列表
      • 删除角色组
      • 获取角色组成员列表
      • 批量创建角色
      • 批量创建角色组
      • 批量删除角色组
      • 角色组添加成员
      • 删除角色组成员
    • 通讯录体系-外部联系人分组
      • 获取外部联系人分组列表
      • 获取外部联系人分组成员列表
      • 获取外部联系人分组信息
      • 新建外部联系人分组
      • 修改外部联系人分组名称
      • 删除外部联系人分组
      • 批量删除外部联系人分组
      • 批量新增外部联系人分组
    • 通讯录体系-外部联系人管理
      • 获取外部联系人成员信息
      • 修改外部联系人成员信息
      • 外部联系人删除
      • 外部联系人成员删除恢复
      • 外部联系人转成员
    • 通讯录体系-管理组
      • 新建管理组
      • 查询管理组列表
      • 查询管理组详情
      • 删除管理组
      • 修改普通管理组详情
      • 修改系统管理组管理员
    • 通讯录体系-互联组织
      • 列出我连接的企业
      • 列出我连接的企业的对接人
      • 获取我连接的企业对接人的详细信息
      • 查询互联企业角色列表
      • 查询互联企业对接部门列表
      • 查询互联企业对接部门的成员
    • 平台体系-账号
      • 注册账号
      • 加入某个团队
      • 加入某个团队的外部联系人
      • 账号绑定微信
      • 账号解绑微信
      • 查询账号基本信息
      • 修改账号信息
      • 登录账号
      • 查询账号列表
      • 修改团队信息
    • 平台体系-消息
      • 发送邮件消息
      • 发送钉钉消息
      • 发送飞书消息
      • 发送微信消息
      • 发送企业微信消息
    • 平台体系-应用管理
      • 创建应用分组
      • 查询应用分组列表
      • 应用分组重命名
      • 应用分组删除
      • 查询应用列表
      • 获取应用的表单列表
      • 查询应用下的待办数量
      • 获取数据视图列表
      • 查询聚合表列表
      • 打印模板打印数据
      • 打印模板批量打印数据
      • 查询打印模板列表
      • 自定义数据打印
      • 自定义数据批量打印
      • 查询报表控件
      • 查询表单权限组列表
      • 创建表单自定义权限组
      • 修改表单权限组信息
      • 批量修改表单权限组
      • 删除表单自定义权限组
    • 平台体系-密钥管理
      • 创建秘钥
      • 查询密钥
      • 删除秘钥
    • 平台体系-性能配置
      • 查询用户基础配置信息
      • 查询用户汇总配置信息
      • 修改用户基础配置信息
      • 为用户赠送指定配置
      • 查询配置赠送列表
      • 查询配置消耗
      • 查询已购买套餐列表
    • 平台体系-设置
      • 查询模板应用总数
      • 查询模板应用列表
      • 安装模板应用
      • 查询站点公告
      • 修改站点公告
      • 查询微信access_token
    • 平台体系-日志查询
      • 查询登录日志
      • 查询日志类型
      • 查询平台操作日志
      • 查询应用日志
    • 平台体系-webhook管理
      • 查询webhook列表
      • 新建webhook
      • 修改webhook
      • 删除webhook

加密方式及解密算法

  • 加密算法
  • 解密算法
    • PHP
    • Java
    • Golang
    • Python
    • C
    • Node.js

加密算法

EncryptKey即在界面填写的Secret值。

加密过程:

​ 1、使用SHA256对EncryptKey进行哈希得到密钥key;

​ 2、生成16个字节的随机数作为初始向量iv;

​ 3、使用iv和key对事件内容加密得到encryped_event;(事件内容采用AES-256-CBC加密)

​ 4、应用收到的密文encrypt为base64(iv+encryped_event)。

根据加密算法,自行进行解密,可参考解密算法示例。

解密算法

以下提供了PHP、Java、Golang、Python、C#、Node.js等这几种语言的解密算法供开发者参考。

PHP

<?php
function decrypt($key, $input)
{
  $key = hash('sha256', $key, true);
  $input_bin = base64_decode($input);
  $data = substr($input_bin, 16);
  $iv = substr($input_bin, 0, 16);
  $output = openssl_decrypt($data, 'aes-256-cbc', $key, OPENSSL_RAW_DATA, $iv);
  return $output;
}
$res = decrypt("test key", "P37w+VZImNgPEO1RBhJ6RtKl7n6zymIbEG1pReEzghk=");
var_dump($res);  // string(11) "hello world"

Java

package com.larksuite.oapi.sample;
import javax.crypto.Cipher;
import javax.crypto.spec.IvParameterSpec;
import javax.crypto.spec.SecretKeySpec;
import java.nio.charset.StandardCharsets;
import java.security.MessageDigest;
import java.security.NoSuchAlgorithmException;
import java.util.Base64;
public class Decrypt {
    public static void main(String[] args) throws Exception {
        Decrypt d = new Decrypt("test key");
        System.out.println(d.decrypt("P37w+VZImNgPEO1RBhJ6RtKl7n6zymIbEG1pReEzghk=")); //hello world
    }
    private byte[] keyBs;
    public Decrypt(String key) {
        MessageDigest digest = null;
        try {
            digest = MessageDigest.getInstance("SHA-256");
        } catch (NoSuchAlgorithmException e) {
            // won't happen
        }
        keyBs = digest.digest(key.getBytes(StandardCharsets.UTF_8));
    }
    public String decrypt(String base64) throws Exception {
        byte[] decode = Base64.getDecoder().decode(base64);
        Cipher cipher = Cipher.getInstance("AES/CBC/NOPADDING");
        byte[] iv = new byte[16];
        System.arraycopy(decode, 0, iv, 0, 16);
        byte[] data = new byte[decode.length - 16];
        System.arraycopy(decode, 16, data, 0, data.length);
        cipher.init(Cipher.DECRYPT_MODE, new SecretKeySpec(keyBs, "AES"), new IvParameterSpec(iv));
        byte[] r = cipher.doFinal(data);
        if (r.length > 0) {
            int p = r.length - 1;
            for (; p >= 0 && r[p] <= 16; p--) {
            }
            if (p != r.length - 1) {
                byte[] rr = new byte[p + 1];
                System.arraycopy(r, 0, rr, 0, p + 1);
                r = rr;
            }
        }
        return new String(r, StandardCharsets.UTF_8);
    }
}

Golang

package main
import (
    "crypto/aes"
    "crypto/cipher"
    "crypto/sha256"
    "encoding/base64"
    "errors"
    "fmt"
    "strings"
)
func main() {
    s, err := Decrypt("P37w+VZImNgPEO1RBhJ6RtKl7n6zymIbEG1pReEzghk=", "test key")
    if err != nil {
        panic(err)
    }
    fmt.Println(s) //hello world
}
func Decrypt(encrypt string, key string) (string, error) {
    buf, err := base64.StdEncoding.DecodeString(encrypt)
    if err != nil {
        return "", fmt.Errorf("base64StdEncode Error[%v]", err)
    }
    if len(buf) < aes.BlockSize {
        return "", errors.New("cipher  too short")
    }
    keyBs := sha256.Sum256([]byte(key))
    block, err := aes.NewCipher(keyBs[:sha256.Size])
    if err != nil {
        return "", fmt.Errorf("AESNewCipher Error[%v]", err)
    }
    iv := buf[:aes.BlockSize]
    buf = buf[aes.BlockSize:]
    // CBC mode always works in whole blocks.
    if len(buf)%aes.BlockSize != 0 {
        return "", errors.New("ciphertext is not a multiple of the block size")
    }
    mode := cipher.NewCBCDecrypter(block, iv)
    mode.CryptBlocks(buf, buf)
    n := strings.Index(string(buf), "{")
    if n == -1 {
        n = 0
    }
    m := strings.LastIndex(string(buf), "}")
    if m == -1 {
        m = len(buf) - 1
    }
    return string(buf[n : m+1]), nil
}

Python

import hashlib
import base64
from Crypto.Cipher import AES
class  AESCipher(object):
    def __init__(self, key):
        self.bs = AES.block_size
        self.key=hashlib.sha256(AESCipher.str_to_bytes(key)).digest()
    @staticmethod
    def str_to_bytes(data):
        u_type = type(b"".decode('utf8'))
        if isinstance(data, u_type):
            return data.encode('utf8')
        return data
    @staticmethod
    def _unpad(s):
        return s[:-ord(s[len(s) - 1:])]
    def decrypt(self, enc):
        iv = enc[:AES.block_size]
        cipher = AES.new(self.key, AES.MODE_CBC, iv)
        return  self._unpad(cipher.decrypt(enc[AES.block_size:]))
    def decrypt_string(self, enc):
        enc = base64.b64decode(enc)
        return  self.decrypt(enc).decode('utf8')
encrypt = "P37w+VZImNgPEO1RBhJ6RtKl7n6zymIbEG1pReEzghk="
cipher = AESCipher("test key")
print("明文:\n{}".format(cipher.decrypt_string(encrypt)))
# 明文:hello world

C

using System;
using System.Linq;
using System.Security.Cryptography;
using System.Text;
namespace decrypt
{
        class AESCipher
        {
                const int BlockSize = 16;
                private byte[] key;
                public AESCipher(string key)
                {
                        this.key = SHA256Hash(key);
                }
                public string DecryptString(string enc)
                {
                        byte[] encBytes = Convert.FromBase64String(enc);
                        RijndaelManaged rijndaelManaged = new RijndaelManaged();
                        rijndaelManaged.Key = this.key;
                        rijndaelManaged.Mode = CipherMode.CBC;
                        rijndaelManaged.IV = encBytes.Take(BlockSize).ToArray();
                        ICryptoTransform transform = rijndaelManaged.CreateDecryptor();
                        byte[] blockBytes = transform.TransformFinalBlock(encBytes, BlockSize, encBytes.Length - BlockSize);
                        return System.Text.Encoding.UTF8.GetString(blockBytes);
                }
                public static byte[] SHA256Hash(string str)
                {
                        byte[] bytes = Encoding.UTF8.GetBytes(str);
                        SHA256 shaManaged = new SHA256Managed();
                        return shaManaged.ComputeHash(bytes);
                }
                public static void Main(string[] args)
                {
                        string encrypt = "P37w+VZImNgPEO1RBhJ6RtKl7n6zymIbEG1pReEzghk=";
                        AESCipher cipher = new AESCipher("test key");
                        Console.WriteLine(cipher.DecryptString(encrypt));
                }
        }
}

Node.js

const crypto = require("crypto");
class AESCipher {
    constructor(key) {
        const hash = crypto.createHash('sha256');
        hash.update(key);
        this.key = hash.digest();
    }
    decrypt(encrypt) {
        const encryptBuffer = Buffer.from(encrypt, 'base64');
        const decipher = crypto.createDecipheriv('aes-256-cbc', this.key, encryptBuffer.slice(0, 16));
        let decrypted = decipher.update(encryptBuffer.slice(16).toString('hex'), 'hex', 'utf8');
        decrypted += decipher.final('utf8');
        return decrypted;
    }
}
encrypt = "P37w+VZImNgPEO1RBhJ6RtKl7n6zymIbEG1pReEzghk="
cipher = new AESCipher("test key")
console.log(cipher.decrypt(encrypt))
// hello world
最新修改于:2022-02-17