Skip to content

נקודות EC דחוסות בתוך SubjectPublicKeyInfo עבור Apple Wallet

מפתח ציבורי EC דחוס: OpenSSL, BouncyCastle, Apple VAS ו־NFC

כל הקוד נועד להמחשה ולא הורץ.

הגדרת NFC ב־Apple Wallet משתמשת במבנה X.509 SubjectPublicKeyInfo ‏(SPKI) בקידוד Base64, המכיל מפתח ציבורי מסוג ECDH על העקום P-256. נקודת EC מקודדת לבדה אינה המבנה הזה. המאמר מפריד בין הפורמטים ומציג דוגמאות ב־OpenSSL, ב־dotnet-passbook וב־BouncyCastle.

קידוד נקודה אינו SPKI שלם

  • נקודת P-256 לא דחוסה כוללת קידומת ואת X ו־Y: בסך הכול 65 בתים.
  • נקודת P-256 דחוסה כוללת קידומת ואת X: בסך הכול 33 בתים. הקידומת מבדילה בין הערכים האפשריים של Y.
  • SPKI מכיל גם מזהי אלגוריתם ועקום. קידוד ה־DER שלו ארוך מהנקודה שבתוכו; Base64 מוסיף שכבת ייצוג נוספת.

התיעוד הציבורי של Apple לשדה NFC דורש SPKI ו־P-256, ולא קובע שכל מסוף NFC דורש דחיסה. יש לבדוק את דרישות הפורמט של המקבל ואת דרישת ההרשאה המיוחדת של Apple. בדיקת אורך של 33 בתים בלבד אינה מאמתת מפתח ציבורי.

1. ייצוא באמצעות OpenSSL

פקודות ההמחשה הבאות קוראות מפתח P-256 פרטי קיים. הראשונה כותבת SPKI ציבורי כ־PEM; השנייה כותבת DER; השלישית מקודדת את כל ה־DER לשדה הדורש Base64. השם private_key.pem הוא שם קובץ קלט מקומי, לא מפתח להורדה. יש לבחור נתיבי פלט נפרדים ולהגן על הקלט הפרטי. הפקודות לא הורצו.

openssl ec -in private_key.pem -pubout -conv_form compressed -out compressed_public_key.pem
openssl ec -in private_key.pem -pubout -conv_form compressed -outform DER -out compressed_public_key.der
openssl base64 -A -in compressed_public_key.der

2. שימוש ב־SPKI המלא ב־dotnet-passbook

הקישור המקורי לספרייה נשמר: dotnet-passbook. בהקשר קיים של יצירת כרטיס, הבנאי מקבל את הודעת ה־NFC ואת המפתח הציבורי המקודד. הדוגמה מניחה ש־request הוא אובייקט בקשת הכרטיס הקיים של היישום ושמרחב השמות של הספרייה מיובא. קובץ ה־DER הוא הפלט הציבורי מהשלב הקודם, לא מפתח פרטי. יש לבדוק את ה־API מול גרסת החבילה המקובעת בפרויקט. תמיכה ב־NFC דורשת גם הרשאה מיוחדת של Apple; מפתח לבדו אינו מפעיל אותה.

string spkiBase64 = Convert.ToBase64String(
    File.ReadAllBytes("compressed_public_key.der"));
request.Nfc = new Nfc("15407F6", spkiBase64);

3. בניית SPKI ב־C# באמצעות BouncyCastle

פונקציית העזר להמחשה משתמשת ב־API של BouncyCastle.Cryptography ומקבלת מחרוזת PEM מסוג PUBLIC KEY, לא PEM של מפתח פרטי או תעודה. היא בודקת את העקום המזוהה בשמו, מנתחת את מפתח ה־EC ומחזירה את הנקודה הדחוסה לתוך SPKI. לעומת זאת, Q.GetEncoded(true) לבדו מחזיר רק את הנקודה. אין כאן טענה שבוצעו הידור, הרצה או בדיקת תאימות למכשיר.

using System;
using System.IO;
using Org.BouncyCastle.Asn1;
using Org.BouncyCastle.Asn1.X509;
using Org.BouncyCastle.Asn1.X9;
using Org.BouncyCastle.Crypto.Parameters;
using Org.BouncyCastle.Security;
using Org.BouncyCastle.Utilities.IO.Pem;

public static class CompressedSpki
{
    public static string FromPublicPem(string publicPem)
    {
        using var text = new StringReader(publicPem);
        using var reader = new PemReader(text);
        var pem = reader.ReadPemObject()
            ?? throw new ArgumentException("Missing PUBLIC KEY PEM block.");
        if (pem.Type != "PUBLIC KEY")
            throw new ArgumentException("Expected public SPKI, not a private key.");

        var spki = SubjectPublicKeyInfo.GetInstance(
            Asn1Object.FromByteArray(pem.Content));
        if (!spki.Algorithm.Algorithm.Equals(X9ObjectIdentifiers.IdECPublicKey)
            || !DerObjectIdentifier.GetInstance(spki.Algorithm.Parameters)
                .Equals(X9ObjectIdentifiers.Prime256v1))
            throw new ArgumentException("Expected named-curve P-256 EC SPKI.");

        var key = (ECPublicKeyParameters)PublicKeyFactory.CreateKey(spki);
        byte[] point = key.Q.GetEncoded(true);
        if (point.Length != 33 || (point[0] != 2 && point[0] != 3))
            throw new ArgumentException("Unexpected P-256 point encoding.");

        var compressedSpki = new SubjectPublicKeyInfo(spki.Algorithm, point);
        return Convert.ToBase64String(compressedSpki.GetDerEncoded());
    }
}

סיכום

יש להבחין בין בתי הנקודה, SPKI בקידוד DER, ‏PEM ו־Base64. אמתו את העקום ואת המבנה בעזרת מנתח קריפטוגרפי, ואז בדקו את הדרישות המדויקות של הצרכן. אין להציב נקודה בת 33 בתים במקום שדה SPKI.

מקורות ראשוניים

המאמר המקורי באנגלית: Generating subject public key info in a compressed form. תאריך הפרסום המקורי: 2024-04-03T12:21:00.003+03:00.

Post a Comment

Previous Post Next Post