Déchiffrer en Go ce qui a été chiffré avec AES en mode CFB en Python

Boucleur

Publier

Je veux pouvoir décrypter en Go ce qui a été crypté en Python. Les fonctions de chiffrement/déchiffrement fonctionnent respectivement dans chaque langue, mais pas lorsque je chiffre en Python et que je déchiffre en Go, je suppose qu'il y a quelque chose qui ne va pas avec l'encodage car j'obtiens une sortie charabia :

Rx����d��I�K|�ap���k��B%F���UV�~d3h�����|�����>�B��B�

Chiffrement/déchiffrement en Python

def encrypt(plaintext, key=config.SECRET, key_salt='', no_iv=False):
    """Encrypt shit the right way"""

    # sanitize inputs
    key = SHA256.new((key + key_salt).encode()).digest()
    if len(key) not in AES.key_size:
        raise Exception()
    if isinstance(plaintext, string_types):
        plaintext = plaintext.encode('utf-8')

    # pad plaintext using PKCS7 padding scheme
    padlen = AES.block_size - len(plaintext) % AES.block_size
    plaintext += (chr(padlen) * padlen).encode('utf-8')

    # generate random initialization vector using CSPRNG
    if no_iv:
        iv = ('\0' * AES.block_size).encode()
    else:
        iv = get_random_bytes(AES.block_size)
    log.info(AES.block_size)
    # encrypt using AES in CFB mode
    ciphertext = AES.new(key, AES.MODE_CFB, iv).encrypt(plaintext)

    # prepend iv to ciphertext
    if not no_iv:
        ciphertext = iv + ciphertext
    # return ciphertext in hex encoding
    log.info(ciphertext)
    return ciphertext.hex()


def decrypt(ciphertext, key=config.SECRET, key_salt='', no_iv=False):
    """Decrypt shit the right way"""

    # sanitize inputs
    key = SHA256.new((key + key_salt).encode()).digest()
    if len(key) not in AES.key_size:
        raise Exception()
    if len(ciphertext) % AES.block_size:
        raise Exception()
    try:
        ciphertext = codecs.decode(ciphertext, 'hex')
    except TypeError:
        log.warning("Ciphertext wasn't given as a hexadecimal string.")

    # split initialization vector and ciphertext
    if no_iv:
        iv = '\0' * AES.block_size
    else:
        iv = ciphertext[:AES.block_size]
        ciphertext = ciphertext[AES.block_size:]

    # decrypt ciphertext using AES in CFB mode
    plaintext = AES.new(key, AES.MODE_CFB, iv).decrypt(ciphertext).decode()

    # validate padding using PKCS7 padding scheme
    padlen = ord(plaintext[-1])
    if padlen < 1 or padlen > AES.block_size:
        raise Exception()
    if plaintext[-padlen:] != chr(padlen) * padlen:
        raise Exception()
    plaintext = plaintext[:-padlen]

    return plaintext

Cryptage/Décryptage en Go

// PKCS5Padding adds padding to the plaintext to make it a multiple of the block size
func PKCS5Padding(src []byte, blockSize int) []byte {
    padding := blockSize - len(src)%blockSize
    padtext := bytes.Repeat([]byte{byte(padding)}, padding)
    return append(src, padtext...)
}

// Encrypt encrypts the plaintext,the input salt should be a random string that is appended to the plaintext
// that gets fed into the one-way function that hashes it.
func Encrypt(plaintext) string {
    h := sha256.New()
    h.Write([]byte(os.Getenv("SECRET")))
    key := h.Sum(nil)
    plaintextBytes := PKCS5Padding([]byte(plaintext), aes.BlockSize)
    block, err := aes.NewCipher(key)
    if err != nil {
        panic(err)
    }
    // The IV needs to be unique, but not secure. Therefore it's common to
    // include it at the beginning of the ciphertext.
    ciphertext := make([]byte, aes.BlockSize+len(plaintextBytes))
    iv := ciphertext[:aes.BlockSize]
    if _, err := io.ReadFull(rand.Reader, iv); err != nil {
        panic(err)
    }
    stream := cipher.NewCFBEncrypter(block, iv)
    stream.XORKeyStream(ciphertext[aes.BlockSize:], plaintextBytes)
    // return hexadecimal representation of the ciphertext
    return hex.EncodeToString(ciphertext)
}
func PKCS5UnPadding(src []byte) []byte {
    length := len(src)
    unpadding := int(src[length-1])
    return src[:(length - unpadding)]
}
func Decrypt(ciphertext string) string {

    h := sha256.New()
    // have to check if the secret is hex encoded
    h.Write([]byte(os.Getenv("SECRET")))
    key := h.Sum(nil)
    ciphertext_bytes := []byte(ciphertext)
    block, err := aes.NewCipher(key)
    if err != nil {
        panic(err)
    }
    log.Print(aes.BlockSize)
    // The IV needs to be unique, but not secure. Therefore it's common to
    // include it at the beginning of the ciphertext.
    iv := ciphertext_bytes[:aes.BlockSize]
    if len(ciphertext) < aes.BlockSize {
        panic("ciphertext too short")
    }
    ciphertext_bytes = ciphertext_bytes[aes.BlockSize:]
    stream := cipher.NewCFBDecrypter(block, iv)
    stream.XORKeyStream(ciphertext_bytes, ciphertext_bytes)
    plaintext := PKCS5UnPadding(ciphertext_bytes)
    return string(plaintext)
}
    
Topaco

Le mode CFB utilise une taille de segment qui correspond aux bits chiffrés par étape de chiffrement, voir CFB .

