|
Code Signing RecapCode signing is a facility by which developers can assign a digital identity to their programs. Starting with Mac OS X 10.5 Leopard, Apple has provided you with the tools necessary to sign your programs (see the codesign manual pages). The code signing solution on Mac OS X is intended to be completely managed by you. This means that it is up to you to create, or purchase, your code signing certificates (preferably using the certificate assistant found in the Keychain Access Application menu). You are also responsible for maintaining your signing certificates. Normal maintenance issues range from provisioning the certificates, i.e., the policy around supplying them, to updating proper revocation lists. Of course generating conventional policy requirements (see Code Requirements) and coordinating the actual process of signing your code is left up to you. In short, code signing is a technology that allows you to dictate how validating mechanisms will interpret your code. Code signing does implement policy checks. Policy is set by the specific subsystem carrying out validation, however, any policy decisions are left up to you and your end users in how you interoperate between a specific set of subsystems. Trust and Code SigningTrust is determined by policy. A security trust policy determines whether a particular code identity, which is essentially the designated requirement (DR) for the code, should be accepted for allowing something to happen on the system, e.g., access to a resource or service, after testing for validity. Each Mac OS X subsystem has its own policy, and makes this determination separately. Thus, it makes no sense to ask whether code signing trusts a particular signature. You have to ask based on the subsystem, and it is more meaningful to ask whether a specific subsystem trusts your signature. In general, most subsystems do not care that your identity certificate chain leads to a trusted anchor, however, some do. Additionally, some subsystems track identities and some don't. Subsystem tracking alludes to how the subsystem verifies an identity after the initial policy decision has been acted upon. For a concrete example, below is a list of current subsystems that verify code signatures: Table 1 : Mac OS X subsystems that verify the validity of code.
The above examples also further emphasize the fact that all policy decisions are determined by a specific subsystem and not by code signing itself. In addition, it highlights the diversity in how code signing can be used by a specific subsystem to carry out policy. For instance:
Note: The keychain access controls can allow you to associate arbitrary code signing requirements with keychain items. This means that trusted anchor requirements can be attached to keychain items, either with explicit API calls, or by creating an item with an application whose designated requirement has been explicitly set to require a trusted anchor. However, this does not happen by default. Many parts of Mac OS X do not care about the identity of the signer. They care only whether the program is validly signed and stable. Stability is determined through the designated requirement (DR) mechanism, and does not depend on the nature of the certificate authority used. The keychain system and parental controls are examples of such usage. Self-signed identities and homemade certificate authorities (CA) work by default for this case. Other parts of Mac OS X constrain acceptable signatures to only those drawn from certificate authorities that are trusted on the system performing the validation. For those checks, the nature of the identity certificate used does matter. The Leopard Application Firewall is one example of this usage. Self-signed identities and self-created CA will not be verified as being valid for this check unless the verifying system has been told to trust them for Leopard Application Firewall purposes. Note: In order for a new identity certificate to be designated as being a trusted anchor for a particular subsystem, the user must take action to accept this policy addition. For a system-wide trust entry higher privileges are needed, therefore, an administrator user is required to accept the policy addition. Please keep in mind that using a signing identity that is system-wide trusted doesn't automatically mean that it's:
Code RequirementsA code requirement is a statement that expresses constraints on a validly signed application. Code signature validation can accept a requirement as input which will then be used to check whether the code is validly signed and satisfies the constraints of the requirement. When signing code, it is not usually necessary to concern yourself with code requirements. They will be managed implicitly. However, you have the ability to explicitly override the default settings to achieve particular effects. To explicitly test whether a program satisfies a particular requirement, use the -R option to the $admin> # Make a copy of the md5 tool. $admin> cp /sbin/md5 . $admin> # The copy still satisifies its DR. $admin> codesign -vvvv ./md5 ./md5: valid on disk ./md5: satisfies its Designated Requirement $admin> # And we can check that it's signed by Apple. $admin> codesign -vvvv -R="anchor apple" ./md5 ./md5: valid on disk ./md5: satisfies its Designated Requirement ./md5: explicit requirement satisfied $admin> # Modify the binary. $admin> chmod u+w ./md5 $admin> dd if=/dev/zero bs=1 count=1 seek=8192 conv=notrunc of=./md5 1+0 records in 1+0 records out 1 bytes transferred in 0.000036 secs (27777 bytes/sec) $admin> # The modified program no longer satisfies its DR. $admin> codesign -vvvv ./md5 ./md5: code or signature modified $admin> # But we can resign the modified program with our signature. $admin> codesign -s my-signing-identity -f ./md5 ./md5: replacing existing signature $admin> # And the modified program now satisfies its DR. $admin> codesign -vvvv ./md5 ./md5: valid on disk ./md5: satisfies its Designated Requirement $admin> # But not our supplement requirement. $admin> codesign -vvvv -R="anchor apple" ./md5 ./md5: valid on disk ./md5: satisfies its Designated Requirement test-requirement: failed to satisfy code requirement(s) $admin> The requirement language is a set of rules that can be chained together using logical operators ("and", "or", "not", and parentheses to denote precedence) to form a requirement expression. Below is the current list of supported rules and their usage: Table 2 : The requirement language syntax.
Some important things to keep in mind:
Code Designated RequirementAll signed code has a designated requirement (DR). This requirement states, from the perspective of the developer of the program, what constraints a program needs to satisfy in order to be considered an instance of this program. Obviously, every program should satisfy its own DR, e.g., By default, the system synthesizes a suitable DR for your code when you sign your program. This will work fine in most cases. However, you may specify an explicit DR when signing your program, and there are situations where you should do so. To see what DR a program has: $admin> codesign -d -r- /sbin/md5 Executable=/sbin/md5 # designated => identifier "com.apple.md5" and anchor apple $admin> Look for the line starting with "designated =>". If it is commented out (starts with a "#" mark), it is implicitly generated. If not, it is explicit. Use the -r option to the codesign command to explicitly specify a DR when signing; for example: $admin> codesign -vvvv -s my-signing-identity -r="designated => anchor trusted" ~/Desktop/CodesignTest /Users/admin/Desktop/CodesignTest: signed Mach-O thin (i386) [CodesignTest] $admin> If you do create a DR, you are responsible for crafting a suitable requirement to use. For example below is a DR for specifying to the policy engine that it should check that the signer of the program leads to a trusted anchor on the calling system and that the program's identifier matches the supplied parameter string. $admin> codesign -vvvv -s my-signing-identity -r="designated => anchor trusted and \ identifier com.foo.bar" ~/Desktop/CodesignTest /Users/admin/Desktop/CodesignTest: signed Mach-O thin (i386) [CodesignTest] $admin> Note: If you're creating a CA for generating code signatures, then the organization (O) element of the subject distinguished name (DN) from the certificate should be kept consistent throughout your chain of certificates. IMPORTANT: If you're using a certificate from a CA, they may require that you place certain criteria in your DR. Consult with your CA on this matter. Certificate ValidityBy default, the code signing and validation engines accept signatures made with expired certificates. This means that your signed code will not become invalid when your certificate expires. In simple applications, you can continue signing with an expired certificate and Mac OS X will continue to accept this. Code signing will reject signatures made with identities that have been revoked according to standard X.509 processing rules (See RFC 32809 for an example). Revocation check instructions have to be embedded in the certificates you want checked. Revocation checking is a per-user preference found in Keychain Access that is not enabled by default. When revocation checking is configured and enabled the system may still be unable to ascertain revocation status if it is disconnected from the network in which case the validation might succeed instead of fail, i.e., if certificate revocation Keychain Access preferences are set as "Best Attempt" instead of "Require if Cert Indicates". Self-signed Identities and Self-created Certificate AuthoritiesDepending on the policy used by the subsystem in question, a self-signed identity can usually be used (your program will reap all the benefits of being signed by it) as long as that identity is set following the respective policy. Obviously, one big downside in using a self-signed identity is that you will never be able to revoke it, however, it may be sufficient for your organization's certificate policy requirements or if you just want to test out the code signing machinery. If you decide to create your own CA then you specify an explicit DR naming your own anchor certificate: $admin> codesign -vvvv -s my-signing-identity -r="designated => anchor rootCert and identifier com.foo.bar" \ ~/Desktop/CodesignTest /Users/admin/Desktop/CodesignTest: signed Mach-O thin (i386) [CodesignTest] $admin> By using your own CA you gain the ability to issue new identities at will, since any signing identities issued from your CA will now satisfy the check. You can do this by selecting "Create a Certificate for Someone Else as a Certificate Authority" as option for the Certificate Assistant in Keychain Access. You can also do this with openssl: $admin> openssl ca -out cert.rsa -config ./openssl.cnf -infiles request.csr Just as in a custom created CA, if you buy a signing certificate from a commercial CA you'll want to craft a DR that expresses the CA vendor's issuance policies. For instance, every time your CA reissues you a new certificate your identities will change which is something that your DR should take care to handle. Creating a Self-signed Code Signing Certificate using OpenSSLIf you already have an SSL identity, i.e., a public and private key pair generated through OpenSSL, and you want to use it for code signing then you will need to use something other than the Certificate Assistant. This is because converting an existing OpenSSL digital identity for use with code signing is currently unsupported by the Certificate Assistant. However, you can solve this problem using
IMPORTANT: If you are generating a code signing identity from scratch, Apple strongly recommends you use the Certificate Assistant component of Keychain Access, which is equivalent to the OpenSSL approach but in addition it:
TroubleshootingSigning Modifies the ExecutableSigning a program will modify its main executable file. There are some situations where this will cause you trouble:
The obvious solution to these problems is to not meddle with your signed program after you've signed it with $admin> # Let's see what the Mach-O file looks like pre-signing: $admin> otool -l ~/Desktop/CodesignTest CodesignTest: Load command 0 cmd LC_SEGMENT cmdsize 56 segname __PAGEZERO vmaddr 0x00000000 vmsize 0x00001000 fileoff 0 filesize 0 maxprot 0x00000000 initprot 0x00000000 nsects 0 flags 0x0 [...] Load command 11 cmd LC_LOAD_DYLIB cmdsize 52 name /usr/lib/libSystem.B.dylib (offset 24) time stamp 2 Wed Dec 31 16:00:02 1969 current version 111.0.0 compatibility version 1.0.0 $admin> # Now let's see what it looks like after signing: $admin> codesign -vvvv -s my-signing-identity ~/Desktop/CodesignTest CodesignTest: signed Mach-O thin (i386) [CodesignTest] $admin> otool -l CodesignTest CodesignTest: Load command 0 cmd LC_SEGMENT cmdsize 56 segname __PAGEZERO vmaddr 0x00000000 vmsize 0x00001000 fileoff 0 filesize 0 maxprot 0x00000000 initprot 0x00000000 nsects 0 flags 0x0 [...] Load command 11 cmd LC_LOAD_DYLIB cmdsize 52 name /usr/lib/libSystem.B.dylib (offset 24) time stamp 2 Wed Dec 31 16:00:02 1969 current version 111.0.0 compatibility version 1.0.0 Load command 12 cmd LC_CODE_SIGNATURE cmdsize 16 dataoff 12816 datasize 5232 $admin> # Notice the extra load command LC_CODE_SIGNATURE at number 12! $admin> # Let's check it out even further with pagestuff: $admin> pagestuff CodesignTest -a File Page 0 contains Mach-O headers [...] File Page 3 contains local of code signature File Page 4 contains local of code signature $admin> IMPORTANT: Even if you're not intending to sign your program but still thinking about rolling your own integrity checking you need to be mindful of the policies enforced by Mac OS X 10.5 Leopard Parental Controls, MCX, and Application Firewall. Signing FrameworksSeeing as frameworks are bundles it would seem logical to conclude that you can sign a framework directly. However, this is not the case. To avoid problems when signing frameworks make sure that you sign a specific version as opposed to the whole framework: $admin> # This is the wrong way: $admin> codesign -s my-signing-identity ../FooBarBaz.framework $admin> # This is the right way: $admin> codesign -s my-signing-identity ../FooBarBaz.framework/Versions/A Frameworks are "versioned bundles", and each contained version should be signed and validated separately. Omitting Files from the Bundle's SealYour bundle's seal is the When you sign your program and do not pass in any explicit set of rules then $admin> cat /Applications/TextEdit.app/Contents/CodeResources cat /Applications/TextEdit.app/Contents/CodeResources <?xml version="1.0" encoding="UTF-8"?> <!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd"> <plist version="1.0"> <dict> <key>files</key> <dict> <key>Resources/DocumentWindow.nib/classes.nib</key> <dict> <key>hash</key> <data> hw0UoNj0U+XUhBMOhS5Tr5l1PqM= </data> <key>optional</key> <true/> </dict> [...] </dict> <key>rules</key> <dict> <key>^Resources/</key> <true/> <key>^Resources/.*\.lproj/</key> <dict> <key>omit</key> <true/> <key>weight</key> <real>10</real> </dict> [...] </dict> </dict> </plist> $admin> It's entirely possible that you may not find your default seal that was generated to be sufficient. Therefore, if you want to omit or add some files from your bundle's seal you'll want to check out the The format of the property list file that you pass by path into the Currently there are three keys that can be used for the rule definitions:
There is a shorthand way of specifying rules: instead of specifying a dictionary as a rule's value, a simple Boolean can be given instead. True means include and false means exclude from the seal. IMPORTANT: Any rule property definition not mentioned above is reserved for future use. Any resource rule key that doesn't fit this pattern may be interpreted differently now or in the future. An example rule definition property list is given below: <?xml version="1.0" encoding="UTF-8"?> <!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd"> <plist version="1.0"> <dict> <key>rules</key> <dict> <key>^Resources/</key> <true/> <key>^Resources/.*\.lproj/</key> <dict> <key>omit</key> <true/> <key>weight</key> <real>10</real> </dict> <key>^Plugins/</key> <dict> <key>optional</key> <true/> <key>weight</key> <real>30</real> <dict> <key>^version.plist$</key> <true/> </dict> </dict> </plist> The above example illustrates how you would:
If you're not familiar with regular expressions, the following might be helpful:
One thing to keep in mind is that the Extended Key UsageStandard X.509 certificates (RFC 2459) contain object identifiers (OID) which form key usage extensions to define what the public and private key can and cannot be used for. Extended key usages just further refine the key usage extensions. An extension is either critical or non-critical. If the extension is critical than the identity must only be used for indicated purpose(s). The X.509 certificates and their code signing extended key usage is obviously required for an identity to be used for code signing on Mac OS X. However, the code signing extended key usage should also be the only extended key usage for a certificate (this code signing extended usage is critical) in order to be valid to the Mac OS X code signing subsystem. It is not possible to create one certificate that can be used for both code signing and other purposes. You can check your certificate to find out whether the code signing extended key usage attribute is present by viewing the certificate in Keychain Access or by using any other X.509 compliant certificate parser. Dumping all the available information about a certificate can also show if the certificate has other usages which will cause it to be an invalid identity for use with code signing on Mac OS X. For example: $admin> # On Mac OS X 10.5 you can use security and certtool $admin> security find-certificate -a -e clarus@apple.com -p > cert.pem $admin> certtool d cert.pem Serial Number : 01 Issuer Name : Common Name : Testing Code Signing Org : Apple Inc. OrgUnit : DTS State : CA Country : US Locality : Cupertino Email addrs : clarus@apple.com Subject Name : Common Name : Testing Code Signing Org : Apple Inc. OrgUnit : DTS State : CA Country : US Locality : Cupertino Email addrs : clarus@apple.com Cert Sig Algorithm : OID : < 06 09 2A 86 48 86 F7 0D 01 01 05 > alg params : 05 00 Not Before : 19:27:16 Nov 14, 2007 Not After : 19:27:16 Nov 13, 2008 Pub Key Algorithm : OID : < 06 09 2A 86 48 86 F7 0D 01 01 01 > alg params : 05 00 Pub key Bytes : Length 270 bytes : 00 00 00 00 00 00 00 00 ... CSSM Key : Algorithm : RSA Key Size : 2048 bits Key Use : CSSM_KEYUSE_VERIFY Signature : 256 bytes : FF FF FF FF FF FF FF FF ... Other field: : OID : < 06 0C 60 86 48 01 86 F8 4D 02 01 01 01 17 > Other field: : OID : < 06 0C 60 86 48 01 86 F8 4D 02 01 01 01 16 > Extension struct : OID : < 06 03 55 1D 0F > Critical : TRUE usage : DigitalSignature Extension struct : OID : < 06 03 55 1D 25 > Critical : TRUE purpose 0 : OID : < 06 08 2B 06 01 05 05 07 03 03 > $admin> Shipping your Signed CodeCode signing uses extended attributes. If the extended attributes are lost then the program's identity will be break. Thus, when you ship your program, you must use a mechanism that preserves extended attributes. One way to guarantee preservation of extended attributes is by packing up your signed code in a read-write disk image (DMG) file before signing and then, after signing, converting to read-only. You should also be careful to not move your application from a system later than Mac OS X 10.3 to a system at or earlier than Mac OS X 10.3 and then back to a system later than MAc OS 10.3 again as it will drop the extended attributes following that transport pattern. You probably don't need to use a disk image until the final package stage so another less heavy-handed method would be to use ZIP files. Mac OS X 10.5 Parental Controls, MCX, and Application FirewallThe Parental Controls, MCX, and Application Firewall subsystems in Leopard, when encountering an unsigned program, will ad hoc sign the program in order to track its identity from launch to launch. This will usually modify the program on disk, and can happen without apparent user input, e.g., when the Application Firewall first notices that your program is trying to accept an inbound network connection. If your program can be damaged by signing, it is imperative that you ship a signed version as soon as practical. Programs signed by their manufacturer are not modified in this way. Helper tools and other executable extrasIf you have bundles that contain helper code, e.g., privileged tools, scripts, plug-ins, libraries, etc., do not put them into the Resources folder of the bundle. Files in the Resource folder are directly sealed to the main executable. Helper tools and libraries should be stored in A suitable place for a helper bundle is the support directory Document Revision History
Posted: 2008-08-06 |
|