ช่องว่างภายในไม่ถูกต้องและไม่สามารถลบออกได้?


126

ฉันได้ตรวจสอบทางออนไลน์ว่าข้อยกเว้นนี้มีความหมายอย่างไรกับโปรแกรมของฉัน แต่ดูเหมือนจะไม่พบวิธีแก้ปัญหาหรือสาเหตุที่เกิดขึ้นกับโปรแกรมเฉพาะของฉัน ฉันใช้ตัวอย่างที่ให้ msdn เพื่อเข้ารหัสและถอดรหัส XmlDocument โดยใช้อัลกอริทึม Rijndael การเข้ารหัสใช้งานได้ดี แต่เมื่อฉันพยายามถอดรหัสฉันได้รับข้อยกเว้นดังต่อไปนี้:

ช่องว่างภายในไม่ถูกต้องและไม่สามารถลบออกได้

ใครช่วยบอกทีว่าฉันทำอะไรได้บ้างเพื่อแก้ปัญหานี้ รหัสของฉันด้านล่างคือที่ที่ฉันได้รับคีย์และข้อมูลอื่น ๆ หาก cryptoMode เป็นเท็จจะเรียกเมธอดถอดรหัสซึ่งเป็นจุดที่เกิดข้อยกเว้น:

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);

}

12
คุณสามารถลองดูได้ไหมโดยตั้งค่าโหมดช่องว่างภายในให้เหมือนกันทั้งการเข้ารหัสและการถอดรหัสเพื่อให้อยู่ในแนวเยื้อง ตัวอย่างเช่น: alg.Padding = PaddingMode.NONE;
NetSquirrel

วิธีการเข้ารหัส () มีลักษณะอย่างไร
csharptest.net

1
ขอบคุณที่ทำงาน
Brown Love

2
@NetSquirrel: ขอบคุณสำหรับคำเตือนสำหรับ PaddingMode.NONE มันทำให้ฉันหลุดพ้นจากข้อผิดพลาดนี้ (ไปยังอีกข้อหนึ่ง) ... กำลังทำ AES ทั้งใน Java และ C # และตอนนี้ไม่รู้ว่าทำไม C # บ่นเกี่ยวกับ Java padding แม้ว่าทั้งคู่จะใช้ PKCS # 7
Hoàng Long

คำตอบ:


81

Rijndael / AES เป็นบล็อกไซเฟอร์ เข้ารหัสข้อมูลในบล็อก 128 บิต (16 อักขระ) Cryptographic paddingใช้เพื่อให้แน่ใจว่าบล็อกสุดท้ายของข้อความมีขนาดที่ถูกต้องเสมอ

วิธีการถอดรหัสของคุณคาดหวังว่าช่องว่างภายในเริ่มต้นคืออะไรและไม่พบ ดังที่ @NetSquirrel กล่าวว่าคุณต้องตั้งค่าช่องว่างภายในอย่างชัดเจนสำหรับทั้งการเข้ารหัสและการถอดรหัส เว้นเสียแต่ว่าคุณมีเหตุผลที่จะทำอย่างอื่นให้ใช้ช่องว่างภายใน PKCS # 7


7
วิธีตั้งค่า padding อย่างชัดเจน ??
Ahmad Hajjar

7
ขอบคุณฉันพบแล้ว rj.Padding = PaddingMode.none; :)
Ahmad Hajjar

7
@AhmadHajjar ไม่มีช่องว่างภายในมีผลกระทบด้านความปลอดภัยอย่าใช้มัน
deviantfan

1
สวัสดีฉันตั้งค่าช่องว่างภายในอย่างชัดเจน แต่ไม่ได้ผล ฉันไม่รู้ว่าฉันทำผิดขั้นตอนอะไร กรุณาช่วย. alg.Padding = PaddingMode.PKCS7;
จอห์นนี่

21
ฉันรู้ว่านี่เป็นกระทู้เก่า แต่สำหรับผู้ที่มาเยี่ยมชมให้แน่ใจว่าคุณได้ล้างบล็อกสุดท้ายเมื่อเข้ารหัสข้อมูล
Markus

