评论

退款通知解密过程遇到的坑和解决方案(go 语言代码)

退款通知解码

坑一

首先遇到的问题是解密算法 AES-256-ECB解密(PKCS7Padding),我一开始没注意到 ECB这个模式,用 go 语言的 cipher.NewCBCEncrypter 这个方法,导致一直解码失败,最后发现是模式不对

坑二

这个应该是语言的坑,没有提供原生的 ECB 模式的加密解密,所以找了很多相关文章都没有找到解决方案,最后解决方案在这篇文章里找到了, 链接

解决方案:

官方给出的流程最终验证没问题,测试代码如下:

test


import (
	"crypto/md5"
	"encoding/base64"
	"encoding/hex"
	"testing"
)

func TestAesDecode(t *testing.T) {
	org := "xxxxxx"
	payKey := "xxxxxxxx"


	// step 1, base64 decode encrypted str
	base64Decode, err := base64.StdEncoding.DecodeString(org)
	if err != nil {
		t.Error(err)
		return
	}

	t.Log(len(base64Decode), len(base64Decode)&31)

	// step 2, get md5 of api key
	hash := md5.New()
	hash.Write([]byte(payKey))
	md5ApiKey := hex.EncodeToString(hash.Sum(nil))

	// step 3 decode
	rs, err := AesECBDecrypt(base64Decode, []byte(md5ApiKey))
	t.Log(string(rs))
}

// 解密密
func AesECBDecrypt(ciphertext []byte, aesKey []byte, paddingType ...string) (plaintext []byte, err error) {
	if len(ciphertext) < aes.BlockSize {
		return nil, errors.New("ciphertext too short")
	}
	// ECB mode always works in whole blocks.
	if len(ciphertext)%aes.BlockSize != 0 {
		return nil, errors.New("ciphertext is not a multiple of the block size")
	}
	block, err := aes.NewCipher(aesKey)
	if err != nil {
		return nil, err
	}
	NewECBDecrypter(block).CryptBlocks(ciphertext, ciphertext)
	if len(paddingType) > 0 {
		switch paddingType[0] {
		case "ZeroUnPadding":
			plaintext = ZeroUnPadding(ciphertext)
		case "PKCS5UnPadding":
			plaintext = PKCS5UnPadding(ciphertext)
		}
	} else {
		plaintext = PKCS5UnPadding(ciphertext)
	}
	return plaintext, nil
}

type ecb struct {
	b         cipher.Block
	blockSize int
}

func newECB(b cipher.Block) *ecb {
	return &ecb{
		b:         b,
		blockSize: b.BlockSize(),
	}
}

// 加密
// NewECBEncrypter returns a BlockMode which encrypts in electronic code book
// mode, using the given Block.
func NewECBEncrypter(b cipher.Block) cipher.BlockMode {
	return newECB(b)
}

func (x *ecb) BlockSize() int { return x.blockSize }

func (x *ecb) CryptBlocks(dst, src []byte) {
	if len(src)%x.blockSize != 0 {
		panic("crypto/cipher: input not full blocks")
	}
	if len(dst) < len(src) {
		panic("crypto/cipher: output smaller than input")
	}
	for len(src) > 0 {
		x.b.Encrypt(dst, src[:x.blockSize])
		src = src[x.blockSize:]
		dst = dst[x.blockSize:]
	}
}

type ecbDecrypter ecb

// NewECBDecrypter returns a BlockMode which decrypts in electronic code book
// mode, using the given Block.
func NewECBDecrypter(b cipher.Block) cipher.BlockMode {
	return (*ecbDecrypter)(newECB(b))
}

func (x *ecbDecrypter) BlockSize() int { return x.blockSize }

func (x *ecbDecrypter) CryptBlocks(dst, src []byte) {
	if len(src)%x.blockSize != 0 {
		panic("crypto/cipher: input not full blocks")
	}
	if len(dst) < len(src) {
		panic("crypto/cipher: output smaller than input")
	}
	for len(src) > 0 {
		x.b.Decrypt(dst, src[:x.blockSize])
		src = src[x.blockSize:]
		dst = dst[x.blockSize:]
	}
}

func ZeroPadding(ciphertext []byte, blockSize int) []byte {
	padding := blockSize - len(ciphertext)%blockSize
	padtext := bytes.Repeat([]byte{0}, padding)
	return append(ciphertext, padtext...)
}

func ZeroUnPadding(origData []byte) []byte {
	return bytes.TrimRightFunc(origData, func(r rune) bool {
		return r == rune(0)
	})
}

func PKCS5Padding(ciphertext []byte, blockSize int) []byte {
	padding := blockSize - len(ciphertext)%blockSize
	padtext := bytes.Repeat([]byte{byte(padding)}, padding)
	return append(ciphertext, padtext...)
}

func PKCS5UnPadding(origData []byte) []byte {
	length := len(origData)
	// 去掉最后一个字节 unpadding 次
	unpadding := int(origData[length-1])
	return origData[:(length - unpadding)]
}

如果使用这部分源码遇到问题或者其他语言类似问题 可以评论区留言共同探讨~

最后一次编辑于  2020-06-24  
点赞 2
收藏
评论
登录 后发表内容