tomasmcguinness / dotnet-passbook

A .Net Library for generating Apple Passbook (Wallet) files for iOS. Please get involved by creating pull requests and opening issues!
MIT License
316 stars 115 forks source link

This is more of question not an issue. #78

Closed velchev closed 5 years ago

velchev commented 5 years ago

I have all the files needed for creating pass. I am using one of the apple example coupon passes. https://developer.apple.com/library/archive/documentation/UserExperience/Conceptual/PassKit_PG/YourFirst.html. I have followed their example using signpass tool: ./signpass -p Lollipop.pass and created a pass which works fine. I am trying to do the same using "dotnet-passbook". Everything seem to have worked as pkpass file was generated successfully but I can't open that in iOS. It seems that is not recognised. The content of the pass is one and the same (pass.json). I have checked everything and the only difference from what apple has done is in signature - the signing of the manifest file. (manifest was fine - same when I unziped both pkpass files). I am using

private void SignManifestFile(PassGeneratorRequest request)
        {
            Trace.TraceInformation("Signing the manifest file...");

            try
            {
                ContentInfo contentInfo = new ContentInfo(manifestFile);

                SignedCms signing = new SignedCms(contentInfo, true);

                CmsSigner signer = new CmsSigner(SubjectIdentifierType.SubjectKeyIdentifier, passCert)
                {
                    IncludeOption = X509IncludeOption.None
                };

                Trace.TraceInformation("Fetching Apple Certificate for signing..");
                Trace.TraceInformation("Constructing the certificate chain..");
                signer.Certificates.Add(appleCert);
                signer.Certificates.Add(passCert);

                signer.SignedAttributes.Add(new Pkcs9SigningTime());

                Trace.TraceInformation("Processing the signature..");
                signing.ComputeSignature(signer);

                signatureFile = signing.Encode();

                Trace.TraceInformation("The file has been successfully signed!");
            }
            catch (Exception exp)
            {
                Trace.TraceError("Failed to sign the manifest file: [{0}]", exp.Message);
                throw new ManifestSigningException("Failed to sign manifest", exp);
            }
        }

Is it suppose when I unzip the two pkpass file to have one and the same files for all the content? The only difference I see is the signature, but if I sign this with one and the same certificates - then they should be the same too. I am thinking if there is a difference in the way the signing is implemented. What is strange in the above code is that CmsSigner is created with passCert input argument and then signer.Certificates.Add(passCert); That could be actually fine. Searching on google I can see different implementations of this. For example: https://blog.dotnetframework.org/2015/09/21/generate-pkpass-apple-passbook-coupons-from-c/ is using

{
IncludeOption = X509IncludeOption.ExcludeRoot
};

I think they can't be both correct for apple.... Can somebody confirm that this code works fine and creates pass that can be opened by iOS device? Just thinking maybe something have changed from apple side since this library have been developed.... Thanks

tomasmcguinness commented 5 years ago

If you would you provide me a copy of the pass that isn't working, I will certainly take a look. I'm working on a tool that can help identify common issues with passes - it's running at https://pkpassvalidator.azurewebsites.net - it's not perfect, but would be a good place to start.

velchev commented 5 years ago

LoyaltyCoupon.zip

I have attached it - is just zip as pkpass extension is not allowed as attached file.

I have managed to create a valid signiture using this tutorial. https://www.raywenderlich.com/2855-beginning-passbook-in-ios-6-part-1-2 It is not the best solution as if I want to use it I will need to start openssl as command line tool and sign the manifest...,which is not the best if you have the power of .net Framework and the classes that are needed. No idea why is not working for me. Your tool is saying that everything is fine with the pass... image

tomasmcguinness commented 5 years ago

The tool does some very high-level checks, which are the most common mistakes. I'll take a closer look today or tomorrow. I've had another user of the library experience something similar, but the WWDRC they were using was wrong. This doesn't seem to be the case with your pass.

velchev commented 5 years ago

Thanks you Tomas. Your help is really much appreciated! Maybe to isolate the problem, you can try without building the pass, but just have the pass and all the required files - images. then create the manifest file. Which is quite easy and easy to verify and then just signing that manifest file with correct certificates for your particular pass. The zip can be done manually. In that way you can just concentrate on the signing part. If you can make a small console app that is doing the signing only will be fantastic and will prove that I am not doing something right and then I can compare it with what I am doing. Thanks you.

