Windows Phone 8/8.1 上的证书锁定






3.33/5 (3投票s)
证书固定确保客户端将服务器证书与该证书的已知副本进行比对
引言
这篇博文主要讲述了我们在 Windows Phone 8 和 8.1 上实现 证书锁定 时遇到的挑战。执行证书锁定的客户端为正常的 TLS 协议或 SSL 协议增加了一个额外的安全步骤。
我们可以说,证书锁定是为了确保客户端根据已知的证书副本检查服务器的证书。证书锁定提供了传输层安全性,并验证客户端和服务器之间的请求。它通过将服务器证书的公钥与客户端证书的公钥进行匹配来实现。它通过阻止无效请求来增强移动设备的安全性。
背景
在实现证书锁定之前,由于 Microsoft/MSDN 站点上缺少文档、博客或任何其他信息,我们确实在 WP 8 和 WP8.1 上面临了很多挑战。我们尝试从以下几个 URL 获取帮助,但由于信息不足,无法就证书锁定得出结论。
流套接字:http://msdn.microsoft.com/en-us/library/windows/apps/xaml/jj150599.aspx
通常,通过验证签名层次结构(服务器证书-中间证书-根证书)来信任站点的证书,因为根证书将存在于证书存储区(受信任的根证书)中。证书锁定是针对特定证书或由该证书颁发机构 (CA) 签名的证书进行验证。
Using the Code
Windows Phone 8.0 方法 - Windows Phone 8 中的低级证书锁定可以使用第三方库(如 Secureblackbox)来实现。
我们尝试了流套接字,但 Windows Phone 8 中的流套接字有 3 种类型
- 普通套接字
- SSL(带加密)
- SSL(不带加密)
可以使用带加密的 SSL 选项以安全的方式进行通信,但它不支持获取认证信息或验证特定站点的公钥的 API。因此,流套接字不能用于证书锁定。
Windows Phone 8.1 方法 - 可以使用原生流套接字对象在 Windows Phone 8.1 中实现低级证书锁定,因为服务器证书信息在套接字中是可读的。虽然在 WP8.1 中使用 HTTP 客户端进行安全/高级证书锁定是可能的,但为此,我们将不得不使用 HTTPClient
从通过 URL 访问的证书对象中提取公钥。
超文本传输协议安全 (HTTPS) 是超文本传输协议 (http) 的安全版本。HTTPS 允许安全的电子商务交易,例如当用户通过 HTTPS 连接到网站时,该网站会使用数字证书加密会话。
///Source code for creation of HTTPClient Object and
///extracting Certificate object from given URL using PostAsync()</p>
Uri serverUri = new Uri(URLName.Text);
HttpClient httpClient = new HttpClient();
string responseData = string.Empty;
HttpResponseMessage response = new HttpResponseMessage();
string data = "test=something";
response = await httpClient.PostAsync(serverUri,
new HttpStringContent(data, Windows.Storage.Streams.UnicodeEncoding.Utf8,
"application/json"));
//response = await httpClient.GetAsync
(serverUri, HttpCompletionOption.ResponseContentRead);
responseData = await response.Content.ReadAsStringAsync();
这将帮助您使用 URL 证书对象(例如根证书、中间证书和子证书)从服务器中提取所有可用证书。一个服务器可以有多个中间证书。
此外,我们在以下源代码中使用的 OID 对于 RSA 算法是全局的,不确定它是否适用于所有 URL,但我们仍然检查了近十个具有不同根证书的不同站点,并且它按预期工作。
for(int i = 0;
i < response.RequestMessage.TransportInformation.ServerIntermediateCertificates.Count; i++)
{
Certificate aCertificate =
response.RequestMessage.TransportInformation.ServerIntermediateCertificates[i];
IBuffer buffer = aCertificate.GetCertificateBlob();
byte[] bCert = buffer.ToArray();
string scert = BitConverter.ToString(bCert);
////Global OID - 1.2.840.113549.1.1.1
byte[] rsaOID = EncodeOID("1.2.840.113549.1.1.1"); //
string sOID = BitConverter.ToString(rsaOID);
int length;
int index = FindX509PubKeyIndex(bCert, rsaOID, out length);
// Found X509PublicKey in certificate so copy it.
if (index > -1)
{
byte[] X509PublicKey = new byte[length];
Array.Copy(bCert, index, X509PublicKey, 0, length);
URLCertPublicKey = BitConverter.ToString(X509PublicKey);
txtServerPK.Text = URLCertPublicKey;
}
虽然我们参考了从 MSDN 博客使用 Base64String 提取给定 OID 的公钥的源代码,请参考相同的代码,但该博客上可用的代码已针对以下例程进行了修改
if (index > -1)
{
// Find outer Sequence
while (index > 0 && Reference[index] != 0x30) index++;
//index++;
while (index > 0 && Reference[index] != 0x30) index++;
}
以下函数将帮助您从设备上可用的证书中获取公钥。
private async Task<string> GetClientPublicKey()
{
string DevCertPublicKey = null;
string CertFileName = "GeoTrustGlobalCArootcert.cer";
try
{
StorageFolder folder = ApplicationData.Current.LocalFolder;
if (folder != null)
{
StorageFile file = await folder.GetFileAsync(CertFileName);
IBuffer CertBlob = await FileIO.ReadBufferAsync(file);
Certificate rootCert =new Certificate(CertBlob);
IBuffer buffer1 = rootCert.GetCertificateBlob();
byte[] bCert1 = buffer1.ToArray();
string scert1 = BitConverter.ToString(bCert1);
byte[] rsaOID1 = EncodeOID("1.2.840.113549.1.1.1"); //
string sOID1 = BitConverter.ToString(rsaOID1);
int length1;
int index1 = FindX509PubKeyIndex(bCert1, rsaOID1, out length1);
// Found X509PublicKey in certificate so copy it.
if (index1 > -1)
{
byte[] X509PublicKey1 = new byte[length1];
Array.Copy(bCert1, index1, X509PublicKey1, 0, length1);
DevCertPublicKey = BitConverter.ToString(X509PublicKey1);
txtClientPK.Text = DevCertPublicKey;
}
return DevCertPublicKey;
}
return DevCertPublicKey;
}
catch (Exception ex)
{
return ex.Message.ToString();
}
}
一旦用户同时拥有公钥,即来自客户端证书和服务器证书,比较它们将给出证书锁定是成功还是失败的结果。我们能够成功地在 WebView 上扩展证书锁定。在 webview 中,在 Start_Navigation
事件中,将能够在重定向到目标 URL 之前验证每个证书。
证书锁定问题
- 可能由于证书锁定而导致应用程序性能问题。
- 嵌入在您的应用程序中的证书最终会过期。您将不得不计划应用程序更新,其中包含更新的证书,或者编写一种方法让应用程序下载新证书。
关注点
我们确实花费了大量的努力来实现这一成功,但最终我们实现了我们想要的,而那种感觉本身就帮助我们忘记了我们面临的所有问题和困难......真的这是一次很棒的经历和团队合作!
来自我的团队 Mukesh Gupta、Preeti Arora、Atanu Ray、Santhiya Raman 以及所有其他与我一起实现这一成功的人。