基于NodeJS开发钉钉回调接口实现AES-CBC加解密

下面是关于基于NodeJS开发钉钉回调接口实现AES-CBC加解密的完整攻略。

简介

钉钉回调接口是钉钉提供的一种主动通知机制,允许开发者注册特定类型的事件(比如用户离职、群组变化等),当事件发生时,钉钉会向开发者指定的服务器推送消息,以便开发者及时获取钉钉中发生的各种变化情况。

为保证安全性,钉钉回调接口推送的消息采用了AES-CBC加密方式,需要在服务器端对消息进行解密才能得到其真正内容。

此文档将讲述如何基于NodeJS开发钉钉回调接口实现消息的AES-CBC加解密。

开发环境

  • Node.js 10.x
  • npm 6.x

步骤

步骤1:安装必要的依赖

使用Node.js开发钉钉回调接口需要使用到一些第三方库来辅助开发,这些库可以通过npm包管理器进行安装。在命令行中执行以下命令:

npm install express body-parser crypto --save

其中:

  • express:一个流行的Node.js Web框架,用于搭建服务器端的Web应用程序;
  • body-parser:一个Node.js中间件,用于解析HTTP请求体中的数据;
  • crypto:Node.js支持的加密解密库。

步骤2:创建服务器

创建一个express实例:

const express = require('express')
const bodyParser = require('body-parser')
const app = express()

app.use(bodyParser.json())
app.use(bodyParser.urlencoded({ extended: true }))

const port = process.env.PORT || 3000

app.listen(port, () => {
  console.log(`Server is listening on port ${port}...`)
})

在此示例中,我们使用express以及body-parser来解析HTTP请求,在端口3000上启动HTTP服务器。

步骤3:接收推送消息并解密

当钉钉推送消息到服务器时,我们需要对消息进行解密以获取其真实内容。下面是解密的具体步骤:

3.1:获取消息体和签名

钉钉推送的消息体保存在HTTP请求体的encrypt参数中,签名则在HTTP请求头的x-ns-signature参数中,因此,我们需要从HTTP请求中提取这两个参数。

app.post('/', (req, res) => {
  const encrypt = req.body.encrypt
  const signature = req.headers['x-ns-signature']
  ...
})

3.2:验证签名

我们需要将消息体和回调url上的token按照顺序拼接,接着使用签名算法(HMAC_SHA256)对拼接后的字符串进行HMAC_SHA256计算,最后将计算结果转为16进制表示的字符串。最终的签名结果应该是和HTTP请求头中的x-ns-signature参数中的值相同。

app.post('/', (req, res) => {
  const encrypt = req.body.encrypt
  const signature = req.headers['x-ns-signature']

  // 验证签名是否正确
  const token = 'your_token'
  const timestamp = req.headers['x-ns-timestamp']
  const plainText = token + timestamp + encrypt
  const hmac = crypto.createHmac('sha256', token)
  hmac.update(plainText)
  if (signature !== hmac.digest('hex')) {
    res.status(401).send('signature invalid')
    return
  }
  ...
})

3.3:解密消息体

我们需要使用AES-CBC算法解密消息体,首先需要解密Base64编码后的AES密钥(HTTP请求头的x-ns-ekey参数),得到真正的AES密钥;接着使用AES密钥和IV(初始向量,HTTP请求头的x-ns-iv参数),对消息体进行解密,得到消息的原始内容。

app.post('/', (req, res) => {
  const encrypt = req.body.encrypt
  const signature = req.headers['x-ns-signature']

  // 验证签名是否正确
  const token = 'your_token'
  const timestamp = req.headers['x-ns-timestamp']
  const plainText = token + timestamp + encrypt
  const hmac = crypto.createHmac('sha256', token)
  hmac.update(plainText)
  if (signature !== hmac.digest('hex')) {
    res.status(401).send('signature invalid')
    return
  }

  // 解密消息体
  const ekey = Buffer.from(req.headers['x-ns-ekey'], 'base64')
  const iv = Buffer.from(req.headers['x-ns-iv'], 'base64')
  const decipher = crypto.createDecipheriv('aes-256-cbc', ekey, iv)
  let decrypted = decipher.update(encrypt, 'base64', 'utf8')
  decrypted += decipher.final('utf8')
  console.log(decrypted)
  ...
})

最终的解密结果即为变量decrypted中保存的内容。

步骤4:构造回复消息并加密

通常情况下,我们需要在接收到消息之后向钉钉服务端回复一条确认消息。回复消息也需要进行加密处理,加密时需要按照以下步骤进行:

4.1:构造回复明文

回复消息的明文格式必须与接收到的消息格式相同,所以我们需要将解密后的消息体中的msgtype值复制到回复消息中,同时指定一个响应指令(HTTP响应头的x-ns-responsekey参数)。

