Xcode Validation Receipt Signature Error

I'm in the process of using in-app purchase in my App... now I want to validate the receipt, and for validating receipt signature I use the below code in the AppDelegate (in order to know if the user already purchased it when launching the App)


actually the code comes from the WWDC 2013 - session 308 ;-)


NSURL *receiptURL = [[NSBundle mainBundle] appStoreReceiptURL];
    NSData *receiptData = [NSData dataWithContentsOfURL:receiptURL];
    NSData *certificateData = [NSData dataWithContentsOfURL:[[NSBundle mainBundle] URLForResource:@"AppleIncRootCertificate" withExtension:@"cer"]];

    // The PKCS #7 container (the receipt) and The Apple root certificate.
    BIO *b_receipt = BIO_new_mem_buf((void *) [receiptData bytes], (int)[receiptData length]);
    BIO *b_x509 = BIO_new_mem_buf((void *)[certificateData bytes], (int)[certificateData length]);

   // Convert receipt data to PKCS #7 Representation
   PKCS7 *p7 = d2i_PKCS7_bio(b_receipt, NULL);

    // Create the root certificate
    X509_STORE *store = X509_STORE_new();
    X509 *appleRootCA = d2i_X509_bio(b_x509, NULL);
    X509_STORE_add_cert(store, appleRootCA);

    // Verify the signature. If the verification is correct, b_receiptPayload will contain the PKCS #7 payload and result will be 1.
    BIO *b_receiptPayload = BIO_new(BIO_s_mem());
    int result = PKCS7_verify(p7, NULL, store, NULL, b_receiptPayload, 0);
    if (result == 1)
    {
        NSLog(@"Receipt Signature is valid");
    } else {
        unsigned long error = ERR_get_error();
        const char* error_str = ERR_error_string(error, NULL);
        NSLog(@"OpenSSL Error --> %s",error_str);
    }


but I always get the following OpenSSL Error: error:2006F079:lib(32):func(111):reason(121)


I already google it but without any result...any suggestion?

Another way to validate receipt signature?


thank you in advance for your cooperation...

Answered by PBK in 182434022

Keep in mind that all you are doing is validating the signature, not the contents of the receipt. An app can have a valid receipt whether or not an IAP was purchased.


Here are the differences between what you do and what I do. I suspect somewhere in here is the error. I suspect it is the OpenSSL_add_all_digests(): but it could be something else:



//you
    BIO *b_receipt = BIO_new_mem_buf((void *) [receiptData bytes], (int)[receiptData length]);
//me
    BIO *b_receipt=BIO_new(BIO_s_mem());  
    BIO_write(b_receipt, [receiptData bytes], (int)[receiptData length]);
//(I think these are equivalent)  

//you
    BIO *b_x509 = BIO_new_mem_buf((void *)[certificateData bytes], (int)[certificateData length]);
//me
    BIO *b_x509 = BIO_new(BIO_s_mem());
    BIO_write(b_x509, (const void *) [certificateData bytes], (int) [certificateData length]);
// I think tehse are equivalent
  
//you
     BIO *b_receiptPayload = BIO_new(BIO_s_mem());
    int result = PKCS7_verify(p7, NULL, store, NULL, b_receiptPayload, 0);
//me
     OpenSSL_add_all_digests();  //  try adding this before this line 
    int result=PKCS7_verify(p7,NULL,store,NULL,NULL,0);
   //  I think you are missing a key line! 
   
  
Accepted Answer

Keep in mind that all you are doing is validating the signature, not the contents of the receipt. An app can have a valid receipt whether or not an IAP was purchased.


Here are the differences between what you do and what I do. I suspect somewhere in here is the error. I suspect it is the OpenSSL_add_all_digests(): but it could be something else:



//you
    BIO *b_receipt = BIO_new_mem_buf((void *) [receiptData bytes], (int)[receiptData length]);
//me
    BIO *b_receipt=BIO_new(BIO_s_mem());  
    BIO_write(b_receipt, [receiptData bytes], (int)[receiptData length]);
//(I think these are equivalent)  

//you
    BIO *b_x509 = BIO_new_mem_buf((void *)[certificateData bytes], (int)[certificateData length]);
//me
    BIO *b_x509 = BIO_new(BIO_s_mem());
    BIO_write(b_x509, (const void *) [certificateData bytes], (int) [certificateData length]);
// I think tehse are equivalent
  
//you
     BIO *b_receiptPayload = BIO_new(BIO_s_mem());
    int result = PKCS7_verify(p7, NULL, store, NULL, b_receiptPayload, 0);
//me
     OpenSSL_add_all_digests();  //  try adding this before this line 
    int result=PKCS7_verify(p7,NULL,store,NULL,NULL,0);
   //  I think you are missing a key line! 
   
  

First of all, thank you for your prompt reply...


I replaced my code with your code, and now the OpenSSL error I get is:


error:0D06B08E:lib(13):func(107):reason(142)

Any idea?

That's progress. Remember, Google is your friend.


You can feed that into the Terminal (in Applications/Utilities) with the command:

"openssl errstr 0D06B08E" and discover

error:0D06B08E:asn1 encoding routines:ASN1_D2I_READ_BIO:not enough data


I think that means that your certificate isn't where you think your certificate is or your receipt is null. Try adding:


if(![[NSFileManager defaultManager] fileExistsAtPath:[[NSBundle mainBundle] URLForResource:@"AppleIncRootCertificate" withExtension:@"cer"].path]){
        //  issue an NSLog indicating the certificate is missing
}

//and

if(![[NSFileManager defaultManager] fileExistsAtPath:receiptURL.path]){
        //  issue an NSLog indicating that the receipt is missing
 }
 

actually my fault as noob with all of this regarding to in-app purchase validations... I did not add the "AppleIncRootCertificate.cer" to my app's resource bundle, this is, to my project 😟


now is working, thank you...🙂


PS: the key in my code was to add this line you told me:

OpenSSL_add_all_digests();

(or OpenSSL_add_all_algorithms(); instead?)


by the way, that line is missed in the WWDC 2013 video I mentioned in my first post...