Working with secure ArcGIS services

Complexity: Beginner Data Requirement: ArcGIS Tutorial Data for Desktop

Layers and Tasks in ArcGIS API for Android communicate with ArcGIS Server web services. These services may be secured to permit access to only authorized users. An ArcGIS Server instance can use one of two authentication methods: token-based authentication or HTTP (including Windows) authentication. Both types of authentication modes are supported by the API. The information below will assist you once you have the connection and login information.

If you are the administrator of an ArcGIS Server system and wish to restrict access to your ArcGIS Web services, information is available in the "Securing Internet connections and Web applications" chapter in the ArcGIS Server Help. The Help is available on your server or online at ESRI for both Windows and Linux.

Services secured with token-based authentication

Services secured with token-based authentication require that a token be included in each request for a map, query, and so on. To use a secure service through the API, you need to know the credentials (username and password) or tokens to access the service. The server administrator can provide this information. Once you know the credentials or tokens to use, you need to pass them to the layer or the task through a UserCredentials object in the Java code or hardcode the tokens in the layout xml by including a long-term token directly in the URL. The following examples demonstrate several different ways to access secure services.

You should instantiate the layer or the task with credentials through UserCredentials object in Java code. For example:

UserCredentials creds = new UserCredentials();
creds.setUserAccount("username", "password ");
// The following line can be omitted if the default token service is used.
creds.setTokenServiceUrl("http://hostname/ArcGIS/tokens");	
ArcGISDynamicMapServiceLayer layer = new ArcGISDynamicMapServiceLayer(
  "http://servicesbeta.esri.com/ArcGIS/rest/services/SanJuan/TrailConditions/MapServer",
  null,
  creds);

Alternatively, instead of passing username and password you can pass in a token obtained from the corresponding token service as so.

UserCredentials creds = new UserCredentials();
creds.setUserToken("token", "referer");
Locator al = new Locator(
  "http://xxx.esri.com/ArcGIS/rest/services/SanFranciscoLocator/GeocodeServer",
  creds);

The API will automatically try to discover the URL of the token service where tokens can be acquired. If you know this information in advance, you can provide it to the UserCredentials object as shown in the above example so that it does not make any unnecessary network requests to discover the same information.

Using self-signed certificates

To safeguard content exchanged over the network from attacks, HTTPS should be used whenever supported by the service. HTTPS connections use Secure Sockets Layer (SSL) to encrypt information that is exchanged over the network and digital certificates to verify identities of the parties involved. The ArcGIS API for Android supports both certificates issued by a trusted certificate authority and self-signed certificates. If you want to trust a server that is using self-signed certificate, you can choose one from two workflows supported by ArcGIS API for Android based on your business requirement.

Workflow 1. Trust a certain server using self-signed certificates by importing the server's certificate and pre-loading the trusted certificate on the client side. This workflow enables server authentication when supplied with a truststore file containing one or several trusted certificates. For example:

// Load self-signed certificate
KeyStore keyStore = KeyStore.getInstance("BKS");
InputStream is = this.getResources().openRawResource(R.raw.xxx);
keyStore.load(is, "xxx".toCharArray());
// Populate Security Info
UserCredentials creds = new UserCredentials();
creds.setUserAccount("username", "password");
UserCredentials.setTrustStore(keyStore);
ArcGISFeatureLayer layer = new ArcGISFeatureLayer(
  "https://xxx.esri.com/ArcGIS/rest/services/dc_fire_sde/FeatureServer/1",
  MODE.ONDEMAND,
  creds);

Workflow 2 allows users to trust a server using self-signed certificates without pre-loading the server certificate. To do so you should create an instance of OnSelfSignedCertificateListener and register it to SelfSignedCertificateHandle which is a final class. OnSelfSignedCertificateListener will be fired when the server is using self-signed certificate. Return true from OnSelfSignedCertificateListener if you want to trust the server. The following code snippet shows such a workflow.

import com.esri.core.io.SelfSignedCertificateHandle;
……
public boolean getUserPermission() {

}s
// Set listener to handle self-signed certificate
SelfSignedCertificateHandle.setOnSelfSignedCertificateListener(
  new OnSelfSignedCertificateListener() {
    public boolean checkServerTrusted(X509Certificate[] chain, String authType) {
      try {
        chain[0].checkValidity();
      } catch (Exception e) {
        return getUserPermission();
      }

      return true;
    }
  s});

Services secured with HTTP/Windows authentication

When a request is made to a service secured with HTTP authentication (HTTP basic or HTTP digest), a user credentials must be supplied to the service through UserCredentials object. For Example:

UserCredentials creds = new UserCredentials();
creds.setUserAccount("xxx", "xxx");                         
Geoprocessor gp = new Geoprocessor(
  "https://hostname/ArcGIS/rest/services/BufferService/GPServer/BufferPoints",
  creds);

Exception Handling

When a layer is failed to be loaded to a map due to security, both the layer and the map will send out EsriSecurityException error. Users can listen to the status changed events on MapView or Layer to handle the error. You can listen to the LAYER_LOADING_FAILED on MapView or INITIALIZATION_FAILED on Layer and get the error detail through getError() method on STATUS. A layer can be re-initialized with a given credential. If the layer is re-initialized successfully, it will be reloaded into the map automatically. The following is an example of exception handling through MapView.

