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

【附源码】告别静态密码!openHiTLS 开源一次性密码协议(HOTP/TOTP),推动动态认证普及

1 前言

近日,openHiTLS社区正式发布基于RFC 4226(HOTP)与RFC 6238(TOTP)标准的双因素认证(2FA)新特性。这是国内密码开源领域在身份安全领域的关键技术突破,通过“动态一次性密码”机制构建“静态密码+动态验证”的纵深防护体系,可广泛应用于金融账户安全、企业权限管理、IoT设备认证等核心场景,为数字身份安全提供标准化、高可靠的技术支撑。

2 静态密码安全瓶颈凸显,动态认证成破局关键

随着数字化进程加速,传统静态密码的安全短板日益突出:一方面,“撞库攻击”、“钓鱼窃取”等手段导致密码泄露事件频发,且泄露后存在持久滥用风险;另一方面,单一认证方式难以抵御APT攻击、短信劫持等新型威胁,无法满足金融、企业等领域的合规与安全需求。

在此背景下,TOTP(Time-based One-Time Password)与HOTP(HMAC-based One-time Password)作为国际主流双因素认证技术,凭借“动态生成、一次有效”的核心特性,成为解决身份安全难题的核心方案。openHiTLS此次推出的新特性,正是针对这一技术需求,为广大行业用户提供符合国际标准的开源实现。

3 技术深度解析:双机制构建动态安全屏障

TOTP与HOTP均基于HMAC哈希算法构建,核心逻辑是通过“密钥+动态因子”生成唯一一次性密码,使认证过程从“静态固定”升级为“动态可变”,二者技术路径各有侧重。

3.1 HOTP:计数器驱动的事件同步认证

HOTP基于一个递增的计数器值C和一个只有鉴别双方持有的静态对称密钥K,使用HMAC-SHA-1算法。其生成算法为HOTP(K,C)=Truncate(HMAC(K,C)),一个6位HOTP简易生成流程如下图所示:

图上算法步骤可分解如下

Step 1. 计算HMAC值

HS = HMAC(K,C):入参K是只有鉴别双方持有的静态对称密钥,入参C是鉴别双方同步的计数器值,为8字节整数,出参HS是一个20字节序列。

Step 2. 动态截断

Snum = Truncate(HS):入参HS是第Step1步的输出,Truncate()是截断函数,出参是一个32位整数(有效位为低31位)

Step 3. 计算HOTP值

HOTP= Snum mod 10^Digit:Snum 是Step2 的输出,Digit是HOTP位数,将Snum对10^Digit取模,并将结果格式化为指定位数的数字字符串,不足位数时在左侧补零。

3.2 TOTP:时间驱动的实时动态认证

TOTP基于当前时间因子T和一个只有鉴别双方持有的静态对称密钥K,依赖HMAC-SHA-1、HMAC-SHA-256或HMAC-SHA-512算法。其生成算法为TOTP=HOTP(K,T),其中T=(Current Unix time - T0) / X。详细算法流程如下:

Step 1. 计算当前时间因子

T=(Current Unix time - T0) / X:Current Unix time是当前的Unix时间戳,是一个8字节整数,入参T0是开始计算时间步长的初始Unix时间戳,为8字节整数,默认为0,入参X是以秒为单位的时间步长大小,默认为30,计算结果为除法向下取整结果,长度为8字节。

Step 2. 计算TOTP值

TOTP = HOTP(K, T):入参K是只有鉴别双方持有的静态对称密钥,而入参T是Step 1的输出。

4 多场景落地:从资金安全到设备防护的全维度覆盖

4.1 金融领域:合规级资金安全防护

针对网银、证券APP、第三方支付等场景,TOTP技术可替代传统短信验证码,构建“静态密码+动态码”的双重认证体系。即使静态密码泄露,攻击者因无法获取30秒内有效的TOTP动态码,无法完成登录或转账操作,有效防范盗刷风险,同时满足金融监管对多因素认证的合规要求。

4.2 企业场景:精细化权限安全管控

在企业OA、CRM系统及服务器运维场景中,可通过HOTP/TOTP实现分级认证:普通员工登录采用手机APP(TOTP)或硬件令牌(HOTP)验证;运维人员操作服务器需叠加SSH密钥与TOTP动态码的双重校验。员工离职后,管理员可快速注销共享密钥,实现权限即时回收,避免数据泄露风险。

4.3 IoT领域:终端身份伪造防御

针对智能门锁、工业传感器等IoT设备,TOTP技术可解决固定密钥通信的安全隐患。设备与云端共享密钥,每30秒生成一次TOTP并上传验证,云端仅允许携带有效动态码的设备接入网络或执行指令,有效抵御设备劫持与身份伪造攻击。

