J'ai cherché en ligne ce que cette exception signifie par rapport à mon programme, mais je n'arrive pas à trouver une solution ou la raison pour laquelle cela se produit dans mon programme spécifique. J'ai utilisé l'exemple fourni mon msdn pour chiffrer et déchiffrer un document XmlDocument à l'aide de l'algorithme Rijndael. Le chiffrement fonctionne bien, mais lorsque j'essaie de déchiffrer, j'obtiens l'exception suivante:
Le rembourrage est invalide et ne peut pas être enlevé
Quelqu'un peut-il me dire ce que je peux faire pour résoudre ce problème? Mon code ci-dessous est l'endroit où j'obtiens la clé et d'autres données. Si le cryptoMode est faux, il appellera la méthode de déchiffrement, à l'endroit où l'exception se produit:
public void Cryptography(XmlDocument doc, bool cryptographyMode)
{
RijndaelManaged key = null;
try
{
// Create a new Rijndael key.
key = new RijndaelManaged();
const string passwordBytes = "Password1234"; //password here
byte[] saltBytes = Encoding.UTF8.GetBytes("SaltBytes");
Rfc2898DeriveBytes p = new Rfc2898DeriveBytes(passwordBytes, saltBytes);
// sizes are devided by 8 because [ 1 byte = 8 bits ]
key.IV = p.GetBytes(key.BlockSize/8);
key.Key = p.GetBytes(key.KeySize/8);
if (cryptographyMode)
{
Ecrypt(doc, "Content", key);
}
else
{
Decrypt(doc, key);
}
}
catch (Exception ex)
{
MessageBox.Show(ex.Message);
}
finally
{
// Clear the key.
if (key != null)
{
key.Clear();
}
}
}
private void Decrypt(XmlDocument doc, SymmetricAlgorithm alg)
{
// Check the arguments.
if (doc == null)
throw new ArgumentNullException("Doc");
if (alg == null)
throw new ArgumentNullException("alg");
// Find the EncryptedData element in the XmlDocument.
XmlElement encryptedElement = doc.GetElementsByTagName("EncryptedData")[0] as XmlElement;
// If the EncryptedData element was not found, throw an exception.
if (encryptedElement == null)
{
throw new XmlException("The EncryptedData element was not found.");
}
// Create an EncryptedData object and populate it.
EncryptedData edElement = new EncryptedData();
edElement.LoadXml(encryptedElement);
// Create a new EncryptedXml object.
EncryptedXml exml = new EncryptedXml();
// Decrypt the element using the symmetric key.
byte[] rgbOutput = exml.DecryptData(edElement, alg); <---- I GET THE EXCEPTION HERE
// Replace the encryptedData element with the plaintext XML element.
exml.ReplaceData(encryptedElement, rgbOutput);
}
Rijndael/AES est un chiffrement de bloc. Il chiffre les données en blocs de 128 bits (16 caractères). Remplissage cryptographique permet de s'assurer que le dernier bloc du message a toujours la taille correcte.
Votre méthode de déchiffrement attend quel que soit son remplissage par défaut et ne le trouve pas. Comme @NetSquirrel le dit, vous devez définir explicitement le remplissage pour le cryptage et le décryptage. Sauf si vous avez une raison de faire autrement, utilisez le remplissage PKCS # 7.
Assurez-vous que les clés que vous utilisez pour chiffrer et déchiffrer sont - le même. La méthode de remplissage, même si elle n’est pas explicitement définie, doit permettre un déchiffrement/chiffrement correct (sinon, ils seront identiques). Toutefois, si, pour une raison quelconque, vous utilisez un ensemble de clés de décryptage différent de celui utilisé pour le cryptage, vous obtenez l'erreur suivante:
Le rembourrage est invalide et ne peut pas être enlevé
Si vous utilisez un algorithme pour générer dynamiquement des clés qui ne fonctionneront pas. Ils doivent être identiques pour le cryptage et le décryptage. Une méthode courante consiste à demander aux appelants de fournir les clés dans le constructeur de la classe de méthodes de chiffrement, afin d’empêcher le processus de chiffrement/déchiffrement d’avoir une main dans la création de ces éléments. Il se concentre sur la tâche à accomplir (chiffrement et déchiffrement des données) et nécessite que les variables iv
et key
soient fournies par l'appelant.
Pour le bénéfice des personnes cherchant, il peut être intéressant de vérifier l'entrée en cours de décryptage. Dans mon cas, les informations envoyées pour le décryptage entraient (à tort) sous forme de chaîne vide. Cela a entraîné une erreur de remplissage.
Cela peut être lié à la réponse de rossum, mais je pense que cela mérite d'être mentionné.
Un temps de combat, j’ai finalement résolu le problème.
(Remarque: J'utilise AES standard comme algorithme symétrique. Cette réponse pourrait ne pas convenir À tout le monde.)
RijndaelManaged
par la variable AESManaged
. KeySize
de la classe d'algorithme, les a laissés par défaut.Voici une liste que vous voulez vérifier quel argument vous avez peut-être manqué:
Si la même clé et le même vecteur d’initialisation sont utilisés pour le codage et le décodage, ce problème ne provient pas du décodage de données, mais du codage de données.
Après avoir appelé la méthode Write sur un objet CryptoStream, vous devez TOUJOURS appeler la méthode FlushFinalBlock avant la méthode Close.
La documentation MSDN sur la méthode CryptoStream.FlushFinalBlock indique:
"L'appel de la méthode Close appellera FlushFinalBlock ..."
https://msdn.Microsoft.com/en-US/library/system.security.cryptography.cryptostream.flushfinalblock(v=vs.110).aspx
C'est faux. L'appel de la méthode Close ferme simplement CryptoStream et le flux de sortie.
Si vous n'appelez pas FlushFinalBlock avant Close après avoir écrit les données à chiffrer, lors du déchiffrement des données, un appel de la méthode Read ou CopyTo sur votre objet CryptoStream déclenche une exception CryptographicException (message: "Le remplissage est invalide et ne peut pas être supprimé. ").
Ceci est probablement vrai pour tous les algorithmes de chiffrement dérivés de SymmetricAlgorithm (Aes, DES, RC2, Rijndael, TripleDES), bien que je l’aie juste vérifiée pour AesManaged et un MemoryStream en tant que flux de sortie.
Ainsi, si vous recevez cette exception CryptographicException lors du déchiffrement, lisez la valeur de votre propriété de longueur de flux en sortie après avoir écrit vos données à chiffrer, puis appelez FlushFinalBlock et relisez sa valeur. Si cela a changé, vous savez que l'appel de FlushFinalBlock n'est PAS facultatif.
Et vous n'avez pas besoin d'effectuer de remplissage par programmation ni de choisir une autre valeur de propriété Padding. Le rembourrage est un travail de méthode FlushFinalBlock.
.........
Remarque supplémentaire pour Kevin:
Oui, CryptoStream appelle FlushFinalBlock avant d'appeler Close, mais il est trop tard: lorsque la méthode CryptoStream Close est appelée, le flux de sortie est également fermé.
Si votre flux de sortie est un MemoryStream, vous ne pouvez pas lire ses données après sa fermeture. Vous devez donc appeler FlushFinalBlock sur votre CryptoStream avant d'utiliser les données cryptées écrites sur le MemoryStream.
Si votre flux de sortie est un FileStream, la situation est pire car l'écriture est mise en tampon. La conséquence est que les derniers octets écrits peuvent ne pas être écrits dans le fichier si vous fermez le flux de sortie avant d'appeler Flush sur FileStream. Par conséquent, avant d'appeler Close sur CryptoStream, vous devez d'abord appeler FlushFinalBlock sur votre CryptoStream, puis Flush sur votre FileStream.
Mon problème était que le mot de passe passPhrase du cryptage ne correspond pas au mot de passe crypté du déchiffrement ... il a donc lancé cette erreur ... un peu trompeur.
La solution qui a corrigé le mien était que j'avais par inadvertance appliqué différentes clés aux méthodes de cryptage et de décryptage.
J'ai rencontré cette erreur de remplissage lorsque je souhaitais modifier manuellement les chaînes cryptées du fichier (à l'aide du bloc-notes) parce que je voulais tester le comportement de la fonction de décryptage si mon contenu crypté était modifié manuellement.
La solution pour moi était de placer un
try
decryption stuff....
catch
inform decryption will not be carried out.
end try
Comme je l'ai dit, mon erreur de remplissage était due au fait que je tapais manuellement sur le texte déchiffré à l'aide du bloc-notes. Peut-être que ma réponse peut vous guider vers votre solution.
J'ai eu le même problème en essayant de porter un programme Go en C #. Cela signifie que beaucoup de données ont déjà été cryptées avec le programme Go. Ces données doivent maintenant être déchiffrées avec C #.
La solution finale était PaddingMode.None
ou plutôt PaddingMode.Zeros
.
Les méthodes cryptographiques dans Go:
import (
"crypto/aes"
"crypto/cipher"
"crypto/sha1"
"encoding/base64"
"io/ioutil"
"log"
"golang.org/x/crypto/pbkdf2"
)
func decryptFile(filename string, saltBytes []byte, masterPassword []byte) (artifact string) {
const (
keyLength int = 256
rfc2898Iterations int = 6
)
var (
encryptedBytesBase64 []byte // The encrypted bytes as base64 chars
encryptedBytes []byte // The encrypted bytes
)
// Load an encrypted file:
if bytes, bytesErr := ioutil.ReadFile(filename); bytesErr != nil {
log.Printf("[%s] There was an error while reading the encrypted file: %s\n", filename, bytesErr.Error())
return
} else {
encryptedBytesBase64 = bytes
}
// Decode base64:
decodedBytes := make([]byte, len(encryptedBytesBase64))
if countDecoded, decodedErr := base64.StdEncoding.Decode(decodedBytes, encryptedBytesBase64); decodedErr != nil {
log.Printf("[%s] An error occur while decoding base64 data: %s\n", filename, decodedErr.Error())
return
} else {
encryptedBytes = decodedBytes[:countDecoded]
}
// Derive key and vector out of the master password and the salt cf. RFC 2898:
keyVectorData := pbkdf2.Key(masterPassword, saltBytes, rfc2898Iterations, (keyLength/8)+aes.BlockSize, sha1.New)
keyBytes := keyVectorData[:keyLength/8]
vectorBytes := keyVectorData[keyLength/8:]
// Create an AES cipher:
if aesBlockDecrypter, aesErr := aes.NewCipher(keyBytes); aesErr != nil {
log.Printf("[%s] Was not possible to create new AES cipher: %s\n", filename, aesErr.Error())
return
} else {
// CBC mode always works in whole blocks.
if len(encryptedBytes)%aes.BlockSize != 0 {
log.Printf("[%s] The encrypted data's length is not a multiple of the block size.\n", filename)
return
}
// Reserve memory for decrypted data. By definition (cf. AES-CBC), it must be the same lenght as the encrypted data:
decryptedData := make([]byte, len(encryptedBytes))
// Create the decrypter:
aesDecrypter := cipher.NewCBCDecrypter(aesBlockDecrypter, vectorBytes)
// Decrypt the data:
aesDecrypter.CryptBlocks(decryptedData, encryptedBytes)
// Cast the decrypted data to string:
artifact = string(decryptedData)
}
return
}
... et ...
import (
"crypto/aes"
"crypto/cipher"
"crypto/sha1"
"encoding/base64"
"github.com/twinj/uuid"
"golang.org/x/crypto/pbkdf2"
"io/ioutil"
"log"
"math"
"os"
)
func encryptFile(filename, artifact string, masterPassword []byte) (status bool) {
const (
keyLength int = 256
rfc2898Iterations int = 6
)
status = false
secretBytesDecrypted := []byte(artifact)
// Create new salt:
saltBytes := uuid.NewV4().Bytes()
// Derive key and vector out of the master password and the salt cf. RFC 2898:
keyVectorData := pbkdf2.Key(masterPassword, saltBytes, rfc2898Iterations, (keyLength/8)+aes.BlockSize, sha1.New)
keyBytes := keyVectorData[:keyLength/8]
vectorBytes := keyVectorData[keyLength/8:]
// Create an AES cipher:
if aesBlockEncrypter, aesErr := aes.NewCipher(keyBytes); aesErr != nil {
log.Printf("[%s] Was not possible to create new AES cipher: %s\n", filename, aesErr.Error())
return
} else {
// CBC mode always works in whole blocks.
if len(secretBytesDecrypted)%aes.BlockSize != 0 {
numberNecessaryBlocks := int(math.Ceil(float64(len(secretBytesDecrypted)) / float64(aes.BlockSize)))
enhanced := make([]byte, numberNecessaryBlocks*aes.BlockSize)
copy(enhanced, secretBytesDecrypted)
secretBytesDecrypted = enhanced
}
// Reserve memory for encrypted data. By definition (cf. AES-CBC), it must be the same lenght as the plaintext data:
encryptedData := make([]byte, len(secretBytesDecrypted))
// Create the encrypter:
aesEncrypter := cipher.NewCBCEncrypter(aesBlockEncrypter, vectorBytes)
// Encrypt the data:
aesEncrypter.CryptBlocks(encryptedData, secretBytesDecrypted)
// Encode base64:
encodedBytes := make([]byte, base64.StdEncoding.EncodedLen(len(encryptedData)))
base64.StdEncoding.Encode(encodedBytes, encryptedData)
// Allocate memory for the final file's content:
fileContent := make([]byte, len(saltBytes))
copy(fileContent, saltBytes)
fileContent = append(fileContent, 10)
fileContent = append(fileContent, encodedBytes...)
// Write the data into a new file. This ensures, that at least the old version is healthy in case that the
// computer hangs while writing out the file. After a successfully write operation, the old file could be
// deleted and the new one could be renamed.
if writeErr := ioutil.WriteFile(filename+"-update.txt", fileContent, 0644); writeErr != nil {
log.Printf("[%s] Was not able to write out the updated file: %s\n", filename, writeErr.Error())
return
} else {
if renameErr := os.Rename(filename+"-update.txt", filename); renameErr != nil {
log.Printf("[%s] Was not able to rename the updated file: %s\n", fileContent, renameErr.Error())
} else {
status = true
return
}
}
return
}
}
Maintenant, décryptage en C #:
public static string FromFile(string filename, byte[] saltBytes, string masterPassword)
{
var iterations = 6;
var keyLength = 256;
var blockSize = 128;
var result = string.Empty;
var encryptedBytesBase64 = File.ReadAllBytes(filename);
// bytes -> string:
var encryptedBytesBase64String = System.Text.Encoding.UTF8.GetString(encryptedBytesBase64);
// Decode base64:
var encryptedBytes = Convert.FromBase64String(encryptedBytesBase64String);
var keyVectorObj = new Rfc2898DeriveBytes(masterPassword, saltBytes.Length, iterations);
keyVectorObj.Salt = saltBytes;
Span<byte> keyVectorData = keyVectorObj.GetBytes(keyLength / 8 + blockSize / 8);
var key = keyVectorData.Slice(0, keyLength / 8);
var iv = keyVectorData.Slice(keyLength / 8);
var aes = Aes.Create();
aes.Padding = PaddingMode.Zeros;
// or ... aes.Padding = PaddingMode.None;
var decryptor = aes.CreateDecryptor(key.ToArray(), iv.ToArray());
var decryptedString = string.Empty;
using (var memoryStream = new MemoryStream(encryptedBytes))
{
using (var cryptoStream = new CryptoStream(memoryStream, decryptor, CryptoStreamMode.Read))
{
using (var reader = new StreamReader(cryptoStream))
{
decryptedString = reader.ReadToEnd();
}
}
}
return result;
}
Comment expliquer le problème du rembourrage? Juste avant le chiffrement, le programme Go vérifie le remplissage:
// CBC mode always works in whole blocks.
if len(secretBytesDecrypted)%aes.BlockSize != 0 {
numberNecessaryBlocks := int(math.Ceil(float64(len(secretBytesDecrypted)) / float64(aes.BlockSize)))
enhanced := make([]byte, numberNecessaryBlocks*aes.BlockSize)
copy(enhanced, secretBytesDecrypted)
secretBytesDecrypted = enhanced
}
La partie importante est la suivante:
enhanced := make([]byte, numberNecessaryBlocks*aes.BlockSize)
copy(enhanced, secretBytesDecrypted)
Un nouveau tableau est créé avec une longueur appropriée, de sorte que la longueur est un multiple de la taille du bloc. Ce nouveau tableau est rempli de zéros. La méthode de copie copie ensuite les données existantes dans celle-ci. Il est garanti que le nouveau tableau est plus grand que les données existantes. En conséquence, il y a des zéros à la fin du tableau.
Ainsi, le code C # peut utiliser PaddingMode.Zeros
. L'alternative PaddingMode.None
ignore simplement tout remplissage, ce qui fonctionne également. J'espère que cette réponse sera utile à quiconque doit transférer le code de Go to C #, etc.
J'ai eu cette erreur et définissait explicitement la taille de bloc: aesManaged.BlockSize = 128;
Une fois que j'ai enlevé ça, ça a fonctionné.
J'ai eu la même erreur. Dans mon cas, c’est parce que j’ai stocké les données chiffrées dans une base de données SQL. La table dans laquelle les données sont stockées a un type de données binaire (1000). Lors de la récupération des données de la base de données, il déchiffrerait ces 1000 octets, alors qu’il en existait 400 octets. Donc, en supprimant le zéro final (600) du résultat, le problème a été résolu.
Je suis tombé sur cette erreur en essayant de transmettre un chemin de fichier non chiffré à la méthode Decrypt. La solution consistait à vérifier si le fichier transmis était chiffré avant de tenter de déchiffrer
if (Sec.IsFileEncrypted(e.File.FullName))
{
var stream = Sec.Decrypt(e.File.FullName);
}
else
{
// non-encrypted scenario
}
Un autre scénario, encore une fois pour le bénéfice des personnes cherchant.
Pour moi, cette erreur s'est produite lors de la méthode Dispose () qui masquait une erreur précédente non liée au cryptage.
Une fois que l'autre composant a été corrigé, cette exception a disparu.