Showing posts with label android security. Show all posts
Signing email with an NFC smart card on Android
Last time we discussed how to access the SIM card and use it as a secure element to enhance Android applications. One of the main problems with this approach is that since SIM cards are controlled by the MNO any applets running on a commercial SIM have to be approved by them. Needless to say, that considerably limits flexibility. Fortunately, NFC-enabled Android devices can communicate with practically any external contactless smart card, and you can install anything on those. Let's explore how an NFC smart card can be used to sign email on Android.
NFC smart cards
IsoDep class. It provides only basic command-response exchange functionality with the transceive() method, any higher level protocol need to be implemented by the client application.Securing email
Signing with S/MIME
The S/MIME, or Secure/Multipurpose Internet Mail Extensions, standard defines how to include signed and/or encrypted content in email messages. It specified both the procedures for creating signed or encrypted (enveloped) content and the MIME media types to use when adding them to the message. For example, a signed message would have a part with theContent-Type: application/pkcs7-signature; name=smime.p7s; smime-type=signed-data which contains the message signature and any associated attributes. To an email client that does not support S/MIME, like most Web mail apps, this would look like an attachment called smime.p7s. S/MIME-compliant clients would instead parse and verify the signature and display some visual indication showing the signature verification status.The more interesting question however is what's in
smime.p7s? The 'p7' stands for PKCS#7, which is the predecessor of the current Cryptographic Message Syntax (CMS). CMS defines structures used to package signed, authenticated or encrypted content and related attributes. As with most PKI X.509-derived standards, those structures are ASN.1 based and encoded into binary using DER, just like certificates and CRLs. They are sequences of other structures, which are in turn composed of yet other ASN.1 structures, which are..., basically sequences all the way down. Let's try to look at the higher-level ones used for signed email. The CMS structure describing signed content is predictably called SignedData and looks like this:SignedData ::= SEQUENCE {
version CMSVersion,
digestAlgorithms DigestAlgorithmIdentifiers,
encapContentInfo EncapsulatedContentInfo,
certificates [0] IMPLICIT CertificateSet OPTIONAL,
crls [1] IMPLICIT RevocationInfoChoices OPTIONAL,
signerInfos SignerInfos }
Here
digestAlgorithms contains the OIDs of the hash algorithms used to produce the signature (one for each signer) and encapContentInfo describes the data that was signed, and can optionally contain the actual data. The optional certificates and crls fields are intended to help verify the signer certificate. If absent, the verifier is responsible for collecting them by other means. The most interesting part, signerInfos, contains the actual signature and information about the signer. It looks like this:SignerInfo ::= SEQUENCE {
version CMSVersion,
sid SignerIdentifier,
digestAlgorithm DigestAlgorithmIdentifier,
signedAttrs [0] IMPLICIT SignedAttributes OPTIONAL,
signatureAlgorithm SignatureAlgorithmIdentifier,
signature SignatureValue,
unsignedAttrs [1] IMPLICIT UnsignedAttributes OPTIONAL }
Besides the signature value and algorithms used,
SignedInfo contains signer identifier used to find the exact certificate that was used and a number of optional signed and unsigned attributes. Signed attributes are included when producing the signature value and can contain additional information about the signature, such as signing time. Unsigned attribute are not covered by the signature value, but can contain signed data themselves, such as counter signature (an additional signature over the signature value).To sum this up, in order to produce a S/MIME signed message, we need to sign the email contents and any attributes, generate the
SignedInfo structure, wrap it into a SignedData, DER encode the result and add it to the message using the appropriate MIME type. Sound easy, right? Let's how this can be done on Android. Using S/MIME on Android
On any platform, you need two things in order to generate an S/MIME message: a cryptographic provider that can perform the actual signing using an asymmetric key and an ASN.1 parser/generator in order to generate theSignedData structure. Android has JCE providers that support RSA, recently even with hardware-backed keys. What's left is an ASN.1 generator. While ASN.1 and DER/BER have been around for ages, and there are quite a few parsers/generators, the practically useful choices are not that many. No one really generates code directly from the ASN.1 modules found in related standards, most libraries implement only the necessary parts, building on available components. Both of Android's major cryptographic libraries, OpenSSL and Bouncy Castle contain ASN.1 parser/generators and have support for CMS. The related API's are not public though, so we need to include our own libraries.As usual we turn to Spongy Castle, which is provides all of Bouncy Castle's functionality under a different namespace. In order to be able process CMS and generate S/MIME messages, we need the optional
scpkix and scmail packages. The first one contains PKIX and CMS related classes, and the second one implements S/MIME. However, there is a twist: Android lacks some of the classes required for generating S/MIME messages. As you may know, Android has implementations for most standard Java APIs, with a few exceptions, most notably the GUI widget related AWT and Swing packages. Those are rarely missed, because Android has its own widget and graphics libraries. However, besides widgets AWT contains classes related to MIME media types as well. Unfortunately, some of those are used in libraries that deal with MIME objects, such as JavaMail and the Bouncy Castle S/MIME implementation. JavaMail versions that include alternative AWT implementations, repackaged for Android have been available for some time, but since they use some non-standard package names, they are not a drop-in replacement. That applies to Spongy Castle as well: some source code modifications are required in order to get scmail to work with the javamail-android library.With that sorted out, generating an S/MIME message on Android is just a matter of finding the signer key and certificate and using the proper Bouncy Castle and JavaMail APIs to generate and send the message:
PrivateKey signerKey = KeyChain.getPrivateKey(ctx, "smime");
X509Certificate[] chain = KeyChain.getCertificateChain(ctx, "smime");
X509Certificate signerCert = chain[0];
X509Certificate caCert = chain[1];
SMIMESignedGenerator gen = new SMIMESignedGenerator();
gen.addSignerInfoGenerator(new JcaSimpleSignerInfoGeneratorBuilder()
.setProvider("AndroidOpenSSL")
.setSignedAttributeGenerator(
new AttributeTable(signedAttrs))
.build("SHA512withRSA", signerKey, signerCert));
Store certs = new JcaCertStore(Arrays.asList(signerCert, caCert));
gen.addCertificates(certs);
MimeMultipart mm = gen.generate(mimeMsg, "SC");
MimeMessage signedMessage = new MimeMessage(session);
Enumeration headers = mimeMsg.getAllHeaderLines();
while (headers.hasMoreElements()) {
signedMessage.addHeaderLine((String) headers.nextElement());
}
signedMessage.setContent(mm);
signedMessage.saveChanges();
Transport.send(signedMessage);
Here we first get the signer key and certificate using the
KeyChain API and then create an S/MIME generator by specifying the key, certificate, signature algorithm and signed attributes. Note that we specify the AndroidOpenSSL provider explicitly which is the only one that can use hardware-backed keys. This is only required if you changed the default provider order when installing Spongy Castle, by default AndroidOpenSSL is the preferred JCE provider. We then add the certificates we want to include in the generated SignedData and generate a multi-part MIME message that includes both the original message (mimeMsg) and the signature. Finally we send the message using the JavaMail Transport class. The JavaMail Session initialization is omitted from the example above, see the sample app for how to set it up to use Gmail's SMTP server. This requires the Gmail account password to be specified, but with a little more work it can be replaced with an OAuth token you can obtain from the system AccountManager.So what about smart cards?
Using a MuscleCard to sign email
- a dual-interface smart cards that supports RSA keys
- a crypto applet that allows us to sign data with those keys
- some sort of middleware that exposes card functionality through a standard crypto API
If the certificate installed in the card has your email in the
Subject Alternative Name extension, you should be able send signed and encrypted emails (if you have the recipient's certificate, of course). But how to achieve the same thing in Android?Using MuscleCard on Android
Android doesn't support PKCS#11 modules, so in order to expose the cards crypto functionality we could implement a custom JCE provider that provides card-backed implementations of theSignature and KeyStrore engine classes. That is quite a bit of work though, and since we are only targeting the Bouncy Castle S/MIME API, we can get away by implementing the ContentSigner interface. It provides an OutputStream clients write data to be signed to, an AlgorithmIdentifer for the signature method used and a getSignature() method that returns the actual signature value. Our MuscleCard-backed implementation could look like this:class MuscleCardContentSigner implements ContentSigner {
private ByteArrayOutputStream baos = new ByteArrayOutputStream();
private MuscleCard msc;
private String pin;
...
@Override
public byte[] getSignature() {
msc.select();
msc.verifyPin(pin);
byte[] data = baos.toByteArray();
baos.reset();
return msc.sign(data);
}
}
Here the
MuscleCard class is our 'middleware' and encapsulates the card's RSA signature functionality. It is implemented by sending the required command APDUs for each operation using Android's IsoDep API and aggregating and converting the result as needed. For example, the verifyPin() is implemented like this: class MuscleCard {
private IsoDep tag;
public boolean verifyPin(String pin) throws IOException {
String cmd = String.format("B0 42 01 00 %02x %s", pin.length(),
toHex(pin.getBytes("ASCII")));
ResponseApdu rapdu = new ResponseApdu(tag.transceive(fromHex(cmd)));
if (rapdu.getSW() != SW_SUCCESS) {
return false;
}
return true;
}
}
Signing is a little more complicated because it involves creating and updating temporary I/O objects, but follows the same principle. Since the applet does not support padding or hashing, we need to generate and pad the PKCS#1 (or PSS) signature block on Android and send the complete data to the card. Finally, we need to plug our signer implementation into the Bouncy Castle CMS generator:
ContentSigner mscCs = new MuscleCardContentSigner(muscleCard, pin);
gen.addSignerInfoGenerator(new JcaSignerInfoGeneratorBuilder(
new JcaDigestCalculatorProviderBuilder()
.setProvider("SC")
.build()).build(mscCs, cardCert));
After that the signed message can be generated exactly like when using local key store keys. Of course, there are a few caveats. Since apps cannot control when an NFC connection is established, we can only sign data after the card has been picked up by the device and we have received an
Intent with a live IsoDep instance. Additionally, since signing can take a few seconds, we need to make sure the connection is not broken by placing the device on top of the card (or use some sort of awkward case with a card slot). Our implementation also takes a few shortcuts by hard-coding the certificate object ID and size, as well as the card PIN, but those can be remedied with a little more code. The UI of our homebrew S/MIME client is shown below.After you import a PKCS#12 file in the system credential store you can sign emails using the imported keys. The 'Sign with NFC' button is only enabled when a compatible card has been detected. The easiest way to verify the email signature is to send a message to a desktop client that supports S/MIME. There are also a few Android email apps that support S/MIME, but setup can be a bit challenging because they often use their own trust and key stores. You can also dump the generated message to external storage using
MimeMessage.writeTo() and then parse the CMS structure using the OpenSSL cms command:$ openssl cms -cmsout -in signed.message -noout -print
CMS_ContentInfo:
contentType: pkcs7-signedData (1.2.840.113549.1.7.2)
d.signedData:
version: 1
digestAlgorithms:
algorithm: sha512 (2.16.840.1.101.3.4.2.3)
parameter: NULL
encapContentInfo:
eContentType: pkcs7-data (1.2.840.113549.1.7.1)
eContent: <absent>
certificates:
d.certificate:
cert_info:
version: 2
serialNumber: 4
signature:
algorithm: sha1WithRSAEncryption (1.2.840.113549.1.1.5)
...
crls:
<empty>
signerInfos:
version: 1
d.issuerAndSerialNumber:
issuer: C=JP, ST=Tokyo, CN=keystore-test-CA
serialNumber: 3
digestAlgorithm:
algorithm: sha512 (2.16.840.1.101.3.4.2.3)
parameter: NULL
signedAttrs:
object: contentType (1.2.840.113549.1.9.3)
value.set:
OBJECT:pkcs7-data (1.2.840.113549.1.7.1)
object: signingTime (1.2.840.113549.1.9.5)
value.set:
UTCTIME:Oct 25 16:25:29 2013 GMT
object: messageDigest (1.2.840.113549.1.9.4)
value.set:
OCTET STRING:
0000 - 88 bd 87 84 15 53 3d d8-72 64 c7 36 f8 .....S=.rd.6.
000d - b0 f3 39 90 b2 a4 77 56-5c 9f e4 2e 7c ..9...wV\...|
001a - 7d 2e 0b 08 b4 b7 e7 6c-e9 b6 61 00 13 }......l..a..
0027 - 25 62 69 2a bc 08 5b 4c-4f c9 73 cf d3 %bi*..[LO.s..
0034 - c6 1e 51 c2 5f c1 64 77-3b 45 e2 cb ..Q._.dw;E..
signatureAlgorithm:
algorithm: rsaEncryption (1.2.840.113549.1.1.1)
parameter: NULL
signature:
0000 - a0 d0 ce 35 46 8c f9 cd-e5 db ed d8 e3 f0 08 ...5F..........
...
unsignedAttrs:
<empty>
Email encryption using the NFC smart card can be implemented in a similar fashion, but this time the card will be required when decrypting the message.
Summary
Using the SIM card as a secure element in Android
Our last post introduced one of Android 4.3's more notable security features -- improved credential storage, and while there are a few other enhancements worth discussing, this post will slightly change direction. As mentioned previously, mobile devices can include some form of a Secure Element (SE), but a smart card based UICC (usually called just 'SIM card') is almost universally present. Virtually all SIM cards in use today are programmable and thus can be used as a SE. Continuing the topic of hardware-backed security, we will now look into how SIMs can be programmed and used to enhance the security of Android applications.
SIM cards
SIM card applications
Ki. To connect to the network the MS needs to authenticate itself and negotiate a session key. Both authentication and session key derivation make use of Ki, which is also known to the network and looked up by IMSI. The MS sends a connection request and includes its IMSI, which the network uses to find the corresponding Ki. The network then uses the Ki to generate a challenge (RAND), expected challenge response (SRES) and session key Kc and sends RAND to the MS. Here's where the GSM application running on the SIM card comes into play: the MS passes the RAND to the SIM card, which in turn generates its own SRES and Kc. The SRES is sent to the network and if it matches the expected value, encrypted communication is established using the session key Kc. As you can see, the security of this protocol hinges solely on the secrecy of the Ki. Since all operations involving the Ki are implemented inside the SIM and it never comes with direct contact with neither the MS or the network, the scheme is kept reasonably secure. Of course, security depends on the encryption algorithms used as well, and major weaknesses that allow intercepted GSM calls to be decrypted using off-the shelf hardware were found in the original versions of the A3/A5 algorithms (which were initially secret). Jumping back to Android for a moment, all of this is implemented by the baseband software (more on this later) and network authentication is never directly visible to the main OS.'7F20' ADF, and the USIM ADF hosts the EF_imsi, EF_keys, EF_sms, etc. files. Practically all SIMs used today are based on Java Card technology and implement GlobalPlatform card specifications. Thus all network applications are implemented as Java Card applets and emulate the legacy file-based structure for backward compatibility. Applets are installed according to GlobalPlatform specifications by authenticating to the Issuer Security Domain (Card Manager) and issuing LOAD and INSTALL commands.Accessing the SIM card
On Android devices all mobile network functionality (dialing, sending SMS, etc.) is provided by the baseband processor (also referred to as 'modem' or 'radio'). Android applications and system services communicate to the baseband only indirectly via the Radio Interface Layer (RIL) daemon (
rild). It in turn talks to the actual hardware by using a manufacturer-provided RIL HAL library, which wraps the proprietary interface the baseband provides. The SIM card is typically connected only to baseband processor (sometimes also to the NFC controller via SWP), and thus all communication needs to go through the RIL. While the proprietary RIL implementation can always access the SIM in order to perform network identification and authentication, as well as read/write contacts and access STK applications, support for transparent APDU exchange is not always available. The standard way to provide this feature is to use extended AT commands such AT+CSIM (Generic SIM access) and AT+CGLA (Generic UICC Logical Channel Access), as defined in 3GPP TS 27.007, but some vendors implement it using proprietary extensions, so support for the necessary AT commands does not automatically provide SIM access.SmartCardService) that can connect to any supported SE (embedded SE, ASSD or UICC) and extensions to the Android telephony framework that allow for transparent APDU exchange with the SIM. As mentioned above, access through the RIL is hardware and proprietary RIL library dependent, so you need both a compatible device and a build that includes the SmartCardService and related framework extensions. Thanks to some work by they u'smile project, UICC access on most variants of the popular Galaxy S2 and S3 handsets is available using a patched CyanogenMod build, so you can make use of the latest SEEK version. Even if you don't own one of those devices, you can use the SEEK emulator extension which lets you use a standard PC/SC smart card reader to connect a SIM to the Android emulator. Note that just any regular Java card won't work out of the box because the emulator will look for the GSM application and mark the card as not usable if it doesn't find one. You can modify it to skip those steps, but a simple solution is to install a dummy GSM application that always returns the expected responses.// connect to the SE service, asynchronous
SEService seService = new SEService(this, this);
// list readers
Reader[] readers = seService.getReaders();
// assume the first one is SIM and open session
Session session = readers[0].openSession();
// open logical (or basic) channel
Channel channel = session.openLogicalChannel(aid);
// send APDU and get response
byte[] rapdu = channel.transmit(cmd);
You will need to request the
org.simalliance.openmobileapi.SMARTCARD permission and add the org.simalliance.openmobileapi extension library to your manifest for this to work. See the official wiki for more details. <manifest ...>
<uses-permission android:name="org.simalliance.openmobileapi.SMARTCARD" />
<application ...>
<uses-library
android:name="org.simalliance.openmobileapi"
android:required="true" />
...
</application>
</manifest>
SE-enabled Android applications
keystore) and be bundled as a system library. This can be accomplished by building a custom ROM which installs our custom keymaster module, but we can also take advantage of the SE without rebuilding the whole system. The most straightforward way to do this is to implement the security critical part of an app inside the SE and have the app act as a client that only provides a user-facing GUI. One such application provided with the SEEK distribution is an SE-backed one-time password (OTP) Google Authenticator app. Since the critical part of OTP generators is the seed (usually a symmetric cryptographic key), they can easily be cloned once the seed is obtained or extracted. Thus OTP apps that store the seed in a regular file (like the official Google Authenticator app) provide little protection if the device OS is compromised. The SEEK GoogleOtpAuthenticator app both stores the seed and performs OTP generation inside the SE, making it impossible to recover the seed from the app data stored on the device.Another type of popular application that could benefit from using an SE is a password manager. Password managers typically use a user-supplied passphrase to derive a symmetric key, which is in turn used to encrypt stored passwords. This makes it hard to recover stored passwords without knowing the passphrase, but naturally security level is totally dependent on its complexity. As usual, because typing a long string with rarely used characters on a mobile device is not a particularly pleasant experience, users tend to pick easier to type, low-entropy passphrases. If the key is stored in an SE, the passphrase can be skipped or replaced with a simpler PIN, making the password manager app both more user-friendly and secure. Let's see how such an SE-backed password manager can be implemented using a Java Card applet and the Open Mobile API.
DIY SIM password manager
Ideally, all key management and encryption logic should be implemented inside the SE and the client application would only provide input (plain text passwords) and retrieve opaque encrypted data. The SE applet should not only provide encryption, but also guarantee the integrity of encrypted data either by using an algorithm that provides authenticated encryption (which most smart card don't natively support currently) or by calculating a MAC over the encrypted data using HMAC or some similar mechanism. Smart cards typically provide some sort of encryption support, starting with DES/3DES for low-end models and going up to RSA and EC for top-of-the-line ones. Since public key cryptography is typically not needed for mobile network authentication or secure OTA (which is based on symmetric algorithms), SIM cards rarely support RSA or EC. A reasonably secure symmetric and hash algorithm should be enough to implement a simple password manager though, so in theory we should be able to use even a lower-end SIM.As mentioned in the previous section, all recent SIM cards are based on Java Card technology, and it is possible to develop and load a custom applet, provided one has access to the Card Manager or OTA keys. Those are naturally not available for commercial MNO SIMs, so we would need to use a blank 'programmable' SIM that allows for loading applets without authentication or comes bundled with the required keys. Those are quite hard, but not impossible to come by, so let's see how such a password manager applet could be implemented. We won't discuss the basics of Java Card programming, but jump straight to the implementation. Refer to the offical documentation, or a tutorial if you need an introduction.
The Java Card API provides a subset of the JCA classes, with an interface optimized towards using pre-allocated, shared byte arrays, which is typical on a memory constrained platform such as a smart card. A basic encryption example would look something like this:
byte[] buff = apdu.getBuffer();
//..
DESKey deskey = (DESKey)KeyBuilder.buildKey(KeyBuilder.TYPE_DES_TRANSIENT_DESELECT,
KeyBuilder.LENGTH_DES3_2KEY, false);
deskey.setKey(keyBytes, (short)0);
Cipher cipher = Cipher.getInstance(Cipher.ALG_DES_CBC_PKCS5, false);
cipher.init(deskey, Cipher.MODE_ENCRYPT);
cipher.doFinal(data, (short) 0, (short) data.length,
buff, (short) 0);
As you can see, a dedicated key object, that is automatically cleared when the applet is deselected, is first created and then used to initialize a
Cipher instance. Besides the unwieldy number of casts to short (necessary because 'classic' Java Card does not support int, but it is still the default integer type) the code is very similar to what you would find in a Java SE or Android application. Hashing uses the MessageDigest class and follows a similar routine. Using the system-provided Cipher and MessageDigest classes as building blocks it is fairly straightforward to implement CBC mode encryption and HMAC for data integrity. However as it happens, our low end SIM card does not provide usable implementations of those classes (even though the spec sheet claims they do), so we would need to start from scratch. Fortunately, since Java cards can execute arbitrary programs (as long as they fit in memory), it is also possible to include our own encryption algorithm implementation in the applet. Even better, a Java Card optimized AES implementation is freely available. This implementation provides only the basic pieces of AES -- key schedule generation and single block encryption, so some additional work is required to match the Java Cipher class functionality. The bigger downside is that by using an algorithm implemented in software we cannot take advantage of the specialized crypto co-processor most smart cards have. With this implementation our SIM (8-bit CPU, 6KB RAM) card takes about 2 seconds to process a single AES block with a 128-bit key. The performance can be improved slightly by reducing the number of AES round to 7 (10 are recommended for 128-bit keys), but that will both lower the security level of the system and result in an non-standard cipher, making testing more difficult. Another disadvantage is that native key objects are usually stored in a secured memory area that is better protected from side channel attacks, but by using our own cipher we are forced to store keys in regular byte arrays. With those caveats, this AES implementation should give us what we need for our demo application. Using the JavaCardAES class as a building block, our AES CBC encryption routine would look something like this:aesCipher.RoundKeysSchedule(keyBytes, (short) 0, roundKeysBuff);
short padSize = addPadding(cipherBuff, offset, len);
short paddedLen = (short) (len + padSize);
short blocks = (short) (paddedLen / AES_BLOCK_LEN);
for (short i = 0; i < blocks; i++) {
short cipherOffset = (short) (i * AES_BLOCK_LEN);
for (short j = 0; j < AES_BLOCK_LEN; j++) {
cbcV[j] ^= cipherBuff[(short) (cipherOffset + j)];
}
aesCipher.AESEncryptBlock(cbcV, OFFSET_ZERO, roundKeysBuff);
Util.arrayCopyNonAtomic(cbcV, OFFSET_ZERO, cipherBuff,
cipherOffset, AES_BLOCK_LEN);
}
Not as concise as using the system crypto classes, but gets the job done. Finally (not shown), the IV and cipher text are copied to the APDU buffer and sent back to the caller. Decryption follows a similar pattern. One thing that is obviously missing is the MAC, but as it turns out a hash algorithm implemented in software is prohibitively slow on our SIM (mostly because it needs to access large tables stored in the slow card EEPROM). While a MAC can be also implemented using the AES primitive, we have omitted it from the sample applet. In practice tampering with the cipher text of encrypted passwords would only result in incorrect passwords, but it is still a good idea to use a MAC when implementing this on a fully functional Java Card.
Our applet can now perform encryption and decryption, but one critical piece is still missing -- a random number generator. The Java Card API has the
RandomData class which is typically used to generate key material and IVs for cryptographic operations, but just as with the Cipher class it is not available on our SIM. Therefore, unfortunately, we need to apply the DIY approach again. To keep things simple and with a (somewhat) reasonable response time, we implement a simple pseudo random number generator (PRNG) based on AES in counter mode. As mentioned above, the largest integer type in classic Java Card is short, so the counter will wrap as soon as it goes over 32767. While this can be overcome fairly easily by using a persistent byte array to simulate a long (or BigInteger if you are more ambitious), the bigger problem is that there is no suitable source of entropy on the smart card that we can use to seed the PRNG. Therefore the PRNG AES key and nonce need to be specified at applet install time and be unique to each SIM. Our simplistic PRNG implementation based on the JavaCardAES class is shown below (buff is the output buffer):Util.arrayCopyNonAtomic(prngNonce, OFFSET_ZERO, cipherBuff,
OFFSET_ZERO, (short) prngNonce.length);
Util.setShort(cipherBuff, (short) (AES_BLOCK_LEN - 2), prngCounter);
aesCipher.RoundKeysSchedule(prngKey, (short) 0, roundKeysBuff);
aeCipher.AESEncryptBlock(cipherBuff, OFFSET_ZERO, roundKeysBuff);
prngCounter++;
Util.arrayCopyNonAtomic(cipherBuff, OFFSET_ZERO, buff, offset, len);
The recent Bitcoin app problems traced to a repeatable PRNG in Android, controversy around the Dual_EC_DRBG PRNG algorithm, which is both believed to be weak by design and is used by default in popular crypto toolkits and finally the low-quality hardware RNG found in FIPS certified smart cards have highlighted the critical impact a flawed PRNG can have on any system that uses cryptography. That is why a DIY PRNG is definitely not something you would like to use in a production system. Do find a SIM that provides working crypto classes and do use
RandomData.ALG_SECURE_RANDOM to initialize the PRNG (that won't help much if the card's hardware RNG is flawed, of course). With that we have all the pieces needed to implement the password manager applet, and what is left is to define and expose a public interface. For Java Card this means defining the values of the
CLA and INS bytes the applet can process. Besides the obviously required encrypt and decrypt commands, we also provide commands to get the current state, initialize and clear the applet.static final byte CLA = (byte) 0x80;
static final byte INS_GET_STATUS = (byte) 0x1;
static final byte INS_GEN_RANDOM = (byte) 0x2;
static final byte INS_GEN_KEY = (byte) 0x03;
static final byte INS_ENCRYPT = (byte) 0x4;
static final byte INS_DECRYPT = (byte) 0x5;
static final byte INS_CLEAR = (byte) 0x6;
Once we have a working applet, implementing the Android client is fairly straightforward. We need to connect to the
SEService, open a logical channel to our applet (AID: 73 69 6d 70 61 73 73 6d 61 6e 01) and send the appropriate APDUs using the protocol outlined above. For example, sending a string to be encrypted requires the following code (assuming we already have an open Session to the SE). Here 0x9000 is the standard ISO 7816-3/4 success status word (SW):Channel channel = session.openLogicalChannel(fromHex("73 69 6d 70 61 73 73 6d 61 6e 01"));
byte[] data = "password".getBytes("ASCII");
String cmdStr = "80 04 00 00 " + String.format("%02x", data.length)
+ toHex(data) + "00";
byte[] rapdu = channel.transmit(fromHex(cmdStr));
short sw = (short) ((rapdu [rapdu.length - 2] << 8) | (0xff & rapdu [rapdu.length - 1]));
if (sw != (short)0x9000) {
// handle error
}
byte[] ciphertext = Arrays.copyOf(rapdu, rapdu.length - 2);
String encrypted= Base64.encodeToString(ciphertext, Base64.NO_WRAP);
Besides calling applet operations by sending commands to the SE, the sample Android app also has a simple database to store encrypted passwords paired with a description, and displays currently managed passwords in a list view. Long pressing on the password name will bring up a contextual action that allows you to decrypt and temporarily display the password so you can copy it and paste it into the target application. The current implementation does not require a PIN to decrypt passwords, but one can easily by provided using Java Card's
OwnerPIN class, optionally disabling the applet once a number of incorrect tries is reached. While this app can hardly compete with popular password managers, it has enough functionality to both illustrate the concept of an SE-backed app and be practially useful. Passwords can be added by pressing the '+' action item and the delete item clears the encryption key and PRNG counter, but not the PRNG seed and nonce. A screenshot of the award-winning UI is shown below. Full source code for both the applet and the Android app is available on Github.Summary
The AOSP version of Android does not provide a standard API to use the SIM card as a SE, but many vendors do, and as long as the device baseband and RIL support APDU exchange, one can be added by using the SEEK for Android patches. This allows to improve the security of Android apps by using the SIM as a secure element and both store sensitive data and implement critical functionality inside it. Commercial SIM do not allow for installing arbitrary user applications, but applets can be automatically loaded by the carrier using the SIM OTA mechanism and apps that take advantage of those applets can be distributed through regular channels, such as the Play Store.Thanks to Michael for developing the Galaxy S2/3 RIL patch and helping with getting it to work on my somewhat exotic S2.
Credential storage enhancements in Android 4.3
Our previous post was not related to Android security, but happened to coincide with the Android 4.3 announcement. Now that the post-release dust has settled, time to give it a proper welcome here as well. Being a minor update, there is nothing ground-breaking, but this 'revenge of the beans' brings some welcome enhancements and new APIs. Enough of those are related to security for some to even call 4.3 a 'security release'. Of course, the big star is SELinux, but credential storage, which has been a somewhat recurring topic on this blog, got a significant facelift too, so we'll look into it first. This post will focus mainly on the newly introduced features and interfaces, so you might want to review previous credential storage posts before continuing.
What's new in 4.3
Public API
KeyGenerator and KeyStore. Both are backed by a new Android JCE provider, AndroidKeyStoreProvider and are accessed by passing "AndroidKeyStore" as the type parameter of the respective factory methods (those APIs were actually available in 4.2 as well, but were not public). For a full sample detailing their usage, refer to the BasicAndroidKeyStore project in the Android SDK. To introduce their usage briefly, first you create a KeyPairGeneratorSpec that describes the keys you want to generate (including a self-signed certificate), initialize a KeyPairGenerator with it and then generate the keys by calling generateKeyPair(). The most important parameter is the alias, which you then pass to KeyStore.getEntry() in order to get a handle to the generated keys later. There is currently no way to specify key size or type and generated keys default to 2048 bit RSA. Here's how all this looks like:// generate a key pair
Context ctx = getContext();
Calendar notBefore = Calendar.getInstance()
Calendar notAfter = Calendar.getInstance();
notAfter.add(1, Calendar.YEAR);
KeyPairGeneratorSpec spec = new KeyPairGeneratorSpec.Builder(ctx)
.setAlias("key1")
.setSubject(
new X500Principal(String.format("CN=%s, OU=%s", alais,
ctx.getPackageName())))
.setSerialNumber(BigInteger.ONE).setStartDate(notBefore.getTime())
.setEndDate(notAfter.getTime()).build();
KeyPairGenerator kpGenerator = KeyPairGenerator.getInstance("RSA", "AndroidKeyStore");
kpGenerator.initialize(spec);
KeyPair kp = kpGenerator.generateKeyPair();
// in another part of the app, access the keys
KeyStore keyStore = KeyStore.getInstance("AndroidKeyStore");
keyStore.load(null);
KeyStore.PrivateKeyEntry keyEntry = (KeyStore.PrivateKeyEntry)keyStore.getEntry("key1", null);
RSAPublicKey pubKey = (RSAPublicKey)keyEntry.getCertificate().getPublicKey();
RSAPrivateKey privKey = (RSAPrivateKey) keyEntry.getPrivateKey();
If the device has a hardware-backed key store implementation, keys will be generated outside of the Android OS and won't be directly accessible even to the system (or root user). If the implementation is software only, keys will be encrypted with a per-user key-encryption master key. We'll discuss key protection in detail later.
Android 4.3 implementation
keystore daemon that used a local socket as its IPC interface. The daemon has finally been retired and replaced with a 'real' Binder service, which implements the IKeyStoreService interface. What's interesting here is that the service is implemented in C++, which is somewhat rare in Android. See the interface definition for details, but compared to the original keymaster-based implementation, IKeyStoreService gets 4 new operations: getmtime(), duplicate(), is_hardware_backed() and clear_uid(). As expected, getmtime() returns the key modification time and duplicate() copies a key blob (used internally for key migration). is_hardware_backed will query the underlying keymaster implementation and return true when it is hardware-backed. The last new operation, clear_uid(), is a bit more interesting. As we mentioned, the key store now supports multi-user devices and each user gets their own set of keys, stored in /data/misc/keystore/user_N, where N is the Android user ID. Keys names (aliases) are mapped to filenames as before, and the owner app UID now reflects the Android user ID as well. When an app that owns key store-managed keys is uninstalled for a user, only keys created by that user are deleted. If an app is completely removed from the system, its keys are deleted for all users. Since key access is tied to the app UID, this prevents a different app that happens to get the same UID from accessing an uninstalled app's keys. Key store reset, which deletes both key files and the master key, also affects only the current user. Here's how key files for the primary user might look like:1000_CACERT_ca
1000_CACERT_cacert
10248_USRCERT_myKey
10248_USRPKEY_myKey
10293_USRCERT_rsa_key0
10293_USRPKEY_rsa_key0
The actual files are owned by the
keystore service (which runs as the keystore Linux user) and it checks the calling UID to decide whether to grant or deny access to a key file, just as before. If the keys are protected by hardware, key files may contain only a reference to the actual key and deleting them may not destroy the underlying keys. Therefore, the del_key() operation is optional and may not be implemented. The hardware in 'hardware-backed'
To give some perspective to the whole 'hardware-backed' idea, let's briefly discuss how it is implemented on the Nexus 4. As you may now, the Nexus 4 is based on Qualcomm's Snapdragon S4 Pro APQ8064 SoC. Like most recent ARM SoC's it is TrustZone-enabled and Qualcomm implement their Secure Execution Environment (QSEE) on top of it. Details are, as usual, quite scarce, but trusted application are separated from the main OS and the only way to interact with them is through the controlled interface the/dev/qseecom device provides. Android applications that wish to interact with the QSEE load the proprietary libQSEEComAPI.so library and use the functions it provides to send 'commands' to the QSEE. As with most other SEEs, the QSEECom communication API is quite low-level and basically only allows for exchanging binary blobs (typically commands and replies), whose contents entirely depends on the secure app you are communicating with. In the case of the Nexus 4 keymaster, the used commands are: GENERATE_KEYPAIR, IMPORT_KEYPAIR, SIGN_DATA and VERIFY_DATA. The keymaster implementation merely creates command structures, sends them via the QSEECom API and parses the replies. It does not contain any cryptographic code itself.An interesting detail is that, the QSEE keystore trusted app (which may not be a dedicated app, but part of more general purpose trusted application) doesn't return simple references to protected keys, but instead uses proprietary encrypted key blobs (not unlike
keymaster key blobs are defined in AOSP code as shown below. This suggest that private exponents are encrypted using AES, most probably in CBC mode, with an added HMAC-SHA256 to check encrypted data integrity. Those might be further encrypted with the Android key store master key when stored on disk.#define KM_MAGIC_NUM (0x4B4D4B42) /* "KMKB" Key Master Key Blob in hex */
#define KM_KEY_SIZE_MAX (512) /* 4096 bits */
#define KM_IV_LENGTH (16) /* AES128 CBC IV */
#define KM_HMAC_LENGTH (32) /* SHA2 will be used for HMAC */
struct qcom_km_key_blob {
uint32_t magic_num;
uint32_t version_num;
uint8_t modulus[KM_KEY_SIZE_MAX];
uint32_t modulus_size;
uint8_t public_exponent[KM_KEY_SIZE_MAX];
uint32_t public_exponent_size;
uint8_t iv[KM_IV_LENGTH];
uint8_t encrypted_private_exponent[KM_KEY_SIZE_MAX];
uint32_t encrypted_private_exponent_size;
uint8_t hmac[KM_HMAC_LENGTH];
};
So, in the case of the Nexus 4, the 'hardware' is simply the ARM SoC. Are other implementations possible? Theoretically, a hardware-backed
keymaster implementation does not need to be based on TrustZone. Any dedicated device that can generate and store keys securely can be used, the usual suspects being embedded secure elements (SE) and TPMs. However, there are no mainstream Android devices with dedicated TPMs and recent flagship devices have began shipping without embedded SEs, most probably due to carrier pressure (price is hardly a factor, since embedded SEs are usually in the same package as the NFC controller). Of course, all mobile devices have some form of UICC (SIM card), which typically can generate and store keys, so why not use that? Well, Android still doesn't have a standard API to access the UICC, even though 'vendor' firmwares often include one. So while one could theoretically implement a UICC-based keymaster module compatible with the UICC's of your friendly neighbourhood MNO, it is not very likely to happen.Security level
So how secure are you brand new hardware-backed keys? The answer is, as usual, it depends. If they are stored in a real, dedicated, tamper-resistant hardware module, such as an embedded SE, they are as secure as the SE. And since this technology has been around for over 40 years, and even recent attacks are only effective against SEs using weak encryption algorithms, that means fairly secure. Of course, as we mentioned in the previous section, there are no currentkeymaster implementations that use actual SEs, but we can only hope.What about TrustZone? It is being aggressively marketed as a mobile security 'silver bullet' and streaming media companies have embraced it as an 'end-to-end' DRM solution, but does it really deliver? While the ARM TrustZone architecture might be sound at its core, in the end trusted applications are just software that runs at a slightly lower level than Android. As such, they can be readily reverse engineered, and of course vulnerabilities have been found. And since they run within the Secure World they can effectively access everything on the device, including other trusted applications. When exploited, this could lead to very effective and hard to discover rootkits. To sum this up, while TrustZone secure applications might provide effective protection against Android malware running on the device, given physical access, they, as well as the TrustZone kernel, are exploitable themselves. Applied to the Android key store, this means that if there is an exploitable vulnerability in any of the underlying trusted applications the
keymaster module depends on, key-encryption keys could be extracted and 'hardware-backed' keys could be compromised.Advanced usage
keystore service directly (as always, not really recommended). Because it is not part of the Android SDK, the IKeyStoreService doesn't have wrapper 'Manager' class, so if you want to get a handle to it, you need to get one directly from the ServiceManager. That too is hidden from SDK apps, but, as usual, you can use reflection. From there, it's just a matter of calling the interface methods you need (see sample project on Github). Of course, if the calling UID doesn't have the necessary permission, access will be denied, but most operations are available to all apps.Class smClass = Class.forName("android.os.ServiceManager");
Method getService = smClass.getMethod("getService", String.class);
IBinder binder = (IBinder) getService.invoke(null, "android.security.keystore");
IKeystoreService keystore = IKeystoreService.Stub.asInterface(binder);
By using the
IKeyStoreService directly you can store symmetric keys or other secret data in the system key store by using the put() method, which the current java.security.KeyStore implementation does not allow (it can only store PrivateKey's). Such data is only encrypted by the key store master key, and even the system key store is hardware-backed, data is not protected by hardware in any way.Accessing hidden services is not the only way to augment the system key store functionality. Since the
sign() operation implements a 'raw' signature operation (RSASP1 in RFC 3447), key store-managed (including hardware-backed) keys can be used to implement signature algorithms not natively supported by Android. You don't need to use the IKeyStoreService interface, because this operation is available through the standard JCE Cipher interface: KeyStore ks = KeyStore.getInstance("AndroidKeyStore");
ks.load(null);
KeyStore.Entry keyEntry = keyStore.getEntry("key1", null);
RSAPrivteKey privKey = (RSAPrivateKey) keyEntry.getPrivateKey();
Cipher c = Cipher.getInstance("RSA/ECB/NoPadding");
cipher.init(Cipher.ENCRYPT_MODE, i privateKey);
byte[] result = cipher.doFinal(in, o, in.length);
If you use this primitive to implement, for example, Bouncy Castle's
AsymmetricBlockCipher interface, you can use any signature algorithm available in the Bouncy Castle lightweight API (we actually use Spongy Castle to stay compatible with Android 2.x without too much hastle). For example, if you want to use a more modern (and provably secure) signature algorithm than Android's default PKCS#1.5 implementation, such as RSA-PSS you can accomplish it with something like this (see sample project for AndroidRsaEngine):AndroidRsaEngine rsa = new AndroidRsaEngine("key1", true);
Digest digest = new SHA512Digest();
Digest mgf1digest = new SHA512Digest();
PSSSigner signer = new PSSSigner(rsa, digest, mgf1digest, 512 / 8);
RSAKeyParameters params = new RSAKeyParameters(false,
pubKey.getModulus(), pubKey.getPublicExponent());
signer.init(true, params);
signer.update(signedData, 0, signedData.length);
byte[] signature = signer.generateSignature();
Likewise, if you need to implement RSA key exchange, you can easily make use of OAEP padding like this:
AndroidRsaEngine rsa = new AndroidRsaEngine("key1", false);
Digest digest = new SHA512Digest();
Digest mgf1digest = new SHA512Digest();
OAEPEncoding oaep = new OAEPEncoding(rsa, digest, mgf1digest, null);
oaep.init(true, null);
byte[] cipherText = oaep.processBlock(plainBytes, 0, plainBytes.length);
The sample application shows how to tie all of those APIs together and features an elegant and fully Holo-compatible user interface:
An added benefit of using hardware-backed keys is that, since they are not generated using Android's default
SecureRandom implementation, they should not be affected by the recently announced SecureRandom vulnerability (of course, since the implementation is closed, we can only hope that trusted apps' RNG actually works...). However, Bouncy Castle's PSS and OAEP implementations do use SecureRandom internally, so you might want to seed the PRNG 'manually' before starting your app to make sure it doesn't start with the same PRNG state as other apps. The keystore daemon/service uses /dev/urandom directly as a source of randomness, when generating master keys used for key file encryption, so they should not be affected. RSA keys generated by the softkeymaster OpenSSL-based software implementation might be affected, because OpenSSL uses RAND_bytes() to generate primes, but are probably OK since the keystore daemon/service runs in a dedicated process and the OpenSSL PRNG automatically seeds itself from /dev/urandom on first access (unfortunately there are no official details about the 'insecure SecureRandom' problem, so we can't be certain).Summary
Code signing in Android's security model
In the previous post we introduced code signing as implemented in Android and saw that it is practically identical to JAR signing. Android requires all installed packages to be signed and makes heavy use of the attached code signing certificates in its security model. This is where the major differences with other platforms that use code signing lie, so we will explore the topic in more detail.
Java access control
policytool). At runtime a security manager (if installed) enforces access control by comparing code elements on the stack with the current policy. It throws a SecurityException if the permissions required to access a resource have not been granted to the requesting code source. Java code that runs (or is started in) the browser, such as applets or Java Web Start applications, is automatically run with a security manager installed, while for local applications you need to explicitly set the java.security.manager in order to install one. In practice, a security manager for local code is only used with some applications servers, and it is usually disabled by default. A wide range of permissions are supported by the platform, the major ones being file and socket-oriented, as well as different types of runtime permissions which control operations ranging from class and library loading to managing the current security manager. By defining multiple code sources and assigning each one specific permissions one can implement fine grained access control for both local and remote code.As we mentioned though, unless you are in the browser plugin or application server development business chances are you hadn't heard about any of this until the beginning of this year. Just when everyone thought that Java applets were for all intents and purposes dead, they made somewhat of a comeback as a malware distribution medium. A series of vulnerabilities were discovered in the Oracle Java implementation that allow applets to escape the sandbox they run in and reset the security manager, effectively granting themselves full privileges. The exploits used to achieve this employ techniques ranging from reflection recursion to direct memory manipulation to bypass runtime security checks. Oracle has responded by releasing a series of patches, changing the default applet execution policy and introducing more visible warnings to let users know that potentially harmful code is being executed. Naturally, different ways to bypass this are being discovered to catch up.
In short, Java has had full-featured code access control for some time, even though the most widely used implementation appears to be lacking in enforcing it. But let's (finally!) get back to Android now. As the Java code access control mechanism can use code signer identity to define code sources and grant permissions, and Android code is required to be signed, one might expect that our favourite mobile OS would be making use of the Java's security model in some form, just as it does with JAR files. As it turns out, this is not the case. Access control related classes are part of the Java API, and are indeed available in Android. However, looking at the implementation reveals that they are practically empty, with just enough code to compile. In addition, they feature a prominent 'Legacy security code; do not use.' notice. So why bother reviewing all of the above then? Even though Android's access control model is very different from the legacy Java one, it does borrow some of the same ideas, and a comparison is helpful when discussing the design decisions made.
Android security architecture basics
READ_LOGS, WRITE_SECURE_SETTINGS) have been introduced that can be granted or revoked on demand using the pm grant/revoke command (or matching system APIs). The system will show a confirmation dialog showing permissions requested by an app before installing. With the exception of the new 'development' permissions, all requested permissions are permanently granted if the the user allows the install. For a certain messaging app it looks like this in Jelly Bean:Android permissions are typically implemented by mapping them to Linux groups that have the necessary read/write access to relevant system resources (files or sockets) and thus are ultimately enforced by the Linux kernel. Some permissions are enforced by system daemons or services by explicitly checking if the calling UID is whitelisted to perform a particular operation. The network access permission (
INTERNET) is somewhat of a hybrid: it is mapped to a group (inet), but since network access is not associated with one particular socket, the kernel checks whether processes trying to open a socket are members of the inet group on each related system call (known as 'paranoid network security').Each permission has an associated 'protection level' that indicates how the system proceeds when deciding whether to grant or deny the permission. The two levels most relevant to our discussion are
signature and signatureOrSystem. The former is granted only to apps signed with the same certificate as the package declaring the permission, while the latter is granted to apps that are in the Android system image, even if the signer is different.Besides the built-in permissions, custom permissions can also be defined by declaring them in the app manifest file. Those can be enforced statically by the system or dynamically by app components. Permissions attached to components (activities, services, broadcast receivers or content providers) defined in
AndroidManifest.xml are automatically enforced by the system. Components can also make use of framework APIs to check whether the calling UID has been granted a required permissions on a case-by-case basis (e.g., only for write operations, etc.). We will introduce other permission related details as necessary later, but you can refer to this Marakana presentation for a more complete and thorough discussion of Android permissions (and more). Of course, some official documentation is also available.The role of code signing
So what are code signing certificates used for then? Two things: making sure updates for an app are coming from the same author (same origin policy), and establishing trust relationships between applications. Both are implemented by comparing the signing certificate of the currently installed target app with the certificate of the update or related application. Comparison boils down to calling
Arrays.equals() on the binary (DER) representation of both certificates. This method naturally knows nothing about CAs or expiration dates. One consequence of this is that once an app (identified by a unique package name) is installed, updates need to use the exact same signing certificates (with one exception, see next section). While multiple signatures on Android apps are not common, if the original application was signed by more than one signer, any updates need to be signed by the same signers, each using its original signing certificate. This means that if your signing certificate(s) expires, you cannot update your app and need to release a new one instead. This would result in not only losing any existing user base or ratings, but more importantly losing access to the legacy app's data and settings (again, there are some exceptions). The solution to this problem is quite simple: don't let your certificate expire. The currently recommended validity period is at least 25 years, and the Google Play Store requires validity until at least October 2033 (Y2K33?). While technically this only amounts to putting off the problem, proper certificate migration support might eventually be added to the platform. Unfortunately, this means that if your signing key is lost or compromised, you are currently out of luck.Let's examine the major uses of code signing in Android in detail.
Application authenticity and identity
PacakgeManagerService, no matter if they are pre-installed, downloaded from an app market or side loaded. It keeps a database of currently installed apps, including their signing certificate(s), granted permissions and additional metadata in the /data/system/packages.xml file. A typical entry for a user-installed app might look like this:<package codepath="/data/app/com.chrome.beta-2.apk"
flags="572996" ft="13e20480558"
installer="com.android.vending"
it="13ca981cbe3" name="com.chrome.beta"
nativelibrarypath="/data/app-lib/com.chrome.beta-2"
userid="10092" ut="13e204816ce" version="1453060">
<sigs count="1">
<cert index="8">
</cert>
</sigs>
<perms>
<item name="android.permission.NFC"/>
...
<item name="com.android.browser.permission.READ_HISTORY_BOOKMARKS"/>
</perms>
</package>
As you can see above, a package entry specifies the package name, the location of the APK and associated libraries, assigned UID and some additional install metadata such as install and update time. This is followed by the number of signatures and the signing certificate as a hexadecimal string. Since a hex-encoded certificate will usually take up around 2K, the actual certificate contents is listed only once. All subsequent packages signed with the same certificate only refer to it by index, as is the case above. The
PackageManagerService uses the <cert/> values in packages.xml to decide whether an update is signed with the same certificate as the original app. The certificate is followed by the list of permissions the package has been granted. All of this information is cached on memory (keyed by package name) at runtime for performance reasons.Just like user-installed apps, pre-installed apps (usually found in
/system/app) can be updated without a full-blown system update, usually via the Play Store or a similar app distribution service. As the /system partition is mounted read-only though, updates are installed in /data, while the original app remains as is. In addition to a <package/> entry, such an app will also have a <updated-package> entry that might look like this:<updated-package name="com.google.android.youtube"
codePath="/system/app/YouTube.apk"
ft="13cd6667b50" it="13ae93df638" ut="13cd6667b50"
version="4216"
nativeLibraryPath="/data/app-lib/com.google.android.youtube-1"
userId="10067">
<perms>
<item name="android.permission.NFC" />
...
</perms>
</updated-package>
The update (in
/data/app) inherits the original app's permissions and UID. System apps receive another special treatment as well: if an updated APK is installed over the original one (in /system/app) it is allowed to be signed with a different certificate. The rationale behind this is that if the installer has enough privileges to write to /system, it can be trusted to change the signing certificate as well. The UID, and any files and permissions are retained. Again, there is an exception though: if the package is part of a shared user (discussed in the next section), the signature cannot be updated, because that would affect other apps as well. In the reverse case, when a new system app signed by a different certificate than that of the currently installed non-system app (with the same package name), the non-system app will be deleted first.Speaking of system apps, most of those are signed by a number of so called 'platform keys'. There are four different keys in the current AOSP tree, named
platform, shared, media and testkey (releasekey for release builds). All packages considered part of the core platform (System UI, Settings, Phone, Bluetooth etc.) are signed with the platform key, launcher and contacts related packages -- with the shared key, the gallery app and media related providers -- with the media key, and everything else (including packages that don't explicitly specify the signing key) -- with the testkey. One thing to note is that the keys distributed with AOSP are in no way special, even though they have 'Google' in the certificate DN. Using them to sign your apps will not give you any specific privileges, you will need the actual keys Google or your carrier/device manufacturer uses. Even though the associated certificates may happen to have the same DN as the ones in AOSP, they are different and very unlikely to be publicly accessible. Custom ROMs are often an exception though, and some, including CyanogenMod, use the AOSP keys, or publicly available keys, as is (there are plans to change this for CyanogenMod though). Sharing the signing key allows packages to work together and establish trust relationships, which we will discuss next.Inter-application trust relationships
Signature permissions
signature protection level. With this level, the permission is only granted if the requesting app is signed by the same signer as the package declaring the permission. This can be thought of as a limited form of mandatory access control (MAC). For custom (app-declared) permission, permissions are declared in the package's AndroidManifest.xml file, and are added to the system when it is installed. Just as other package data, permissions are saved in the /data/system/packages.xml file, as children of the <permissions/> element. Here's how the declaration of a custom permission used by some Google apps looks like: <permissions>
..
<item name="com.google.android.googleapps.permission.ACCESS_GOOGLE_PASSWORD"
package="com.google.android.gsf.login"
protection="2" />
...
</permissions>
The entry has the permission name, declaring package and protection level (2 corresponds to
signature) as attributes. When installing a package that requests this permission, the PackageManagerService will perform binary comparison (just as when upgrading packages) of its signing certificate against the certificate of the Google Login Service (the declaring package, com.google.android.gsf.login) in order to decide whether to grant the permission. A noteworthy detail is that the system cannot grant a permission it doesn't know about. That is, if app A declares permission 'foo' and app B uses it, app B needs to be installed after app A, otherwise you will get a warning at install time and the permission won't be granted. Since app installation order typically cannot be guaranteed, the usual workaround for this situation is to declare the permission in both apps. Permissions can also be added and removed dynamically using the PackageManger.addPermission() API (know as 'dynamic permissions'). However, packages can only add permissions to a permission tree they define (i.e., you cannot add permissions to another app).That mostly explains custom permissions, but what about built-in, system permissions with
signature protection level? They work exactly as custom permissions, except that the package that defines them is special. They are defined in the android package, sometimes also referred as 'the framework' or 'the platform'. The core android framework is the set of classes shared by system services, some of them exposed via the public SDK. Those are packaged in JAR files found in /system/framework. Interestingly, those JAR files are not signed: while Android borrows the JAR format to implement code signing, only APK files are signed, not actual JARs. The only APK file in the framework directory is framework-res.apk. As the name implies, it packages framework resources (animation, drawables, layouts, etc.), but no actual code. Most importantly, it defines the android package and system permissions. Thus any app trying to request a system-level signature permission needs to be signed with the same certificate as the framework resource package. Not surprisingly, it is signed by the platform key discussed in the previous section (usually found in build/target/product/security/platform.pk8|.x509.pem). The associated certificate may looks something like this for an AOSP build:Version: 3 (0x2)
Serial Number: 12941516320735154170 (0xb3998086d056cffa)
Signature Algorithm: md5WithRSAEncryption
Issuer: C=US, ST=California, L=Mountain View, O=Android, OU=Android,
CN=Android/emailAddress=android@android.com
Validity
Not Before: Apr 15 22:40:50 2008 GMT
Not After : Sep 1 22:40:50 2035 GMT
Subject: C=US, ST=California, L=Mountain View, O=Android, OU=Android,
CN=Android/emailAddress=android@android.com
Shared user ID
android:sharedUserId attribute to AndroidManifest.xml's root element. The 'user ID' specified in the manifest needs to be in Java package format (containing at least one '.') and is used as an identifier, much like package names for applications. If the specified shared UID does not exist it is simply created, but if another package with the same shared UID is already installed, the signing certificate is compared to that of the existing package, and if they do not match, a INSTALL_FAILED_SHARED_USER_INCOMPATIBLE error is returned and installation fails. Adding the sharedUserId to the new version of an already installed app will cause it to change its UID, which would result in losing access to its own files (that was the case in some previous Android versions). Therefore, this is disallowed by the system, and it will reject the update with the INSTALL_FAILED_UID_CHANGED error. In short, if you plan to use shared UID for your apps, you have to design for it from the start, and have them use it since the very first release.A shared UID is a first class object in the system's
packages.xml and is treated much like apps are: it has associated signing certificate(s) and permissions. Android has 5 built-in shared UIDs, automatically added when the system is bootstrapped:android.uid.system(SYSTEM_UID, 1000)android.uid.phone(PHONE_UID, 1001)android.uid.bluetooth(BLUETOOH_UID, 1002)android.uid.log(LOG_UID, 1007)android.uid.nfc(NFC_UID, 1027)
Here's how the
system shared UID is defined:<shared-user name="android.uid.system" userId="1000">
<sigs count="1">
<cert index="4" />
</sigs>
<perms>
<item name="android.permission.MASTER_CLEAR" />
<item name="android.permission.CLEAR_APP_USER_DATA" />
<item name="android.permission.MODIFY_NETWORK_ACCOUNTING" />
...
<shared-user/>
As you can see, apart from having a bunch of scary permissions (about 60 on a 4.2 device), the declaration is very similar to the
package declarations we showed previously. Conversely, packages that are a part of a shared UID, do not have an associated granted permission list. They inherit the permissions of the shared UID, which are a union of the permissions requested by all currently installed packages with the same shared UID. A side effect of this is, that if a package is part of a shared UID, it can access APIs it hasn't explicitly requested permissions for, as long as some package with the same shared UID has already requested them. Permissions are dynamically removed from the <shared-user/> declaration as packages are installed or uninstalled though, so the set of available permissions is neither guaranteed nor constant. Here's how the declaration of a system app (KeyChain) that runs under a shared ID looks like. It references the shared UID with the sharedUserId attribute and lacks explicit permission declarations:<package name="com.android.keychain"
codePath="/system/app/KeyChain.apk"
nativeLibraryPath="/data/app-lib/KeyChain"
flags="540229" ft="13cd65721a0"
it="13c2d4721f0" ut="13cd65721a0"
version="17"
sharedUserId="1000">
<sigs count="1">
<cert index="4" />
</sigs>
</package>
The shared UID is not just a package management construct, it actually maps to a shared Linux UID at runtime as well. Here is an example of two system apps running under the
system UID:system 5901 9852 845708 40972 ffffffff 00000000 S com.android.settings
system 6201 9852 824756 22256 ffffffff 00000000 S com.android.keychain
The ultimate trust level on Android is, of course, running in the same process. Since apps that are part of the same shared UID already have the same Linux UID and can access the same system resources, this is not a problem. It can be requested by specifying the same process name in the
process attribute of the <application/> element in the manifest for all apps that need to run in one process. While the obvious result of this is that the apps can share memory and communicate directly instead of using RPC, some system services allow special access to components running in the same process (for example direct access to cached passwords or getting authentication tokens without showing UI prompts). Google apps take advantage of this by requesting to run in the same process as the login service in order to be able to sync data in the background, without user interaction (e.g., Play Services and the Google location service). Naturally, they are signed withe same certificate and part of the com.google.uid.shared shared UID.Summary





