TOTP动态令牌(基于openssl/HAMC)

思路:获取当前的时间戳/60(一分钟内任意时刻的时间戳/60为固定的值,对于C/C++),使用HMAC得到摘要,摘要进行位运算处理得到6位的动态口令

TOTP.hpp

#ifndef __TOTP_H
#define __TOTP_H

#include <iostream>
#include <sstream>
#include <string>
#include <vector>
#include <ctime>
#include <iomanip>
#include <algorithm>

//密钥传入HMAC
#define PUBLIC_KEY "AE448C9CFF39F43B9ED7358852F8CC6BF3D92B2B"
#define CC_SHA1_DIGEST_LENGTH   20
#define CC_SHA256_DIGEST_LENGTH 32
#define CC_SHA512_DIGEST_LENGTH 64

class TOTP
{
public:
    enum class Hash {HMACSHA1, HMACSHA256, HMACSHA512};

private:
    static const int DIGITS_POWER[];
#ifdef __APPLE__
    static void hmac_sha(Hash hash, const void *key, size_t key_len, const void *data, size_t data_len, void *mac_out);
#else
    static unsigned char * hmac_sha(Hash hash, const void *key, int key_len, const unsigned char *d, int n, unsigned char *md, unsigned int *md_len);
#endif
    static void hexStr2Bytes(std::string &hex,std::vector<unsigned char> &bytes);

public:
	//sha1
    static std::string generateTOTP(std::string &key, std::string &time, std::string &returnDigits);
	//sha256
    static std::string generateTOTP256(std::string &key, std::string &time, std::string &returnDigits);
	//sha512
    static std::string generateTOTP512(std::string &key, std::string &time, std::string &returnDigits);
	//使用HMAC接口传入不同的算法(多样性)
    static std::string generateTOTP(std::string &key, std::string &time, std::string &returnDigits, Hash hash);
    //时间戳转为16进制
    static std::string toHexString(long T);
    static int ET_CheckPwdz201(const std::string& otp, int otplen=6, long X=60, time_t T0=0, long t=0);
};

#endif

TOTP.cpp

#ifdef __APPLE__
    #include <CommonCrypto/CommonHMAC.h>
#else
    #include <openssl/evp.h>
    #include <openssl/hmac.h>
#endif

#include <vector>

#include "TOTP.h"

const int TOTP::DIGITS_POWER[] = {1, 10, 100, 1000, 10000, 100000, 1000000, 10000000, 100000000};

//16进制数转为字节流匹配HMAC接口参数
void TOTP::hexStr2Bytes(std::string &hex,std::vector<unsigned char> &bytes)
{
    std::stringstream ss;
    unsigned int buffer;
    unsigned int offset = 0;

    while (offset < hex.length())
    {
        ss.clear();
        ss << std::hex << hex.substr(offset, 2);
        ss >> buffer;

        bytes.emplace_back(static_cast<unsigned char>(buffer));
        offset += 2;
    }
}

#ifdef __APPLE__
void TOTP::hmac_sha(Hash hash, const void *key, size_t key_len, const void *data, size_t data_len, void *mac_out)
{
    CCHmacAlgorithm algorithm;

    switch (hash) {
        default:
        case Hash::HMACSHA1: algorithm = kCCHmacAlgSHA1; break;
        case Hash::HMACSHA256: algorithm = kCCHmacAlgSHA256; break;
        case Hash::HMACSHA512: algorithm = kCCHmacAlgSHA512; break;
    }

    CCHmac(algorithm, key, key_len, data, data_len, mac_out);
}
#else
unsigned char * TOTP::hmac_sha(Hash hash, const void *key, int key_len, const unsigned char *d, int n, unsigned char *md, unsigned int *md_len)
{
    const EVP_MD *evp_md;
    switch(hash)
    {
        default:
        case Hash::HMACSHA1: evp_md = EVP_sha1(); break;
        case Hash::HMACSHA256: evp_md = EVP_sha256(); break;
        case Hash::HMACSHA512: evp_md = EVP_sha512(); break;
    }
	//使用openssl中的HMAC接口生成摘要hmac
    return HMAC(evp_md, key, key_len, d, n, md, md_len);
}
#endif

