You signed in with another tab or window. Reload to refresh your session.You signed out in another tab or window. Reload to refresh your session.You switched accounts on another tab or window. Reload to refresh your session.Dismiss alert
package main
import (
"bytes""crypto/aes""crypto/cipher""crypto/md5""crypto/sha256""encoding/base64""encoding/hex""encoding/json""errors""fmt""hash""io""log""net/http""os""strings"
)
const (
pkcs5SaltLen=8aes256KeyLen=32
)
typeCookieCloudBodystruct {
Uuidstring`json:"uuid,omitempty"`Encryptedstring`json:"encrypted,omitempty"`
}
funcmain() {
apiUrl:=strings.TrimSuffix(os.Getenv("COOKIE_CLOUD_HOST"), "/")
uuid:=os.Getenv("COOKIE_CLOUD_UUID")
password:=os.Getenv("COOKIE_CLOUD_PASSWORD")
ifapiUrl==""||uuid==""||password=="" {
log.Fatalf("COOKIE_CLOUD_HOST, COOKIE_CLOUD_UUID and COOKIE_CLOUD_PASSWORD env must be set")
}
vardata*CookieCloudBodyres, err:=http.Get(apiUrl+"/get/"+uuid)
iferr!=nil {
log.Fatalf("Failed to request server: %v", err)
}
ifres.StatusCode!=200 {
log.Fatalf("Server return status %d", res.StatusCode)
}
deferres.Body.Close()
body, err:=io.ReadAll(res.Body)
iferr!=nil {
log.Fatalf("Failed to read server response: %v", err)
}
err=json.Unmarshal(body, &data)
iferr!=nil {
log.Fatalf("Failed to parse server response as json: %v", err)
}
keyPassword:=Md5String(uuid, "-", password)[:16]
decrypted, err:=DecryptCryptoJsAesMsg(keyPassword, data.Encrypted)
iferr!=nil {
log.Fatalf("Failed to decrypt: %v", err)
}
fmt.Printf("Decrypted: %s\n", decrypted)
}
// Decrypt a CryptoJS.AES.encrypt(msg, password) encrypted msg.// ciphertext is the result of CryptoJS.AES.encrypt(), which is the base64 string of// "Salted__" + [8 bytes random salt] + [actual ciphertext].// actual ciphertext is padded (make it's length align with block length) using Pkcs7.// CryptoJS use a OpenSSL-compatible EVP_BytesToKey to derive (key,iv) from (password,salt),// using md5 as hash type and 32 / 16 as length of key / block.// See: https://stackoverflow.com/questions/35472396/how-does-cryptojs-get-an-iv-when-none-is-specified ,// https://stackoverflow.com/questions/64797987/what-is-the-default-aes-config-in-crypto-jsfuncDecryptCryptoJsAesMsg(passwordstring, ciphertextstring) ([]byte, error) {
constkeylen=32constblocklen=16rawEncrypted, err:=base64.StdEncoding.DecodeString(ciphertext)
iferr!=nil {
returnnil, fmt.Errorf("failed to base64 decode Encrypted: %v", err)
}
iflen(rawEncrypted) <17||len(rawEncrypted)%blocklen!=0||string(rawEncrypted[:8]) !="Salted__" {
returnnil, fmt.Errorf("invalid ciphertext")
}
salt:=rawEncrypted[8:16]
encrypted:=rawEncrypted[16:]
key, iv:=BytesToKey(salt, []byte(password), md5.New(), keylen, blocklen)
newCipher, err:=aes.NewCipher(key)
iferr!=nil {
returnnil, fmt.Errorf("failed to create aes cipher: %v", err)
}
cfbdec:=cipher.NewCBCDecrypter(newCipher, iv)
decrypted:=make([]byte, len(encrypted))
cfbdec.CryptBlocks(decrypted, encrypted)
decrypted, err=pkcs7strip(decrypted, blocklen)
iferr!=nil {
returnnil, fmt.Errorf("failed to strip pkcs7 paddings (password may be incorrect): %v", err)
}
returndecrypted, nil
}
// From https://github.com/walkert/go-evp .// BytesToKey implements the Openssl EVP_BytesToKey logic.// It takes the salt, data, a hash type and the key/block length used by that type.// As such it differs considerably from the openssl method in C.funcBytesToKey(salt, data []byte, h hash.Hash, keyLen, blockLenint) (key, iv []byte) {
saltLen:=len(salt)
ifsaltLen>0&&saltLen!=pkcs5SaltLen {
panic(fmt.Sprintf("Salt length is %d, expected %d", saltLen, pkcs5SaltLen))
}
var (
concat []bytelastHash []bytetotalLen=keyLen+blockLen
)
for ; len(concat) <totalLen; h.Reset() {
// concatenate lastHash, data and salt and write them to the hashh.Write(append(lastHash, append(data, salt...)...))
// passing nil to Sum() will return the current hash valuelastHash=h.Sum(nil)
// append lastHash to the running total bytesconcat=append(concat, lastHash...)
}
returnconcat[:keyLen], concat[keyLen:totalLen]
}
// BytesToKeyAES256CBC implements the SHA256 version of EVP_BytesToKey using AES CBCfuncBytesToKeyAES256CBC(salt, data []byte) (key []byte, iv []byte) {
returnBytesToKey(salt, data, sha256.New(), aes256KeyLen, aes.BlockSize)
}
// BytesToKeyAES256CBCMD5 implements the MD5 version of EVP_BytesToKey using AES CBCfuncBytesToKeyAES256CBCMD5(salt, data []byte) (key []byte, iv []byte) {
returnBytesToKey(salt, data, md5.New(), aes256KeyLen, aes.BlockSize)
}
// return the MD5 hex hash string (lower-case) of input string(s)funcMd5String(inputs...string) string {
keyHash:=md5.New()
for_, str:=rangeinputs {
io.WriteString(keyHash, str)
}
returnhex.EncodeToString(keyHash.Sum(nil))
}
// from https://gist.github.com/nanmu42/b838acc10d393bc51cb861128ce7f89c .// pkcs7strip remove pkcs7 paddingfuncpkcs7strip(data []byte, blockSizeint) ([]byte, error) {
length:=len(data)
iflength==0 {
returnnil, errors.New("pkcs7: Data is empty")
}
iflength%blockSize!=0 {
returnnil, errors.New("pkcs7: Data is not block-aligned")
}
padLen:=int(data[length-1])
ref:=bytes.Repeat([]byte{byte(padLen)}, padLen)
ifpadLen>blockSize||padLen==0||!bytes.HasSuffix(data, ref) {
returnnil, errors.New("pkcs7: Invalid padding")
}
returndata[:length-padLen], nil
}
The text was updated successfully, but these errors were encountered:
写了一个 Go 版本的解密代码,供参考。
The text was updated successfully, but these errors were encountered: