Зашифрованная строка Spring Security - дешифрование в Go завершается неудачно - PullRequest
0 голосов
/ 24 марта 2020

Шифрование будет выполняться на стороне клиента с помощью следующего кода на основе 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
}

1 Ответ

1 голос
/ 26 марта 2020

Итак, основные моменты:

  • Для того, чтобы применить соль и получить правильный ключ pbkdf2.Key() необходимо использовать, как показано ниже
  • nonce (или * Размер 1008 *) в Spring Security составляет 16 байтов, в отличие от 12 байтов в go

В приведенном ниже фрагменте обработки ошибок не используется, чтобы подчеркнуть суть решения:

const nonceSize = 16
func decryptWithAes256GcmPbkdf2(cipherBytes []byte, password string, salt string) (string) {
    key := pbkdf2.Key([]byte(password), []byte(salt), 1024, 32, sha1.New)
    c, _ := aes.NewCipher(key)
    gcm, _ := cipher.NewGCMWithNonceSize(c, nonceSize)
    plaintextBytes, _ := gcm.Open(nil, cipherBytes[:nonceSize], cipherBytes[nonceSize:], nil)
    return string(plaintextBytes)
}
Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...