std::string TOTP::generateTOTP(std::string &key, std::string &time, std::string &returnDigits)
{
    return generateTOTP(key, time, returnDigits, Hash::HMACSHA1);
}

std::string TOTP::generateTOTP256(std::string &key, std::string &time, std::string &returnDigits)
{
    return generateTOTP(key, time, returnDigits, Hash::HMACSHA256);
}

std::string TOTP::generateTOTP512(std::string &key, std::string &time, std::string &returnDigits)
{
    return generateTOTP(key, time, returnDigits, Hash::HMACSHA512);
}

//当前时间戳通过openssl中的HMAC求出摘要从而获得(六位的验证码)
std::string TOTP::generateTOTP(std::string &key, std::string &time, std::string &returnDigits, Hash hash)
{
    unsigned int codeDigits = atoi(returnDigits.c_str());
    std::string result;

    while (time.length() < 16)
    {
        time = "0" + time;
    }
	
    std::vector<unsigned char> msg;
    std::vector<unsigned char> k;
    hexStr2Bytes(time,msg);
    hexStr2Bytes(key,k);

    unsigned int HASH_LENGTH;
    switch (hash) {
        default:
        case Hash::HMACSHA1: HASH_LENGTH = CC_SHA1_DIGEST_LENGTH; break;
        case Hash::HMACSHA256: HASH_LENGTH = CC_SHA256_DIGEST_LENGTH; break;
        case Hash::HMACSHA512: HASH_LENGTH = CC_SHA512_DIGEST_LENGTH; break;
    }

    unsigned char hmac[HASH_LENGTH];
    unsigned int hmac_length;
#ifdef __APPLE__
    hmac_sha(hash, k.data(), k.size(), msg.data(), msg.size(), hmac);

    hmac_length = HASH_LENGTH;
#else
    hmac_sha(hash, k.data(), k.size(), msg.data(), msg.size(), hmac, &hmac_length);
#endif
	//取摘要hmac最后一个字节的后四位 offset范围0-15 (哪一位无所谓)
    int offset = hmac[hmac_length - 1] & 0xf;
	//随机取任意几个字节 位运算(随机性使得binary值不确定) 此处位运算可以自己随意设置
    int binary =
        ((hmac[offset] & 0x7f) << 24) |
        ((hmac[offset + 1] & 0xff) << 16) |
        ((hmac[offset + 2] & 0xff) << 8) |
        (hmac[offset + 3] & 0xff);

    int otp = binary % DIGITS_POWER[codeDigits];
    result = std::to_string(otp);

	//补0满6位 验证码
    while (result.length() < codeDigits)
        result = "0" + result;

    return result;
}

//时间戳转换为16进制存到string
std::string TOTP::toHexString(long T)
{
    std::string hex;
    std::ostringstream oss;

    oss << std::hex << T;
    hex = oss.str();
    std::transform(hex.begin(), hex.end(), hex.begin(), ::toupper);

    return hex;
}

//输入的验证码和计算的作为比较相同则进入我们的客户端否则不能启动
int TOTP::ET_CheckPwdz201(const std::string& otp, int otplen, long X, time_t T0, long t)
{
    std::string steps = "";
    std::string return_digits = std::to_string(otplen);

    time_t ltime = t;
    long T;
	//时间戳/60(一分钟内的任何时刻的时间戳/60后的值相同,这里取long型)
    T = (ltime - T0) / X;
    steps = toHexString(T);
    std::string publicKey = PUBLIC_KEY;
    std::string passwd = TOTP::generateTOTP(publicKey, steps, return_digits, TOTP::Hash::HMACSHA1);

    /* 第一次验证*/
    if (passwd == otp)
    {
        return 0;
    }

	//增加容错 防止口令在使用是为58 59秒而输入时已经下一分钟了(给出前后两秒的容错)
    /* 若在两秒的范围内可以认为密码正确 */
    time_t i = ltime - 2;
    while (i < ltime + 3)
    {
        if (i == ltime)
        {
            ++i;
            continue;
        }
        T = (i - T0) / X;
        steps = toHexString(T);
        passwd = TOTP::generateTOTP(publicKey, steps, return_digits, TOTP::Hash::HMACSHA1);

        if (passwd == otp)
        {
            return 0;
        }
        ++i;
    }	
    return -1;
}
  • 0
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值