简体   繁体   中英

How to modify source code of app after Building? xCode-Sparkle

I'm doing a Mac agent that needs to use two variables, those two variables need to be set every time a user downloads the agent,my first attempt was to modify the Info.plist file and do the signing for Sparkle, but after that I realized that everytime I modify that file and do the signature, that signature would differ from the agents already downloaded and could cause a problem with Sparkle:

  1. Segue for security concerns Since Sparkle is downloading executable code to your users' systems, you must be very careful about security. To let Sparkle know that a downloaded update is not corrupted and came from you (instead of a malicious attacker), we recommend: List item

    • code-signing the published update archive with a DSA signature matching a public DSA key included in your app.

https://sparkle-project.org/documentation/

Any advice on how to achieve this?

Here is the script I was doing to modify and do the signature:

import plistlib, sys, tempfile, subprocess, os, datetime


# Read the plist file generated by xCode, and write the OrganizationID and OrganizationToken.
plist_file = plistlib.Plist.fromFile("Agent.app/Contents/Info.plist") 
plist_file['OrganizationID'] = sys.argv[1]
plist_file['OrganizationToken'] = sys.argv[2]


plistlib.writePlist(plist_file, "Agent.app/Contents/Info.plist")

VERSION = plist_file['CFBundleVersion']
DOWNLOAD_BASE_URL="https://url/core/mac/agent"
RELEASENOTES_URL= DOWNLOAD_BASE_URL + "/release-notes.html#version-$VERSION"
ARCHIVE_FILENAME="Agent %s.zip" % str(VERSION)
DOWNLOAD_URL="%s/$%s" % (DOWNLOAD_BASE_URL, ARCHIVE_FILENAME)
KEYCHAIN_PRIVKEY_NAME="sparkle_private_key/dsa_priv.pem"
os.environ['openssl']= "/usr/bin/openssl"
SIGNATURE= '$openssl dgst -sha1 -binary < "%s" | $openssl dgst -dss1 -sign "%s" | $openssl enc -base64' %  (ARCHIVE_FILENAME, KEYCHAIN_PRIVKEY_NAME)
signature = subprocess.check_output(SIGNATURE, shell=True).strip()
SIZE = 'stat -f %%z "%s"' % ARCHIVE_FILENAME
size = subprocess.check_output(SIZE, shell=True).strip()
PUBDATE = 'LC_TIME=en_US date +"%a, %d %b %G %T %z"'
pubdate = subprocess.check_output(PUBDATE, shell=True).strip()


xml = '''<rss xmlns:sparkle="http://www.andymatuschak.org/xml-namespaces/sparkle" xmlns:dc="http://purl.org/dc/elements/1.1/" version="2.0">
<channel>
<title>Update</title>
<link>
http://sparkle-project.org/files/sparkletestcast.xml
</link>
<description>Most recent changes with links to updates.</description>
<language>en</language>
<item>
<title>Version %s</title>
<sparkle:releaseNotesLink>
%s
</sparkle:releaseNotesLink>
<pubDate>%s</pubDate>
<enclosure
url="%s"
sparkle:version="%s"
type="application/octet-stream"
length="%s"
sparkle:dsaSignature="%s"
/>
</item>
</channel>
</rss>''' % (VERSION, RELEASENOTES_URL, pubdate, DOWNLOAD_URL, VERSION,  size, signature)

If you mean that every user downloading should get a unique bundle (bundle whose Info.plist has been modified) to download, ie that you intend to compute a new DSA signature again for every single download (that's how your question can be understood), what you intend to do isn't very HTTP caching friendly (your server nor nothing in between your server and the end user's machine will be able to cached the app archive being downloaded). I would discourage doing that unless the bundle being downloaded is tiny. In fact, as Developer ID signing is practically required these days to get your software successfully deployed on most Macs effortlessly (Sierra makes unsigned app deployment harder still), you would need to recreate not just the DSA signature for Sparkle (which is actually optional, see below), but use codesign to re-sign app bundle contents itself after the Info.plist contents have changed, meaning that your server that serves your agent downloads would need to run macOS and have the developer tools included.

Incidentally, the DSA signature validation done by Sparkle is optional and is only done if the app is not Developer ID signed (or if the appcast is served over HTTP – which is otherwise also vulnerable to exploits ).

Without knowing more about what kind of per-user state you intend to keep, I would propose instead some kind of scheme where your app would call back to a service of yours to fetch the variable values (for instance passing in input some host identity such as some salted hash of the MAC address or serial number , or even just some per user created random value that is stored in user defaults if user tampering with it is not important), and you would also do runtime signature validations when the app is running to ensure that the end user is not tampering with the app.

I hope this helps! I'm not sure I got your question quite right based on how it was put.

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM