Шифрование будет выполняться на стороне клиента с помощью следующего кода на основе Spring Security-Encryptors:
package at.wrwks.pipe.baumgmt.component.documentpreview;
import static java.nio.charset.StandardCharsets.UTF_8;
import java.net.URLEncoder;
import java.util.Base64;
import org.springframework.security.crypto.codec.Hex;
import org.springframework.security.crypto.encrypt.Encryptors;
import org.springframework.stereotype.Component;
@Component
public class SecureResourceUrlComposer {
public String compose(final String resource) {
final var salt = new String(Hex.encode("salt".getBytes(UTF_8)));
final var encryptor = Encryptors.stronger("password", salt);
final var encryptedResource = encryptor.encrypt(resource.getBytes(UTF_8));
final var base64EncodedEncryptedResource = Base64.getEncoder().encodeToString(encryptedResource);
final var urlEncodedBase64EncodedEncryptedResource = URLEncoder.encode(base64EncodedEncryptedResource, UTF_8);
return "https://target" + "?resource=" + urlEncodedBase64EncodedEncryptedResource;
}
}
Пример ресурса: aResource
в кодировке URL и base64 вывод: https://target?resource=yEAdq1toEfbcTKcAeTJmw7zLYdk4fA2waASPzSfqQxAxiq7bmUarUYE%3D
Дешифрование завершается неудачно с cipher: message authentication failed
в следующем бэкэнд-коде, записанном в Go в gcm.Open
:
func decryptGcmAes32(ciphertext, key string) (plaintext string, err error) {
if len(key) != 32 {
msg := fmt.Sprintf("Unexpected key length (!= 32) '%s' %d", key, len(key))
err = errors.New(msg)
log.Warn(err)
sentry.CaptureException(err)
return
}
keyBytes := []byte(key)
c, err := aes.NewCipher(keyBytes)
if err != nil {
log.Warn("Couldn't create a cipher block", err)
sentry.CaptureException(err)
return
}
gcm, err := cipher.NewGCM(c)
if err != nil {
log.Warn("Couldn't wrap in gcm mode", err)
sentry.CaptureException(err)
return
}
nonceSize := gcm.NonceSize()
if len(ciphertext) < nonceSize {
msg := fmt.Sprintf("Ciphertext shorter than nonce size %d < %d", len(ciphertext), nonceSize)
err = errors.New(msg)
log.Warn(err)
sentry.CaptureException(err)
return
}
ciphertextBytes := []byte(ciphertext)
nonce, ciphertextBytes := ciphertextBytes[:nonceSize], ciphertextBytes[nonceSize:]
plaintextBytes, err := gcm.Open(nil, nonce, ciphertextBytes, nil)
if err != nil {
log.Warn("Couldn't decode", err)
sentry.CaptureException(err)
return
}
plaintext = string(plaintextBytes)
return
}
Следующий тест в go
работает только , если iv
одинаково для шифрования и дешифрования
package main
import (
"crypto/aes"
"crypto/cipher"
"crypto/rand"
"crypto/sha1"
"golang.org/x/crypto/pbkdf2"
"log"
"testing"
)
var iv = make([]byte, 12)
func TestCrypto(t *testing.T) {
rand.Read(iv)
encrypted, _ := encrypt("aResource")
if decrypted, err := decrypt(encrypted); err != nil {
log.Println(err)
} else {
log.Printf("DECRYPTED: %s\n", decrypted)
}
}
func encrypt(secret string) (result []byte, err error) {
salt := []byte("salt")
key := pbkdf2.Key([]byte("b0226e4e9bef40d4b8aed039c208ae3e"), salt, 1024, 16, sha1.New)
b, err := aes.NewCipher(key)
aesgcm, err := cipher.NewGCM(b)
result = aesgcm.Seal(nil, iv, []byte(secret), nil)
return
}
func decrypt(ciphertext []byte) (result string, err error) {
salt := []byte("salt")
key := pbkdf2.Key([]byte("b0226e4e9bef40d4b8aed039c208ae3e"), salt, 1024, 16, sha1.New)
b, err := aes.NewCipher(key)
aesgcm, err := cipher.NewGCM(b)
decrypted, err := aesgcm.Open(ciphertext[:0], iv, ciphertext, nil)
result = string(decrypted)
return
}