68 lines
2.0 KiB
Go
68 lines
2.0 KiB
Go
// Package jasypt decrypts values encrypted with Jasypt the way
|
|
// jasypt-spring-boot 3.0 and later does by default: ENC(...) around the
|
|
// base64 of a random salt, a random IV and the ciphertext, with
|
|
// PBEWITHHMACSHA512ANDAES_256 (PBKDF2-HMAC-SHA512, 1000 iterations,
|
|
// AES-256-CBC).
|
|
package jasypt
|
|
|
|
import (
|
|
"crypto/aes"
|
|
"crypto/cipher"
|
|
"crypto/pbkdf2"
|
|
"crypto/sha512"
|
|
"encoding/base64"
|
|
"errors"
|
|
"strings"
|
|
"unicode/utf8"
|
|
)
|
|
|
|
const iterations = 1000
|
|
|
|
// ErrDecrypt is a value that doesn't decrypt with the password: a wrong
|
|
// password, or one encrypted with another algorithm.
|
|
var ErrDecrypt = errors.New("doesn't decrypt: a wrong password, or not Jasypt's default algorithm (PBEWITHHMACSHA512ANDAES_256)")
|
|
|
|
// Is reports whether v is a Jasypt value, ENC(...).
|
|
func Is(v string) bool {
|
|
return len(v) > len("ENC()") && strings.HasPrefix(v, "ENC(") && strings.HasSuffix(v, ")")
|
|
}
|
|
|
|
// Decrypt decrypts an ENC(...) value.
|
|
func Decrypt(v, password string) (string, error) {
|
|
b, err := base64.StdEncoding.DecodeString(strings.TrimSuffix(strings.TrimPrefix(v, "ENC("), ")"))
|
|
if err != nil {
|
|
return "", errors.New("not Jasypt: ENC(...) isn't base64")
|
|
}
|
|
const size = aes.BlockSize // the salt's and the IV's too
|
|
if len(b) < 3*size || len(b)%size != 0 {
|
|
return "", ErrDecrypt
|
|
}
|
|
salt, iv, data := b[:size], b[size:2*size], b[2*size:]
|
|
key, err := pbkdf2.Key(sha512.New, password, salt, iterations, 32)
|
|
if err != nil {
|
|
return "", err
|
|
}
|
|
block, err := aes.NewCipher(key)
|
|
if err != nil {
|
|
return "", err
|
|
}
|
|
plain := make([]byte, len(data))
|
|
cipher.NewCBCDecrypter(block, iv).CryptBlocks(plain, data)
|
|
// A wrong password decrypts to noise: its padding is almost never
|
|
// valid, and the rest rarely UTF-8.
|
|
n := int(plain[len(plain)-1])
|
|
if n == 0 || n > size {
|
|
return "", ErrDecrypt
|
|
}
|
|
for _, c := range plain[len(plain)-n:] {
|
|
if int(c) != n {
|
|
return "", ErrDecrypt
|
|
}
|
|
}
|
|
plain = plain[:len(plain)-n]
|
|
if !utf8.Valid(plain) {
|
|
return "", ErrDecrypt
|
|
}
|
|
return string(plain), nil
|
|
}
|