53

ตรวจสอบให้แน่ใจว่าคีย์ที่คุณใช้ในการเข้ารหัสและถอดรหัสเป็นเดียวกัน วิธีการขยายแม้ว่าจะไม่ได้ตั้งค่าไว้อย่างชัดเจนก็ควรอนุญาตให้มีการถอดรหัส / การเข้ารหัสที่เหมาะสม (หากไม่ได้ตั้งค่าไว้ก็จะเหมือนกัน) อย่างไรก็ตามหากคุณใช้ชุดคีย์ในการถอดรหัสที่แตกต่างจากที่ใช้ในการเข้ารหัสด้วยเหตุผลบางประการคุณจะได้รับข้อผิดพลาดนี้:

ช่องว่างภายในไม่ถูกต้องและไม่สามารถลบออกได้

หากคุณกำลังใช้อัลกอริทึมบางอย่างเพื่อสร้างคีย์แบบไดนามิกซึ่งจะไม่ทำงาน ต้องเหมือนกันทั้งการเข้ารหัสและการถอดรหัส วิธีหนึ่งที่พบบ่อยคือให้ผู้เรียกใช้คีย์ในตัวสร้างของคลาสวิธีการเข้ารหัสเพื่อป้องกันไม่ให้กระบวนการเข้ารหัส / ถอดรหัสมีมือใด ๆ ในการสร้างรายการเหล่านี้ จะมุ่งเน้นไปที่งานในมือ (การเข้ารหัสและถอดรหัสข้อมูล) และต้องการivและkeyจัดหาโดยผู้โทร


เคล็ดลับนี้มีประโยชน์มากเพราะบางครั้งคีย์จะถูกเก็บไว้ใน app.config และเราต้องแน่ใจเสมอว่าคีย์ที่ใช้เข้ารหัสนั้นเหมือนกับที่ใช้ในการถอดรหัส
Mário Meyrelles

@atconway คุณช่วยดูคำถามของฉันได้ไหม ฉันมีปัญหาคล้ายกัน แต่ใน C ++ / CLI: stackoverflow.com/questions/57139447/…
ง่าย ๆ

1
ฉันขอแนะนำว่าในการใช้งานประจำวันนี่อาจเป็นสาเหตุที่เป็นไปได้มากที่สุดที่ผู้คนจะพบข้อผิดพลาดนี้ โดยเฉพาะอย่างยิ่งถ้าคุณไม่ยุ่งกับการตั้งค่าช่องว่างภายใน
ด่าน

28

เพื่อประโยชน์ของผู้ที่กำลังค้นหาคุณควรตรวจสอบอินพุตที่ถอดรหัสอยู่ ในกรณีของฉันข้อมูลที่ถูกส่งไปเพื่อถอดรหัสนั้น (ผิด) ไปในรูปแบบสตริงว่างเปล่า ส่งผลให้เกิดข้อผิดพลาดในการขยาย

สิ่งนี้อาจเกี่ยวข้องกับคำตอบของ rossum แต่คิดว่าควรค่าแก่การกล่าวถึง


ฉันเห็นด้วยเกิดขึ้นกับฉันเหมือนกันตรวจสอบข้อมูลที่ถอดรหัสก่อนทำการตรวจสอบอื่น ๆ ฉันได้รับ 1 ไบต์มากกว่าสิ่งที่ฉันอิจฉา ...
Andrea Antonangeli

สตริงว่างเปล่าก็เป็นตัวการสำหรับฉันเช่นกัน
dotNET

กรณีของฉันคือไม่ได้ตั้งค่าข้อความรหัสผ่าน (ใช่ฉันรู้) แต่คำตอบนี้ทำให้ฉันไปถูกทาง
จิม