5 三重技术优势:安全、兼容与易用的平衡

  • 安全性:基于HMAC-SHA1/SHA256算法(可扩展至国密SM3),结合“动态密码一次有效”特性,抗暴力破解、字典攻击能力突出;支持密钥加盐、动态窗口控制等增强机制,提升高级威胁对抗能力。
  • 广兼容性:完全遵循国际标准,可无缝对接Google Authenticator、YubiKey等主流终端与Windows、Linux、iOS、Android等系统,无需重构现有认证体系;提供简洁API接口与开发文档,降低企业部署成本。
  • 高易用性:用户通过手机APP或硬件令牌即可获取动态码,操作门槛低;支持多设备同步、二维码密钥备份等功能,兼顾安全性与可用性。

6 未来展望:共建数字身份安全新生态

TOTP/HOTP技术作为数字身份认证的“动态安全闸门”,其开源化落地将加速国内动态认证技术的普及与应用。openHiTLS社区诚邀广大开发者、企业用户加入,共同推进技术深度优化与场景创新,实现从“技术研发”到“产业落地”的高效转化,最终构建兼顾身份安全、合规监管与用户体验的全域数字身份防护体系。

如需了解更多技术细节或参与社区协作,可关注openHiTLS官网获取开发文档与最新动态。

【附源代码】

完整代码:https://gitcode.com/openHiTLS/openhitls/tree/main/auth/otp/src

