{"id":2941,"date":"2015-11-14T17:42:32","date_gmt":"2015-11-14T20:42:32","guid":{"rendered":"http:\/\/brockerhoff.net\/blog\/?p=2941"},"modified":"2015-11-20T11:48:54","modified_gmt":"2015-11-20T14:48:54","slug":"a-tale-of-two-certs","status":"publish","type":"post","link":"https:\/\/brockerhoff.net\/blog\/2015\/11\/14\/a-tale-of-two-certs\/","title":{"rendered":"A Tale of Two Certs"},"content":{"rendered":"<p>I&#8217;m keeping this post\u00a0<strong>updated<\/strong> as details develop&#8230;<\/p>\n<p>About ten days ago, something strange happened on my Mac: I was debugging the next version of my <a href=\"\/RB\/AppCheckerLite\" target=\"_blank\">RB App Checker Lite<\/a>\u00a0app and suddenly I saw the dreaded dialog box:<img loading=\"lazy\" decoding=\"async\" class=\"aligncenter size-full wp-image-2942\" src=\"\/blog\/wp-content\/uploads\/2015\/11\/Damaged.png\" alt=\"Damaged\" width=\"532\" height=\"274\" srcset=\"https:\/\/brockerhoff.net\/blog\/wp-content\/uploads\/2015\/11\/Damaged.png 532w, https:\/\/brockerhoff.net\/blog\/wp-content\/uploads\/2015\/11\/Damaged-300x155.png 300w\" sizes=\"auto, (max-width: 532px) 100vw, 532px\" \/><\/p>\n<p>Completely abnormal, especially as I was debugging using the Developer ID version (not the Mac App Store version!) from inside Xcode. When I opened Terminal, the same dialog; when I opened Safari, same thing! No new process was allowed to run. Of course I had to reboot to be able to do anything, everything worked fine afterwards, and I couldn&#8217;t reproduce the problem, so&#8230;<\/p>\n<p>OK, a couple of days ago I concluded all was ready and I uploaded my app for review. A few hours after I announced so on Twitter, <a href=\"http:\/\/mjtsai.com\/blog\/2015\/11\/12\/no-one-minding-the-store\/\" target=\"_blank\">the reports<\/a> began appear: the sky is falling! Major Mac App Store meltdown, everybody was getting the \u201cdamaged\u201d dialog, Apple&#8217;s certificates were the culprit. I started testing my local apps from the MAS and, sure enough, the MAS leaf cert had expired; no problems, some of them asked anew for the AppleID password, some didn&#8217;t. RB App Checker Lite showed the expiration but no other problems, but I pulled it from review just in case.<\/p>\n<p>Two days of confusion and frantic coding later, I had submitted (and pulled!) 4 more builds until I was reasonably sure that everything\u00a0was working correctly. Thanks to several\u00a0fellow developers on Twitter, the upcoming version seems to show everything correctly; it turned out that my receipt checks were somewhat obsolete. I usually publish the direct download version only after the MAS version has passed review, but decided to release version 1.1.4, build 351\u00a0immediately: you can <a href=\"\/RB\/AppCheckerLite\" target=\"_blank\">get it here<\/a>. It has a long list of improvements and fixes.<\/p>\n<p>Meanwhile, the consensus is that rebooting and re-entering the AppleID and passwords (or even deleting and reinstalling) the affected apps solves 99% of the problems.<\/p>\n<p>There are actually several different unfortunate problems here. First, the \u201cdamaged\u201d dialog seems to be caused by some sort of cache or memory corruption in the system processes that coordinate to implement GateKeeper and the app store updates; some reports say killing the \u201cstoreagentd\u201d process solves this problem without rebooting. (My system doesn&#8217;t seem to run this, FWIW.) What not everyone knows is that this dialog appears before the app it allowed to run; that is, it&#8217;s not affected by any checking done inside the app itself!<\/p>\n<p>Second, asking for a new AppleID password. This is caused by the app itself checking the store receipt; something strongly recommended by Apple, since otherwise, it&#8217;s easy to copy a downloaded app to another computer and having it run there; I remember some early games not doing this and being widely pirated.<\/p>\n<p>When an app is downloaded from the MAS, a proper receipt for that AppleID and that computer <em>is already inside<\/em>. A missing or corrupted receipt is the\u00a0<em>only<\/em> normal circumstance in which the \u201cdamaged\u201d dialog should appear. But if you copy the app to another computer, this will be noticed by the app itself.<\/p>\n<p>Once a MAS app starts up, the first thing it should do is to <a href=\"https:\/\/developer.apple.com\/library\/mac\/releasenotes\/General\/ValidateAppStoreReceipt\/Chapters\/ValidateLocally.html#\/\/apple_ref\/doc\/uid\/TP40010573-CH1-SW2\" target=\"_blank\">check\u00a0the receipt<\/a>. It&#8217;s a complex process and not everybody implements it the same way. At first, checking the receipt&#8217;s cert chain would cause the receipt to be rejected in the case of expiration; the app exits with a special numeric code (exit 173) and this code signals the system to put up the dialog asking to confirm the purchaser&#8217;s AppleID and password. This, in turn, will cause a new receipt to be downloaded, and the app can now run with no problems.\u00a0<strong>Update:<\/strong> reports indicate that, in at least some cases, the system doesn&#8217;t respond properly to exit 173.<\/p>\n<p>A few years ago receipts began to include a new field containing the receipt&#8217;s creation date, and developers now had to check the certs against that date (and not against the current date), therefore obviating the need to reenter the password. Unfortunately this was not widely divulged, and Apple&#8217;s own sample code hasn&#8217;t yet been updated accordingly; I confess to not seeing this myself!<\/p>\n<p>As is usual in disasters, several things have to go wrong at the same time: some bug corrupts a critical system cache, certificates expire normally, some apps incorrectly test for expiration, receipts are corrupted or the AppleID validation servers become\u00a0slow or unreachable (because of the huge number of simultaneous requests), and&#8230; boom.<\/p>\n<p>Many\u00a0articles, unfortunately, published factual errors or wrong assumptions.Let&#8217;s try to counter a few:<\/p>\n<ul>\n<li><em>Apple \u201callowed\u201d their Mac App Store certificate to expire.<\/em>\u00a0Wrong on several levels. First, there&#8217;s not one but 5\u00a0(!) certificates involved in any app from the store: Apple&#8217;s root certificate:<a href=\"\/blog\/wp-content\/uploads\/2015\/11\/AppleRootCA.png\"><img loading=\"lazy\" decoding=\"async\" class=\"aligncenter size-full wp-image-2944\" src=\"\/blog\/wp-content\/uploads\/2015\/11\/AppleRootCA.png\" alt=\"\" width=\"450\" height=\"85\" srcset=\"https:\/\/brockerhoff.net\/blog\/wp-content\/uploads\/2015\/11\/AppleRootCA.png 450w, https:\/\/brockerhoff.net\/blog\/wp-content\/uploads\/2015\/11\/AppleRootCA-300x57.png 300w\" sizes=\"auto, (max-width: 450px) 100vw, 450px\" \/><\/a> and 4 others: two intermediate and two leaf certificates.<br \/>\nThe way these certs work is by so-called certificate chains; every cert vouches for the lower-level ones. At the top is Apple&#8217;s Root certificate, which is one of a hundred or so in the System Keychain. There are two different certificate chains in every MAS app; the first is used in the code signature:<a href=\"\/blog\/wp-content\/uploads\/2015\/11\/CodesignChain.png\"><img loading=\"lazy\" decoding=\"async\" class=\"aligncenter size-full wp-image-2948\" src=\"\/blog\/wp-content\/uploads\/2015\/11\/CodesignChain.png\" alt=\"\" width=\"444\" height=\"190\" srcset=\"https:\/\/brockerhoff.net\/blog\/wp-content\/uploads\/2015\/11\/CodesignChain.png 444w, https:\/\/brockerhoff.net\/blog\/wp-content\/uploads\/2015\/11\/CodesignChain-300x128.png 300w\" sizes=\"auto, (max-width: 444px) 100vw, 444px\" \/><\/a>and the second is used to sign the store receipt:<a href=\"\/blog\/wp-content\/uploads\/2015\/11\/ReceiptChain.png\"><img loading=\"lazy\" decoding=\"async\" class=\"aligncenter size-full wp-image-2946\" src=\"\/blog\/wp-content\/uploads\/2015\/11\/ReceiptChain.png\" alt=\"\" width=\"444\" height=\"188\" srcset=\"https:\/\/brockerhoff.net\/blog\/wp-content\/uploads\/2015\/11\/ReceiptChain.png 444w, https:\/\/brockerhoff.net\/blog\/wp-content\/uploads\/2015\/11\/ReceiptChain-300x127.png 300w\" sizes=\"auto, (max-width: 444px) 100vw, 444px\" \/><\/a>Note the expired certificate there? This is a leaf certificate. These, usually, have a short life \u2014 one or two years \u2014 and the intermediate certificates usually last a little longer.<br \/>\nSo, when a cert expires, is that a serious problem? No \u2013 unless it is the root cert, which is why they all expire somewhen in the 2030s \u2014 hopefully, by that time, they&#8217;ll have figured out something better, Apple will have updated the cert via Software Update, or <a href=\"http:\/\/www-personal.umich.edu\/~jlawler\/aue\/sig.html\" target=\"_blank\">the horse will have learned to sing<\/a>.<br \/>\nThe root cert can be updated via Software Update because it&#8217;s stored in System Keychain \u2014 but it&#8217;s impractical to push cert updates to each and every signed app, bundle or library; there are many thousands of them! So an expired cert in the code signature doesn&#8217;t affect the app at all. What&#8217;s important is that the certs were valid <em>when the app was signed<\/em>. When and if you get a new version of the app, all certs will probably be new ones. So there&#8217;s no \u201callowing\u201d a leaf cert to expire \u2014 they do so naturally.<\/li>\n<li><em>Apple \u201cpushed\u201d\u00a0a new certificate that expires in 2035.<\/em> This is probably just looking in the wrong place \u2014 not knowing which certificate had expired, someone glanced at the root certificate and noticed the \u201cnew\u201d 2035 date. Nothing new to see, of course; that cert was created in 2006! Even more confusingly, someone else deduced from that that Apple let their original root cert expire; also wrong.<\/li>\n<li><em>The system hasn&#8217;t been updated to check SHA2 (256) certificates.<\/em> Wrong; it&#8217;s true that older systems used a version of the OpenSSL library that understood only SHA1 (128) certs, but that actually means 10.5 or so. Newer systems understand SHA2, and in any event, since the MAS went up, Apple has always recommended developers to\u00a0<em>not<\/em> use the system\u2019s OpenSSL library (I think it&#8217;s not even included anymore), so only very old apps would be affected by that.<br \/>\n<strong>Update:<\/strong> <a href=\"http:\/\/www.macworld.com\/article\/3004917\/security\/mas-extinction-apple-letting-a-certificate-die-isnt-a-security-issue-just-an-embarrassment.html\" target=\"_blank\">Glenn Fleishman<\/a> has informed me about the SSL situation: there&#8217;s the new 1.0.x library branch and the older 0.9.x branch. Both apparently got SHA2 support in 2010, when 1.0.0 and 0.9.8o came out, but some developers seem to have kept older versions, no doubt for valid reasons; space precludes, etc.<\/li>\n<li><em>Apple is blaming developers.<\/em> Apparently this can be traced to a <a href=\"https:\/\/twitter.com\/thestopbutton\/status\/664960566453075968\" target=\"_blank\">single report of misinformation<\/a> from an anonymous Apple Support person. As I write this, Apple hasn&#8217;t yet said anything; I doubt they&#8217;ll say anything over the weekend.<\/li>\n<li><em>This is a serious security\/cryptography failure.\u00a0<\/em> Nope. This confusion arises from the fact that digital certificates (and libraries like OpenSSL) \u00a0are used for both secure, encrypted communications and for app\/receipt signing. In the latter case, an expired cert doesn&#8217;t expose any information or makes the system or apps easier to hack.<\/li>\n<li><em>Developers are better off not doing any, or little, receipt checking.<\/em> Not really. True, apps which don&#8217;t do full receipt checking\u00a0might have not been affected in this single instance, but under usual circumstances\u00a0they&#8217;re more vulnerable to hacking or piracy.<\/li>\n<li><em>Apple&#8217;s store\/system infrastructure is brittle and can&#8217;t be trusted.<\/em>\u00a0True, it&#8217;s a very complex system that depends on many twisted little interlocking parts to work properly. And, as we&#8217;ve seen, this particular instance of failure is as self-amplifying as electrical grid failures _ once it starts, the demands on the working parts grow so huge that those fail, too. In Apple&#8217;s defense, it&#8217;s very hard to test for or simulate. Let&#8217;s hope that all involved have learned something from this incident; I certainly have learned a lot.<\/li>\n<\/ul>\n<p><strong>Update:<\/strong> forgot to comment <a href=\"http:\/\/wanderingcoder.net\/2015\/11\/13\/mac-app-store-preservation\/\" target=\"_blank\">on this particular post<\/a>:<\/p>\n<blockquote><p>But when I tried to convince my Mac to run this app as an unsigned app, I encountered what is extremely likely to be the store DRM: I initially got the \u201cyour app was bought on another machine\u201d message, so I tried deleting the receipt, but then I got the dreaded \u201capp damaged\u201d message, at which point I removed the signature.<\/p>\n<p>&#8230;the only way I can see is to create a new root CA which I install on the machine as a trusted root, and redo the signing chain, and even that might not work if the DRM is somehow tied to the signature chain.<\/p><\/blockquote>\n<p>While I can understand the frustration implicit in not being able to run purchased apps \u201cforever\u201d, I think this\u00a0is a fundamentally wrong approach. Let&#8217;s educate developers to check receipts properly, as I mentioned above. Figuring out a way to run store apps (or even developer-ID purchased apps) without \u201cDRM\u201d means that anyone else can use the same method to install pirated copies; we wouldn&#8217;t be able to trust users anymore.<\/p>\n<p>Much worse, re-signing someone else&#8217;s app and expecting it to run is an even greater violation of trust. The days when you could hack someone&#8217;s app with ResEdit and having fun making it\u00a0look different, or do unexpected things, are long gone. I implement very strict checks that my complete app bundle has not been altered in any way <em>and<\/em>\u00a0that it&#8217;s running with my original signature, otherwise any\u00a0user could freely alter files, hack the code, change graphic resources or even \u2014 and such cases have happened! \u2014 repost the app somewhere else as being their own. No, flawed as the current approach may be in implementation, I\u00a0see no better alternative.<\/p>\n<p><strong>Update:<\/strong>\u00a0reports are in from some <a href=\"https:\/\/twitter.com\/atomicbird\/status\/666764763695857665\" target=\"_blank\">helpful fellow developers<\/a>, confirming my suspicions of cache corruption \u2014 RB App Checker Lite says the app bundle and receipt contents are OK, yet the apps will not run. I use the same APIs (hopefully) that the system processes use \u2014 but those APIs can take a long time to run, so the results are cached somewhere.<\/p>\n<p><strong>Update:<\/strong> Apple sent email to all developers:<\/p>\n<blockquote><p>In anticipation of the expiration of the old Mac App Store certificate, we issued a new certificate in September.<\/p><\/blockquote>\n<p>As I said \u2014 no \u201clet[ting] certificates expire\u201d. They all do.<\/p>\n<blockquote><p>We are addressing this caching issue in an upcoming OS X update.<\/p><\/blockquote>\n<p>Confirms this is a caching issue, as I suspected.<\/p>\n<blockquote><p>&#8230;some apps are running receipt validation code using very old versions of OpenSSL that don&#8217;t support SHA-2. We addressed this by replacing the new SHA-2 certificate with a new SHA-1 certificate last Thursday night.<\/p><\/blockquote>\n<p>I&#8217;m a little surprised that\u00a0the number of apps using\u201cvery old versions\u201d justifies going back to SHA1; but, OK.<\/p>\n<blockquote><p>Please ensure your code adheres to the <a class=\"aapl-link\" href=\"https:\/\/developer.apple.com\/library\/mac\/releasenotes\/General\/ValidateAppStoreReceipt\/Chapters\/ValidateRemotely.html#\/\/apple_ref\/doc\/uid\/TP40010573-CH104-SW1\">Receipt Validation Programming Guide<\/a> and check that all receipt validation issues are resolved.<\/p><\/blockquote>\n<p>Good, but:<\/p>\n<ul>\n<li>the link goes to the page detailing the\u00a0<em>online<\/em>\u00a0receipt validation. Very few apps use that IMHO \u2014 you have to be online every time the app runs, you have to have a reasonably fast connection, and app launch will be significantly slower. Linking to <a href=\"https:\/\/developer.apple.com\/library\/mac\/releasenotes\/General\/ValidateAppStoreReceipt\/Chapters\/ValidateLocally.html#\/\/apple_ref\/doc\/uid\/TP40010573-CH1-SW2\" target=\"_blank\">this page<\/a>\u00a0(Validating Receipts Locally) would&#8217;ve been better;<\/li>\n<li>it would\u00a0have been more helpful to call out specifically the certificate expiration check\u00a0<em>and<\/em> update the sample code to properly use the receipt creation date.<\/li>\n<\/ul>\n<p><strong>Update:<\/strong>\u00a0I&#8217;ve now submitted\u00a0rdar:\/\/\/23611335 \u2014 a bug report to call attention to this\u00a0documentation problem.<\/p>\n<p><strong>Update:<\/strong>\u00a0Fixed the &#8220;Validating Receipts Locally&#8221; link, which was also pointing to the wrong page. Sorry. Also, here&#8217;s one way to do the correct date checking (copied from <a href=\"https:\/\/gist.github.com\/mattstevens\/fa099d99f2fa7247c65e\" target=\"_blank\">Matt Steven&#8217;s code<\/a>):<\/p>\n<p><code>X509_STORE *store;<br \/>\n\/\/ set up the store<br \/>\nX509_VERIFY_PARAM *param = X509_VERIFY_PARAM_new();<br \/>\nX509_VERIFY_PARAM_set_time(param, time_from_receipt); \/\/ option 1: verify using a specific time<br \/>\nX509_STORE_set1_param(store, param);<br \/>\nX509_VERIFY_PARAM_free(param);<br \/>\n\/\/ call PKCS7_verify() using configured store<\/code><\/p>\n","protected":false},"excerpt":{"rendered":"<p>I&#8217;m keeping this post\u00a0updated as details develop&#8230; About ten days ago, something strange happened on my Mac: I was debugging the next version of my RB App Checker Lite\u00a0app and suddenly I saw the dreaded dialog box: Completely abnormal, especially as I was debugging using the Developer ID version (not the Mac App Store version!) [&hellip;]<\/p>\n","protected":false},"author":2,"featured_media":0,"comment_status":"open","ping_status":"closed","sticky":false,"template":"","format":"standard","meta":{"jetpack_post_was_ever_published":false,"_jetpack_newsletter_access":"","_jetpack_dont_email_post_to_subs":false,"_jetpack_newsletter_tier_id":0,"_jetpack_memberships_contains_paywalled_content":false,"_jetpack_memberships_contains_paid_content":false,"footnotes":""},"categories":[3,4,19],"tags":[43,23,42],"class_list":["post-2941","post","type-post","status-publish","format-standard","hentry","category-apple","category-dev","category-software","tag-app-store","tag-mac","tag-rb-utilities"],"featured_image_src":null,"author_info":{"display_name":"Rainer Brockerhoff","author_link":"https:\/\/brockerhoff.net\/blog\/author\/rbrockerhoff\/"},"jetpack_featured_media_url":"","jetpack_shortlink":"https:\/\/wp.me\/p1q3Zc-Lr","jetpack_sharing_enabled":true,"_links":{"self":[{"href":"https:\/\/brockerhoff.net\/blog\/wp-json\/wp\/v2\/posts\/2941","targetHints":{"allow":["GET"]}}],"collection":[{"href":"https:\/\/brockerhoff.net\/blog\/wp-json\/wp\/v2\/posts"}],"about":[{"href":"https:\/\/brockerhoff.net\/blog\/wp-json\/wp\/v2\/types\/post"}],"author":[{"embeddable":true,"href":"https:\/\/brockerhoff.net\/blog\/wp-json\/wp\/v2\/users\/2"}],"replies":[{"embeddable":true,"href":"https:\/\/brockerhoff.net\/blog\/wp-json\/wp\/v2\/comments?post=2941"}],"version-history":[{"count":0,"href":"https:\/\/brockerhoff.net\/blog\/wp-json\/wp\/v2\/posts\/2941\/revisions"}],"wp:attachment":[{"href":"https:\/\/brockerhoff.net\/blog\/wp-json\/wp\/v2\/media?parent=2941"}],"wp:term":[{"taxonomy":"category","embeddable":true,"href":"https:\/\/brockerhoff.net\/blog\/wp-json\/wp\/v2\/categories?post=2941"},{"taxonomy":"post_tag","embeddable":true,"href":"https:\/\/brockerhoff.net\/blog\/wp-json\/wp\/v2\/tags?post=2941"}],"curies":[{"name":"wp","href":"https:\/\/api.w.org\/{rel}","templated":true}]}}