2
ปัญหาของฉันคือสตริงที่จะถอดรหัสถูกแปลงเป็นตัวพิมพ์เล็กก่อนที่ฉันจะพยายามถอดรหัส ฉันหมกมุ่นอยู่กับช่องว่างภายในและการเข้ารหัสและทุกสิ่งนั้น แต่กลับกลายเป็นว่ามันเป็นข้อมูลที่ไม่ดี บางครั้งคุณก็ต้องถอยหลัง!
Tom Gerken

15

หากใช้คีย์และเวกเตอร์เริ่มต้นเดียวกันในการเข้ารหัสและถอดรหัสปัญหานี้ไม่ได้มาจากการถอดรหัสข้อมูล แต่มาจากการเข้ารหัสข้อมูล

หลังจากที่คุณเรียกวิธีการเขียนบนวัตถุ CryptoStream คุณต้องเรียกใช้เมธอด FlushFinalBlock ก่อนปิดเมธอดเสมอ

เอกสาร MSDN บนเมธอด CryptoStream.FlushFinalBlock กล่าวว่า:
"การเรียกใช้เมธอด Close จะเรียก FlushFinalBlock ... "
https://msdn.microsoft.com/en-US/library/system.security.cryptography.cryptostream.flushfinalblock(v=vs .110) .aspx
นี่ไม่ถูกต้อง การเรียกใช้วิธีปิดเพียงแค่ปิด CryptoStream และเอาต์พุตสตรีม
หากคุณไม่เรียกใช้ FlushFinalBlock ก่อนปิดหลังจากที่คุณเขียนข้อมูลที่จะเข้ารหัสเมื่อถอดรหัสข้อมูลการเรียกใช้เมธอด Read หรือ CopyTo บนวัตถุ CryptoStream ของคุณจะเพิ่มข้อยกเว้น CryptographicException (ข้อความ: "Padding ไม่ถูกต้องและไม่สามารถลบออกได้")

นี่อาจเป็นจริงสำหรับอัลกอริธึมการเข้ารหัสทั้งหมดที่ได้มาจาก SymmetricAlgorithm (Aes, DES, RC2, Rijndael, TripleDES) แม้ว่าฉันเพิ่งตรวจสอบว่าสำหรับ AesManaged และ MemoryStream เป็นสตรีมเอาต์พุต

ดังนั้นหากคุณได้รับข้อยกเว้น CryptographicException เกี่ยวกับการถอดรหัสให้อ่านค่าคุณสมบัติ Stream Length เอาต์พุตของคุณหลังจากที่คุณเขียนข้อมูลของคุณที่จะเข้ารหัสแล้วเรียก FlushFinalBlock และอ่านค่าอีกครั้ง หากมีการเปลี่ยนแปลงคุณจะทราบว่าการเรียก FlushFinalBlock ไม่ใช่ทางเลือก

และคุณไม่จำเป็นต้องดำเนินการ padding ใด ๆ โดยทางโปรแกรมหรือเลือกค่าคุณสมบัติ Padding อื่น Padding คืองานเมธอด FlushFinalBlock

.........

ข้อสังเกตเพิ่มเติมสำหรับ Kevin:

ใช่ CryptoStream เรียก FlushFinalBlock ก่อนที่จะเรียก Close แต่มันสายเกินไป: เมื่อเรียกใช้เมธอด CryptoStream Close สตรีมเอาต์พุตจะถูกปิดด้วย

หากสตรีมเอาต์พุตของคุณเป็น MemoryStream คุณจะไม่สามารถอ่านข้อมูลได้หลังจากปิดไปแล้ว ดังนั้นคุณต้องเรียก FlushFinalBlock บน CryptoStream ของคุณก่อนที่จะใช้ข้อมูลเข้ารหัสที่เขียนบน MemoryStream

หากสตรีมเอาต์พุตของคุณเป็น FileStream สิ่งต่างๆจะแย่ลงเนื่องจากการเขียนถูกบัฟเฟอร์ ผลที่ตามมาคือไบต์ที่เขียนครั้งสุดท้ายอาจไม่ถูกเขียนลงในไฟล์หากคุณปิดสตรีมเอาต์พุตก่อนที่จะเรียกใช้ Flush บน FileStream ดังนั้นก่อนที่จะเรียกปิดบน CryptoStream ก่อนอื่นคุณต้องเรียก FlushFinalBlock บน CryptoStream ของคุณจากนั้นเรียก Flush บน FileStream ของคุณ


