在Golang中双击加密

时间:2017-11-16 14:39:49

标签: go

doc:https://developers.google.com/ad-exchange/rtb/response-guide/decrypt-price#sample_code

没有官方" Double Click Crypto" golang中的示例代码,所以我尝试自己实现。但我无法通过doc测试。请帮帮我!

verLessThan

这是我的代码:

skU7Ax_NL5pPAFyKdkfZjZz2-VhIN8bjj1rVFOaJ_5o=  // Encryption key (e_key)

arO23ykdNqUQ5LEoQ0FVmPkBd7xB5CO89PDZlSjpFxo=  // Integrity key (i_key)

WEp8wQAAAABnFd5EkB2k1wJeFcAj-Z_JVOeGzA  // 100 CPI micros

WEp8sQAAAACwF6CtLJrXSRFBM8UiTTIyngN-og  // 1900 CPI micros

WEp8nQAAAAADG-y45xxIC1tMWuTjzmDW6HtroQ  // 2700 CPI micros
package main

// https://developers.google.com/ad-exchange/rtb/response-guide/decrypt-price?hl=zh-CN

import (
    "bytes"
    "crypto/hmac"
    "crypto/sha1"
    "encoding/base64"
    "encoding/binary"
    "fmt"
)

var base64Codec = base64.URLEncoding.WithPadding(base64.NoPadding)

func safeXORBytes(dst, a, b []byte) int {
    n := len(a)
    if len(b) < n {
        n = len(b)
    }
    for i := 0; i < n; i++ {
        dst[i] = a[i] ^ b[i]
    }
    return n
}

func EncryptPrice(eKey []byte, iKey []byte, price uint64, iv []byte) (finalMessage string, err error) {
    if len(iv) != 16 {
        err = fmt.Errorf("len(iv) = %d != 16", len(iv))
        return
    }

    h1 := hmac.New(sha1.New, eKey)
    h1.Write(iv)
    pad := h1.Sum(nil)[:8]

    priceBytes := make([]byte, 8)
    binary.BigEndian.PutUint64(priceBytes, price)

    encPrice := make([]byte, 8)
    n := safeXORBytes(encPrice, priceBytes, pad)
    if n != 8 {
        err = fmt.Errorf("safeXORBytes n != %d", n)
        return
    }

    h2 := hmac.New(sha1.New, iKey)
    h2.Write(priceBytes)
    h2.Write(iv)
    signature := h2.Sum(nil)[:4]

    finalMessage = base64Codec.EncodeToString(append(append(iv, encPrice...), signature...))
    return
}

func DecryptPrice(eKey []byte, iKey []byte, finalMessage string) (price uint64, err error) {
    finalMessageBytes, err := base64Codec.DecodeString(finalMessage)
    if err != nil {
        return
    }
    if len(finalMessageBytes) != 28 {
        err = fmt.Errorf("len(finalMessageBytes) = %d != 28", len(finalMessageBytes))
        return
    }

    iv := finalMessageBytes[:16]
    encPrice := finalMessageBytes[16:24]
    signature := finalMessageBytes[24:]

    h1 := hmac.New(sha1.New, eKey)
    h1.Write(iv)
    pad := h1.Sum(nil)[:8]

    priceBytes := make([]byte, 8)
    n := safeXORBytes(priceBytes, encPrice, pad)
    if n != 8 {
        err = fmt.Errorf("safeXORBytes n != %d", n)
        return
    }

    h2 := hmac.New(sha1.New, iKey)
    h2.Write(priceBytes)
    h2.Write(iv)
    confSignature := h2.Sum(nil)[:4]

    if bytes.Compare(confSignature, signature) != 0 {
        err = fmt.Errorf("sinature mismatch: confSignature = %s, sinature = %s", confSignature, signature)
        return
    }

    price = binary.BigEndian.Uint64(priceBytes)
    return
}

测试结果:

package main

import (
    "github.com/stretchr/testify/assert"
    "testing"
)