#include <stdint.h>
#include "securec.h"
#include "bsl_errno.h"
#include "bsl_err_internal.h"
#include "bsl_sal.h"
#include "bsl_bytes.h"
#include "auth_errno.h"
#include "auth_params.h"
#include "auth_otp.h"
#include "crypt_errno.h"
#include "otp.h"int32_t HITLS_AUTH_OtpInit(HITLS_AUTH_OtpCtx *ctx, uint8_t *key, uint32_t keyLen)
{if (ctx == NULL || keyLen == 0) {BSL_ERR_PUSH_ERROR(HITLS_AUTH_OTP_INVALID_INPUT);return HITLS_AUTH_OTP_INVALID_INPUT;}if (ctx->key.data != NULL) {BSL_SAL_Free(ctx->key.data);}ctx->key.dataLen = keyLen;ctx->key.data = (uint8_t *)BSL_SAL_Malloc(ctx->key.dataLen);if (ctx->key.data == NULL) {BSL_ERR_PUSH_ERROR(BSL_MALLOC_FAIL);return BSL_MALLOC_FAIL;}if (key == NULL) {int32_t ret = ctx->method.random(ctx->key.data, ctx->key.dataLen);if (ret != CRYPT_SUCCESS) {BSL_SAL_Free(ctx->key.data);ctx->key.data = NULL;BSL_ERR_PUSH_ERROR(ret);return ret;}} else {(void)memcpy_s(ctx->key.data, ctx->key.dataLen, key, keyLen);}return HITLS_AUTH_SUCCESS;
}typedef enum {OTP_HMAC_SHA1SIZE = 20,OTP_HMAC_SHA256SIZE = 32,OTP_HMAC_SHA512SIZE = 64,
} OTP_HmacSize;int32_t GenericOtpGen(HITLS_AUTH_OtpCtx *ctx, uint64_t movingFactor, char *otp, uint32_t *otpLen)
{// Put movingFactor value into byte arrayuint8_t counter[sizeof(movingFactor)];for (uint32_t i = 0; i < sizeof(movingFactor); i++) {counter[sizeof(movingFactor) - 1 - i] = (movingFactor >> (8 * i)) & 0xFF; // 8: the number of bits in a byte.}// Compute HMAC hashuint8_t hmac[OTP_HMAC_SHA512SIZE];uint32_t hmacLen;switch (ctx->hashAlgId) {case HITLS_AUTH_OTP_CRYPTO_SHA1:hmacLen = OTP_HMAC_SHA1SIZE;break;case HITLS_AUTH_OTP_CRYPTO_SHA256:hmacLen = OTP_HMAC_SHA256SIZE;break;case HITLS_AUTH_OTP_CRYPTO_SHA512:hmacLen = OTP_HMAC_SHA512SIZE;break;default:BSL_ERR_PUSH_ERROR(HITLS_AUTH_OTP_INVALID_INPUT);return HITLS_AUTH_OTP_INVALID_INPUT;}uint32_t ret = ctx->method.hmac(ctx->libCtx, ctx->attrName, ctx->hashAlgId, ctx->key.data, ctx->key.dataLen,(uint8_t *)&counter, sizeof(counter), hmac, &hmacLen);if (ret != HITLS_AUTH_SUCCESS) {BSL_ERR_PUSH_ERROR(ret);return ret;}// Dynamic truncationuint8_t offset = hmac[hmacLen - 1] & 0x0F;uint32_t binOtp = BSL_ByteToUint32(&hmac[offset]) & 0x7FFFFFFF;// Stringifyfor (uint32_t i = 0, div = 10, mod = 1; i < ctx->digits; i++, div *= 10, mod *= 10) { // 10: decimal numberotp[ctx->digits - i - 1] = '0' + binOtp % div / mod;}*otpLen = ctx->digits;return HITLS_AUTH_SUCCESS;
}int32_t HotpGen(HITLS_AUTH_OtpCtx *ctx, const BSL_Param *param, char *otp, uint32_t *otpLen, uint64_t *movingFactorOut)
{uint64_t movingFactor = 0;uint32_t movingFactorLen = (uint32_t)sizeof(movingFactor);const BSL_Param *tmpParam = BSL_PARAM_FindConstParam(param, AUTH_PARAM_OTP_HOTP_COUNTER);if (tmpParam == NULL) {BSL_ERR_PUSH_ERROR(HITLS_AUTH_OTP_NO_COUNTER);return HITLS_AUTH_OTP_NO_COUNTER;}int32_t ret = BSL_PARAM_GetValue(tmpParam, AUTH_PARAM_OTP_HOTP_COUNTER, BSL_PARAM_TYPE_OCTETS, &movingFactor,&movingFactorLen);if (ret != BSL_SUCCESS) {BSL_ERR_PUSH_ERROR(ret);return ret;}if (movingFactorOut != NULL) {*movingFactorOut = movingFactor;}return GenericOtpGen(ctx, movingFactor, otp, otpLen);
}int32_t TotpGen(HITLS_AUTH_OtpCtx *ctx, const BSL_Param *param, const int32_t offset, char *otp, uint32_t *otpLen,uint64_t *movingFactorOut)
{uint64_t curTime = 0;uint32_t curTimeLen = (uint32_t)sizeof(curTime);const BSL_Param *tmpParam = BSL_PARAM_FindConstParam(param, AUTH_PARAM_OTP_TOTP_CURTIME);if (tmpParam == NULL) {BSL_ERR_PUSH_ERROR(HITLS_AUTH_OTP_NO_CURTIME);return HITLS_AUTH_OTP_NO_CURTIME;}int32_t ret =BSL_PARAM_GetValue(tmpParam, AUTH_PARAM_OTP_TOTP_CURTIME, BSL_PARAM_TYPE_OCTETS, &curTime, &curTimeLen);if (ret != BSL_SUCCESS) {BSL_ERR_PUSH_ERROR(ret);return ret;}TotpCtx *totpCtx = (TotpCtx *)ctx->ctx;uint64_t movingFactor = ((curTime - totpCtx->startOffset) / totpCtx->timeStepSize) + offset;if (movingFactorOut != NULL) {*movingFactorOut = movingFactor;}return GenericOtpGen(ctx, movingFactor, otp, otpLen);
}int32_t HITLS_AUTH_OtpGen(HITLS_AUTH_OtpCtx *ctx, const BSL_Param *param, char *otp, uint32_t *otpLen)
{if (ctx == NULL || ctx->key.data == NULL || param == NULL || otp == NULL || otpLen == NULL) {BSL_ERR_PUSH_ERROR(HITLS_AUTH_OTP_INVALID_INPUT);return HITLS_AUTH_OTP_INVALID_INPUT;}if (*otpLen < ctx->digits) {BSL_ERR_PUSH_ERROR(HITLS_AUTH_OTP_INVALID_INPUT);return HITLS_AUTH_OTP_INVALID_INPUT;}switch (ctx->protocolType) {case HITLS_AUTH_OTP_HOTP:return HotpGen(ctx, param, otp, otpLen, NULL);case HITLS_AUTH_OTP_TOTP:return TotpGen(ctx, param, 0, otp, otpLen, NULL);default:BSL_ERR_PUSH_ERROR(HITLS_AUTH_OTP_INVALID_PROTOCOL_TYPE);return HITLS_AUTH_OTP_INVALID_PROTOCOL_TYPE;}
}int32_t HotpValidate(HITLS_AUTH_OtpCtx *ctx, const BSL_Param *param, const char *otp, const uint32_t otpLen,uint64_t *matched)
{char targetOtp[OTP_MAX_DIGITS];uint32_t targetOtpLen = sizeof(targetOtp);uint64_t movingFactor;int32_t ret = HotpGen(ctx, param, targetOtp, &targetOtpLen, &movingFactor);if (ret != HITLS_AUTH_SUCCESS) {BSL_ERR_PUSH_ERROR(ret);return ret;}if (strncmp(otp, targetOtp, otpLen) != 0) {BSL_ERR_PUSH_ERROR(HITLS_AUTH_OTP_VALIDATE_MISMATCH);return HITLS_AUTH_OTP_VALIDATE_MISMATCH;}if (matched != NULL) {*matched = movingFactor;}return HITLS_AUTH_SUCCESS;
}int32_t TotpValidate(HITLS_AUTH_OtpCtx *ctx, const BSL_Param *param, const char *otp, const uint32_t otpLen,uint64_t *matched)
{int32_t ret;char targetOtp[OTP_MAX_DIGITS];uint32_t targetOtpLen = sizeof(targetOtp);uint32_t validWindow = ((TotpCtx *)ctx->ctx)->validWindow;uint64_t movingFactor;for (int64_t offset = -(int64_t)validWindow; offset < (int64_t)validWindow + 1; offset++) {ret = TotpGen(ctx, param, offset, targetOtp, &targetOtpLen, &movingFactor);if (ret != HITLS_AUTH_SUCCESS) {BSL_ERR_PUSH_ERROR(ret);return ret;}if (strncmp(otp, targetOtp, otpLen) == 0) {if (matched != NULL) {*matched = movingFactor;}return HITLS_AUTH_SUCCESS;}}BSL_ERR_PUSH_ERROR(HITLS_AUTH_OTP_VALIDATE_MISMATCH);return HITLS_AUTH_OTP_VALIDATE_MISMATCH;
}int32_t HITLS_AUTH_OtpValidate(HITLS_AUTH_OtpCtx *ctx, const BSL_Param *param, const char *otp, const uint32_t otpLen,uint64_t *matched)
{if (ctx == NULL || ctx->key.data == NULL || param == NULL || otp == NULL || otpLen == 0) {BSL_ERR_PUSH_ERROR(HITLS_AUTH_OTP_INVALID_INPUT);return HITLS_AUTH_OTP_INVALID_INPUT;}if (otpLen != ctx->digits) {BSL_ERR_PUSH_ERROR(HITLS_AUTH_OTP_VALIDATE_MISMATCH);return HITLS_AUTH_OTP_VALIDATE_MISMATCH;}switch (ctx->protocolType) {case HITLS_AUTH_OTP_HOTP:return HotpValidate(ctx, param, otp, otpLen, matched);case HITLS_AUTH_OTP_TOTP:return TotpValidate(ctx, param, otp, otpLen, matched);default:BSL_ERR_PUSH_ERROR(HITLS_AUTH_OTP_INVALID_PROTOCOL_TYPE);return HITLS_AUTH_OTP_INVALID_PROTOCOL_TYPE;}
}

