适用于 Android 和 iOS 的具有相互证书的消息安全性

Posted

技术标签:

【中文标题】适用于 Android 和 iOS 的具有相互证书的消息安全性【英文标题】:Message Security with Mutual Certificates for Android & iOS 【发布时间】:2019-02-24 22:42:06 【问题描述】:

我想问以下问题。我们有一款适用于 androidios 的移动应用,可与 .NET 服务器交换数据。

Android 使用 ksoap2 库,而 iOS 使用 Alamofire 和 AEXML 库。

我们希望为服务器和应用程序之间的通信启用加密,特别是带有相互证书的消息安全 (https://docs.microsoft.com/en-us/dotnet/framework/wcf/feature-details/message-security-with-mutual-certificates)

我无法找到任何关于 Android 或 iOS 客户端如何加密/解密请求/响应的信息。

您能否提供任何相关信息?

提前致谢!

【问题讨论】:

你的意思是加密请求的正文? 不,整条消息都基于这个en.wikipedia.org/wiki/WS-Security 【参考方案1】:

对于 iOS 部分。

默认情况下,Alamofire 将使用安全框架提供的 Apple 内置验证来评估服务器提供的证书链。

虽然这保证了证书链是有效的,但它并不能防止中间人 (MITM) 攻击或其他潜在漏洞。

为了减轻 MITM 攻击,处理敏感客户数据或财务信息的应用程序应使用 ServerTrustPolicy 提供的证书或公钥固定。

服务器信任策略 当通过安全的 HTTPS 连接连接到服务器时,ServerTrustPolicy 枚举评估通常由 URLAuthenticationChallenge 提供的服务器信任。

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

有许多不同的服务器信任评估案例让您可以完全控制验证过程:

performDefaultEvaluation:使用默认的服务器信任评估,同时允许您控制是否验证挑战提供的主机。

pinCertificates:使用固定证书来验证服务器信任。如果固定证书之一与服务器证书之一匹配,则认为服务器信任有效。

pinPublicKeys:使用固定的公钥来验证服务器信任。

如果固定的公钥之一与服务器证书公钥之一匹配,则认为服务器信任有效。

disableEvaluation:禁用所有评估,这反过来将始终将任何服务器信任视为有效。

customEvaluation:使用关联的闭包来评估服务器信任的有效性,从而让您完全控制验证过程。谨慎使用。

阿拉莫火documentation

对于Android部分,我没有经验,但我遇到有人问同样的事情并得到了答案

您需要做的只是将您的证书安装到网络服务器中并调用网络服务 URL,如 https://my.webservice.url/ 而不是 http://my.webservice.url/。

如果您的证书是自签名证书,则意味着您没有 从证书颁发机构购买的,您需要设置 SSLSocketFactory。您可以查看项目 wiki 以了解如何执行此操作: http://code.google.com/p/ksoap2-android/wiki/CodingTipsAndTricks#How_to_set_the_SSLSocketFactory_on_a_https_connection__in_order

检查Here。

This 可能也很有帮助

更新:我找到了这个框架SOAPEEngine 这个。 支持基本、摘要和 NTLM 身份验证、WS-Security、客户端证书和自定义安全标头。

您也可以查看example 以获得更多说明。

【讨论】:

感谢您与我们联系。但是我面临的问题与消息的加密部分有关。现有 API 使用用户名密码安全机制以及证书固定等,但为了提高安全性,必须在发送前对整个消息进行加密。 您好,感谢您的反馈。我知道这个库,但您可以检查限制:“对于 WCF 服务,仅支持基本的 http 绑定 (basicHttpBinding)。”而不是 wsHttpBinding。【参考方案2】:

使用 WCF 的消息加密是通过 WS-Security 协议完成的,方法是将安全属性 mode 设置为 Message。毫无疑问,您现在已经意识到,WS-Security 在 Android 和 iOS 平台上并不完全流行,主要是因为它已被其他技术(如 HTTPS)取代,因此您在现有库方面的选择并不丰富。 not even Microsoft-owned Xamarin supports it 这个事实说明了很多。

首先,关于 WS-Security,该协议提供了三种主要的增强消息安全性的手段:

通过安全令牌进行身份验证 签署 SOAP 消息 SOAP 消息的加密

因此,符合要求的实现应该真正提供所有这三个功能,但我们对这里的加密最感兴趣,因为从问题和 cmets 看来,您的身份验证部分正在工作。

因此,假设我们正在寻找一个提供与 WS-Security 签名和加密的最小 WCF 兼容性的移动平台库:

安卓

在 Android 上,最接近您的需求是 WSS-Client for Android。本项目:

... 实现了 OASIS Web 服务安全 (WSS) 标准 Android 平台并使 XML 加密和 XML 签名可用 适用于平板电脑和智能手机。

请注意,这是 GPL 许可软件。自述文件说要联系作者以获取商业许可的详细信息。但是,它似乎可以满足您的要求。与服务器协商密钥交换后,要使用 SOAPUtil 类加密先前构建的 SOAP 消息,您可以执行以下操作:

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);

