广告竞价中常用的价格解密算法

Real-Time Bidding Protocol Decrypt Price Confirmations 谷歌实时竞价文档

加密结果格式

{初始化向量 (16 bytes)}{加密的价格(8 bytes)}{完整性签名(4 bytes)}

加密价格的长度固定为 28 个字节,其中包含 16 个字节的初始化矢量、8 个字节的密 文以及 4 个字节的完整性签名。

加密方式

使用基于 SHA-1 HMAC 算法的加密解密示例如下: 加密阶段伪代码:

pad = hmac(e_key, iv) // 取前 8 个字节
enc_price = pad <xor> price
signature = hmac(i_key, price || iv) // 取前 4 个字节
final_message = WebSafeBase64Encode( iv || enc_price || signature )

解密阶段伪代码

(iv || enc_price || signature) = WebSafeBase64Decode(final_message)
pad = hmac(e_key, iv)
price = enc_price <xor> pad
conf_sig = hmac(i_key, price || iv)
success = (conf_sig == signature)

字符与表达式解释

iv: 初始化矢量(16 个字节) e_key: 加密密钥(32 个字节) i_key: 完整性密钥(32 个字节) price: 价格 (8 个字节) hmac(k, d): 数据 d 的 SHA-1 HMAC,使用密钥 k a || b: 字符串 a 和字符串 b 并列

解密算法 nodejs 版示例

const crypto = require('crypto')
const e_key = ''
const i_key = ''

function WebSafeBase64Decode(base) {
    var s = base.replace(/\-/g, '+').replace(/\_/g, '/')
    if (s.length % 4 == 2) {
        s += '=='
    } else if (s.length % 4 == 3) {
        s += '='
    }
    return s
}

function decodePrice(e_enc) {
    let buffer = new Buffer(WebSafeBase64Decode(e_enc), 'base64') // base64解码

    let iv = buffer.slice(0, 16) // 初始化矢量数组
    let en_price = buffer.slice(16, 24) // 加密的价格数组
    let integrity = buffer.slice(24, 28).toString('hex') // 完整性签名的前4位, 转换成16进制字符串
    let de_byte = crypto
        .createHmac('sha1', e_key)
        .update(iv)
        .digest()
        .slice(0, 8) // HmacSHA1 加密得到加密数组, 取前8个字节

    let dec_price = new Buffer.allocUnsafe(8) // 创建一个长度为8的buffer
    for (let i = 0; i < 8; i++) {
        dec_price[i] = en_price[i] ^ de_byte[i] // 将异或运算后的新值存入dec_price
    }
    let price = parseInt(dec_price.toString('hex'), 16) / 1000000 // 转换成价格
    // 加密签名验证
    let o_ikey = crypto
        .createHmac('sha1', i_key)
        .update(Buffer.concat([dec_price, iv]))
        .digest()
        .slice(0, 4)
        .toString('hex')
    if (integrity == o_ikey) {
        // 比较两个签名数组是否相同
        return price
    } else {
        return -1
    }
}

decodePrice('加密价格字符串')