http://www.dtcms.com/a/604942.html

相关文章:

  • UniApp 小程序中使用地图组件
  • 25华北理工大学考情数据分析
  • Unity Shader Graph 3D 实例 - 基础的模型贴图渲染
  • 17.TCP编程
  • Java高级特性:单元测试、反射、注解、动态代理
  • python机器学习工程化demo(包含训练模型,预测数据,模型列表,模型详情,删除模型)支持线性回归、逻辑回归、决策树、SVC、随机森林等模型
  • 逻辑回归在个性化推荐中的原理与应用
  • 织梦网站后台怎么登陆郑州知名做网站公司有哪些
  • 免费做网站的软件跨境电商自建站平台
  • 本机oracle连接延时41970 毫秒
  • 不到一块钱的带USB 2.4G收发 SOC芯片,集成2.4G射频 32位MCU
  • Ubuntu 24.04 安装 PostgreSQL
  • 数据科学每日总结--Day18--数据库
  • 【ZeroRange WebRTC】WebRTC 基于 STUN 的 srflx 直连原理与实现
  • neovim等模态编辑器最优雅的输入法解决方案
  • FaceBook叫板OpenAI!开源 Omnilingual ASR:支持1600多种语言的开源多语言语音识别
  • 分享一个MySQL万能备份脚本
  • 大模型数据洞察能力方法调研
  • 32位MCU芯片国产品牌(32系列单片机常用型号有哪些)
  • 网站底部留言代码赤峰建设淘宝网站
  • 方特网站是谁做的照片做视频的网站
  • Java 9 新特性详解
  • Spring boot 3.3.1 官方文档 中文
  • Sora 2——开启 AI 视频创作新时代
  • 异世界网络:BGP联邦的建立
  • PHP客户端调用由Go服务端GRPC接口
  • Java 开发 - 粘包处理器 - 基于消息头 + 消息体
  • dify零基础入门示例
  • 跨语言智能再升级!Multi-LMentry 打造多语理解新基准;Nemotron-Personas-USA重塑虚拟人画像生成
  • 门户网站建设项目书免费拒绝收费网站