不过,请准备好进行大量配置工作和调试,以使其符合您服务器的需求。

如果您正在寻找更新且积极开发的产品,Quasar Development 提供了适用于 Android 的 WS-Security 实现。

iOS

iOS 方面的情况看起来要黯淡得多。有几个库声称对 WSS 的支持程度不同,但似乎没有一个库能满足您的需求:

一开始SOAPEngine 看起来最有希望,正如它所声称的那样 支持 WS-Security。然而,在一个脚注中它说它有一个 限制它只支持 WCF basicHttpBinding。这 如果属实,实际上会没问题。您在示例代码中使用的绑定 问题中的链接是wsHttpBinding,但这很重要 注意wsHttpBindingbasicHttpBinding 都有支持 通过 WS-Security 进行加密。不同之处在于 wsHttpBinding 默认支持 WS-Security(而它需要 启用basicHttpBinding),它还支持 WS-ReliableMessaging 和 some other features 你可能会也可能不会 关心。但是basicHttpBinding 是用于 与其他技术的兼容性。所以为了拥有 WCF 服务器上的 WS-Security 加密并最大限度地提高兼容性 与其他技术同时使用,就可以了 basicHttpBinding 并通过以下方式启用 WS-Security 签名和加密 将mode 安全属性设置为Message。随着 Message属性,from the docs:

使用 SOAP 消息安全性提供安全性。默认情况下,正文 已加密和签名。对于此绑定,系统要求 服务器证书以带外方式提供给客户端。唯一的 此绑定的有效 ClientCredentialType 是 Certificate。

但这没有用,因为 SOAPEngine 不支持 加密消息(或者至少我找不到任何支持它 在 API 中)。它支持的唯一 WS-Security 功能是 验证。所以它支持 WS-Security 的说法似乎 由于支持非常有限,因此具有误导性。

ServiceNow 对 WS-Security 的支持非常有限。它只是 支持验证服务器签名。没有加密或签名 客户端。 Chilkat 有一些基本的 XML 支持,并且有示例 WS-Security 身份验证的代码。我没有看到任何支持或 WS-Security 加密的示例代码。

因此,据我所知,对于 iOS,您的两个选择是:

选择最符合您其他需求的现有库之一 并联系开发人员,看看您是否可以让他们添加 您需要的 WS-Security 功能。 实现最低限度的功能 你需要你自己。 The spec 其实没那么复杂 那里有示例代码(适用于非移动平台),您 可以用作指南,例如 WSS4J。

【讨论】:

【参考方案3】:

在安卓中:

我使用 kasoap2 调用 Web 服务,但在调用之前,要启用与客户端证书的相互身份验证,您需要使用客户端身份验证密钥 (KeyManager) 初始化 SSLContext。 为此,您必须在 KeyStore 对象中加载您的证书和相应的密码,我的证书是一个 *.pfx 文件。我创建了一个“PKCS12”KeyStore 实例。然后你需要一个 KeyManagerFactory 对象来获取 KeyManager 数组。我使用“PKIX” KeyManagerFactory 实例。初始化 SSLContext 需要 KeyManager 数组。

这是一个例子:

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();
    

检查这些链接,对我帮助最大。

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

【讨论】:

以上是关于适用于 Android 和 iOS 的具有相互证书的消息安全性的主要内容,如果未能解决你的问题,请参考以下文章

一个自签名证书来统治他们? Chrome、Android 和 iOS

我正在使用 `fcm` gem 发送推送通知,它适用于 android 但不适用于 IOS

适用于 ios 的 Android 应用程序包(动态功能)?

适用于 iOS 的 Google Firebase 推送通知在生产环境中不起作用

没有适用于Ios App的合格套装ID,解决

适用于 Android 和 iOS 应用程序的 WebSockets