ערכי גיבוב שחושבו מראש חתימות RSA ומאפיינים חתומים ב־CMS
הקוד להמחשה ולא הורץ.
המקור האנגלי פורסם בתאריך 2022-09-01T12:41:00.005+03:00. המאמר המקורי באנגלית
המאמר הארכיוני מתאים דוגמת Java ו־Bouncy Castle לחותם ב־.NET המשתמש בתעודה. יש להפריד בין שני ערכי גיבוב: גיבוב התוכן, וכאשר קיימים מאפיינים חתומים ב־CMS, גיבוב קידוד ה־DER שלהם. קריאה ל־SignHash על גיבוב התוכן יוצרת חתימת RSA גולמית; כשלעצמה היא אינה יוצרת אובייקט CMS SignedData. [1][2]
חתימה גולמית על גיבוב SHA-256 ידוע
פונקציית העזר הבאה מקבלת בדיוק 32 בתי גיבוב ותעודה שסופקה במפורש. בחירת הריפוד חייבת להתאים לפרוטוקול המקבל; PKCS#1 v1.5 כאן תואם לכוונת sha256WithRSAEncryption שבארכיון. אין חיפוש במאגר תעודות, וידית ה־RSA שהתקבלה משוחררת. פונקציה זו ושום פעולה אחרת על מפתח במאמר לא הורצו. [1][5]
using System;
using System.Security.Cryptography;
using System.Security.Cryptography.X509Certificates;
public static class PrecomputedRsaExample
{
public static byte[] SignSha256Digest(byte[] digest, X509Certificate2 certificate)
{
if (digest == null || digest.Length != 32)
throw new ArgumentException("A SHA-256 digest is exactly 32 bytes.", nameof(digest));
if (certificate == null) throw new ArgumentNullException(nameof(certificate));
using RSA rsa = certificate.GetRSAPrivateKey()
?? throw new CryptographicException("An accessible RSA private key is required.");
return rsa.SignHash(digest, HashAlgorithmName.SHA256, RSASignaturePadding.Pkcs1);
}
}תיקון הגבול בין מקטעי הנתונים
ISigner.BlockUpdate מקבל היסט ומספר בתים ועשוי להיקרא שוב ושוב. הביטוי Take(length) בארכיון מתעלם מההיסט; חתימה על כל מקטע דורסת את התוצאה הקודמת. מפעל חתימות חייב לעבד כל בית פעם אחת, לפי הסדר, ולחתום רק בסיום. המחלקה העצמאית להלן מדגימה את הגבול הזה בלי להציג את עצמה כמימוש מלא של ISigner לגרסה מסוימת של Bouncy Castle. האחריות לאובייקט RSA נשארת בידי הקוד הקורא. [3]
// Add to a file with using System and System.Security.Cryptography.
public sealed class Sha256SigningAccumulator : IDisposable
{
private readonly IncrementalHash hash = IncrementalHash.CreateHash(HashAlgorithmName.SHA256);
private bool finished;
public void Append(byte[] input, int offset, int count)
{
if (finished) throw new InvalidOperationException("Already finalized.");
if (input == null) throw new ArgumentNullException(nameof(input));
if (offset < 0 || count < 0 || offset > input.Length - count)
throw new ArgumentOutOfRangeException(nameof(offset));
hash.AppendData(input, offset, count);
}
public byte[] Finish(RSA rsa)
{
if (finished) throw new InvalidOperationException("Already finalized.");
if (rsa == null) throw new ArgumentNullException(nameof(rsa));
finished = true;
byte[] digest = hash.GetHashAndReset();
try { return rsa.SignHash(digest, HashAlgorithmName.SHA256, RSASignaturePadding.Pkcs1); }
finally { CryptographicOperations.ZeroMemory(digest); }
}
public void Dispose() { finished = true; hash.Dispose(); }
}CMS הוא מבנה נפרד
ב־CMS מנותק עם מאפיינים חתומים, יש לקשור את messageDigest לתוכן החיצוני בפועל, לכלול contentType תואם, לקודד ב־DER את קבוצת המאפיינים החתומים ולחתום על בתיה המקודדים. אין לשים מערך בתים ריק בתוכן המוטמע ולספק גיבוב של מסמך אחר. לכן Generate(..., empty, true) המקורי אינו יכול לייצג את המסמך הזה כראוי. תהליך המבוסס על גיבוב בלבד מחייב שילוב מכוון של תוכן מנותק וגיבוב מחושב מראש, הנתמך בגרסת הספרייה שנבחרה; במצב זה המצבר צריך לקבל את קידוד המאפיינים החתומים, ולא את גיבוב התוכן. [2][4]
כאשר התוכן זמין, יש להשתמש במסלול ה־CMS הרגיל של הספרייה המבוסס על התוכן. מתאם לייצור חייב לממש את כל מחזור החיים הנדרש בגרסה שלו, לרבות אתחול, איפוס, עדכון של בית יחיד ומתודות span ככל שנדרשות, ואסור לו להתעלם בשקט מפעולות שאינן נתמכות. לפני שימוש יש לבדוק קלט ריק, היסטים שאינם אפס, מקטעים רבים, תוכן ששונה ואימות CMS בלתי תלוי. אלו בדיקות מוצעות, ולא תוצאות של בדיקות שבוצעו. [3]
הכללת תעודות אינה החלטת אמון. על המקבל לאמת את החותם למטרה המיועדת בהתאם למדיניות האמון והביטול שלו. אין להתעלם מכישלון X509Chain.Build, להשבית בדיקת ביטול כקיצור דרך בייצור או להתייחס לחתימת RSA תקינה כהוכחה לכך שהתעודה מהימנה. [2]
מקורות ראשוניים
- Microsoft RSA.SignHash
- RFC 5652 sections 5.2–5.6 and 11
- Bouncy Castle ISigner source
- Bouncy Castle CmsProcessableByteArray source
- Microsoft GetRSAPrivateKey
קישורי המקור בהקשר היסטורי
הקישורים המקוריים נשמרו לצורכי ייחוס. הם אינם אישור לתקינות הקוד הישן; זמינותם הנוכחית לא אומתה.