Search code examples
javaandroidgalaxyopen-mobile-api

Setting up SEEK for Android with Samsung Galaxy S3


Here's some background 1st:

I'm trying to do a proof-of-concept Android application that will check if I can send some APDU commands to a SIM card applet and process the response.

I'm using SEEK for Android as a reference implementation of Open Mobile API.

The application should work on Samsung Galaxy S3 smartphone which comes with Open Mobile API, as stated here.

I am not allowed to use a custom ROM, nor do any modifications to Android source.

What I have done so far:

  1. I've downloaded Android API lvl 18 with Open Mobile API and Open Mobile API packages.
  2. I've created a sample application that should try to access an applet on my SIM card, as described here.
  3. On button click, I'm getting SecurityException

    06-23 12:57:15.620: I/HelloSmartcard(5386): creating SEService object
    06-23 12:57:15.655: I/SEService(5386): Connected
    06-23 12:57:22.525: I/HelloSmartcard(5386): Retrieve available readers...
    06-23 12:57:22.530: I/HelloSmartcard(5386): Create Session from the UICC reader...
    06-23 12:57:23.275: I/HelloSmartcard(5386): Create logical channel within the session...
    06-23 12:57:23.285: E/HelloSmartcard(5386): Error occured:
    06-23 12:57:23.285: E/HelloSmartcard(5386): java.lang.SecurityException: Access Control Enforcer: access denied: EF_DIR not found!!
    06-23 12:57:23.285: E/HelloSmartcard(5386):     at org.simalliance.openmobileapi.SEService.checkForException(SEService.java:234)
    06-23 12:57:23.285: E/HelloSmartcard(5386):     at org.simalliance.openmobileapi.Session.openLogicalChannel(Session.java:302)
    06-23 12:57:23.285: E/HelloSmartcard(5386):     at com.example.testsmartcardaccess2.MainActivity$1.onClick(MainActivity.java:81)
    06-23 12:57:23.285: E/HelloSmartcard(5386):     at android.view.View.performClick(View.java:4475)
    06-23 12:57:23.285: E/HelloSmartcard(5386):     at android.view.View$PerformClick.run(View.java:18786)
    06-23 12:57:23.285: E/HelloSmartcard(5386):     at android.os.Handler.handleCallback(Handler.java:730)
    06-23 12:57:23.285: E/HelloSmartcard(5386):     at android.os.Handler.dispatchMessage(Handler.java:92)
    06-23 12:57:23.285: E/HelloSmartcard(5386):     at android.os.Looper.loop(Looper.java:176)
    06-23 12:57:23.285: E/HelloSmartcard(5386):     at android.app.ActivityThread.main(ActivityThread.java:5419)
    06-23 12:57:23.285: E/HelloSmartcard(5386):     at java.lang.reflect.Method.invokeNative(Native Method)
    06-23 12:57:23.285: E/HelloSmartcard(5386):     at java.lang.reflect.Method.invoke(Method.java:525)
    06-23 12:57:23.285: E/HelloSmartcard(5386):     at com.android.internal.os.ZygoteInit$MethodAndArgsCaller.run(ZygoteInit.java:1046)
    06-23 12:57:23.285: E/HelloSmartcard(5386):     at com.android.internal.os.ZygoteInit.main(ZygoteInit.java:862)
    06-23 12:57:23.285: E/HelloSmartcard(5386):     at dalvik.system.NativeStart.main(Native Method)
    06-23 12:57:23.285: I/Choreographer(5386): Skipped 45 frames!  The application may be doing too much work on its main thread.
    
  4. I have org.simalliance.openmobileapi.jar as dependency:

  5. I have the permission:

    <uses-permission android:name="org.simalliance.openmobileapi.SMARTCARD"/>
    
  6. I have 3 applets on SIM card and I'm trying to call the one under AID F9 F4 0F 65 18 C9 54 1E CD AD

Here is the rough code template I'm using:

package com.example.testsmartcardaccess2;

import org.simalliance.openmobileapi.Channel;
import org.simalliance.openmobileapi.Reader;
import org.simalliance.openmobileapi.SEService;
import org.simalliance.openmobileapi.Session;

import android.app.Activity;
import android.os.Bundle;
import android.util.Log;
import android.view.Menu;
import android.view.MenuItem;
import android.view.View;
import android.view.View.OnClickListener;
import android.widget.Button;
import android.widget.LinearLayout;
import android.widget.LinearLayout.LayoutParams;
import android.widget.Toast;

public class MainActivity extends Activity implements SEService.CallBack {

    private SEService seService;
    private Reader uuicReader;
    private boolean seServiceConnected;

    private Button button;

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        final String LOG_TAG = "HelloSmartcard";

        try {
            Log.i(LOG_TAG, "creating SEService object");
            this.seServiceConnected = false;
            seService = new SEService(MainActivity.this, MainActivity.this);
        } catch (SecurityException e) {
            Log.e(LOG_TAG,
                    "Binding not allowed, uses-permission org.simalliance.openmobileapi.SMARTCARD?");
        } catch (Exception e) {
            Log.e(LOG_TAG, "Exception: " + e.getMessage());
        }