mcabriel commented 5 years ago

Hi @tomasmcguinness,

If you may, kindly check my pkpass as well as I've been trying a lot to add those in the iphone passwallet but always say, "This type of attachment can't be accessed on this device.". I tried several changes in fields and other properties of the pkpass to do some trial and error.

Already used the pkpass validator and always passed on every criteria. The certificate is new as well and will expire on 2019.

Here are some samples:

Passbook_TestSamples.zip

tomasmcguinness commented 5 years ago

@velchev I just ran your pass on your phone and got an interesting error!

For file pass.json, manifest's listed SHA1 hash 7b35b1e81fdfc29081b1eed32b52371afa0a936a doesn't match computed hash, 244bc3c72d5db077756c67376dd123a292d1b7ea

This seems to imply that the SHA1 hash that is in the manifest isn't correct. I've tested the SHA1 of your pass.json file and it should be 244BC3C72D5DB077756C67376DD123A292D1B7EA, so the manifest.json file is wrong.

Do you generate this file or did dotnet-passbook generate it?

tomasmcguinness commented 5 years ago

@mcabriel I've moved this into a new issue, so keep it separate from the one created by @velchev . I hope that's okay.

tomasmcguinness commented 5 years ago

@velchev Hi. Any update?

velchev commented 5 years ago

I will check today, and give an update. Sorry I do not know how I missed the notification emails from GitHub.

velchev commented 5 years ago

Hi, @tomasmcguinness, sorry for the delayed replay.

I did some investigations. So ... it is very very strange. I use the same pass.json with the example I have sent you and I am signing it with dotnet-passbook and with another code - projectx.

With projectX I have:

{
  "icon.png" : "f8a2bb1b52c426275312c98c626d5be92758170e",
  "icon@2x.png" : "4204eafa4ac2df2339cf3308a2b0ecd228732589",
  "logo.png" : "2147d5e2561b98bc2d9653c51349947d6ddc419d",
  "logo@2x.png" : "b98b0504f4f067de4f7a6c1e95df8a78024dc3bb",
  "pass.json" : "6f9cce92f9204696e9bc3a68642f7e81db246e5d"
}

With dotnet-passbook I have:

{
  "pass.json": "57d8b3b6711cf43250d523301ec305a34f5645a7",
  "icon.png": "f8a2bb1b52c426275312c98c626d5be92758170e",
  "icon@2x.png": "4204eafa4ac2df2339cf3308a2b0ecd228732589",
  "logo.png": "2147d5e2561b98bc2d9653c51349947d6ddc419d",
  "logo@2x.png": "b98b0504f4f067de4f7a6c1e95df8a78024dc3bb"
}

The passes are different and they are different from what you have. With projectX after I signed the pass with OpenSSL I can open the pass in my iPhone.

So obviously something should be wrong with the signature in the dotnet-passbook.

I have modified the method CreatePassFile in the library with

  private void CreatePassFile(PassGeneratorRequest request)
        {
            var file = File.ReadAllBytes(
                @"X:\Projects\ProjectX\softopia-dotnet-passbook\Passbook.Sample.Web\Icons\LoyaltyCoupon\pass.json");
            _passFile = file;
        }

I am just reading the already generated file as I had difficulties to configure the library to generate the exact pass file I need. That's the only change I did. How do you generate your pass? Could you please share the code you used to configure that? How do you get the hash that was required by apple when comparing packages? It's very strange as with projectX I have different hash but then sign this with openssl and works fine. Hash of one thing is always the same thing... that's the whole idea of this one way function!!! I had a look how you sign the hash and the code is the same with porjectX and dotnet-passbook. So then must be the content which is the same... I have no idea....

velchev commented 5 years ago

Hi @tomasmcguinness, please can you help with the issue when you have some time? Thanks

tomasmcguinness commented 5 years ago

Those values are SHA1 hashes, so for the same files, they should be identical. As I pointed out above, dotnet-passbook seems to be generating the right SHA1 value that iOS expects. Are you absolutely positive you've supplying the same files to both?

tomasmcguinness commented 5 years ago

@velchev Do you still need help?

tomasmcguinness commented 5 years ago

I'm closing this issue due to inactivity. @velchev If you still need help, let me know.