1
ทำไมถึงบอกว่าผิด รหัสสำหรับการโทรStream.Close() this.Dispose(true)รหัสสำหรับCryptoStream.Dispose(bool)คือ:if (disposing) { if (!this._finalBlockTransformed) { this.FlushFinalBlock(); } this._stream.Close(); }
Kevin Doyon

1
วิธีนี้ช่วยแก้ปัญหาของฉันได้ ฉันกำลังกำจัด cryptoStream อย่างถูกต้อง แต่การโทรกำจัดเกิดขึ้น "สายเกินไป" อย่างที่คุณพูด ซึ่งส่งผลให้เกิดข้อผิดพลาด "ช่องว่างภายในไม่ถูกต้อง" ตามที่อธิบายไว้ เมื่อเพิ่ม cryptoStream.FlushFinalBlock () ข้อผิดพลาดของช่องว่างภายในที่ไม่ถูกต้องได้รับการแก้ไข ขอบคุณ!
Daniel Lambert

14

ครั้งหนึ่งของการต่อสู้ในที่สุดฉันก็แก้ปัญหาได้
(หมายเหตุ: ฉันใช้ AES มาตรฐานเป็นอัลกอริทึมสมมาตรคำตอบนี้อาจไม่เหมาะสำหรับทุกคน)

  1. เปลี่ยนคลาสอัลกอริทึม แทนที่RijndaelManagedชั้นเรียนเป็นAESManagedหนึ่ง
  2. อย่าตั้งค่าKeySizeคลาสของอัลกอริทึมอย่างชัดเจนปล่อยให้เป็นค่าเริ่มต้น
    (นี่เป็นขั้นตอนที่สำคัญมากฉันคิดว่ามีจุดบกพร่องในคุณสมบัติ KeySize)

นี่คือรายการที่คุณต้องการตรวจสอบว่าคุณอาจพลาดอาร์กิวเมนต์ใด:

  • คีย์
    (อาร์เรย์ไบต์ความยาวต้องเป็นหนึ่งใน 16, 24, 32 ไบต์สำหรับขนาดคีย์ที่ต่างกัน)
  • IV
    (อาร์เรย์ไบต์ 16 ไบต์)
  • CipherMode
    (หนึ่งใน CBC, CFB, CTS, ECB, OFB)
  • PaddingMode
    (หนึ่งใน ANSIX923, ISO10126, ไม่มี, PKCS7, ศูนย์)