MapView map;
ArcGISFeatureLayer fl;

public void onCreate(Bundle savedInstanceState) {
  super.onCreate(savedInstanceState);
  map = new MapView(this);
  String url = ...;
  fl = new ArcGISFeatureLayer(url, MODE.ONDEMAND);
  map.addLayer(fl);
  setContentView(map);
  ...
  // Handle status change event on MapView
  map.setOnStatusChangedListener(new OnStatusChangedListener() {
    private static final long serialVersionUID = 1L;

    public void onStatusChanged(Object source, STATUS status) {
      // Check if a layer is failed to be loaded due to security
      if (status == STATUS.LAYER_LOADING_FAILED) {
        if ((status.getError()) instanceof EsriSecurityException) {
          EsriSecurityException securityEx = (EsriSecurityException) status.getError();
          if (securityEx.getCode() == EsriSecurityException.AUTHENTICATION_FAILED)
            Toast.makeText(map.getContext(), "Authentication Failed!", 
                Toast.LENGTH_SHORT).show();
          else if (securityEx.getCode() == EsriSecurityException.TOKEN_INVALID)
            Toast.makeText(map.getContext(), "Invalid Token!", Toast.LENGTH_SHORT).show();
          else if (securityEx.getCode() == EsriSecurityException.TOKEN_SERVICE_NOT_FOUND)
            Toast.makeText(map.getContext(), "Token Service Not Found!", 
                Toast.LENGTH_SHORT).show();
          else if (securityEx.getCode() == EsriSecurityException.UNTRUSTED_SERVER_CERTIFICATE)
            Toast.makeText(map.getContext(), "Untrusted Host!", Toast.LENGTH_SHORT).show();
              
          if (source instanceof ArcGISFeatureLayer) {
            // Set user credential through username and password
            UserCredentials creds = new UserCredentials();
            creds.setUserAccount("username", "password");
            fl.reinitializeLayer(creds); 
          }                
        }						
      }
    }        
  });  
  ...

When an operational layer of a WebMap is failed to be loaded due to security, the map will send out EsriSecurityException error. Users can listen to the WebMap loading events to handle the error as so.

MapView map;

public void onCreate(Bundle savedInstanceState) {
  ...
  OnWebMapLoadListener listener = new OnWebMapLoadListener() {

    public MapLoadAction<UserCredentials> onWebMapLoadError(MapView source,
      WebMap webmap, WebMapLayer wmlayer, Layer layer, Throwable error,
      UserCredentials credentials) {
      if (error instanceof EsriSecurityException) {
        UserCredentials creds;
        creds = new UserCredentials();
        creds.setUserAccount("username", "password");

        if (credentials != null && (credentials.getUserName() == creds.getUserName() 
            && credentials.getPassword() == creds.getPassword())) 
          return null;
										
        return new MapLoadAction<UserCredentials>(Action.CONTINUE_OPEN_WITH_THE_PARAMETER, creds);
      }
      return null;
   }

   public void onWebMapLayerAdd(MapView source, WebMap webmap,
     WebMapLayer wmlayer, Layer layer, UserCredentials credentials) {
     ...
   }

  };

  String url = ...;
  map = new MapView(this, url, "", "", "", listener);
  ...
 }

Public Key Infrastructure

A public-key infrastructure (PKI) is a set of policies and services used to support authentication and secure communication over insecure networks. In a PKI, the identity of a user, organization or software agent is represented by a pair of digital keys. Users in a PKI are required to authenticate themselves by presenting their digital keys and are never issued a user name and password. PKI uses a mathematical technique called public key cryptography to generate the digital keys that represent a user\organization.

Using ArcGIS Server in a PKI

ArcGIS Server will leverage the PKI solution in COTS Web Servers (IIS, WebLogic and Websphere, etc) through the use of the ArcGIS Web Adaptor. When a request is made for a resource on ArcGIS Server, the Web Server will authenticate the user by validating the client certificate provided. The request (along with the user name) is then forwarded to ArcGIS Server via the Web Adaptor. ArcGIS Server will verify that the specified user has access to the requested resource before sending back the appropriate response. You can find more information about how to enable SSL using a new CA-signed certificate here.

Accessing PKI ArcGIS Service

Accessing PKI services from Android devices is OS dependent. You can install certificates on your device from Settings > Security > Install certificates from storage. At issue is the fact that you might not be able to access certificates installed in this manner from devices with v3.x (Honeycomb) or below. Version 4.0.x Ice Cream Sandwich (ICS) introduces a new KeyChain class to access these certificates. For those using devices pre ICS you can access some of the installed certificates in a keystore located in the following locations:

  • Pre ICS > /system/etc/security/cacerts.bks
  • ICS or greater > /system/etc/security/cacerts

You can just the read certificate from there, but beware some devices don't list the one you just installed. In this case you can access your certificate from anywhere on the device that is accesible by the API. In order to support all devices with API 2.2 and above we use the KeyStore class which maintains keys and their owners. An example is offered below:

Access PKI from sdcard location

KeyStore keystore = KeyStore.getInstance("BKS");
certInputStream = new FileInputStream("/sdcard/cert/example.bks"); 
keystore.load(is, "web.adf".toCharArray());
UserCredentials.setTrustStore(keystore, "web.adf", keystore);
1/24/2013