app.post('/', (req, res) => {
  const encrypt = req.body.encrypt
  const signature = req.headers['x-ns-signature']

  // 验证签名是否正确
  const token = 'your_token'
  const timestamp = req.headers['x-ns-timestamp']
  const plainText = token + timestamp + encrypt
  const hmac = crypto.createHmac('sha256', token)
  hmac.update(plainText)
  if (signature !== hmac.digest('hex')) {
    res.status(401).send('signature invalid')
    return
  }

  // 解密消息体
  const ekey = Buffer.from(req.headers['x-ns-ekey'], 'base64')
  const iv = Buffer.from(req.headers['x-ns-iv'], 'base64')
  const decipher = crypto.createDecipheriv('aes-256-cbc', ekey, iv)
  let decrypted = decipher.update(encrypt, 'base64', 'utf8')
  decrypted += decipher.final('utf8')
  console.log(decrypted)

  // 构造回复消息
  const response = {
    msgtype: JSON.parse(decrypted).msgtype,
    ... // 其他自定义属性
  }
  const responseText = JSON.stringify(response)
  const responseKey = 'your_response_key'
  ...
})

4.2:加密回复密文

使用步骤3.3中获取到的AES-CBC密钥和IV,对回复消息进行加密处理,得到回复密文。

app.post('/', (req, res) => {
  const encrypt = req.body.encrypt
  const signature = req.headers['x-ns-signature']

  // 验证签名是否正确
  const token = 'your_token'
  const timestamp = req.headers['x-ns-timestamp']
  const plainText = token + timestamp + encrypt
  const hmac = crypto.createHmac('sha256', token)
  hmac.update(plainText)
  if (signature !== hmac.digest('hex')) {
    res.status(401).send('signature invalid')
    return
  }

  // 解密消息体
  const ekey = Buffer.from(req.headers['x-ns-ekey'], 'base64')
  const iv = Buffer.from(req.headers['x-ns-iv'], 'base64')
  const decipher = crypto.createDecipheriv('aes-256-cbc', ekey, iv)
  let decrypted = decipher.update(encrypt, 'base64', 'utf8')
  decrypted += decipher.final('utf8')
  console.log(decrypted)

  // 构造回复消息
  const response = {
    msgtype: JSON.parse(decrypted).msgtype,
    ... // 其他自定义属性
  }
  const responseText = JSON.stringify(response)
  const responseKey = 'your_response_key'

  // 加密回复消息
  const responseIv = crypto.randomBytes(16)
  const ekey2 = crypto.createHash('sha256').update(responseKey).digest().slice(0, 32)
  const cipher = crypto.createCipheriv('aes-256-cbc', ekey2, responseIv)
  let encrypted = cipher.update(responseText, 'utf8', 'base64')
  encrypted += cipher.final('base64')

  res.header('Content-Type', 'application/json')
  res.header('x-ns-ekey', Buffer.from(ekey2).toString('base64'))
  res.header('x-ns-iv', responseIv.toString('base64'))
  res.header('x-ns-responsekey', 'your_response_key')
  res.send({ encrypt: encrypted })
})

最终得到的回复消息密文保存在变量encrypted中,可以将其直接返回到钉钉服务器。

示例

下面是两个钉钉回调接口的示例,可供参考:

示例1:获取用户信息

请求

POST /user/get HTTP/1.1
Host: your_callback_url
x-ns-signature: 1ca36f3b164901f28a5e83aa4fced54acc73e2beabcedf9ded100bf682588bbc
x-ns-timestamp: 1580469492913
Content-Type: application/json;charset=UTF-8
Accept-Encoding: gzip, deflate, br
Connection: keep-alive
Content-Length: 264

{
    "encrypt": "rkoZgNlKv...TBlxdFxmlF/S452J1Y4LoJQb24="
}

其中encrypt参数为加密后的消息体。

响应

HTTP/1.1 200 OK
Content-Type: application/json
x-ns-ekey: RoTyMNawUTwWHGQBNSz1GfWb+3qGOU6hJo57eOhy/vA=
x-ns-iv: OQtLErqQyKK+bl4b+1b1Tg==
x-ns-responsekey: your_response_key

{
    "encrypt": "lpikvT7NVJYW+...5U+IKTn5UPNsDD04g1qXlw=="
}

其中encrypt参数为加密后的响应消息体。

示例2:更新部门信息

请求

POST /department/update HTTP/1.1
Host: your_callback_url
x-ns-signature: 1ca36f3b164901f28a5e83aa4fced54acc73e2beabcedf9ded100bf682588bbc
x-ns-timestamp: 1580469492913
Content-Type: application/json;charset=UTF-8
Accept-Encoding: gzip, deflate, br
Connection: keep-alive
Content-Length: 264

{
    "encrypt": "rkoZgNlKv...TBlxdFxmlF/S452J1Y4LoJQb24="
}

其中encrypt参数为加密后的消息体。

响应