3
ไม่ได้ตั้งค่าไว้อย่างชัดเจนKeySizeสำหรับฉันในทันที โอ้นิสัยของ. NET :-(
ยอห์น

โปรดสังเกตว่านี่เป็นการถดถอยใน. NET Framework เอง ฉันมีรหัสที่เคยทำงานกับ RijndaelManaged แต่หยุดทำงานและเพียงแค่เปลี่ยนเป็น AesManaged / AesCryptoServiceProvider ก็ใช้งานได้อีกครั้ง ฉันไม่ได้มีรหัสที่ชัดเจนในการตั้งค่า KeySize ดังนั้นหากคุณโดนสิ่งนี้รู้สึกดีขึ้น - ความผิดอาจไม่ได้อยู่กับคุณ แต่ด้วย. NET Framework เอง
Usas

6

ปัญหาของฉันคือ passPhrase ของการเข้ารหัสไม่ตรงกับ passPhrase ของถอดรหัส ... ดังนั้นจึงทำให้เกิดข้อผิดพลาดนี้ .. ทำให้เข้าใจผิดเล็กน้อย


จริงๆแล้วมันเป็นความจริงเราใช้ PaddingMode.PKCS7 สำหรับการเข้ารหัสและถอดรหัส แต่ฉันได้รับข้อความแสดงข้อผิดพลาดเดียวกัน นอกจากนี้เรายังมีสภาพแวดล้อม Stage และ Dev ที่มีค่าคีย์ที่แตกต่างกัน เมื่อฉันใช้ - เงื่อนไขเฉพาะที่เหมาะสม - คีย์ข้อยกเว้นนี้ได้รับการแก้ไข ...
เมเจอร์

แม้ว่าคำตอบข้างต้นทั้งหมดจะดีและคุณต้องใช้ช่องว่างภายในเดียวกันสำหรับเข้ารหัสและถอดรหัส (ไม่แนะนำให้ใช้!) อันที่จริงคำตอบนี้ก็เป็นจริงได้เช่นกัน เมื่อฉันใช้ - สภาพแวดล้อมเฉพาะที่เหมาะสม - คีย์ข้อยกเว้น "System.Security.Cryptography.CryptographicException: Padding ไม่ถูกต้องและไม่สามารถลบออกได้" ได้รับการแก้ไข ใช่แล้วมันอาจทำให้เข้าใจผิดได้
เมเจอร์

หากโดย "passPhrase" คุณกำลังพูดถึงค่าที่แน่นอนในการเข้ารหัส / ถอดรหัส (ไม่ใช่ปัญหาในการใช้คีย์ผิด) ใช่นี่เป็นปัญหาของฉัน กรณีของฉันคือค่าที่เข้ารหัสดั้งเดิมนั้นยาวเกินกว่าที่ช่องตารางฐานข้อมูลของฉันอนุญาตดังนั้นจึงถูกตัดทอนให้พอดีโดยที่ฉันไม่รู้ตัว จากนั้นเมื่อถอดรหัสค่าที่ถูกตัดทอนข้อยกเว้นนี้ถูกโยนทิ้ง
David Gunderson

2

วิธีแก้ปัญหาของฉันคือการที่ฉันใช้คีย์ที่แตกต่างกันในวิธีการเข้ารหัสและถอดรหัสโดยไม่ได้ตั้งใจ


1

ฉันเจอข้อผิดพลาดนี้ขณะพยายามส่งเส้นทางไฟล์ที่ไม่ได้เข้ารหัสไปยังเมธอด Decrypt วิธีแก้ปัญหาคือตรวจสอบว่าไฟล์ที่ส่งผ่านถูกเข้ารหัสก่อนหรือไม่ก่อนที่จะพยายามถอดรหัส

if (Sec.IsFileEncrypted(e.File.FullName))
{
    var stream = Sec.Decrypt(e.File.FullName);
} 
else
{
    // non-encrypted scenario  
}

1
ฉันท้าทาย Hit and Run Coward ใด ๆ เกี่ยวกับความถูกต้องของโซลูชันนี้
ประโยชน์

+1 เนื่องจากข้อยกเว้นนี้เกิดขึ้นเมื่อคุณถอดรหัสสองครั้งหรือคุณถอดรหัสบางอย่างที่ไม่ได้เข้ารหัส ฉันจึงอ่านคำตอบนี้ว่า "คุณแน่ใจหรือไม่ว่าข้อมูลถูกเข้ารหัสจริง"
Gerardo Grignoli

0

สถานการณ์อื่นอีกครั้งเพื่อประโยชน์ของผู้ที่ค้นหา

สำหรับฉันข้อผิดพลาดนี้เกิดขึ้นระหว่างเมธอด Dispose () ซึ่งปิดบังข้อผิดพลาดก่อนหน้านี้ที่ไม่เกี่ยวข้องกับการเข้ารหัส

เมื่อแก้ไขส่วนประกอบอื่นแล้วข้อยกเว้นนี้ก็หายไป


3
ข้อผิดพลาดก่อนหน้านี้ไม่เกี่ยวข้องกับการเข้ารหัสอะไร
NStuke

0

ฉันพบข้อผิดพลาดในการเพิ่มช่องว่างนี้เมื่อฉันแก้ไขสตริงที่เข้ารหัสในไฟล์ด้วยตนเอง (โดยใช้ notepad) เพราะฉันต้องการทดสอบว่าฟังก์ชันถอดรหัสจะทำงานอย่างไรหากเนื้อหาที่เข้ารหัสของฉันถูกแก้ไขด้วยตนเอง

ทางออกสำหรับฉันคือการวางไฟล์

        try
            decryption stuff....
        catch
             inform decryption will not be carried out.
        end try

เช่นเดียวกับที่ฉันบอกว่าข้อผิดพลาดในการเติมของฉันเป็นเพราะฉันพิมพ์ข้อความที่ถอดรหัสด้วยตนเองโดยใช้แผ่นจดบันทึก คำตอบของฉันอาจเป็นแนวทางให้คุณแก้ปัญหาได้


0

ฉันมีข้อผิดพลาดเดียวกัน ในกรณีของฉันเป็นเพราะฉันเก็บข้อมูลที่เข้ารหัสไว้ในฐานข้อมูล SQL ตารางที่เก็บข้อมูลมีชนิดข้อมูลไบนารี (1,000) เมื่อเรียกคืนข้อมูลจากฐานข้อมูลข้อมูลจะถอดรหัส 1,000 ไบต์เหล่านี้ในขณะที่มี 400 ไบต์ ดังนั้นการลบศูนย์ต่อท้าย (600) ออกจากผลลัพธ์มันช่วยแก้ปัญหาได้



0

ฉันมีปัญหาเดียวกันกับการพยายามพอร์ตโปรแกรม Go ไปที่ C # ซึ่งหมายความว่าข้อมูลจำนวนมากได้รับการเข้ารหัสด้วยโปรแกรม Go แล้ว ตอนนี้ข้อมูลนี้ต้องถอดรหัสด้วย C #

ทางออกสุดท้ายคือPaddingMode.Noneหรือมากกว่าPaddingMode.Zerosนั้น

วิธีการเข้ารหัสใน 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
}

