证书和公钥固定
证书和公钥固定
证书和公钥固定是实现证书和公钥固定的技术指南,如弗吉尼亚章节的介绍在移动空间中保护无线信道所述。本指南的重点是提供清晰,简单,可操作的指导,以便在恶劣的环境中保护渠道,在这种环境中,参与者可能是恶意的,信任会议是一种责任。
Pinning Cheat Sheet提供备忘单。
[ 隐藏 ]
介绍
安全渠道是远程和移动工作的用户和员工的基石。用户和开发人员在发送和接收数据时需要端到端的安全性 - 特别是受VPN,SSL或TLS保护的信道上的敏感数据。虽然控制DNS和CA的组织可能会将风险降低到大多数威胁模型下的微不足道的水平,但用户和开发人员屈服于其他DNS和公共CA层次结构会面临非常微不足道的风险。事实上,历史已经表明那些依赖外部服务的人在其安全渠道中遭受了长期违规。
大流行滥用信任导致用户,开发人员和应用程序就不受信任的输入做出安全相关的决策。这种情况有点悖论:DNS和CA等实体是受信任的,应该提供可靠的输入; 但他们的输入不可信任。依赖不安全的输入来进行与安全相关的决策不仅是不良业力,而且还违反了许多安全编码原则(例如,参见OWASP的注入理论和数据验证)。
固定有效地消除了“信任会议”。在做出与对等方身份相关的安全决策时,固定证书或公钥的应用程序不再需要依赖其他人(例如DNS或CA)。对于熟悉SSH的人,您应该意识到公钥锁定几乎与SSH的StrictHostKeyChecking选项完全相同。SSH一直都是正确的,世界其他地方开始意识到通过公钥直接识别主机或服务的优点。
积极参与销售活动的其他人包括Google及其浏览器Chrome。Chrome成功地发现了DigiNotar的妥协,发现了伊朗政府对其公民的可疑拦截。最初的妥协报告可以在这个MITM攻击Gmail的SSL中找到?; 谷歌安全公司立即回应了关于中间人攻击的最新消息。
有什么问题?
用户,开发人员和应用程序期望在其安全通道上实现端到端安全性,但某些安全通道无法满足预期。具体而言,使用众所周知的协议(如VPN,SSL和TLS)构建的通道可能容易受到大量攻击。
本文的讨论选项卡中列出了过去失败的示例。这个备忘单不会试图对行业中的故障进行编目,调查脚手架中的设计缺陷,证明缺乏对提供商的责任或责任,解释服务中的底层竞争,或揭开例如之间的勾结的神秘面纱。 ,浏览器和CA. 如需更多阅读,请访问PKI已破损且互联网已损坏。
病人0
最初的问题是密钥分配问题。可以通过加密将不安全通信转换为安全通信问题。加密通信可以通过签名转换为身份问题。身份问题终止于密钥分发问题。他们是同样的问题。
治愈
密钥分发问题有三种方法。首先是掌握您的合作伙伴或同伴(即同伴,服务器或服务)的第一手资料。这可以通过SneakerNet解决。遗憾的是,SneakerNet无法扩展,也无法用于解决密钥分发问题。
第二种是依赖其他人,它有两种变体:(1)信任网,(2)信任等级。Web of Trust和Trust of Trust在无菌环境中解决密钥分发问题。但是,信任网和信任层次都要求我们依赖他人 - 或者给予信任。在实践中,信任他人显示出有问题。
什么是固定?
固定是将主机与其预期的 X509证书或公钥相关联的过程。一旦知道或看到主机的证书或公钥,证书或公钥就会与主机关联或“固定”。如果可以接受多个证书或公钥,则该程序将保留一个pinset(取自Jon Larimer和Kenny Root Google I / O对话)。在这种情况下,广告标识必须与pinset中的一个元素匹配。
主机或服务的证书或公钥可以在开发时添加到应用程序,也可以在首次遇到证书或公钥时添加。前者 - 在开发时添加 - 是首选,因为在带外预加载证书或公钥通常意味着攻击者无法污染引脚。如果在首次遇到时添加了证书或公钥,您将使用密钥连续性。如果攻击者在第一次遭遇期间具有特权位置,则密钥连续性可能会失败。
固定利用对用户与组织或服务之间预先存在的关系的了解,以帮助做出更好的安全相关决策。因为您已经拥有服务器或服务的信息,所以您不需要依赖用于解决密钥分发问题的通用机制。也就是说,您无需为名称/地址映射或CA提供绑定和状态的DNS。一个例外是撤销,下面将在Pinning Gaps中进行讨论。
值得一提的是,Pinning不是Stapling。Stapling在同一请求中发送证书和OCSP响应者信息,以避免客户端在路径验证期间应执行的额外提取。
什么时候你别?
您应该在任何时候想要相对确定远程主机的身份或在恶劣环境中操作时固定。由于其中一个或两个几乎总是正确的,所以你应该总是固定。
一个完美的例子:在准备演示和备忘单两周左右的时间里,我们观察到三个相关和相关的失败。首先是诺基亚/ Opera故意打破安全通道 ; 第二个是DigiCert发布恶意软件代码签名证书 ; 第三是Bit9丢失其根签名密钥。环境不仅充满敌意,而且有毒。
什么时候你白名单?
如果您正在为一个实施“出口过滤”的组织工作,作为数据丢失防护(DLP)策略的一部分,您可能会遇到拦截代理。我喜欢把这些东西称为“好”坏人(而不是“坏”坏人),因为它们都破坏了端到端的安全性,我们无法区分它们。在这种情况下,不要将拦截代理列入白名单,因为它会破坏您的安全目标。在风险接受人员的指示下,将拦截代理的公钥添加到您的pinset 。
注意:如果将其他主机的证书或公钥列入白名单(例如,为了容纳拦截代理),则不再固定主机的预期证书和密钥。渠道的安全性和完整性可能会受到影响,并且肯定会打破用户和组织的端到端安全期望。
有关拦截代理的更多阅读,它们赋予的额外风险以及它们如何失败,请参阅Matthew Green博士的拦截代理如何失败?和Jeff Jarmoc的BlackHat谈论SSL / TLS拦截代理和传递信任。
怎么针?
我们的想法是重用现有的协议和基础设施,但要以强化的方式使用它们。为了重复使用,程序将继续执行建立安全连接时所做的事情。
为了强化通道,程序将利用库,框架或平台提供的OnConnect回调。在回调中,程序将通过验证其证书或公钥来验证远程主机的身份。虽然固定不必在OnConnect回调中发生,但它通常最方便,因为底层连接信息随时可用。
什么应该固定?
首先要决定的是应该固定什么。对于这个选择,您有两种选择:您可以(1)固定证书; 或(2)固定公钥。如果选择公钥,则还有两个选择:(a)固定subjectPublicKeyInfo ; 或(b)固定其中一种具体类型,例如RSAPublicKey或DSAPublicKey。
下面将更详细地解释这三种选择。我鼓励你固定subjectPublicKeyInfo,因为它有公共参数(例如RSA公钥的{e,n})和上下文信息,如算法和OID。上下文将帮助您有时保持轴承,下面的图1显示了可用的附加信息。
编码/格式
出于本文的目的,对象采用X509兼容的表示格式(PKCS#1遵循X509,两者都使用ASN.1)。如果您有PEM编码对象(例如,----- BEGIN CERTIFICATE -----,----- END CERTIFICATE -----),则将对象转换为DER编码。下面以格式转换提供了使用OpenSSL的转换。
证书是通过签名将实体(例如个人或组织)绑定到公钥的对象。证书是DER编码的,并且具有关联的数据或属性,例如主题(被识别或绑定),发行者(签名者),有效性(NotBefore和NotAfter)以及公钥。
证书具有subjectPublicKeyInfo。subjectPublicKeyInfo是具有附加信息的密钥。ASN.1类型包括算法ID,版本和用于保存具体公钥的可扩展格式。下面的图1和图2示出了相同RSA密钥的不同视图,其是subjectPublicKeyInfo。关键是网站random.org,它用于下面的示例程序和列表中。
|
|
具体公钥是编码的公钥。密钥格式通常在其他地方指定 - 例如,在RSA公钥的情况下,PKCS#1。在RSA公钥的情况下,类型是RSAPublicKey,参数{e,n}将是ASN.1编码的。上面的图1和2清楚地显示了模量(线28处的n)和指数(线289处的e)。对于DSA,具体类型是DSAPublicKey,ASN.1编码的参数是{p,q,g,y}。
最后的要点:(1)证书将实体与公钥绑定; (2)证书有subjectPublicKeyInfo; (3)subjectPublicKeyInfo具有具体的公钥。对于那些想要了解更多信息的人,可以在Code Project的文章Cryptographic Interoperability:Keys中找到程序员角度的更深入的讨论。
证书
证书最容易固定。您可以从网站上获取带外证书,让IT人员通过电子邮件向您发送公司证书,使用openssl s_client检索证书等。证书过期后,您将更新您的申请。假设您的应用程序没有错误或安全缺陷,应用程序将每年或每两年更新一次。在运行时,您将在回调中检索网站或服务器的证书。在回调中,您将检索到的证书与程序中嵌入的证书进行比较。如果比较失败,则方法或功能失败。
钉住证书有一个缺点。如果站点定期轮换其证书,则需要定期更新您的应用程序。例如,Google会轮换其证书,因此您需要每月更新一次应用程序(如果它依赖于Google服务)。即使Google轮换其证书,底层公钥(在证书中)仍然是静态的。
公钥
由于从证书中提取公钥所需的额外步骤,公钥锁定更灵活,但有点棘手。与证书一样,程序使用其公钥的嵌入副本检查提取的公钥。两个公钥固定有两个缺点。首先,由于您通常必须从证书中提取密钥,因此更难使用密钥(而不是证书)。在Java和.Net中,提取是一个小小的不便,但它在Cocoa / CocoaTouch和OpenSSL中很不舒服。其次,密钥是静态的,可能违反密钥轮换策略。
哈希
虽然上面的三个选择使用DER编码,但也可以使用信息的散列(或其他变换)。事实上,原始的示例程序是使用消化的证书和公钥编写的。更改样本以允许程序员使用dumpasn1和其他ASN.1解码器等工具检查对象。
哈希还提供了三个额外的好处。首先,哈希允许您匿名化证书或公钥。如果您的应用程序担心在反编译和重新设计期间泄漏信息,这可能很重要。
其次,消化的证书指纹通常可用作许多库的本机API,因此使用起来非常方便。
最后,组织可能希望在主要身份被泄露的情况下提供保留(或备份)身份。散列可确保您的对手在使用之前不会看到保留的证书或公钥。实际上,谷歌的IETF草案websec-key-pinning使用了这种技术。
钉扎的例子
本节演示Android Java,iOS,.Net和OpenSSL中的证书和公钥固定。
HTTP固定
RFC 7469引入了一个新的HTTP标头,允许SSL服务器使用不应更改这些证书的时间范围声明其证书的哈希值。例如:
Public-Key-Pins:max-age = 2592000; 销-SHA256 = “E9CZ9INDbd + 2eRQozYqqbQ2yXLVKB9 + xcprMF + 44U1g =”; 销-SHA256 = “LPJNul + wow4m6DsqxbninhsWHlwfp0JecwQzYpOLmCQ =”; 报告-URI = “http://example.com/pkp-report”
请注意,RFC 7469存在争议,因为它允许覆盖本地安装的权限。也就是说,它允许成功捕获用户的对手或其他方使用非真实或欺诈性信息覆盖已知良好的pinset。其次,报告机制被抑制了破坏的pinset,因此一个合规的用户代理将在事后掩盖。也就是说,断开的pinset的报告被称为必须不报告[1 ]。
Android的
自Android N以来,实现固定的首选方法是利用Android的网络安全配置功能,该功能允许应用程序在安全的声明性配置文件中自定义其网络安全设置,而无需修改应用程序代码。
要启用固定,可以使用`<pin-set>`配置设置。
如果需要支持运行早于N的Android版本的设备,则可以通过https://github.com/datatheorem/TrustKit-Android上的TrustKit Android库获得网络安全配置固定功能的后退端口。
最后,Android文档提供了一个示例,说明如何在未知CA实施文档中的应用程序代码中自定义SSL验证(以实现固定)。但是,应该避免从头开始实施固定验证,因为实施错误极有可能并且通常会导致严重的漏洞。
iOS版
TrustKit是一个用于iOS和macOS的开源SSL固定库,可从https://github.com/datatheorem/TrustKit获得。它提供了一个易于使用的API来实现固定,并已部署在许多应用程序中。
否则,有关如何在iOS上自定义SSL验证(以实现固定)的更多详细信息,请参阅https://developer.apple.com/library/content/technotes/tn2232上的“HTTPS服务器信任评估”技术说明。/_index.html。但是,应该避免从头开始实施固定验证,因为实施错误极有可能并且通常会导致严重的漏洞。
。净
.Net固定可以通过使用ServicePointManager来实现,如下所示。
下载:.Net示例程序。
//编码RSAPublicKey private static String PUB_KEY =“30818902818100C4A06B7B52F8D17DC1CCB47362”+ “C64AB799AAE19E245A7559E9CEEC7D8AA4DF07CB0B21FDFD763C63A313A668FE9D764E”+ “D913C51A676788DB62AF624F422C2F112C1316922AA5D37823CD9F43D1FC54513D14B2”+ “9E36991F08A042C42EAAEEE5FE8E2CB10167174A359CEBF6FACC2C9CA933AD403137EE”+ “2C3F4CBED9460129C72B0203010001”; public static void Main(string [] args) { ServicePointManager.ServerCertificateValidationCallback = PinPublicKey; WebRequest wr = WebRequest.Create(“https://encrypted.google.com/”); wr.GetResponse(); } public static bool PinPublicKey(对象发送者,X509Certificate证书,X509Chain链, SslPolicyErrors sslPolicyErrors) { if(null == certificate) 返回false; String pk = certificate.GetPublicKeyString(); if(pk.Equals(PUB_KEY)) 返回true; // 坏狗 返回false; }
OpenSSL的
使用OpenSSL可以在两个地方之一进行固定。首先是用户提供的verify_callback。第二个是通过SSL_get_peer_certificate建立连接之后。这两种方法都允许您访问对等方的证书。
虽然OpenSSL执行X509检查,但您必须使连接失败并在出错时拆除套接字。根据设计,不提供证书的服务器将导致X509_V_OK具有NULL证书。要检查习惯验证的结果:(1)您必须调用SSL_get_verify_result并验证返回码是X509_V_OK; (2)您必须调用SSL_get_peer_certificate并验证证书是否为非NULL。
下载:OpenSSL示例程序。
int pkp_pin_peer_pubkey(SSL * ssl) { if(NULL == ssl)返回FALSE; X509 * cert = NULL; FILE * fp = NULL; /* 刮 */ int len1 = 0,len2 = 0; unsigned char * buff1 = NULL,* buff2 = NULL; / *结果返回给调用者* / int ret = 0,result = FALSE; 做 { / * http://www.openssl.org/docs/ssl/SSL_get_peer_certificate.html * / cert = SSL_get_peer_certificate(ssl); if(!(cert!= NULL)) 打破; / *失败* / / *开始回放以获得subjectPublicKeyInfo * / / *感谢Viktor Dukhovni在OpenSSL邮件列表上* / / * http://groups.google.com/group/mailing.openssl.users/browse_thread/thread/d61858dae102c6c7 * / len1 = i2d_X509_PUBKEY(X509_get_X509_PUBKEY(cert),NULL); if(!(len1> 0)) 打破; / *失败* / /* 刮 */ unsigned char * temp = NULL; / * http://www.openssl.org/docs/crypto/buffer.html * / buff1 = temp = OPENSSL_malloc(len1); if(!(buff1!= NULL)) 打破; / *失败* / / * http://www.openssl.org/docs/crypto/d2i_X509.html * / len2 = i2d_X509_PUBKEY(X509_get_X509_PUBKEY(cert),&temp); / *这些检查验证我们恢复了与调整缓冲区大小时相同的值。* / / *它非常弱,因为它们应该始终相同。但它给了我们一些测试。* / if(!((len1 == len2)&&(temp!= NULL)&&((temp - buff1)== len1))) 打破; / *失败* / / *结束回转* / / *见上面的警告!!! * / / * http://pubs.opengroup.org/onlinepubs/009696699/functions/fopen.html * / fp = fopen(“random-org.der”,“rx”); if(NULL == fp){ fp = fopen(“random-org.der”,“r”); if(!(NULL!= fp)) 打破; / *失败* / / *寻求eof来确定文件的大小* / / * http://pubs.opengroup.org/onlinepubs/009696699/functions/fseek.html * / ret = fseek(fp,0,SEEK_END); if(!(0 == ret)) 打破; / *失败* / / *获取文件的大小* / / * http://pubs.opengroup.org/onlinepubs/009696699/functions/ftell.html * / long size = ftell(fp); / *任意大小,但应相对较小(小于1K或2K)* / if(!(size!= -1 && size> 0 && size <2048)) 打破; / *失败* / / *快退到开始执行读取* / / * http://pubs.opengroup.org/onlinepubs/009696699/functions/fseek.html * / ret = fseek(fp,0,SEEK_SET); if(!(0 == ret)) 打破; / *失败* / / *重用buff2和len2 * / buff2 = NULL; len2 =(int)size; / * http://www.openssl.org/docs/crypto/buffer.html * / buff2 = OPENSSL_malloc(len2); if(!(buff2!= NULL)) 打破; / *失败* / / * http://pubs.opengroup.org/onlinepubs/009696699/functions/fread.html * / / *返回读取的元素数,应为1 * / ret =(int)fread(buff2,(size_t)len2,1,fp); if(!(ret == 1)) 打破; / *失败* / / *重复使用大小。MIN和MAX宏低于...... * / size = len1 <len2?len1:len2; / ******** / /***** 财源 *****/ / ******** / if(len1!=(int)size || len2!=(int)size || 0!= memcmp(buff1,buff2,(size_t)size)) 打破; / *失败* / / *一个好的退出点* / result = TRUE; } while(0); if(fp!= NULL) FCLOSE(FP); / * http://www.openssl.org/docs/crypto/buffer.html * / if(NULL!= buff2) OPENSSL_free(buff2); / * http://www.openssl.org/docs/crypto/buffer.html * / if(NULL!= buff1) OPENSSL_free(BUFF1); / * http://www.openssl.org/docs/crypto/X509_new.html * / if(NULL!= cert) X509_free(CERT); 返回结果; }
杂
本节介绍了与固定相关的administrivia和其他项目。
短暂的钥匙
临时密钥是用于协议执行的一个实例的临时密钥,然后被丢弃。短暂密钥具有提供前向保密的好处,这意味着站点或服务的长期(静态)签名密钥的折衷不利于解密过去的消息,因为密钥是临时的并且被丢弃(一旦会话终止)。
临时密钥不会影响固定,因为Ephemeral密钥是在单独的ServerKeyExchange消息中传递的。此外,临时密钥是密钥而不是证书,因此它不会更改证书链的构造。也就是说,感兴趣的证书仍然位于证书[0]。
固定差距
由于重用现有的基础设施和协议,在固定时存在两个空白。首先,程序不会根据服务器的公共信息向对等服务器发送明确的质询。因此程序永远不知道对等体是否可以实际解密消息。然而,由于对手将收到无法解密的消息,因此缺点通常在实践中是学术性的。
第二是撤销。客户端通常不会进行吊销检查,因此可以在pinset中使用已知的错误证书或密钥。即使撤销处于活动状态,证书撤销列表(CRL)和在线证书状态协议(OCSP)也可以在恶意环境中失败。应用程序可以采取措施进行修复,主要方法是新鲜度。也就是说,应用程序应在关键安全性参数更改时立即更新和分发。
没关系^ @ $!
如果您没有预先存在的关系,那么一切都不会丢失。首先,您可以在第一次遇到主机或服务器的证书或公钥时固定它们。如果遇到证书或公钥时坏人没有活动,他或她将无法成功应对未来有趣的业务。
其次,由于Chromium和Certificate Patrol等项目以及EFF的SSL天文台等计划,在现场更快地发现了不良证书。
第三,帮助正在进行中,有许多未来将有助于这些努力:
- 公钥固定(http://www.ietf.org/id/draft-ietf-websec-key-pinning-09.txt) - HTTP协议的扩展,允许Web主机操作员指示用户代理(UA)记住(“pin”)主机在给定时间段内的加密身份。
- 基于DNS的命名实体认证(DANE)(https://datatracker.ietf.org/doc/rfc6698/) - 使用安全DNS将证书与S / MIME,SMTP与TLS,DNSSEC和TLSA记录的域名相关联。
- Sovereign Keys(http://www.eff.org/sovereign-keys) - 通过DNSSEC提供可选且安全的方式将域名与公钥相关联。仍然使用PKI(分层)。半集中式,仅附加日志记录。
- Convergence(http://convergence.io) - 站点及其相关数据(证书和公钥)的不同[地理]视图。使用Web of Trust。半集中。
虽然Sovereign Keys和Convergence仍然要求我们向外界提供信托,但有关各方不会为股东提供服务或贪图收入流。他们的兴趣是行业透明度和用户安全性。
更多信息?
钉扎是一种旧的新东西,已被震动,搅拌和重新包装。虽然“pinning”和“pinsets”对于旧事物来说是相对较新的术语,但Jon Larimer和Kenny Root在Google I / O 2012上花了很多时间来讨论Android应用中的安全和隐私问题。
格式转换
为方便读者,以下使用OpenSSL在PEM和DER格式之间进行转换。
#公钥,X509 $ openssl genrsa -out rsa-openssl.pem 3072 $ openssl rsa -in rsa-openssl.pem -pubout -outform DER -out rsa-openssl.der
#私钥,PKCS#8 $ openssl genrsa -out rsa-openssl.pem 3072 $ openssl pkcs8 -nocrypt -in rsa-openssl.pem -inform PEM -topk8 -outform DER -out rsa-openssl.der
参考
- OWASP 注射理论
- OWASP 数据验证
- OWASP 运输层保护备忘单
- IETF 公钥固定
- IETF RFC 5054(SRP)
- IETF RFC 4764(EAP-PSK)
- IETF RFC 1421(PEM编码)
- IETF RFC 5280(Internet X.509,PKIX)
- IETF RFC 4648(Base16,Base32和Base64编码)
- IETF RFC 3279(PKI,X509算法和CRL配置文件)
- IETF RFC 4055(PKI,X509附加算法和CRL配置文件)
- IETF RFC 2246(TLS 1.0)
- IETF RFC 4346(TLS 1.1)
- IETF RFC 5246(TLS 1.2)
- IETF RFC 6698,草案(DANE)
- EFF 主权钥匙
- Thoughtcrime Labs Convergence
- RSA实验室PKCS#1,RSA加密标准
- RSA实验室PKCS#6,扩展证书语法标准
- ITU 基本编码规则(BER),规范编码规则(CER)和可分辨编码规则(DER)的规范
- TOR项目检测证书颁发机构妥协和Web浏览器合谋
- 代码项目加密互操作性:密钥
- Android应用中的 Google I / O 安全和隐私
- Trevor Perrin 透明度,信任敏捷性,固定(服务器身份验证的最新发展)
- Peter Gutmann博士的PKI是破碎的
- 马修格林博士的互联网是破碎的
- Matthew Green博士的拦截代理如何失败?
- 演示:适用于iOS和Android的SSL Pinning实现和绕过
作者和主要编辑
- Jeffrey Walton - jeffrey,owasp.org
- 约翰史蒂文 - 约翰,owasp.org
- Jim Manico - jim,owasp.org
- 凯文沃尔 - 凯文,owasp.org
- Ricardo Iramar - ricardo.iramar,owasp.org
原文:
Certificate and Public Key Pinning
Certificate and Public Key Pinning is a technical guide to implementing certificate and public key pinning as discussed at the Virginia chapter's presentation Securing Wireless Channels in the Mobile Space. This guide is focused on providing clear, simple, actionable guidance for securing the channel in a hostile environment where actors could be malicious and the conference of trust a liability.
A cheat sheet is available at Pinning Cheat Sheet.
[hide]
Introduction
Secure channels are a cornerstone to users and employees working remotely and on the go. Users and developers expect end-to-end security when sending and receiving data - especially sensitive data on channels protected by VPN, SSL, or TLS. While organizations which control DNS and CA have likely reduced risk to trivial levels under most threat models, users and developers subjugated to other's DNS and a public CA hierarchy are exposed to non-trivial amounts of risk. In fact, history has shown those relying on outside services have suffered chronic breaches in their secure channels.
The pandemic abuse of trust has resulted in users, developers and applications making security related decisions on untrusted input. The situation is somewhat of a paradox: entities such as DNS and CAs are trusted and supposed to supply trusted input; yet their input cannot be trusted. Relying on untrusted input for security related decisions is not only bad karma, it violates a number of secure coding principals (see, for example, OWASP's Injection Theory and Data Validation).
Pinning effectively removes the "conference of trust". An application which pins a certificate or public key no longer needs to depend on others - such as DNS or CAs - when making security decisions relating to a peer's identity. For those familiar with SSH, you should realize that public key pinning is nearly identical to SSH's StrictHostKeyCheckingoption. SSH had it right the entire time, and the rest of the world is beginning to realize the virtues of directly identifying a host or service by its public key.
Others who actively engage in pinning include Google and its browser Chrome. Chrome was successful in detecting the DigiNotar compromise which uncovered suspected interception by the Iranian government on its citizens. The initial report of the compromise can be found at Is This MITM Attack to Gmail's SSL?; and Google Security's immediate response at An update on attempted man-in-the-middle attacks.
What's the problem?
Users, developers, and applications expect end-to-end security on their secure channels, but some secure channels are not meeting the expectation. Specifically, channels built using well known protocols such as VPN, SSL, and TLS can be vulnerable to a number of attacks.
Examples of past failures are listed on the discussion tab for this article. This cheat sheet does not attempt to catalogue the failures in the industry, investigate the design flaws in the scaffolding, justify the lack of accountability or liability with the providers, explain the race to the bottom in services, or demystify the collusion between, for example, Browsers and CAs. For additional reading, please visit PKI is Broken and The Internet is Broken.
Patient 0
The original problem was the Key Distribution Problem. Insecure communications can be transformed into a secure communication problem with encryption. Encrypted communications can be transformed into an identity problem with signatures. The identity problem terminates at the key distribution problem. They are the same problem.
The Cures
There are three cures for the key distribution problem. First is to have first hand knowledge of your partner or peer (i.e., a peer, server or service). This could be solved with SneakerNet. Unfortunately, SneakerNet does not scale and cannot be used to solve the key distribution problem.
The second is to rely on others, and it has two variants: (1) web of trust, and (2) hierarchy of trust. Web of Trust and Hierarchy of Trust solve the key distribution problem in a sterile environment. However, Web of Trust and Hierarchy of Trust each requires us to rely on others - or confer trust. In practice, trusting others is showing to be problematic.
What Is Pinning?
Pinning is the process of associating a host with their expected X509 certificate or public key. Once a certificate or public key is known or seen for a host, the certificate or public key is associated or 'pinned' to the host. If more than one certificate or public key is acceptable, then the program holds a pinset (taking from Jon Larimer and Kenny Root Google I/O talk). In this case, the advertised identity must match one of the elements in the pinset.
A host or service's certificate or public key can be added to an application at development time, or it can be added upon first encountering the certificate or public key. The former - adding at development time - is preferred since preloading the certificate or public key out of band usually means the attacker cannot taint the pin. If the certificate or public key is added upon first encounter, you will be using key continuity. Key continuity can fail if the attacker has a privileged position during the first encounter.
Pinning leverages knowledge of the pre-existing relationship between the user and an organization or service to help make better security related decisions. Because you already have information on the server or service, you don't need to rely on generalized mechanisms meant to solve the key distribution problem. That is, you don't need to turn to DNS for name/address mappings or CAs for bindings and status. One exception is revocation and it is discussed below in Pinning Gaps.
It is also worth mention that Pinning is not Stapling. Stapling sends both the certificate and OCSP responder information in the same request to avoid the additional fetches the client should perform during path validations.
When Do You Pin?
You should pin anytime you want to be relatively certain of the remote host's identity or when operating in a hostile environment. Since one or both are almost always true, you should probably pin all the time.
A perfect case in point: during the two weeks or so of preparation for the presentation and cheat sheet, we've observed three relevant and related failures. First was Nokia/Opera willfully breaking the secure channel; second was DigiCert issuing a code signing certificate for malware; and third was Bit9's loss of its root signing key. The environment is not only hostile, it's toxic.
When Do You Whitelist?
If you are working for an organization which practices "egress filtering" as part of a Data Loss Prevention (DLP) strategy, you will likely encounter Interception Proxies. I like to refer to these things as "good" bad guys (as opposed to "bad" bad guys) since both break end-to-end security and we can't tell them apart. In this case, do not offer to whitelist the interception proxy since it defeats your security goals. Add the interception proxy's public key to your pinset after being instructed to do so by the folks in Risk Acceptance.
Note: if you whitelist a certificate or public key for a different host (for example, to accommodate an interception proxy), you are no longer pinning the expected certificates and keys for the host. Security and integrity on the channel could suffer, and it surely breaks end-to-end security expectations of users and organizations.
For more reading on interception proxies, the additional risk they bestow, and how they fail, see Dr. Matthew Green's How do Interception Proxies fail? and Jeff Jarmoc's BlackHat talk SSL/TLS Interception Proxies and Transitive Trust.
How Do You Pin?
The idea is to re-use the existing protocols and infrastructure, but use them in a hardened manner. For re-use, a program would keep doing the things it used to do when establishing a secure connection.
To harden the channel, the program would take advantage of the OnConnect callback offered by a library, framework or platform. In the callback, the program would verify the remote host's identity by validating its certificate or public key. While pinning does not have to occur in an OnConnect callback, its often most convenient because the underlying connection information is readily available.
What Should Be Pinned?
The first thing to decide is what should be pinned. For this choice, you have two options: you can (1) pin the certificate; or (2) pin the public key. If you choose public keys, you have two additional choices: (a) pin the subjectPublicKeyInfo; or (b) pin one of the concrete types such as RSAPublicKey or DSAPublicKey.
The three choices are explained below in more detail. I would encourage you to pin the subjectPublicKeyInfo because it has the public parameters (such as {e,n} for an RSA public key) and contextual information such as an algorithm and OID. The context will help you keep your bearings at times, and Figure 1 below shows the additional information available.
Encodings/Formats
For the purposes of this article, the objects are in X509-compatible presentation format (PKCS#1 defers to X509, both of which use ASN.1). If you have a PEM encoded object (for example, -----BEGIN CERTIFICATE-----, -----END CERTIFICATE-----), then convert the object to DER encoding. Conversion using OpenSSL is offered below in Format Conversions.
A certificate is an object which binds an entity (such as a person or organization) to a public key via a signature. The certificate is DER encoded, and has associated data or attributes such as Subject (who is identified or bound), Issuer (who signed it), Validity (NotBefore and NotAfter), and a Public Key.
A certificate has a subjectPublicKeyInfo. The subjectPublicKeyInfo is a key with additional information. The ASN.1 type includes an Algorithm ID, a Version, and an extensible format to hold a concrete public key. Figures 1 and 2 below show different views of the same RSA key, which is the subjectPublicKeyInfo. The key is for the site random.org, and it is used in the sample programs and listings below.
|
|
The concrete public key is an encoded public key. The key format will usually be specified elsewhere - for example, PKCS#1 in the case of RSA Public Keys. In the case of an RSA public key, the type is RSAPublicKey and the parameters {e,n} will be ASN.1 encoded. Figures 1 and 2 above clearly show the modulus (n at line 28) and exponent (e at line 289). For DSA, the concrete type is DSAPublicKey and the ASN.1 encoded parameters would be {p,q,g,y}.
Final takeaways: (1) a certificate binds an entity to a public key; (2) a certificate has a subjectPublicKeyInfo; and (3) a subjectPublicKeyInfo has an concrete public key. For those who want to learn more, a more in-depth discussion from a programmer's perspective can be found at the Code Project's article Cryptographic Interoperability: Keys.
Certificate
The certificate is easiest to pin. You can fetch the certificate out of band for the website, have the IT folks email your company certificate to you, use openssl s_client to retrieve the certificate etc. When the certificate expires, you would update your application. Assuming your application has no bugs or security defects, the application would be updated every year or two.At runtime, you retrieve the website or server's certificate in the callback. Within the callback, you compare the retrieved certificate with the certificate embedded within the program. If the comparison fails, then fail the method or function.
There is a downside to pinning a certificate. If the site rotates its certificate on a regular basis, then your application would need to be updated regularly. For example, Google rotates its certificates, so you will need to update your application about once a month (if it depended on Google services). Even though Google rotates its certificates, the underlying public keys (within the certificate) remain static.
Public Key
Public key pinning is more flexible but a little trickier due to the extra steps necessary to extract the public key from a certificate. As with a certificate, the program checks the extracted public key with its embedded copy of the public key.There are two downsides two public key pinning. First, its harder to work with keys (versus certificates) since you usually must extract the key from the certificate. Extraction is a minor inconvenience in Java and .Net, buts its uncomfortable in Cocoa/CocoaTouch and OpenSSL. Second, the key is static and may violate key rotation policies.
Hashing
While the three choices above used DER encoding, its also acceptable to use a hash of the information (or other transforms). In fact, the original sample programs were written using digested certificates and public keys. The samples were changed to allow a programmer to inspect the objects with tools like dumpasn1 and other ASN.1 decoders.
Hashing also provides three additional benefits. First, hashing allows you to anonymize a certificate or public key. This might be important if you application is concerned about leaking information during decompilation and re-engineering.
Second, a digested certificate fingerprint is often available as a native API for many libraries, so its convenient to use.
Finally, an organization might want to supply a reserve (or back-up) identity in case the primary identity is compromised. Hashing ensures your adversaries do not see the reserved certificate or public key in advance of its use. In fact, Google's IETF draft websec-key-pinning uses the technique.
Examples of Pinning
This section demonstrates certificate and public key pinning in Android Java, iOS, .Net, and OpenSSL.
HTTP pinning
RFC 7469 introduced a new HTTP header that allows SSL servers to declare hashes of their certificates with time scope in which these certificates should not be changed. For example:
Public-Key-Pins: max-age=2592000; pin-sha256="E9CZ9INDbd+2eRQozYqqbQ2yXLVKB9+xcprMF+44U1g="; pin-sha256="LPJNul+wow4m6DsqxbninhsWHlwfp0JecwQzYpOLmCQ="; report-uri="http://example.com/pkp-report"
Please note that RFC 7469 is controversial since it allows overrides for locally installed authorities. That is, it allows an adversary or other party who successfully phishes the user to override a known good pinset with non-authentic or fraudulent information. Second, the reporting mechanism is suppressed from broken pinsets, so a complying user agent will be complicit in the cover up after the fact. That is, the reporting of the broken pinset is called out as MUST NOT report [1].
Android
Since Android N, the preferred way for implementing pinning is by leveraging Android's Network Security Configuration feature, which lets apps customize their network security settings in a safe, declarative configuration file without modifying app code.
To enable pinning, the `<pin-set>` configuration setting can be used.
If devices running a version of Android that is earlier than N need to be supported, a backport of the Network Security Configuration pinning functionality is available via the TrustKit Android library at https://github.com/datatheorem/TrustKit-Android.
Lastly, the Android documentation provides an example of how SSL validation can be customized within the app's code (in order to implement pinning) in the Unknown CA implementation document. However, implementing pinning validation from scratch should be avoided, as implementation mistakes are extremely likely and usually lead to severe vulnerabilities.
iOS
TrustKit, an open-source SSL pinning library for iOS and macOS is available at https://github.com/datatheorem/TrustKit. It provides an easy-to-use API for implementing pinning, and has been deployed in many apps.
Otherwise, more details regarding how SSL validation can be customized on iOS (in order to implement pinning) are available in the "HTTPS Server Trust Evaluation" technical note at https://developer.apple.com/library/content/technotes/tn2232/_index.html. However, implementing pinning validation from scratch should be avoided, as implementation mistakes are extremely likely and usually lead to severe vulnerabilities.
.Net
.Net pinning can be achieved by using ServicePointManager as shown below.
Download: .Net sample program.
// Encoded RSAPublicKey private static String PUB_KEY = "30818902818100C4A06B7B52F8D17DC1CCB47362" + "C64AB799AAE19E245A7559E9CEEC7D8AA4DF07CB0B21FDFD763C63A313A668FE9D764E" + "D913C51A676788DB62AF624F422C2F112C1316922AA5D37823CD9F43D1FC54513D14B2" + "9E36991F08A042C42EAAEEE5FE8E2CB10167174A359CEBF6FACC2C9CA933AD403137EE" + "2C3F4CBED9460129C72B0203010001"; public static void Main(string[] args) { ServicePointManager.ServerCertificateValidationCallback = PinPublicKey; WebRequest wr = WebRequest.Create("https://encrypted.google.com/"); wr.GetResponse(); } public static bool PinPublicKey(object sender, X509Certificate certificate, X509Chain chain, SslPolicyErrors sslPolicyErrors) { if (null == certificate) return false; String pk = certificate.GetPublicKeyString(); if (pk.Equals(PUB_KEY)) return true; // Bad dog return false; }
OpenSSL
Pinning can occur at one of two places with OpenSSL. First is the user supplied verify_callback. Second is after the connection is established via SSL_get_peer_certificate. Either method will allow you to access the peer's certificate.
Though OpenSSL performs the X509 checks, you must fail the connection and tear down the socket on error. By design, a server that does not supply a certificate will result in X509_V_OK with a NULL certificate. To check the result of the customary verification: (1) you must call SSL_get_verify_result and verify the return code is X509_V_OK; and (2) you must call SSL_get_peer_certificate and verify the certificate is non-NULL.
Download: OpenSSL sample program.
int pkp_pin_peer_pubkey(SSL* ssl) { if(NULL == ssl) return FALSE; X509* cert = NULL; FILE* fp = NULL; /* Scratch */ int len1 = 0, len2 = 0; unsigned char *buff1 = NULL, *buff2 = NULL; /* Result is returned to caller */ int ret = 0, result = FALSE; do { /* http://www.openssl.org/docs/ssl/SSL_get_peer_certificate.html */ cert = SSL_get_peer_certificate(ssl); if(!(cert != NULL)) break; /* failed */ /* Begin Gyrations to get the subjectPublicKeyInfo */ /* Thanks to Viktor Dukhovni on the OpenSSL mailing list */ /* http://groups.google.com/group/mailing.openssl.users/browse_thread/thread/d61858dae102c6c7 */ len1 = i2d_X509_PUBKEY(X509_get_X509_PUBKEY(cert), NULL); if(!(len1 > 0)) break; /* failed */ /* scratch */ unsigned char* temp = NULL; /* http://www.openssl.org/docs/crypto/buffer.html */ buff1 = temp = OPENSSL_malloc(len1); if(!(buff1 != NULL)) break; /* failed */ /* http://www.openssl.org/docs/crypto/d2i_X509.html */ len2 = i2d_X509_PUBKEY(X509_get_X509_PUBKEY(cert), &temp); /* These checks are verifying we got back the same values as when we sized the buffer. */ /* Its pretty weak since they should always be the same. But it gives us something to test. */ if(!((len1 == len2) && (temp != NULL) && ((temp - buff1) == len1))) break; /* failed */ /* End Gyrations */ /* See the warning above!!! */ /* http://pubs.opengroup.org/onlinepubs/009696699/functions/fopen.html */ fp = fopen("random-org.der", "rx"); if(NULL ==fp) { fp = fopen("random-org.der", "r"); if(!(NULL != fp)) break; /* failed */ /* Seek to eof to determine the file's size */ /* http://pubs.opengroup.org/onlinepubs/009696699/functions/fseek.html */ ret = fseek(fp, 0, SEEK_END); if(!(0 == ret)) break; /* failed */ /* Fetch the file's size */ /* http://pubs.opengroup.org/onlinepubs/009696699/functions/ftell.html */ long size = ftell(fp); /* Arbitrary size, but should be relatively small (less than 1K or 2K) */ if(!(size != -1 && size > 0 && size < 2048)) break; /* failed */ /* Rewind to beginning to perform the read */ /* http://pubs.opengroup.org/onlinepubs/009696699/functions/fseek.html */ ret = fseek(fp, 0, SEEK_SET); if(!(0 == ret)) break; /* failed */ /* Re-use buff2 and len2 */ buff2 = NULL; len2 = (int)size; /* http://www.openssl.org/docs/crypto/buffer.html */ buff2 = OPENSSL_malloc(len2); if(!(buff2 != NULL)) break; /* failed */ /* http://pubs.opengroup.org/onlinepubs/009696699/functions/fread.html */ /* Returns number of elements read, which should be 1 */ ret = (int)fread(buff2, (size_t)len2, 1, fp); if(!(ret == 1)) break; /* failed */ /* Re-use size. MIN and MAX macro below... */ size = len1 < len2 ? len1 : len2; /*************************/ /***** PAYDIRT *****/ /*************************/ if(len1 != (int)size || len2 != (int)size || 0 != memcmp(buff1, buff2, (size_t)size)) break; /* failed */ /* The one good exit point */ result = TRUE; } while(0); if(fp != NULL) fclose(fp); /* http://www.openssl.org/docs/crypto/buffer.html */ if(NULL != buff2) OPENSSL_free(buff2); /* http://www.openssl.org/docs/crypto/buffer.html */ if(NULL != buff1) OPENSSL_free(buff1); /* http://www.openssl.org/docs/crypto/X509_new.html */ if(NULL != cert) X509_free(cert); return result; }
Miscellaneous
This sections covers administrivia and miscellaneous items related to pinning.
Ephemeral Keys
Ephemeral keys are temporary keys used for one instance of a protocol execution and then thrown away. An ephemeral key has the benefit of providing forward secrecy, meaning a compromise of the site or service's long term (static) signing key does not facilitate decrypting past messages because the key was temporary and discarded (once the session terminated).
Ephemeral keys do not affect pinning because the Ephemeral key is delivered in a separate ServerKeyExchange message. In addition, the ephemeral key is a key and not a certificate, so it does not change the construction of the certificate chain. That is, the certificate of interest will still be located at certificates[0].
Pinning Gaps
There are two gaps when pinning due to reuse of the existing infrastructure and protocols. First, an explicit challenge is not sent by the program to the peer server based on the server's public information. So the program never knows if the peer can actually decrypt messages. However, the shortcoming is usually academic in practice since an adversary will receive messages it can't decrypt.
Second is revocation. Clients don't usually engage in revocation checking, so it could be possible to use a known bad certificate or key in a pinset. Even if revocation is active, Certificate Revocation Lists (CRLs) and Online Certificate Status Protocol (OCSP) can be defeated in a hostile environment. An application can take steps to remediate, with the primary means being freshness. That is, an application should be updated and distributed immediately when a critical security parameter changes.
No Relationship ^@$!
If you don't have a pre-existing relationship, all is not lost. First, you can pin a host or server's certificate or public key the first time you encounter it. If the bad guy was not active when you encountered the certificate or public key, he or she will not be successful with future funny business.
Second, bad certificates are being spotted quicker in the field due to projects like Chromium and Certificate Patrol, and initiatives like the EFF's SSL Observatory.
Third, help is on its way, and there are a number of futures that will assist with the endeavors:
- Public Key Pinning (http://www.ietf.org/id/draft-ietf-websec-key-pinning-09.txt) – an extension to the HTTP protocol allowing web host operators to instruct user agents (UAs) to remember ("pin") the hosts' cryptographic identities for a given period of time.
- DNS-based Authentication of Named Entities (DANE) (https://datatracker.ietf.org/doc/rfc6698/) - uses Secure DNS to associate Certificates with Domain Names For S/MIME, SMTP with TLS, DNSSEC and TLSA records.
- Sovereign Keys (http://www.eff.org/sovereign-keys) - operates by providing an optional and secure way of associating domain names with public keys via DNSSEC. PKI (hierarchical) is still used. Semi-centralized with append only logging.
- Convergence (http://convergence.io) – different [geographical] views of a site and its associated data (certificates and public keys). Web of Trust is used. Semi-centralized.
While Sovereign Keys and Convergence still require us to confer trust to outside parties, the parties involved do not serve share holders or covet revenue streams. Their interests are industry transparency and user security.
More Information?
Pinning is an old new thing that has been shaken, stirred, and repackaged. While "pinning" and "pinsets" are relatively new terms for old things, Jon Larimer and Kenny Root spent time on the subject at Google I/O 2012 with their talk Security and Privacy in Android Apps.
Format Conversions
As a convenience to readers, the following with convert between PEM and DER format using OpenSSL.
# Public key, X509 $ openssl genrsa -out rsa-openssl.pem 3072 $ openssl rsa -in rsa-openssl.pem -pubout -outform DER -out rsa-openssl.der
# Private key, PKCS#8 $ openssl genrsa -out rsa-openssl.pem 3072 $ openssl pkcs8 -nocrypt -in rsa-openssl.pem -inform PEM -topk8 -outform DER -out rsa-openssl.der