HTTP/1.1 200 OK
Content-Type: application/json
x-ns-ekey: RoTyMNawUTwWHGQBNSz1GfWb+3qGOU6hJo57eOhy/vA=
x-ns-iv: OQtLErqQyKK+bl4b+1b1Tg==
x-ns-responsekey: your_response_key

{
    "encrypt": "lpikvT7NVJYW+...5U+IKTn5UPNsDD04g1qXlw=="
}

其中encrypt参数为加密后的响应消息体。

总结

本文介绍了如何使用Node.js开发钉钉回调接口的AES-CBC加解密功能,主要包括解析HTTP请求、检测签名正确性、解密回调消息体、加密回复消息体等步骤。

在实际开发中,需要注意检测签名的正确性,以保证消息能够安全可靠地在客户端和服务端之间传递。另外,需要注意加解密所使用的算法和密钥,这些信息需要保密存储,避免泄漏给攻击者。

本站文章如无特殊说明,均为本站原创,如若转载,请注明出处:基于NodeJS开发钉钉回调接口实现AES-CBC加解密 - Python技术站

(0)
上一篇 2023年6月8日
下一篇 2023年6月8日

相关文章

  • nodejs异步编程基础之回调函数用法分析

    Node.js异步编程基础之回调函数用法分析 在 Node.js 中使用异步编程非常重要,因为 Node.js 应用程序一般都需要处理高并发、高 I/O 的情况。而回调函数是 Node.js 中异步编程的基础。 本篇攻略主要介绍 Node.js 中回调函数的用法,重点讲解如何编写和调用回调函数,以及如何处理回调函数中出现的错误。 什么是回调函数 回调函数是一…

    node js 2023年6月8日
    00
  • node.js中的http.response.end方法使用说明

    我来详细讲解一下node.js中的http.response.end方法使用说明。 http.response.end方法是什么? 在node.js中,当服务器收到客户端的请求后,可以使用http.response对象向客户端发送响应。而http.response对象中的end()方法就是用来结束响应并发送数据给客户端的。 http.response.end…

    node js 2023年6月8日
    00
  • node.js express捕获全局异常的三种方法实例分析

    Node.js Express捕获全局异常的三种方法实例分析 在Node.js Express应用开发中,捕获全局异常肯定是一个必要的技能。那么,在Node.js Express中,我们有哪些方法可以捕获全局异常呢?接下来,我们将会详细讲解使用三种不同方法捕获全局异常的实例分析。 方法一:process.on(“uncaughtException”)函数 使…

    node js 2023年6月8日
    00
  • 使用nodejs+express实现简单的文件上传功能

    实现一个简单的文件上传功能主要分为三个步骤: 创建上传表单 配置express路由 处理上传文件 下面逐步介绍实现方法。 创建上传表单 前端代码: <form action="/upload" method="post" enctype="multipart/form-data"> &l…

    node js 2023年6月8日
    00
  • 10个最优秀的Node.js MVC框架

    以下是“10个最优秀的Node.js MVC框架”的完整攻略。 1. MVC框架简介 MVC是一种设计模式,将应用程序分为三个部分:模型(model)、视图(view)和控制器(controller)。它是一种简化的开发方法,有利于代码的复用、组织和测试。Node.js MVC框架是基于MVC设计模式构建的框架,它们都有自己的特定功能和优点。 2. 常用的N…

    node js 2023年6月8日
    00
  • node.js express和koa中间件机制和错误处理机制

    Node.js是一种基于事件驱动和非阻塞I/O模型的轻量级JavaScript运行时环境。在Node.js中,可以通过搭建Web服务器来处理HTTP请求和响应,而Express和Koa是Node.js中常用的Web开发框架。 Express和Koa都实现了中间件机制,以支持开发者扩展框架的功能。中间件是指在处理请求和响应的过程中,处理HTTP请求的一些函数。…

    node js 2023年6月8日
    00
  • 详解使用nodeJs安装Vue-cli

    请跟我一起来详解使用Node.js安装Vue-cli的完整攻略。 1. 安装Node.js Node.js是一个基于Chrome V8引擎的JavaScript运行时环境,可以用于服务器端JavaScript环境的搭建。因为Vue-cli是基于Node.js开发的,所以安装Node.js是使用Vue-cli的前提。Node.js支持多操作系统安装,例如Win…

    node js 2023年6月8日
    00
  • 详解React Angular Vue三大前端技术

    详解React Angular Vue三大前端技术 React、Angular和Vue是目前前端技术中最受欢迎的三种框架。在这篇攻略中,我们将会详细讲解这三种框架的特点、优缺点以及如何选择适合自己的框架。 React React是由Facebook开发并维护的一个JavaScript库,用于构建大型、高性能的用户界面。它有以下特点: 采用Virtual DO…

    node js 2023年6月8日
    00
合作推广
合作推广
分享本页
返回顶部