... และ ...

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
    }
}

ตอนนี้การถอดรหัสใน 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;
}

จะอธิบายปัญหาเกี่ยวกับช่องว่างภายในได้อย่างไร? ก่อนเข้ารหัสโปรแกรม Go จะตรวจสอบช่องว่างภายใน:

// 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
}

ส่วนสำคัญคือ:

enhanced := make([]byte, numberNecessaryBlocks*aes.BlockSize)
copy(enhanced, secretBytesDecrypted)

อาร์เรย์ใหม่ถูกสร้างขึ้นด้วยความยาวที่เหมาะสมเพื่อให้ความยาวเป็นผลคูณของขนาดบล็อก อาร์เรย์ใหม่นี้เต็มไปด้วยศูนย์ วิธีการคัดลอกจะคัดลอกข้อมูลที่มีอยู่ลงไป มั่นใจได้ว่าอาร์เรย์ใหม่มีขนาดใหญ่กว่าข้อมูลที่มีอยู่ ดังนั้นจึงมีเลขศูนย์ที่ท้ายอาร์เรย์

ดังนั้นรหัส C # สามารถPaddingMode.Zerosใช้ได้ อีกทางเลือกหนึ่งPaddingMode.Noneเพียงแค่ละเว้นช่องว่างภายในใด ๆ ซึ่งก็ใช้ได้เช่นกัน ฉันหวังว่าคำตอบนี้จะเป็นประโยชน์สำหรับทุกคนที่ต้องพอร์ตโค้ดจาก Go to C # เป็นต้น


0

ฉันได้รับรายงานข้อผิดพลาดเดียวกันโดยลูกค้า ฉันเองไม่สามารถตำหนิได้ มองไปที่รหัสของการเข้ารหัสและถอดรหัสวิธีการทั้งสองมีPaddingชุดPaddingMode.PKCS7 Decryptมีลักษณะเช่นนี้และฉันไม่เห็นปัญหาเกี่ยวกับ ' FlushFinalBlock ' มีใครช่วยส่องไฟให้หน่อยได้ไหม

