Closed mohalibou closed 2 months ago
Hi there! Yeah my guess would be that given the error is from swift-asn1
it probably is a bad formatted cert/key.
What does your certificate and key look like? They should be formatted like this example:
cert.pem | key.pem |
---|---|
``` -----BEGIN CERTIFICATE----- blahblahblahblahblahblahblahblah blahblahblahblahblahblahblahblah ... blahblahblah -----END CERTIFICATE----- ``` | ``` -----BEGIN PRIVATE KEY----- blahblahblahblahblahblahblahblah blahblahblahblahblahblahblahblah ... blahblahblah -----END PRIVATE KEY----- ``` |
One thing to note is that the cert/key does not (right now) support being exported with a password so that could also be the problem (but given the error you are getting, less likely the culprit). Does this help at all?
This is how it appears to me:
Certificate.pem:
Bag Attributes
friendlyName: Pass Type ID: pass.com.blahblah.blahblah
localKeyID: 69 69 69 69 69 69 69 69 69 69 69 69 69 69 69 69 69 69 69 69
subject=UID=pass.com.blahblah.blahblah, CN=Pass Type ID: pass.com.blahblah.blahblah OU=A1B2C3D4E5, O=My Name, C=US
issuer=CN=Apple Worldwide Developer Relations Certification Authority, OU=G4, O=Apple Inc., C=US
-----BEGIN CERTIFICATE-----
blahblahblahblahblahblahblahblah
blahblahblahblahblahblahblahblah
...
blahblahblah
-----END CERTIFICATE-----
Key.pem:
Bag Attributes
friendlyName: My Name
localKeyID: 69 69 69 69 69 69 69 69 69 69 69 69 69 69 69 69 69 69 69 69
Key Attributes: <No Attributes>
-----BEGIN PRIVATE KEY-----
blahblahblahblahblahblahblahblah
blahblahblahblahblahblahblahblah
...
blahblahblah
-----END PRIVATE KEY-----
I did change my code a bit. The error that gets printed to me is a bit different now, as it doesn't reference swift-asn1
, but still shows me invalidPEMDocument
.
I obtained the data from the below code:
var generator = try PassGenerator()
guard let certificateURL = Bundle.main.url(forResource: "Certificate", withExtension: "pem") else { /* ... */ }
guard let keyURL = Bundle.main.url(forResource: "Key", withExtension: "pem") else { /* ... */ }
let certificateData = try Data(contentsOf: certificateURL)
let keyData = try Data(contentsOf: keyURL)
if let certificateString = String(data: certificateData, encoding: .utf8) { print(certificateString) }
if let keyString = String(data: keyData, encoding: .utf8) { print(keyString) }
I know the certificate and key work, as I've tried signing a pass manually, and it works without issue:
https://github.com/user-attachments/assets/5de11780-9d7f-46b2-aa47-0cf104282b5a
Could you try the following to see if we can figure out why its not happy?
import Foundation
import X509
import _CryptoExtras
do {
let certUrl = Bundle.main.url(forResource: "Certificate", withExtension: "pem")!
let certData = try Data(contentsOf: certUrl)
let certString = String(decoding: certData, as: UTF8.self)
let cert = try Certificate(pemEncoded: certString)
} catch {
print("Cert error: \(error)") // is this the error, if so what?
}
do {
let keyUrl = Bundle.main.url(forResource: "Key", withExtension: "pem")!
let keyData = try Data(contentsOf: keyUrl)
let keyString = String(decoding: keyData, as: UTF8.self)
let key = try _RSA.Signing.PrivateKey(pemRepresentation: keyString)
} catch {
print("Key error: \(error)") // is this the error, if so what?
}
This is what I get:
Key error: invalidPEMDocument
So it looks like this is an issue with the key. I'm not sure if this will be helpful, but these are the steps I went through to get my key:
.cer
file and converted it to a .p12
file with Keychain Access..p12
file to create the Certificate.pem
and Key.pem
files using the below terminal commands:
openssl pkcs12 -in Certificates.p12 -out Certificate.pem -clcerts -nokeys -legacy
openssl pkcs12 -in Certificates.p12 -out Key.pem -nocerts -nodes -legacy
So it looks like this is an issue with the key
Ok glad we are narrowing it down a bit!
openssl pkcs12 -in Certificates.p12 -out Key.pem -nocerts -nodes -legacy
Could you try without the -nodes
argument for generating the key out of the p12? Just looking at the notes I made for myself for when I need to regenerate both, seems I do not have the -nodes
option in there. The other option to try would be to explicitly generate with an empty password also -passout pass:
.
Please let me know if either of these help! Otherwise we can keep digging.
Here are the commands I tried used:
openssl pkcs12 -in Certificates.p12 -out Key.pem -nocerts -legacy -passout pass:
openssl pkcs12 -in Certificates.p12 -out Key.pem -nocerts -legacy
I added the file to my project, and I still run into the same error:
Key error: invalidPEMDocument
BTW, I don't know if this is helpful, but the -nodes
option is so that I don't have to enter a PEM pass phrase when creating the file. The private key won't be encrypted, and instead be stored in plain text.
Ok last thought before I try doing a deep dive is to just remove all the content before the -----BEGIN PRIVATE KEY-----
in the Key.pem file.
Ie remove this:
Bag Attributes
friendlyName: My Name
localKeyID: 69 69 69 69 69 69 69 69 69 69 69 69 69 69 69 69 69 69 69 69
Key Attributes: <No Attributes>
If that doesn't work ill start trying to repro with a new cert on my end.
Removing this fixed the issue.
Unrelated but, I am still a bit new to using passes. After getting the zip data using generator.archiveData()
, how would I go about saving it as a .pkpass
file and distributing it? And giving the user the option to add the pass to their wallet?
A pkpass is just a zip archive with a different extension (as long as the contents follow the spec format). So all you need to do is take the data returned from archiveData()
and save it to a file with the .pkpass
extensions:
let data = try generator.archiveData()
let url = URL(fileURLWithPath: "/users/.../mypass.pkpass")
try data.write(to: url) // now you have the pass on your filesystem at that url
Or you can take that data and add it to the user wallet in an iOS app:
import PassKit
let data = try generator.archiveData()
let pass = PKPass(data: data)
let vc = PKAddPassesViewController(pass: pass)!
present(vc, animated: true) // this will show the ui to add the pass to wallet from your app
Or you can just send it as an email attachment or via messages and users can add to wallet from there. Your server could return it from a route (or it could be a link on a webpage that triggers the download), just make sure you set the content type header Content-Type: application/vnd.apple.pkpass
and then the system should know what to do with it.
Hope this helps!
Thank you for all your help. Wasn't expecting the replies to be so quick lol.
The guidance was much appreciated. šš
I have been trying to use
func signatureData(manifest: Data, cert: Data, key: Data) throws -> Data
to get a signature for my pass. However, when I attempt to use it, I get this error:Failed to create pass: ASN1Error.invalidPEMDocument: could not find PEM start marker SwiftASN1/PEMDocument.swift:135
I realize that this error comes from the
swift-asn1
package, although I figured it was needed to be mentioned here.For reference, this is the a snippet of the code I am using:
I'm guessing this has something to do with the certificates I'm using? They should be fine though, so I'm confused where the problem may be arising.