Go ne prend en charge qu'une taille de segment de 128 bits (CFB128), du moins sans modifications plus profondes (voir ici et ici ). En revanche, la taille de segment dans PyCryptodome est configurable et par défaut à 8 bits (CFB8), s. ici . Le code Python publié utilise cette valeur par défaut, les deux codes sont donc incompatibles. Comme la taille du segment n'est pas ajustable dans le code Go, elle doit être définie sur CFB128 dans le code Python :

cipher = AES.new(key, AES.MODE_CFB, iv, segment_size=128) 

De plus, le texte chiffré est encodé en hexadécimal dans le code Python, il doit donc être décodé en hexadécimal dans le code Go, ce qui ne se produit pas encore dans le code posté.

Avec ces deux modifications, le texte chiffré produit avec le code Python peut être déchiffré.


Le texte chiffré dans le code Go suivant a été créé avec le code Python en utilisant une taille de segment de 128 bits et la phrase secrète my passphraseet est déchiffré avec succès :

package main

import (
    "crypto/aes"
    "crypto/cipher"
    "crypto/sha256"
    "encoding/hex"
    "fmt"
)

func main() {
    ciphertextHex := "546ddf226c4c556c7faa386940f4fff9b09f7e3a2ccce2ed26f7424cf9c8cd743e826bc8a2854bb574df9f86a94e7b2b1e63886953a6a3eb69eaa5fa03d69ba5" // Fix 1: Apply CFB128 on the Python side
    fmt.Println(Decrypt(ciphertextHex))                                                                                                                 // The quick brown fox jumps over the lazy dog
}

func PKCS5UnPadding(src []byte) []byte {
    length := len(src)
    unpadding := int(src[length-1])
    return src[:(length - unpadding)]
}
func Decrypt(ciphertext string) string {
    h := sha256.New()
    //h.Write([]byte(os.Getenv("SECRET")))
    h.Write([]byte("my passphrase")) // Apply passphrase from Python side
    key := h.Sum(nil)
    //ciphertext_bytes := []byte(ciphertext)
    ciphertext_bytes, _ := hex.DecodeString(ciphertext) // Fix 2. Hex decode ciphertext
    block, err := aes.NewCipher(key)
    if err != nil {
        panic(err)
    }
    iv := ciphertext_bytes[:aes.BlockSize]
    if len(ciphertext) < aes.BlockSize {
        panic("ciphertext too short")
    }
    ciphertext_bytes = ciphertext_bytes[aes.BlockSize:]
    stream := cipher.NewCFBDecrypter(block, iv)
    stream.XORKeyStream(ciphertext_bytes, ciphertext_bytes)
    plaintext := PKCS5UnPadding(ciphertext_bytes)
    return string(plaintext)
}

Sécurité:

  • L'utilisation d'un résumé comme fonction de dérivation de clé n'est pas sécurisée. Appliquez une fonction de dérivation de clé dédiée comme PBKDF2.
  • Un sel statique ou manquant est également précaire. Utilisez un sel généré aléatoirement pour chaque chiffrement. Concaténez le sel non secret avec le texte chiffré (analogue au IV), par exemple salt|IV|ciphertext.
  • La variante no_iv=Trueapplique un IV statique (zéro IV), qui n'est pas sûr et ne doit pas être utilisé. La manière correcte est décrite avec la variante no_iv=False.
  • CFB est un mode de chiffrement de flux et ne nécessite donc pas de remplissage/décompression, qui peut donc être supprimé des deux côtés.

Cet article est collecté sur Internet, veuillez indiquer la source lors de la réimpression.

En cas d'infraction, veuillez [email protected] Supprimer.

modifier le
0

laisse moi dire quelques mots

0commentaires
connexionAprès avoir participé à la revue

Articles connexes

TOP liste

  1. 1

    comment afficher un bouton au-dessus d'un autre élément ?

  2. 2

    impossible d'obtenir l'image d'arrière-plan en plein écran dans reactjs

  3. 3

    Je continue à obtenir l'objet 'WSGIRequest' n'a pas d'attribut 'Get' sur django

  4. 4

    comment supprimer "compte de connexion google" à des fins de développement - actions sur google

  5. 5

    Conversion double en BigDecimal en Java

  6. 6

    Impossible d'accéder à la vue personnalisée pendant le test de l'interface utilisateur dans XCode

  7. 7

    Algorithme: diviser de manière optimale une chaîne en 3 sous-chaînes

  8. 8

    Passer la taille d'un tableau 2D à une fonction ?

  9. 9

    Comment obtenir l'intégration contextuelle d'une phrase dans une phrase à l'aide de BERT ?

  10. 10

    Comment changer le navigateur par défaut en Microsoft Edge pour Jupyter Notebook sous Windows 10 ?

  11. 11

    CSS: before ne fonctionne pas sur certains éléments,: after fonctionne très bien

  12. 12

    Comment créer un bot à compte à rebours dans Discord en utilisant Python

  13. 13

    Comment ajouter une entrée à une table de base de données pour une combinaison de deux tables

  14. 14

    Exporter la table de l'arborescence vers CSV avec mise en forme

  15. 15

    Comment activer le message Pylint "too-many-locals" dans VS Code?

  16. 16

    Créer un système Buzzer à l'aide de python

  17. 17

    Spring @RequestParam DateTime format comme ISO 8601 Date Heure facultative

  18. 18

    Empêcher l'allocation de mémoire dans la génération de combinaison récursive

  19. 19

    Déplacement des moindres carrés d'ajustement pour les déplacements de points ayant des problèmes

  20. 20

    Comment choisir le nombre de fragments et de répliques Elasticsearch

  21. 21

    Microsoft.WebApplication.targets

chaudétiquette

Archive