        super.onCreate(savedInstanceState);

        LinearLayout layout = new LinearLayout(this);
        layout.setLayoutParams(new LayoutParams(LayoutParams.WRAP_CONTENT,
                LayoutParams.WRAP_CONTENT));

        button = new Button(this);
        button.setLayoutParams(new LayoutParams(LayoutParams.WRAP_CONTENT,
                LayoutParams.WRAP_CONTENT));

        button.setText("Click Me");
        button.setEnabled(false);
        button.setOnClickListener(new OnClickListener() {
            public void onClick(View v) {
                try {
                    Log.i(LOG_TAG, "Retrieve available readers...");
                    Reader[] readers = seService.getReaders();
                    if (readers.length < 1)
                        return;

                    uuicReader = null;

                    for (Reader reader : readers) {
                        if (reader.getName().equalsIgnoreCase("SIM - UICC")) {
                            uuicReader = reader;
                            break;
                        }
                    }

                    Log.i(LOG_TAG, "Create Session from the UICC reader...");
                    Session session = uuicReader.openSession();

                    Log.i(LOG_TAG,
                            "Create logical channel within the session...");

                    Channel channel = session.openLogicalChannel(
                            new byte[] {
                                (byte) 0xF9, 
                                (byte) 0xF4, 
                                (byte) 0x0F,
                                (byte) 0x65,
                                (byte) 0x18, 
                                (byte) 0xC9,
                                (byte) 0x54, 
                                (byte) 0x1E, 
                                (byte) 0xCD, 
                                (byte) 0xAD
                            }
                    );

                    Log.d(LOG_TAG, "Send HelloWorld APDU command");
                    byte[] respApdu = channel.transmit(new byte[] {
                            (byte) 0x90, 0x10, 0x00, 0x00, 0x00 });

                    channel.close();

                    // Parse response APDU and show text but remove SW1 SW2
                    // first
                    byte[] helloStr = new byte[respApdu.length - 2];
                    System.arraycopy(respApdu, 0, helloStr, 0,
                            respApdu.length - 2);
                    Toast.makeText(MainActivity.this, new String(helloStr),
                            Toast.LENGTH_LONG).show();
                } catch (Exception e) {
                    Log.e(LOG_TAG, "Error occured:", e);
                    return;
                }

            }
        });

        layout.addView(button);
        setContentView(layout);

    }

    @Override
    public boolean onCreateOptionsMenu(Menu menu) {

        // Inflate the menu; this adds items to the action bar if it is present.
        getMenuInflater().inflate(R.menu.main, menu);
        return true;
    }

    @Override
    public boolean onOptionsItemSelected(MenuItem item) {
        // Handle action bar item clicks here. The action bar will
        // automatically handle clicks on the Home/Up button, so long
        // as you specify a parent activity in AndroidManifest.xml.
        int id = item.getItemId();
        if (id == R.id.action_settings) {
            return true;
        }
        return super.onOptionsItemSelected(item);
    }

    @Override
    public void serviceConnected(SEService arg0) {
        Log.i("SEService", "Connected");
        this.seServiceConnected = this.seService.isConnected();

        updateButtonStatus(button, this.seServiceConnected);
    }

    private void updateButtonStatus(Button button, boolean enabled) {
        button.setEnabled(enabled);
    }

    @Override
    protected void onDestroy() {
        if (seService != null && seService.isConnected()) {
            seService.shutdown();
            this.seServiceConnected = false;
            updateButtonStatus(button, this.seServiceConnected);
        }
        super.onDestroy();
    }

}

I believe I don't have something set up right on my SIM card regarding applet access, but since that part is not my domain, I don't know what to do to fix this.

I've ran into this discussion on Google Groups that sound similar to my predicament, but I'm not sure how to interpret it.

Any help is appreciated!


Solution

  • The error

    java.lang.SecurityException: Access Control Enforcer: access denied: EF_DIR not found!!
    

    is quite clear. The Open Mobiel API implementation on the S3's stock firmware requires a PKCS#15 file structure (Access Control File System, ARF) to be present on the secure element (UICC). (Instead of an Access Control Applet (ARA) as introduced by the GlobalPlatform Secure Element Access Control specification.)

    So you would need a file system that looks something like this (see this for an example and see the GlobalPlatform Secure Element Access Control specification for further reference) to be present on the UICC:

    MF (3F00)
    |- EF DIR (2F00) --> shall reference PKCS-15
    |
    |- DF PKCS-15
    |
    |- ODF --> shall reference DODF
    |- DODF --> shall reference EF ACMain
    |- EF ACMain --> shall reference EF ACRules
    |- EF ACRules --> shall reference EF ACConditions files
    |- EF ACConditions1
    |- ...