func TestEncryptPrice(t *testing.T) {
    eKeyBase64Encoded := "skU7Ax_NL5pPAFyKdkfZjZz2-VhIN8bjj1rVFOaJ_5o"
    eKey, err := base64Codec.DecodeString(eKeyBase64Encoded)
    assert.Nil(t, err)

    iKeyBase64Encoded := "arO23ykdNqUQ5LEoQ0FVmPkBd7xB5CO89PDZlSjpFxo"
    iKey, err := base64Codec.DecodeString(iKeyBase64Encoded)
    assert.Nil(t, err)

    finalMessage, err := EncryptPrice(eKey, iKey, uint64(100), []byte{88, 74, 124, 193, 0, 0, 0, 0, 103, 21, 222, 68, 144, 29, 164, 215})
    assert.Nil(t, err)
    assert.Equal(t, "WEp8wQAAAABnFd5EkB2k1wJeFcAj-Z_JVOeGzA", finalMessage)
}

func TestDecryptPrice(t *testing.T) {
    eKeyBase64Encoded := "skU7Ax_NL5pPAFyKdkfZjZz2-VhIN8bjj1rVFOaJ_5o"
    eKey, err := base64Codec.DecodeString(eKeyBase64Encoded)
    assert.Nil(t, err)

    iKeyBase64Encoded := "arO23ykdNqUQ5LEoQ0FVmPkBd7xB5CO89PDZlSjpFxo"
    iKey, err := base64Codec.DecodeString(iKeyBase64Encoded)
    assert.Nil(t, err)

    price, err := DecryptPrice(eKey, iKey, "WEp8wQAAAABnFd5EkB2k1wJeFcAj-Z_JVOeGzA")
    assert.Nil(t, err)
    assert.Equal(t, uint64(100), price)
}

3 个答案:

答案 0 :(得分:0)

此代码可以通过https://github.com/google/openrtb-doubleclick/blob/0.9.0/doubleclick-core/src/test/java/com/google/doubleclick/crypto/DoubleClickCryptoTest.java中的测试 ,所以我想文档中的加密价格不会被文档中的e_key和i_key删除。

答案 1 :(得分:0)

我今天遇到了同样的问题。最后,我发现错误是“ ekey”和“ ikey”不需要base64Codec.DecodeString,只需使用[] byte(eKeyBase64Encoded)。

eKeyBase64Encoded := "skU7Ax_NL5pPAFyKdkfZjZz2-VhIN8bjj1rVFOaJ_5o"
eKey := []byte(eKeyBase64Encoded)
assert.Nil(t, err)

iKeyBase64Encoded := "arO23ykdNqUQ5LEoQ0FVmPkBd7xB5CO89PDZlSjpFxo"
iKey :=[]byte(iKeyBase64Encoded)
assert.Nil(t, err)

price, err := DecryptPrice(eKey, iKey, "WEp8wQAAAABnFd5EkB2k1wJeFcAj-Z_JVOeGzA")
....

答案 2 :(得分:0)

此文档https://developers.google.com/authorized-buyers/rtb/response-guide/decrypt-price中列出的加密价格已使用它们在价格上方显示的密钥进行了编码。这些例子是正确的。我写了一个小的Go程序包,它提供了Decrypt功能,我可能很快会添加一个加密功能:https://github.com/matipan/doubleclick。下面我留下一个示例和代码片段,以方便访问。

首先,为了能够解析密钥,我们需要遵守网络安全的base64解码,这意味着我们需要使用base64.URLEncoding!解码密钥:

// ParseKeys parses the base64 web-safe encoded keys as explained in Google's documentation:
// https://developers.google.com/authorized-buyers/rtb/response-guide/decrypt-price
func ParseKeys(ic, ec []byte) (icKey []byte, ecKey []byte, err error) {
    icKey = make([]byte, base64.URLEncoding.DecodedLen(len([]byte(ic))))
    n, err := base64.URLEncoding.Decode(icKey, []byte(ic))
    if err != nil {
        return nil, nil, fmt.Errorf("%w: could not decode price integrity key", err)
    }

    icKey = icKey[:n]

    ecKey = make([]byte, base64.URLEncoding.DecodedLen(len([]byte(ec))))
    n, err = base64.URLEncoding.Decode(ecKey, []byte(ec))
    if err != nil {
        return nil, nil, fmt.Errorf("%w: could not decode price encryption key", err)
    }

    ecKey = ecKey[:n]

    return icKey, ecKey, nil
}

