dotnet-passbook

repository·master·Indexed 43 days ago

https://github.com/tomasmcguinness/dotnet-passbook

A .NET Standard 2.0 library for generating Apple Wallet (Passbook) packages. It handles PKI signing requirements, supports single .pkpass files and .pkpasses bundles for iOS 15+, and provides functionality for NFC keys (v2.0.1), barcode integration, and PassKit Web Service configuration for pass updates.

Tokens
3.2K
Snippets
11
Records
12
Agent score
6%

What's inside dotnet-passbook

  1. Generate a single Apple Wallet pass

    master

    To generate a pass, follow these steps:

    1. Instantiate a PassGenerator.
    2. Create a PassGeneratorRequest and populate mandatory fields (PassTypeIdentifier, TeamIdentifier, SerialNumber, Description, OrganizationName, LogoText).
    3. Set colors using HTML (e.g., #FFFFFF) or RGB (e.g., rgb(255,255,255)) formats.
    4. Assign your AppleWWDRCACertificate and PassbookCertificate.
    5. Add images as byte[] (standard and retina sizes are required).
    6. Set the Style (e.g., PassStyle.BoardingPass) and add fields to the appropriate sections (Primary, Secondary, Auxiliary).
    7. Call generator.Generate(request) to receive a byte[] representing the .pkpass file.
    PassGenerator generator = new PassGenerator();
    PassGeneratorRequest request = new PassGeneratorRequest();
    
    // Mandatory Data
    request.PassTypeIdentifier = "pass.tomsamcguinness.events";   
    request.TeamIdentifier = "RW121242";
    request.SerialNumber = "121212";
    request.Description = "My first pass";
    request.OrganizationName = "Tomas McGuinness";
    request.LogoText = "My Pass";
    
    // Colors
    request.BackgroundColor = "#FFFFFF";
    request.LabelColor = "#000000";
    request.ForegroundColor = "#000000";
    
    // Certificates
    request.AppleWWDRCACertificate = new X509Certificate(...);
    request.PassbookCertificate = new X509Certificate(...);
    
    // Images (Standard and Retina required)
    request.Images.Add(PassbookImage.Icon, System.IO.File.ReadAllBytes("icon.png"));
    request.Images.Add(PassbookImage.Icon2X, System.IO.File.ReadAllBytes("icon@2x.png"));
    request.Images.Add(PassbookImage.Icon3X, System.IO.File.ReadAllBytes("icon@3x.png"));
    
    // Pass Specifics
    request.Style = PassStyle.BoardingPass;
    request.AddPrimaryField(new StandardField("origin", "San Francisco", "SFO"));
    request.AddPrimaryField(new StandardField("destination", "London", "LDN"));
    request.AddSecondaryField(new StandardField("boarding-gate", "Gate", "A55"));
    request.AddAuxiliaryField(new StandardField("seat", "Seat", "G5"));
    request.AddAuxiliaryField(new StandardField("passenger-name", "Passenger", "Thomas Anderson"));
    
    request.TransitType = TransitType.PKTransitTypeAir;
    
    // Barcode
    request.AddBarcode(BarcodeType.PKBarcodeFormatPDF417, "01927847623423234234", "ISO-8859-1", "01927847623423234234");
    
    // Generate
    byte[] generatedPass = generator.Generate(request);
  2. Create dotnet-passbook certificates using OpenSSL

    master

    To use dotnet-passbook, you need two certificates: the Apple Worldwide Developer Relations certificate (G1) and an application-specific certificate. If you are not using IIS and the Windows certificate store, you can generate these using OpenSSL following these steps:

    1. Download the Apple WWDR Certificate: Download the G1 version from Apple and save it as AppleWWDRCA.cer.
      • ⚠️ Important: Only the G1 certificate will generate valid Apple Wallet passes. Other generations (G2, G3, etc.) will result in the error: "Sorry, your Pass cannot be installed to Passbook at this time."
    2. Create a Pass Type ID: In the Apple Developer portal, go to Certificates, Identifiers & Profiles > Identifiers, select Pass Type IDs from the dropdown, and create a new one (e.g., pass.com.company.appname).
    3. Generate a Certificate Signing Request (CSR): Use OpenSSL to create a .csr file and a private .key file.
    4. Sign the CSR: Upload your .csr to the Apple Developer portal under Certificates > Services > Pass Type ID Certificate to download a pass.cer file.
    5. Convert to PEM: Convert the binary pass.cer to a text-based PEM format.
    6. Convert to PKCS12: Convert the PEM certificate and your private key into a .pfx file.
    7. Configure Application: Add both the AppleWWDRCA.cer and the passbook.pfx to your project and provide the paths and the export password to the PassGeneratorRequest.
    var request = new PassGeneratorRequest();
    request.AppleWWDRCACertificate = File.ReadAllBytes("AppleWWDRCA.cer");
    request.Certificate = File.ReadAllBytes("passbook.pfx");
    request.CertificatePassword = "<your_export_password>";
  3. Generate a Certificate Signing Request (CSR) with OpenSSL

    master

    Use the following OpenSSL command to generate a new 2048-bit RSA private signing key (passbook.key) and a certificate signing request (passbook.csr).

    When prompted, you must provide a passphrase for the private key. You will need this passphrase later when exporting the certificate to PKCS12 format and when configuring your .NET application.

    openssl req -new -newkey rsa:2048 -out passbook.csr -keyout passbook.key
  4. Convert Apple certificates to PEM and PKCS12 formats

    master

    After downloading your signed pass.cer from Apple, you must convert it into formats compatible with .NET.

    1. Convert DER to PEM: Convert the binary-encoded pass.cer into a plain text PEM format.
    2. Convert PEM to PKCS12: Convert the PEM certificate and your private passbook.key into a .pfx file. You will be prompted for the private key's passphrase and asked to set an Export Password.
    # Convert DER to PEM
    openssl x509 -in pass.cer -inform der -out passbook.pem
    
    # Convert PEM to PKCS12 (.pfx)
    openssl pkcs12 -export -out passbook.pfx -inkey passbook.key -in passbook.pem
  5. Configure required Apple certificates

    master

    To generate valid passes, you must provide two specific certificates as X509Certificate instances in your PassGeneratorRequest:

    1. Passbook Certificate: Obtained from your Apple Developer Portal. Ensure it includes the private key component.
    2. Apple WWDR (WorldWide Developer Relations) Certificate: Downloadable from Apple's Certificate Authority.

    Note on WWDR versions:

    • If your Passbook certificate was generated on or before January 27, 2022, use the G1 WWDR certificate.
    • Otherwise, use the G4 certificate.

    When loading certificates in cloud environments, it is recommended to use MachineKeySet and Exportable flags to avoid signing issues:

    X509KeyStorageFlags flags = X509KeyStorageFlags.MachineKeySet | X509KeyStorageFlags.Exportable;
    X509Certificate2 certificate = new X509Certificate2(bytes, password, flags);
  6. Configure pass updates via Web Service

    master

    To allow Apple Wallet to update your passes, you must provide a callback mechanism in your PassGeneratorRequest. You must set:

    • AuthenticationToken: A secret string that will be included in the header of all requests made by Apple to your API. You are responsible for validating this token.
    • WebServiceUrl: The HTTPS endpoint of your API that implements the PassKit Web Service protocol.
    request.AuthenticationToken = "<a secret to ensure authorized access>";
    request.WebServiceUrl = "https://<your api>";
  7. Troubleshoot IIS certificate access issues

    master

    If running within an IIS application, the application pool may lack permission to access the private key of your certificates.

    To fix this:

    1. Open MMC.
    2. Add the Certificates (Local Computer) snap-in.
    3. Navigate to Personal > Certificates.
    4. Right-click your certificate > All Tasks > Manage Private Keys.
    5. Add the identity IIS AppPool\YourAppPoolName (or IIS_IUSRS) and grant it Full control.
  8. Generate a bundle of multiple passes (.pkpasses)

    master

    iOS 15+ supports bundling multiple passes into a single .pkpasses file. You can achieve this by passing a collection of PassGeneratorRequest objects to the Generate method. The resulting byte[] should be served with the MIME type application/vnd.apple.pkpasses.

    PassGenerator generator = new PassGenerator();
    List<PassGeneratorRequest> requests = new List<PassGeneratorRequest>();
    
    // Build multiple requests
    requests.Add(myFirstRequest);
    requests.Add(mySecondRequest);
    
    byte[] generatedBundle = generator.Generate(requests);
    
    // Return as file in ASP.NET
    return new FileContentResult(generatedBundle, "application/vnd.apple.pkpasses")
    {
        FileDownloadName = "tickets.pkpasses.zip"
    };
  9. Generate a CSR with inline subject details

    master

    You can avoid the interactive prompts by providing the Distinguished Name (DN) details directly using the -subj argument in OpenSSL.

    openssl req -new -newkey rsa:2048 -out passbook.csr -keyout passbook.key \
      -subj "/C=GB/ST=London/L=London/O=Ursatile Ltd/OU=iOS Development/CN=passbook.ursatile.com"
  10. Configure NFC support in PassGeneratorRequest

    master

    As of version 2.0.1, you can include NFC keys in your pass generation requests. To enable this, set the Nfc property on a PassGeneratorRequest object with a new Nfc instance.

    Note that both the message and encodedPublicKey values are mandatory. The specific values required for these fields are not publicly documented by Apple, so you must provide the appropriate NFC message and encoded public key relevant to your implementation.

    PassGeneratorRequest request = new PassGeneratorRequest();
    request.Nfc = new Nfc("THE NFC Message", "<encoded private key>");
  11. Configure PassGeneratorRequest with certificates

    master

    When initializing a PassGeneratorRequest in your .NET application, you must provide the byte arrays for both the Apple WWDR certificate and your application's PKCS12 certificate, along with the password used during the PKCS12 export process.

    var request = new PassGeneratorRequest();
    request.AppleWWDRCACertificate = File.ReadAllBytes("AppleWWDRCA.cer");
    request.Certificate = File.ReadAllBytes("passbook.pfx");
    request.CertificatePassword = "<your_export_password>";