ueman / passkit

Various Dart & Flutter packages to work with Apple's PassKit
16 stars 3 forks source link

Write signature #74

Closed ueman closed 2 months ago

ueman commented 3 months ago
ueman commented 3 months ago

There's some code at which may help with it: https://github.com/tomasmcguinness/dotnet-passbook/blob/master/Passbook.Generator/PassGenerator.cs

// How to create a `RSAPrivateKey` from a pem file

import 'dart:typed_data';
import 'package:passkit/src/apple_wwdr_certificate.dart';
import 'package:pem/pem.dart';
import 'package:pkcs7/pkcs7.dart';
import 'package:pointycastle/pointycastle.dart';

RSAPrivateKey _createPrivateKey(String userCertificate) {
  final pem = decodePemBlocks(PemLabel.privateKey, userCertificate);

  final modulus =
      ASN1Object.fromBytes(Uint8List.fromList(pem[1])) as ASN1Integer;
  final exponent =
      ASN1Object.fromBytes(Uint8List.fromList(pem[3])) as ASN1Integer;
  final p = ASN1Object.fromBytes(Uint8List.fromList(pem[4])) as ASN1Integer;
  final q = ASN1Object.fromBytes(Uint8List.fromList(pem[5])) as ASN1Integer;

  return RSAPrivateKey(
    modulus.integer!,
    exponent.integer!,
    p.integer,
    q.integer,
  );
}
ueman commented 3 months ago

As a temporary solution, we could provide a callback for writing the signature. There are multiple examples for signing PKPass files with OpenSSL, and there are also OpenSSL bindings for Dart/Flutter. I however intend this library (and it's dependencies) to be Dart only.

Pseudo code example:

final pass = PkPass(...);
final pkPassFile = pass.create(signatureWriter: (contentHashes) {
  // create signature here based on the contentHashes
  return signature;
});
ueman commented 2 months ago

Maybe something like this could work?

import 'dart:typed_data';

import 'package:passkit/src/apple_wwdr_certificate.dart';
import 'package:pem/pem.dart';
import 'package:pkcs7/pkcs7.dart';
import 'package:pointycastle/pointycastle.dart';

Uint8List writeSignature(
  String userCertificate, // this is private key and certificate in one file
  Uint8List manifestBytes, // I'm guessing this should be the manifest content, not the hash of the manifest
) {
  final privateKey = _createPrivateKey(userCertificate);
  final issuer = X509.fromPem(userCertificate);

  final pkcs7Builder = Pkcs7Builder();

  pkcs7Builder.addCertificate(issuer);
  pkcs7Builder.addCertificate(wwdrG4);

  final signerInfo = Pkcs7SignerInfoBuilder.rsa(
    issuer: issuer,
    privateKey: privateKey,
    digestAlgorithm: HashAlgorithm.sha1,
  );

  signerInfo.addSMimeDigest(digest: manifestBytes, signingTime: DateTime.now());
  pkcs7Builder.addSignerInfo(signerInfo);

  final pkcs7 = pkcs7Builder.build();
  return pkcs7.der;
}

RSAPrivateKey _createPrivateKey(String certificate) {
  final pem = decodePemBlocks(PemLabel.privateKey, certificate);

  final modulus =
      ASN1Object.fromBytes(Uint8List.fromList(pem[1])) as ASN1Integer;
  final exponent =
      ASN1Object.fromBytes(Uint8List.fromList(pem[3])) as ASN1Integer;
  final p = ASN1Object.fromBytes(Uint8List.fromList(pem[4])) as ASN1Integer;
  final q = ASN1Object.fromBytes(Uint8List.fromList(pem[5])) as ASN1Integer;

  return RSAPrivateKey(
    modulus.integer!,
    exponent.integer!,
    p.integer,
    q.integer,
  );
}