When a developer (or any distributor of software) code-signs their software, they are digitally signing the executable file that is being distributed. This signature serves as a ‘wax seal’ of sorts – consumers of the software take the intact signature as proof that the code has not been tampered with from between the time it was distributed to the time it was installed on the consumer’s device. In other words, code signing is the process of using public key encryption to affix distributable files with digital signatures, in order to prove to consumers that they are consuming the software in the state the developer intended it to be consumed in.
Code signing is a popular practice across platforms, finding application in fields including but not limited to software upgrades, Mac and PC programs, and mobile applications (primarily on iOS and Android).
Today, the internet is the primary means by which software is distributed, accessed, and used – almost every application on your computer was probably downloaded online. The ubiquity of this medium has also introduced a risk factor involving foul play. Hackers and cyber criminals could, for instance, obtain the source code of an executable file, insert malware into it, and allow the program to be found and downloaded online. Naturally, any user downloading and installing this file would fall victim to the malware.
This scenario is thwarted by the existence of code signing. When you download and proceed to install any program, your operating system does not allow the installation to proceed without checking for the existence of a code signing certificate (more on this below). In the case of a missing certificate from a recognized vendor, the user is alerted to this, and can then choose to proceed with the installation at their discretion.
When a developer code-signs software, what he/she is actually doing is affixing a digital certificate to the file. Like all digital certificates, code-signing certificates also operate on the principle of public key cryptography (more on that here – PKI).
Typical code-signing operations involve two steps: encryption and hashing.
First, the developer needs to generate a unique private key that can be used to encrypt the information. According to the concept of public key cryptography, a private-public key pair is a set of encryption mechanisms to perform encryption/decryption. Once the key pair has been generated, the public key is sent to a trusted issuing body called a Certificate Authority (CA) which verifies the developer’s authenticity, and then affixes their public key with a digitally signed certificate which is the developer’s proof that they are the rightful owner of the key. This certificate is the code-signing certificate in question. The CA sends the public key along with the certificate back to the developer who requested it.
Now that the developer is in possession of a code-signing certificate and an encryption key pair, they must hash the software’s code before they can encrypt and sign it. Hashing is a procedure in which a hash function is used to convert code into an arbitrary fixed value. The output of hashing, called a digest, is then encrypted using the private key. Next, the developer combines this digest with the code-signing certificate and the hash function to create something called a signature block, which is essentially all the above items combined into a piece of code that can be conveniently inserted into software.
Once the developer injects the signature block into the software, it is effectively code-signed, and can be distributed.
What happens when a user downloads an application that has been code-signed?
Most consumer operating systems are pre-configured to check for the presence of a code-signing certificate before an application is installed. When an installation is prompted, the OS first confirms the legitimacy of the certificate, and then uses a public key obtained from the CA to decrypt the digest. This is the original digest, hashed in the previous step – it is kept aside to be used for a comparison, more on that below.
Next, the OS uses the same hash function used by the developer to create another digest from the code. The new digest is compared to the original (the one we kept aside). If they are identical, the OS takes it as proof that the public key corresponds to the private one used to encrypt the code – this can be extrapolated to the logical assumption and fact that the code has not been tampered with since it was distributed by the developer.
In the event of the OS not recognizing the certificate, or if it is unable to match the digests, it asks the user if they want to proceed with the installation, which could be potentially unsafe. Users may then choose to proceed, or to terminate the installation.
Code-signing is a component of almost all commercially packaged and distributed software these days. Apart from Windows, Java, Linux, OSX, and other applications, code such as VBA macros are also code-signed before they’re shared.
Apps that are distributed on major mobile platforms – Google’s Play Store and iOS’ AppStore – have to be signed before they are distributed on their respective platforms. It works a little differently in each case – for instance, in the case of iOS, Apple generates the public-private key pair for the requesting party, and acts as the root certificate for all certificates provided to app developers.
Code signing does not render software completely immune to misuse, tampering, or malicious intent. There is a significant human factor involved with the handling of private keys. Following some industry-standard best practices can contribute towards code-signing being as effective as possible.
Control access to keys: Enforce role-based access to private keys, which can limit the probability of it being misappropriated or stolen – a hacker with access to a legitimate private key could masquerade as the developer and distribute questionable code to unsuspecting users.
Maximize key security standards: Using secure vaults and Hardware Security Modules (HSMs) to increase the degree of security your private keys are getting. This goes a long way towards promoting integrity of private keys and other encryption assets.
Use test-signing certificates: Employ self-signed certificates or those issued by an internal CA to sign pre-release codes.
Exercise caution: The level of security imposed by software distributors notwithstanding, it will all be for naught if the user decides to ignore warnings and install software that has not been code-signed. One must use their best judgement and abstain from installing software that is not trusted in order to make the best use of the system.