public string Decrypt(string cipherText)
{
  if (string.IsNullOrEmpty(cipherText))
    return "";
  string result;
  Encoding byteEncoder = Encoding.Default;

  byte[] rijnKey = byteEncoder.GetBytes(Password);
  byte[] rijnIv = byteEncoder.GetBytes(InitialVector);
  RijndaelManaged rijn = new RijndaelManaged { Mode = CipherMode.ECB, Padding = PaddingMode.PKCS7 };

  using (MemoryStream msDecrypt = new MemoryStream(Convert.FromBase64String(cipherText)))
  {
    using (ICryptoTransform decryptor = rijn.CreateDecryptor(rijnKey, rijnIv))
    {
      using (CryptoStream csDecrypt = new CryptoStream(msDecrypt, decryptor, CryptoStreamMode.Read))
      {
                    using (StreamReader swDecrypt = new StreamReader(csDecrypt))
                    {
                        result = swDecrypt.ReadToEnd();
                    }
                }
    }
  }
  rijn.Clear();      
  return result.Replace("\0", "");
}

0

ฉันมีข้อผิดพลาดเดียวกัน ในกรณีของฉันรหัสผ่านที่กำหนดมากกว่า 16 หมายความว่ามีการเข้ารหัส แต่ในขณะที่ถอดรหัสฉันได้รับข้อผิดพลาดนี้ การเข้ารหัสลับ:

string keyString = "CDFUYP@ssw0rd123";
            var key = Encoding.UTF8.GetBytes(keyString);            
            using (var aesAlg = Aes.Create())
            {
                using (var encryptor = aesAlg.CreateEncryptor(key, aesAlg.IV))
                {
                    using (var msEncrypt = new MemoryStream())
                    {
                        using (var csEncrypt = new CryptoStream(msEncrypt, encryptor, CryptoStreamMode.Write))
                        using (var swEncrypt = new StreamWriter(csEncrypt))
                        {
                            swEncrypt.Write(text);
                        }                          
                        var iv = aesAlg.IV;

                        var decryptedContent = msEncrypt.ToArray();

                        var result = new byte[iv.Length + decryptedContent.Length];

                        Buffer.BlockCopy(iv, 0, result, 0, iv.Length);
                        Buffer.BlockCopy(decryptedContent, 0, result, iv.Length, decryptedContent.Length);

                        var encryptedString = Convert.ToBase64String(result);
                        var decryptedString = Decrypt(encryptedString);
                        if (decryptedString == null)
                        {
                            return null;
                        }
                        return encryptedString;

                    }
                }

ถอดรหัส:

 string keyString = "CDFUYP@ssw0rd123";
            var fullCipher = Convert.FromBase64String(cipherText);
            var iv = new byte[16];
            var cipher = new byte[16];
            Buffer.BlockCopy(fullCipher, 0, iv, 0, iv.Length);
            Buffer.BlockCopy(fullCipher, iv.Length, cipher, 0, iv.Length);
            var key = Encoding.UTF8.GetBytes(keyString);

            using (var aesAlg = Aes.Create())
            {
                using (var decryptor = aesAlg.CreateDecryptor(key, iv))
                {
                    string result;
                    using (var msDecrypt = new MemoryStream(cipher))
                    {
                        using (var csDecrypt = new CryptoStream(msDecrypt, decryptor, CryptoStreamMode.Read))
                        {
                            using (var srDecrypt = new StreamReader(csDecrypt))
                            {
                                result = srDecrypt.ReadToEnd();
                            }
                        }
                    }

                    return result;
                }
            }

สวัสดี @sundarraj นี่คือคำถาม?
Tiago Martins Peres 李大仁
โดยการใช้ไซต์ของเรา หมายความว่าคุณได้อ่านและทำความเข้าใจนโยบายคุกกี้และนโยบายความเป็นส่วนตัวของเราแล้ว
Licensed under cc by-sa 3.0 with attribution required.