10

I would like to ask the following. We have a mobile app both for Android & iOS that exchanges data with a .NET server.

For Android the ksoap2 library is used, while for iOS the Alamofire with AEXML libraries are used.

We would like to enable encryption for the communication between the server and the apps, specifically Message Security with Mutual Certificates (https://learn.microsoft.com/en-us/dotnet/framework/wcf/feature-details/message-security-with-mutual-certificates)

I am not able to find any information how either the Android or the iOS client could encrypt/decrypt the requests/responses.

Can you please provide any relative information?

Thanks in advance!

Dimitris Makris
  • 5,183
  • 2
  • 34
  • 54

3 Answers3

3

For the iOS Part.

By default, Alamofire will evaluate the certificate chain provided by the server using Apple's built in validation provided by the Security framework.

While this guarantees the certificate chain is valid, it does not prevent man-in-the-middle (MITM) attacks or other potential vulnerabilities.

In order to mitigate MITM attacks, applications dealing with sensitive customer data or financial information should use certificate or public key pinning provided by the ServerTrustPolicy.

ServerTrustPolicy The ServerTrustPolicy enumeration evaluates the server trust generally provided by an URLAuthenticationChallenge when connecting to a server over a secure HTTPS connection.

let serverTrustPolicy = ServerTrustPolicy.pinCertificates(
    certificates: ServerTrustPolicy.certificates(),
    validateCertificateChain: true,
    validateHost: true
)

There are many different cases of server trust evaluation giving you complete control over the validation process:

performDefaultEvaluation: Uses the default server trust evaluation while allowing you to control whether to validate the host provided by the challenge.

pinCertificates: Uses the pinned certificates to validate the server trust. The server trust is considered valid if one of the pinned certificates match one of the server certificates.

pinPublicKeys: Uses the pinned public keys to validate the server trust.

The server trust is considered valid if one of the pinned public keys match one of the server certificate public keys.

disableEvaluation: Disables all evaluation which in turn will always consider any server trust as valid.

customEvaluation: Uses the associated closure to evaluate the validity of the server trust thus giving you complete control over the validation process. Use with caution.

AlamoFire documentation

For Android part, i am not experienced with but i came across someone asking about the same thing and got an answer with

what you need to do is only to install your certificate into the webserver and call the webservice URL like https://my.webservice.url/ instead of http://my.webservice.url/.

If your certificate is a self-signed certificate, means you did not bought it from a certificate authority, you will need to set the SSLSocketFactory. You can check the project wiki on how to do that: http://code.google.com/p/ksoap2-android/wiki/CodingTipsAndTricks#How_to_set_the_SSLSocketFactory_on_a_https_connection__in_order

Check Here.

This might be Helpful too

UPDATE: i've found this framework SOAPEEngine this one. Supports Basic, Digest and NTLM Authentication, WS-Security, Client side Certificate and custom security header.

you check its example for more clarifications too.

Mohmmad S
  • 5,001
  • 4
  • 18
  • 50
  • Thank you for reaching out. However the problem I face is related to the encryption part of the message. The existing API uses the Username Password security mechanism along with certificate pinning, etc., but for increased security the whole message must be encrypted before sent. – Dimitris Makris Sep 27 '18 at 15:11
  • Hi, thanks for the feedback. I am aware of this library, but as you can check in the limitations: "for WCF services, only supports basic http bindings (basicHttpBinding)." and not wsHttpBinding. – Dimitris Makris Sep 27 '18 at 16:29
3

Message encryption with WCF is done through the WS-Security protocol, by setting the security attribute mode to Message. As you have undoubtedly realized by now, WS-Security is not exactly popular on the Android and iOS platforms, mostly due to it having been superseded by other technologies (like HTTPS,) so your choices in terms of existing libraries are not abundant. The fact that not even Microsoft-owned Xamarin supports it says a lot.

First, a word on WS-Security, This protocol provides three main means of enhancing message security:

  • Authentication through security tokens
  • Signing SOAP messages
  • Encryption of SOAP messages

So a conforming implementation should really provide all three of these functions, but we are mostly interested in encryption here, as from the question and comments it seems like you have the authentication part working.

Therefore, assuming we are looking for a mobile platform library providing minimal WCF compatibility with WS-Security signing and encryption:

Android

On Android, the closes to your needs is WSS-Client for Android. This project:

... implements the OASIS Web Service Security (WSS) standard for Android platforms and makes XML Encryption and XML Signature available for tablets and smartphones.

Note that this is GPL-licensed software. The readme says to contact the author for commercial license details. However, it seems to do what you're looking for. Once you have negotiated the key exchange with the server, to encrypt a previously constructed SOAP message using the SOAPUtil class you would do something like:

SOAPMessage message = SOAPUtil.createSOAPMessage();
//... Populate the message (possibly with another library)
SecCrypto serverCrypto = new SecCrypto(serverCertificate, null);
SecCrypto clientCrypto = new SecCrypto(clientPublicKey, clientPrivateKey);
SOAPMessage securedMessage = SOAPUtil.secureSOAPMessage(message, new HashMap<String,String>(SecConstants.REQ_ENCRYPT_SIGN, "yes"), clientCrypto, serverCrypto);
//...
SOAPMessage returnedSecuredMessage = SOAPUtil.sendSOAPMessage(context, securedMessage, endpoint,  cryptoParams);
SOAPMessage returnedMessage = SOAPUtil.validateSOAPMessage(returnedSecuredMessage, new HashMap<String,String>(SecConstants.RES_DECRYPT_VERIFY, "yes", decCrypto);

Nevertheless, be prepared to do quite a bit of configuration work and debugging to make it match your server's needs.

If you are looking for a more current and actively developed product, Quasar Development offers a WS-Security implementation for Android.

iOS

Things look a lot more bleak on the iOS side. There are a few libraries claiming varying degrees of support for WSS, but none of them seem to match your needs:

  • At first SOAPEngine looks the most promising, as it claims support for WS-Security. However, in a footnote it says that it has a limitation that it only supports the WCF basicHttpBinding. This would actually be OK if true. The binding used in the sample code you linked to in the question is wsHttpBinding however it's important to note that both wsHttpBinding and basicHttpBinding have support for encryption though WS-Security. The difference is that wsHttpBinding supports WS-Security by default (whereas it needs to be enabled with basicHttpBinding) and it also supports WS-ReliableMessaging and some other features you may or may not care about. But the basicHttpBinding is the one intended for compatibility with other technologies. So in order to have WS-Security encryption on your WCF server and maximize compatibility with other technologies at the same time, it would be OK to use basicHttpBinding and enable WS-Security signing and encryption by setting the mode security attribute to Message. With the Message attribute, from the docs:

    Security is provided using SOAP message security. By default, the body is encrypted and signed. For this binding, the system requires that the server certificate be provided to the client out of band. The only valid ClientCredentialType for this binding is Certificate.

    But this is of no use as SOAPEngine does not have any support for encrypting messages (or at least I could not find any support for it in the API). The only WS-Security function it supports is authentication. So the claim that it supports WS-Security seems misleading as the support is quite limited.

  • ServiceNow offers very limited support for WS-Security. It only supports verifying server signatures. No encryption or signing on the client side.
  • Chilkat has some rudimentary XML support and there is sample code for WS-Security authentication. I didn't see any support or sample code for WS-Security encryption.

Therefore for iOS, to the best of my knowledge, your two options are:

  • Pick one of the existing libraries that best matches your other needs and reach out to the developer and see if you can get them to add the WS-Security features you need.
  • Implement the bare minimum features you need yourself. The spec is actually not that complicated and there is sample code out there (for non-mobile platforms) that you can use as guide, like WSS4J for example.
mnistic
  • 10,866
  • 2
  • 19
  • 33
0

In Android:

I use kasoap2 to call the web services, but before the call, to enable mutual authentication with client certificate you need to initialize a SSLContext with the client authentication keys (KeyManager). To do that you have to load your certificate and the corresponding password in a KeyStore object, my certificate is a *.pfx file. I cerate a "PKCS12" KeyStore instance. Then you need a KeyManagerFactory object to obtain the KeyManager array. I use a "PKIX" KeyManagerFactory instance. The KeyManager array is needed to init the SSLContext.

Here is an example:

public void enableMutualAuthentication(String filename, String password) {
    try {
        // InputStream to read the certificate file
        FileInputStream cert = new FileInputStream(filename);
        char[] pass = password.toCharArray();

        KeyStore keystore = KeyStore.getInstance("PKCS12");
        keystore.load(cert ,pass);
        cert.close();

        KeyManagerFactory keymanagerfactory = javax.net.ssl.KeyManagerFactory.getInstance("PKIX");
        keymanagerfactory.init(keystore, pass);
        KeyManager[] keymanagers = keymanagerfactory.getKeyManagers();

        // This is not for the mutual authentication.
        // Create a trust manager that does not validate certificate chains
        TrustManager[] trustAllCerts = new TrustManager[] { new X509TrustManager() {
            public java.security.cert.X509Certificate[] getAcceptedIssuers() {
                return null;
            }

            public void checkClientTrusted(X509Certificate[] certs, String authType) {
            }

            public void checkServerTrusted(X509Certificate[] certs, String authType) {
            }
        } };

        // Install the mutual authentication manager
        // Install the all-trusting trust manager
        SSLContext sc = SSLContext.getInstance("SSL");
        sc.init(keymanagers, trustAllCerts, new java.security.SecureRandom());
        HttpsURLConnection.setDefaultSSLSocketFactory(sc.getSocketFactory());

        // Create all-trusting host name verifier
        HostnameVerifier allHostsValid = new HostnameVerifier() {
            public boolean verify(String hostname, SSLSession session) {
                return true;
            }
        };

        // Install the all-trusting host verifier
        HttpsURLConnection.setDefaultHostnameVerifier(allHostsValid);
    } catch (NoSuchAlgorithmException e) {
        e.printStackTrace();
    } catch (KeyManagementException e) {
        e.printStackTrace();
    } catch (IOException e) {
        e.printStackTrace();
    } catch (CertificateException e) {
        e.printStackTrace();
    } catch (UnrecoverableKeyException e) {
        e.printStackTrace();
    } catch (KeyStoreException e) {
        e.printStackTrace();
    }
}

Check those links, was what help me most.

https://chariotsolutions.com/blog/post/https-with-client-certificates-on/ http://callistaenterprise.se/blogg/teknik/2011/11/24/android-tlsssl-mutual-authentication/

frojas
  • 11
  • 2