我们可以调用此代码并解密显示的密钥:

    icKey, ecKey, err = ParseKeys([]byte("arO23ykdNqUQ5LEoQ0FVmPkBd7xB5CO89PDZlSjpFxo="), []byte("skU7Ax_NL5pPAFyKdkfZjZz2-VhIN8bjj1rVFOaJ_5o="))

具有更多错误处理功能的解密价格的功能是:

// ErrInvalidPrice is the error returned when the price parsed
// by DecryptPrice is not correct.
var ErrInvalidPrice = errors.New("price is invalid")

// DecryptPrice decrypts the price with google's doubleclick cryptography encoding.
// encPrice is an unpadded web-safe base64 encoded string according to RFC 3548.
// https://developers.google.com/authorized-buyers/rtb/response-guide/decrypt-price
func DecryptPrice(icKey, ecKey, encPrice []byte) (uint64, error) {
    if len(icKey) == 0 || len(ecKey) == 0 {
        return 0, errors.New("encryption and integrity keys are required")
    }

    if len(encPrice) != 38 {
        return 0, fmt.Errorf("%w: invalid length, expected 28 got %d", ErrInvalidPrice, len(encPrice))
    }

    dprice := make([]byte, base64.RawURLEncoding.DecodedLen(len(encPrice)))
    n, err := base64.RawURLEncoding.Decode(dprice, encPrice)
    if err != nil {
        return 0, fmt.Errorf("%w: invalid base64 string", err)
    }
    dprice = dprice[:n]

    if len(dprice) != 28 {
        return 0, fmt.Errorf("%w: invalid decoded price length. Expected 28 got %d", ErrInvalidPrice, len(dprice))
    }

    // encrypted price is composed of parts of fixed lenth. We break it up according to:
    // {initialization_vector (16 bytes)}{encrypted_price (8 bytes)}{integrity (4 bytes)}
    iv, p, sig := dprice[0:16], dprice[16:24], dprice[24:]
    h := hmac.New(sha1.New, ecKey)
    n, err = h.Write(iv)
    if err != nil || n != len(iv) {
        return 0, fmt.Errorf("%w: could not write hmac hash for iv. err=%s, n=%d, len(iv)=%d", ErrInvalidPrice, err, n, len(iv))
    }
    pricePad := h.Sum(nil)

    price := safeXORBytes(p, pricePad)
    if price == nil {
        return 0, fmt.Errorf("%w: price xor price_pad failed", ErrInvalidPrice)
    }

    h = hmac.New(sha1.New, icKey)
    n, err = h.Write(price)
    if err != nil || n != len(price) {
        return 0, fmt.Errorf("%w: could not write hmac hash for price. err=%s, n=%d, len(price)=%d", ErrInvalidPrice, err, n, len(price))
    }

    n, err = h.Write(iv)
    if err != nil || n != len(iv) {
        return 0, fmt.Errorf("%w: could not write hmac hash for iv. err=%s, n=%d, len(iv)=%d", ErrInvalidPrice, err, n, len(iv))
    }

    confSig := h.Sum(nil)[:4]
    if bytes.Compare(confSig, sig) != 0 {
        return 0, fmt.Errorf("%w: integrity of price is not valid", ErrInvalidPrice)
    }

    return binary.BigEndian.Uint64(price), nil
}

func safeXORBytes(a, b []byte) []byte {
    n := len(a)
    if len(b) < n {
        n = len(b)
    }

    if n == 0 {
        return nil
    }

    dst := make([]byte, n)

    for i := 0; i < n; i++ {
        dst[i] = a[i] ^ b[i]
    }

    return dst
}

我们现在可以使用1900微米的示例来调用此代码:YWJjMTIzZGVmNDU2Z2hpN7fhCuPemC32prpWWw

price, err := DecryptPrice(icKey, ecKey, []byte("YWJjMTIzZGVmNDU2Z2hpN7fhCuPemC32prpWWw"))

价格将为1900