分类目录归档:系统编程

pfsense 可以直接安装 freebsd 的软件包

pfsense 可以手动安装 freebsd 的软件包. 只需要三步.
1. 在这个页面搜索并复制软件包的链接: https://pkg.freebsd.org/freebsd:10:x86:64/latest/All/
2. 执行 pkg add:

pkg add https://pkg.freebsd.org/freebsd:10:x86:64/latest/All/socat-1.7.3.2_2.txz
  1. 执行 rehash
rehash

参考资料:
1. https://forum.pfsense.org/index.php?topic=46116.0
2. https://doc.pfsense.org/index.php/Installing_FreeBSD_Packages
byNeil
byNeil.com

原文来自 Blog by Neil, post pfsense 可以直接安装 freebsd 的软件包 转载请注明出处。本站保留一切权力

如何 显示 cpp 文件的 include 树形结构

在排除一下 cpp 编译错误时, 可能需要搞清楚文件包含关系.
linux:

gcc  -M showtime.c  my.cpp

windows:

cl.exe  /showIncludes  my.cpp

参考:

  1. https://stackoverflow.com/questions/5834778/how-to-tell-where-a-header-file-is-included-from
  2. https://stackoverflow.com/questions/1137966/displaying-the-include-hierarchy-for-a-c-file-in-visual-studio

byNeil
byNeil.com

原文来自 Blog by Neil, post 如何 显示 cpp 文件的 include 树形结构 转载请注明出处。本站保留一切权力

solaris 上安装 git

今天要在solaris上安装git。
记录一下步骤:

pkgadd -d http://get.opencsw.org/now
/opt/csw/bin/pkgutil -U
/opt/csw/bin/pkgutil -y -i git 
/usr/sbin/pkgchk -L CSWgit # list files

https://www.opencsw.org/package/git/

byNeil
byNeil.com

原文来自 Blog by Neil, post solaris 上安装 git 转载请注明出处。本站保留一切权力

Mac和 iOS 下的对称和非对称加密算法的使用

分享在Mac 和 iOS 上使用到的对称和非对称加密算法. 包括RSA,DSA, AES, DES, 3DES 和 blowfish 等等.
因为要实现ssh协议, 所以用到了这些算法, 这些算法在mac和ios上的接口比较难用, 我在这折腾了好长时间, 这里分享出来, 希望对大家有帮助.
(这里不提 openssl 在 apple 上的接口了)

主要参考了apple的文档:

Cryptographic Services Guide

Apple Encrypting and Hashing Data

先大致概括一下整体情况:
基本上这两个平台上的加密解密都和 keychain service 密不可分. keychain是mac和ios上用来保存证书,密码,key 等等敏感信息的存储区. 有专门的api访问这些接口. 有时候我们为了获得一个 key 的实例,必须想办法把数据导入到keychain中,然后才能通过keychain获得一个key的实例. 后面再说.

在mac 上, 有三个方式实现加密解密签名服务:

  1. Security Transforms API —a Core-Foundation-level API that provides support for signing and verifying, symmetric cryptography, and Base64 encoding and decoding.
    是 CoreFoundation 级别的api, 提供了, 最全的功能和算法支持. 包括对称,非对称算法. 实现加密,签名功能. 但遗憾的是这个接口只在mac上有效. iOS上没有. 但有些功能必须要用到, 所以要实现跨平台的代码,需要一些补丁.

  2. Common Crypto—a C-level API that can perform most symmetric encryption and decryption tasks
    这是一个 C 风格的接口. 好消息是它在mac和ios上都有, 可以跨平台. 但坏消息是, 它只包含了, 对称加密算法, 却没有非对称算法. 因此只能加密解密,而不能签名和验证. 其实之前 Apple上还有一个 ComonCryptoRSA 模块, 但后来不知为何消失了.

  3. CDSA/CSSM —a legacy API that should be used only to perform tasks not supported by the other two APIs, such as asymmetric encryption
    这个名字比较吓人, Common Data Security Architecture (CDSA) 通用数据安全架构. 很奇怪,它被apple接受后不久, 就被废弃了. 现在已经不建议使用了. 所以就不提它了.

在 iOS 上, 基本上有两种方式:

  1. Common Crypto. 这个在上面已经说过了. 对称算法接口.

  2. 使用系统提供的特有api实现加密,解密, 签名和验证:

系统提供了下面4个函数:

SecKeyEncrypt—encrypts a block of data using the specified key.

SecKeyDecrypt—decrypts a block of data using the specified key.

SecKeyRawSign—signs a block of data using the specified key.

SecKeyRawVerify—verifies a signature against a block of data and a specified key.

基于上面的分析, 我们秉着尽可能减少代码重复, 跨平台开发的原则: 对称算法就使用 "Common Crypto" 模块了. 因为两个平台都有. 而非对称则需要分别实现了.

下面详细分享一些细节:

一, 非对称加密算法, 签名和验证.(RSA/DSA signature and verity)

这需要在两个平台独立开发.

  1. Mac 平台.

在 mac 平台上, 我们使用它的 Security Transforms API.

参考这里: Security Transforms Programming Guide-Signing and Verifying

上面有很好的 代码 片段. 需要注意的是如何把 RSA 的参数 变成 api 需要的 SecKeyRef 对象.

这是它的导入片段.

    params.keyUsage = NULL;
    params.keyAttributes = NULL;

    SecExternalItemType itemType = kSecItemTypeCertificate;
    SecExternalFormat externalFormat = kSecFormatPEMSequence;
    int flags = 0;

 oserr = SecItemImport(cfdataprivatekey,
        NULL, // filename or extension
        &externalFormat, // See SecExternalFormat for details
        &itemType, // item type
        flags, // See SecItemImportExportFlags for details
        &params,
        NULL, // Don't import into a keychain
        &temparray);
    if (oserr) {
        fprintf(stderr, "SecItemImport failed (oserr=%d)\n", oserr);
        CFShow(temparray);
        exit(-1);
    }

    privatekey = (SecKeyRef)CFArrayGetValueAtIndex(temparray, 0);

这里是为了创建 SecKeyRef 实例. 通过 SecItemImport 把数据导入.变成SecKeyRef实例. 数据放在 cfdataprivatekey 中. 这个数据必须是 Pem格式的证书. 因为这个case下需要私钥, 所以这个证书需要包含私钥, 都是pem格式.

这里特别介绍一下, 如何从ssh 的公钥格式导入. 以RSA为例, RSA的公钥其实是一个底数e, 和一个大整数 m ,

e = [int32(len), bytes(value)]
m = [int32(len), bytes(value)]

e 和 m 的结构一样. 先是4个字节的长度, 然后紧跟上字节序列. len是 大端在前的, 跟通常的小端是有区别的.
完整的机构大概是这个样子的:

Binary = [0x00, 0x00, 0x00, 0x07, 'ssh-rsa', e, m]

keydata = 'ssh-rsa' + Base64Encode(Binary)

这个keydata 就可以用来构建上面用到的参数cfdataprivatekey了.

对于DSA, 结构跟上面类似:

p = [int32(len), bytes(value)]
q = [int32(len), bytes(value)]
g = [int32(len), bytes(value)]
y = [int32(len), bytes(value)]


Binary = [0x00, 0x00, 0x00, 0x07, 'ssh-dss', p, q, g, y]

keydata = 'ssh-dss' + Base64Encode(Binary)

  1. 对于 iOS, 平台, 我们使用上面说的两个函数来签名和验证:
SecKeyRawSign—signs a block of data using the specified key.

SecKeyRawVerify—verifies a signature against a block of data and a specified key.

这两个函数要命的是都需要一个 SecKeyRef 参数, iOS 上还真没有直接的方式可以通过大整数直接创建 SecKeyRef的实例.

要么通过 keychain 读取. 或者通过 SecPKCS12Import() 函数导入 pkcs12 格式的包含私钥的证书, 然后获得 SecIdentityRef 实例. 然后再通过 SecIdentityCopyPrivateKey() 函数把其中的 私钥导出成 SecKeyRef实例.

OSStatus extractIdentityAndTrust(CFDataRef inPKCS12Data,
                                 SecIdentityRef *outIdentity,
                                 SecTrustRef *outTrust,
                                 CFStringRef keyPassword)
{
    OSStatus securityError = errSecSuccess;


    const void *keys[] =   { kSecImportExportPassphrase };
    const void *values[] = { keyPassword };
    CFDictionaryRef optionsDictionary = NULL;

    /* Create a dictionary containing the passphrase if one
       was specified.  Otherwise, create an empty dictionary. */
    optionsDictionary = CFDictionaryCreate(
                                                  NULL, keys,
                                                  values, (keyPassword ? 1 : 0),
                                                  NULL, NULL);  // 1

    CFArrayRef items = NULL;
    securityError = SecPKCS12Import(inPKCS12Data,
                                    optionsDictionary,
                                    &items);                    // 2


    //
    if (securityError == 0) {                                   // 3
        CFDictionaryRef myIdentityAndTrust = CFArrayGetValueAtIndex (items, 0);
        const void *tempIdentity = NULL;
        tempIdentity = CFDictionaryGetValue (myIdentityAndTrust,
                                                       kSecImportItemIdentity);
        CFRetain(tempIdentity);
        *outIdentity = (SecIdentityRef)tempIdentity;
        const void *tempTrust = NULL;
        tempTrust = CFDictionaryGetValue (myIdentityAndTrust, kSecImportItemTrust);

        CFRetain(tempTrust);
        *outTrust = (SecTrustRef)tempTrust;
    }

    if (optionsDictionary)                                      // 4
        CFRelease(optionsDictionary);

    if (items)
        CFRelease(items);

    return securityError;
}

另外一个方法是, 苹果官方给的示例代码, 强制拼凑 SecKeyRef示例.
这里有 SecKeyWrapper 的实例代码: SecKeyWrapper 的实例代码

并且可以在这里直接下载到 源码: SecKeyWrapper 源码

这个源码里面有很多 苹果写的例子. 非常好. 对剑使用这里面的代码实现.

二, 对于对称加密算法.
这个比较简单了, 我们直接使用 Common Crypto 模块. 在mac 和ios上可以跨平台.

请参考这里: Apple Common Crypto library

  1. 使用 CCCryptorCreate 或者 CCCryptorCreateWithMode 创建 CCCryptorRef 对象.
    然后不断的调用 CCCryptorUpdate. 进行加密/解密.
    最后调用:CCCryptorFinal. 获取最后一块加密方法.

建议使用 CCCryptorCreateWithMode 方法.因为它能指定更多的参数. 比如 加密算法的padding 和ciphermode.

最后再顺便分享一下Mac 和iOS上生成 密码学安全的随机数的方法: Generating Random Numbers

简单的来说. 在mac上, 可以通过 fopen 读取 /dev/random 设备获得密码学安全的随机数.

FILE *fp = fopen("/dev/random", "r");

if (!fp) {
    perror("randgetter");
    exit(-1);
}

uint64_t value = 0;
int i;
for (i=0; i<sizeof(value); i++) {
    value <<= 8;
    value |= fgetc(fp);
}

fclose(fp);

而在 iOS 上, 由于不能读取设备, 它提供了 专门的 方法: SecRandomCopyBytes , 用起来非常简单.

欢迎访问我的独立博客 https://blog.byneil.com

byNeil
byNeil.com

原文来自 Blog by Neil, post Mac和 iOS 下的对称和非对称加密算法的使用 转载请注明出处。本站保留一切权力

Radius 认证协议介绍

老规矩, 先看维基: 远端用户拨入验证服务(RADIUS, Remote Authentication Dial In User Service)是一个AAA协议,意思就是同时兼顾验证(authentication)、授权(authorization)及计费(accounting)三种服务的一种网络传输协议(protocol),通常用于网络存取、或流动IP服务,适用于局域网及漫游服务。
https://zh.wikipedia.org/wiki/RADIUS

上面的介绍来自维基百科. 说的权威,但是不太好懂. 我们这里再详细介绍以下几个问题, 希望能给初次接触radius的小伙伴有所帮助:

1) Radius 到底是什么.
2) Radius 应用和工作方式.
3) Radius 的协议细节.

一. Radius 到底是什么.

上面维基百科 说的很具体了, Radius 远程拨入服务, 它集成了 认证, 授权 和计费功能. 怎么理解这个问题呢. 举个不是十分贴切的栗子, 宽带 ADSL 拨号上网的后台认证和计费系统就可以用radius, 或者 V-P-N 系统的后台验证计费系统就是radius 的使用场景.

当然, 并不能把 radius 的概念限制在 "拨号", 实际上, 不光是这种 "拨号" 服务, 其他几乎任何服务, 都可以使用 radius 来进行认证,授权和计费服务.

二. Radius 的工作方式.

我们以搭建 V-P-N 为例, 介绍radius的工作方式.

v1

图中有三个角色, v-p-n(下面简称XXX服务器)服务器, 客户端 和radius 服务器.

Radius 主要工作在 xxx 服务器和 Radius 服务器之间, 客户几乎直接接触不到.

对于radius 协议来说, xxx 服务器实际上是 一个client, 而 radius 服务器是真正提供服务的server.
(如果看英文文档的话, rfc有专门解释 client 和server 的章节.)

这样看起来, radius 更像是 C/S 模式的协议.

所以后面的讨论主要集中在 xxx 服务器和 radius 服务器之间, 请自行脑补.

三. Radius 的协议细节.
这里还是介绍一下大体的细节, 重点介绍如何阅读rfc. 有了方向,再读rfc就容易多了. 不会详细介绍每个字节如何生成, 这是rfc的责任.

作为参考, 这是 radius 的rfc : https://tools.ietf.org/html/rfc2865

1) 首先, radius 是基于udp的协议, 所有数据包都是封装在udp协议中.
至于为什么是udp,而不是tcp, 请参看 rfc 的解释: Why udp?

2) radius 协议包的格式.
基本上, radius 协议有四种包:

Access-Request
Access-Accept
Access-Reject
Access-Challenge

这四种包都有统一的格式: https://tools.ietf.org/html/rfc2865#page-13

这四种包在 xxx服务器和radius 服务器之间通信,来完成整个过程.

r1

上图中, 前面三个交互是必须的, 后面两个绿色的带星号的交互不是必须的.

首先, xxx 服务器向radius发送 Access-Request 数据包, 包含用户信息和密码哈希等等信息用于认证.
此时服务器有四个选择:
第一, 如果认证通过,就返回一个 "Access-Accept" 数据包, 认证完成.
第二, 如果认证不通过,则返回 "Access-Reject",认证失败.
第三, 如果服务器认为必要, 可以返回一个"Access-Challenge" 数据包, 让用户提供更多的附加信息以完成认证. 而xxx服务器在收到 "Access-Challenge" 消息之后, 需要向用户索要必须的附加信息,然后组成一个新的 " Access-Request" 数据包发给radius服务器来继续认证. 此时服务器可以重复第一,或第二.
第四, 如果 "Access-Request" 数据包有问题, 服务器还可以采取 "静默丢弃"(silently discard) 的方式, 不做任何反应.

这就是主要的通信流程了.

一般来讲, 作为 radius 的客户端, "Access-Request" 数据包的格式比较重要. 他负责将认证信息加密传输给radius 服务器.
我们再介绍一下 "Access-Request" 数据包的格式:
https://tools.ietf.org/html/rfc2865#page-17

  0                   1                   2                   3
    0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1
   +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
   |     Code      |  Identifier   |            Length             |
   +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
   |                                                               |
   |                     Request Authenticator                     |
   |                                                               |
   |                                                               |
   +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
   |  Attributes ...
   +-+-+-+-+-+-+-+-+-+-+-+-+-

我们现在介绍后面的 "Attributes" 数据区. 这里包含了用户的密码信息.
radius 协议将用户名,密码等等身份信息 都封装成一个一个的 attribute.
举个栗子, 可能最少需要两个 attribute, 一个是用户名, 另一个是password hash. 这是最简单的方式了.

https://tools.ietf.org/html/rfc2865#page-22

目前, radius 的密码封装方式有两种,
一种是: User-Password, https://tools.ietf.org/html/rfc2865#page-27
另一种是: CHAP-Password, https://tools.ietf.org/html/rfc2865#page-28

两种方式只是密码的hash计算方法不一样而已. 具体生成方法比较明确. rfc 写的很明显,就不多说了.

到这里, radius 认证协议的客户端过程基本完成了. 至于服务端, 一般不用自己来写. 可以使用开源的现成服务端.

欢迎访问我的个人独立博客: https://blog.byNeil.com

byNeil
byNeil.com

原文来自 Blog by Neil, post Radius 认证协议介绍 转载请注明出处。本站保留一切权力

Windows 以及 Xcode下编译调试 libcurl 源码

curl 这个工具大家都很熟悉. 前几天因为要跟踪curl的实现细节, 不得不设法搭建curl的调试工程. 我们分别在windows visual studio 和 mac 上的 xcode 下搭建调试. 这里记录一些细节,分享出来.

curl 可以在数十个平台上编译运行.
这是官网地址: http://curl.haxx.se

请在这里下载源码: http://curl.haxx.se/download.html

下载解压.

一. Windows 调试:
在 windows 上调试比较简单. 自带工程文件.
直接用 VS2012 打开 \projects\Windows\VC11 里面的 工程文件就可以了。
务必用vs2012 打开。

直接 F10 就可以单步 到 main 函数。

二. Mac Xcode 调试.
我们主要说说 xcode 调试.
大概是因为 *nix 上的编译比较方便, 所以都是使用 makefile 编译. 所以没有自带工程文件.

所幸的是, 开源项目 cmake 可以根据 makefile 和配置文件逆向生成工程文件. 而更好的消息是 curl 早期是支持 cmake 的. 所以我们需要修改一下 curl 的配置文件, 并结合 cmake 来逆向生成 它在 xcode 上的工程文件.

开工.

1) 需要先安装cmake

下载 cmake 源码, http://www.cmake.org/files/v3.1/cmake-3.1.0-rc1.tar.gz
然后解压, 进去: ./configure && make && make install
一切顺利的话,应该就能成功了。

2) 生成 makefile

第二步, 我们需要生成curl 的 makefile. 进入curl的源码目录:

./configure  --without-libssh2  --with-darwinssl
make

说明一下, 我们这里指定了两个参数, 我们不需要 curl 支持 ssh, 所以把"--without-libssh2 " 排除. 而且"--with-darwinssl" 指定使用 mac 上的 ssl 实现, 而不需要 openssl.

这样可以减少出错的可能性, 而且, 我们就需要 darwinssl.

3) 生成 xcode 工程.

前面几步都没什么特别的, 这一步是关键, 使用 cnamke 逆向生成工程.

在 curl 的源码根目录中, 有一个文件 "CMakeLists.txt" 这个是逆向生成所必须的配置文件. 这是curl 官方提供的, 但是已经很旧了, 年久失修, 不能工作了.

我们可以先尝试一下:

mkdir -p build/xcode
cd build/xcode
cmake ../../$CURL -G Xcode

说明一下, 创建一个子目录保存工程. 然后使用命令:

cmake ../../$CURL -G Xcode

来生成工程.

这一步会报大量的错误在"CMakeLists.txt" 中.

请使用附件中这个我修改过的版本代替源码中的版本, 然后再重复执行上面的命令生成.
CMakeLists.txt

工程文件已经生成了.

x1

但是此时打开工程编译不过. 需要先对工程文件做一些修改:
具体请查看下面的命令.

sed -i "" "s/OTHER_LDFLAGS = \"/OTHER_LDFLAGS = \"-framework Cocoa -framework CoreFoundation -framework CoreServices -framework Security -fobjc-link-runtime -framework LDAP/"  CURL.xcodeproj/project.pbxproj

在工程中添加了一些 link target. 比较简单. 不多说了.

现在设置一下 调试工程, 就可以调试了.

x2

然后选中 curl 这个target:

x3

然后找到 main 函数. 就可以单步调试了:
x4

欢迎访问我的个人独立博客: http://byNeil.com

byNeil
byNeil.com

原文来自 Blog by Neil, post Windows 以及 Xcode下编译调试 libcurl 源码 转载请注明出处。本站保留一切权力

翻译,NTLM和频道绑定哈希(EPA)

为了过NTLM 的EPA认证, 参考了这篇文章,现在翻译过来,备查。

如果你知道NTLM,并且需要过EPA, 那么这篇文章一定是你最想知道的。

原文地址:

NTLM and Channel Binding Hash (aka Extended Protection for Authentication) - Microsoft Open Spec

 

=======================

Extended Protection for Authnetication (EPA) was introduced in Windows 7/WS2008R2 to thwart reflection attacks. This blog describes the changes in the implementation of NTLM Authentication that are needed to successfully authenticate to servers that have EPA enabled. Windows 7/WS 2008R2 and Windows 8/ WS2012 have EPA enabled out of the box.

认证的扩展保护(EPA) 在Windows7/WS2008R2 中引进, 用来对抗反射攻击. 本文介绍了要通过服务器端EPA认证,NTLM实现上的必要的改变.  Windows7/WS2008R2 和Windows8/WS2012默认开启了EPA.

You can read the details about EPA here http://technet.microsoft.com/en-us/security/advisory/973811

这里有EPA进一步的细节.

The concept in EPA is that authentication packets should be bound to the secure channel on which they are transmitted. This concept is not new and is known as channel binding (RFC 5056). RFC 5929 describes channel bindings for TLS that Winodws uses to bind the secure channel to authentication. Please note that EPA also uses Service Pricipal Name (SPN) but it is not used for TLS and we will not discuss it here.

EPA的概念是认证的数据包应该绑定到传输它的安全频道上.  这个概念并不是全新的, 它就是众所周知的频道绑定(RFC5056).  RFC5929描述了描述了Tls的频道绑定, Windows用它绑定安全频道到认证.  请注意, EPA同样也使用了服务主体名称(SPN), 但是这没有被用于TLS, 我们在这不讨论它.

Let's take an example of HTTPS, a protocol that uses TLS. Once a secure channel is established and cipher change has happened, HTTP traffic starts flowing. In this example, we are only considering services that require authentication. NTLM or Kerberos will be used if you are using Windows authentication. You are most likely to use NTLM since the whole point of using HTTP and TLS is to allow clients to connect over internet (In Windows 8, Kerberos can be used on the internet but we will concentrate on NTLM here). In case of Windows client, these are the steps that are taken to incorporate channel binding in the authentication process after secure channel has been established:

我们看看HTTPS协议, 它使用了TLS.  一旦安全频道建立,并且密码交换完成, HTTP就开始传输.  在这个例子中, 我们只关心需要认证的服务.  如果你正在使用Wndows认证的话, NTLM或者Kerberos将会被应用.你很可能会使用NTLM,  因为在TLS上使用HTTP的全部原因就是允许客户端通过Internet连接. (在Windows8中, Kerberos也能引用于internet, 但是我们仍然集中在NTLM上.) 对于Windows客户端,  在安全频道建立之后, 认证过程中, 构成频道绑定的步骤如下:

1. The hashing algorithm for the signature in the certificate is identified, if present.

如果存在, 证书签名的哈希算法先被识别出来.

2. SSPI calculates a hash (almost always SHA256 hash, see below for details/exceptions) of the certificate, appends other data relevent to the type of channel bindings and returns it to the application.

SSPI 计算证书的哈希值(通常是SHA256, 后面会有细节和例外),  加上频道绑定类型的相关数据, 然后返回给应用程序.

4. SSPI calculates the MD5 hash of channel bindings and uses it in the calculation of NTLM version 2 response.

SSPI 计算频道绑定的MD5哈希值,  用于计算 NTLM version 2 的回复

5. When server recives authenticate message, it queries SSPI for channel bindings. SSPI does exactly the same thing as on the client side and returns the data to the service. The service includes it in the call to method Accept Security Context (ASC)

当服务器接收到认证消息之后, 它像SSPI查询频道绑定.  SSPI 进行域客户端完全一样的操作, 并把数据返回给服务.  服务端在 接受安全上下文(ASC)的调用中包含这个数据.

6. In the process of verifying authenticate message, SSPI also takes into account the channel bindings. It calculates the MD5 hash of the channel bindings that were provided by the application (service) and compares it to the one sent by the client. If they match and rest of the authentication requirements are met, authentication is successful.

在校验的认证消息的过程中, SSPI同样要考虑频道绑定. 它会计算应用(服务)提供的频道绑定的MD5值, 并且和客户端发送过来的值进行对比.  如果匹配, 并且人权衡的其他条件满足, 则认证成功.

I'll now elaborate on each of the step listed above with a concrete example of RPC-over-HTTP traffic. The TLS network traffic is encrypted and I used Network Monitor expert Network Monitor Decryption Expert (NmDecrypt) to decrypt it. The decrypted network trace is attached to this blog.

下面使用一个具体的 RPC-over-HTTP 的传输来具体描述上面的每一步.  Tls的网络传输都是加密的, 我使用了 Network Monitor expert Network Monitor Decryption Expert (NmDecrypt) 来解密. 解密的网络跟踪在帖子的附件中.

If you open the network trace in Network Monitor, you'll see that in frame 16 server sends a certificate to client, as below (copied and pasted from the trace):

打开 网络跟踪日志, 你会发现在第16帧, 服务端发送了一个证书给客户端, 如下:

......

After secure channel is established and cipher change has taken place, HTTP traffic starts flowing.

在安全频道建立之后,并且密码交换完成, HTTP传输就开始了.

In this example, HTTP is being used as a transport for RPC and RPC server requires authentication. For authentication, the client application first calculates the channel binding by using the following process(in Windows this is done by SSPI but that is not important in this discussion ). This process is based on RFC 5929.

在这个例子中, HTTP用来传输RPC, 并且RPC服务器要求认证.  对于认证, 客户端首先计算频道绑定, 过程如下(在Windows中, 由SSPI完成, 但这不重要): 这个过程基于RFC5929.

1. The channel binding type for this example is "tls-server-end-point" since a certificate is used in handshake (RFC5929).

频道绑定类型在本例中是 "tls-server-end-point", 因为在握手时使用了证书.

2. The client calculates a hash of the certificate. The hashing algorithm is SHA-256, unless all of the following conditions are met, in which case the signature algorithm in the certificate will be used.

客户端计算证书的哈希值. 哈希算法是SHA256, 除非下面的条件都满足, 此时将会使用证书的签名算法.

A certificate signature algorithm exist

证书签名算法存在

The algorithm is only implemented in CNG (ALG_ID is CALG_OID_INFO_CNG_ONLY)

(证书签名)算法只在CNG中实现(ALG_ID 为 CALG_OID_INFO_CNG_ONLY).

The algorithm has a corresponding CNG algorithm identifier string (pwszCNGAlgid)

(证书签名)算法有相应的CNG算法标示字符串(pwszCNGAlgid)

The algorithm is not SHA1

算法不是SHA1

The algorithm is not MD5

算法不是MD5

3. The SHA-256 hash of the above certificate is: ea 05 fe fe cc 6b 0b d5 71 db bc 5b aa 3e d4 53 86 d0 44 68 35 f7 b7 4c 85 62 1b 99 83 47 5f 95

上面证书的SHA-256哈希值是:  ea 05 fe fe cc 6b 0b d5 71 db bc 5b aa 3e d4 53 86 d0 44 68 35 f7 b7 4c 85 62 1b 99 83 47 5f 95

4. The Channel binding unique prefix (RFC5929) "tls-server-end-point" is prefixed to the hash above (with a colon), resulting in 

74 6c 73 2d 73 65 72 76 65 72 2d 65 6e 64 2d 70 6f 69 6e 74 3a ea 05 fe fe cc 6b 0b d5 71 db bc 5b aa 3e d4 53 86 d0 44 68 35 f7 b7 4c 85 62 1b 99 83 47 5f 95

频道绑定前缀"tls-server-end-point"被附加到这个hash值得前面(冒号分隔):

74 6c 73 2d 73 65 72 76 65 72 2d 65 6e 64 2d 70 6f 69 6e 74 3a ea 05 fe fe cc 6b 0b d5 71 db bc 5b aa 3e d4 53 86 d0 44 68 35 f7 b7 4c 85 62 1b 99 83 47 5f 95

5. The above value is inserted as the value of application_data field of gss_channel_bindings_struct structure, as pointed out by MS-NLMP section 2.2.2.1

上面这个值插入到 gss_channel_bindings_struct 结构的 application_data 字段 .  这个字段在  MS-NLMP section 2.2.2.1 描述.

6. Windows always sets the other fields of gss_channel_bindings_struct as zeros (SEC_CHANNEL_BINDINGS

structure). The resulting gss_channel_bindings_struct is as follows (little endian format):

Windows 总是将 gss_channel_bindings_struct 机构的其他字段设为 0.  gss_channel_bindings_struct 结构的结果如下(小端格式):

00 00 00 00 //initiator_addtype 

00 00 00 00 //initiator_address length

00 00 00 00 //initiator_address pointer

00 00 00 00 //acceptor_addrtype

00 00 00 00 //acceptor_address length

00 00 00 00 //acceptor_address pointer

35 00 00 00 //application_data length (53 bytes)

20 00 00 00 //application_data pointer (32 bytes from start of this structure)

74 6c 73 2d //application data, as calculated above

73 65 72 76

65 72 2d 65

6e 64 2d 70

6f 69 6e 74

3a ea 05 fe

fe cc 6b 0b

d5 71 db bc

5b aa 3e d4

53 86 d0 44

68 35 f7 b7

4c 85 62 1b

99 83 47 5f

95

After calculating channel binding, the client application starts authentication and include channel binding as part of authentication. In case of NTLM, the gss_channel_bindings_struct is called ClientChannelBindingUnhashed (MS-NLMP section 3.1.1.2). As explained in MS-NLMP section 3.1.5.1.2, the client adds an AV_PAIR structure and set the AvId field to MsvAvChannelBindings and the Value field to MD5(ClientChannelBindingsUnhashed). The MD5 hash of the above gss_channel_bindings_struct turns out to be:

频道绑定完成之后, 客户端开始认证, 将频道绑定作为认证的一部分包含进来.  对于NTLM, gss_channel_bindings_struct 结构 被叫做: ClientChannelBindingUnhashed (MS-NLMP section 3.1.1.2). 正如  MS-NLMP section 3.1.5.1.2, 中所介绍的,  客户端添加一个AV_PAIR  结构, AvId字段设为 MsvAvChannelBindings, 而Value字段设为 MD5(ClientChannelBindingsUnhashed). 上面的gss_channel_bindings_struct  结构的MD5值如下:
65 86 E9 9D 81 C2 FC 98 4E 47 17 2F D4 DD 03 10

This value is part of the AUTHENTICATE_MESSAGE in frame 27 in the network trace attached (in the network trace it is shown in Base64 encoding as 45 41 42 6C 68 75 6D 64 67 63 4C 38 6D 45 35 48 46 79 2F 55 33 51 4D 51 with AvLen) .

这个值作为AUTHENTICATE_MESSAGE 认证消息的一部分, 在附件的第27帧(附件中显示为Base64的编码值:   45 41 42 6C 68 75 6D 64 67 63 4C 38 6D 45 35 48 46 79 2F 55 33 51 4D 51 包含 AvLen   ).

When server receives the AUTHENTICATE_MESSAGE, in addition to the regular authentication processing, it also verifies the channel binding hash by calculating it the same way the client did. If the channel binding hash does not match, the authentication will not be successful. The subsequent behavior is server dependent. In this example (IIS), the server will stop communication on unsuccessful authentication.

当服务端收到 AUTHENTICATE_MESSAGE 认证消息之后, 除了做常规的认证处理之外, 它还校验频道绑定数据, 与客户端同样计算一遍. 如果频道绑定数据不匹配, 认证就失败. 之后的行为就和服务器相关. 在这个例子中(IIS), 服务器会停止通信.

Please note that two step hashing is being employed here. First the application creates a hash of the certificate which becomes a part of gss_channel_bindings_struct structure. This structure is MD5 hashed again to be included in AUTHENTICATE_MESSAGE.

请注意, 这里应用了2步哈希. 首先应用程序计算证书的哈希值, 使其成为 gss_channel_bindings_struct结构的一部分.  这个结构又被计算MD5 哈希值, 用来包含在 AUTHENTICATE_MESSAGE中.

There are configurations on both Windows client and server side to disable the EPA. For the server side, please consult the server specific documentation. As for the server in this example, IIS, please consult http://www.iis.net/configreference/system.webserver/security/authentication/windowsauthentication/extendedprotection.

在Windows客户端和服务端都有配置来禁用EPA.  对于服务端, 请参考服务端规范. 对于本例中使用的IIS, 请参考:

http://www.iis.net/configreference/system.webserver/security/authentication/windowsauthentication/extendedprotection

On the client side, there is a registry setting that is described in KB976918 (http://support.microsoft.com/kb/976918) that can be used to configure EPA.

对于客户端,  KB976918 描述了一个注册表设置(http://support.microsoft.com/kb/976918) 能用来配置EPA.

 

==============================

本文不是完全对照翻译,但是力求主题意思清楚。  请参考原文阅读:

NTLM and Channel Binding Hash (aka Extended Protection for Authentication) - Microsoft Open Spec

 

欢迎大家交流讨论。

byNeil
byNeil.com

原文来自 Blog by Neil, post 翻译,NTLM和频道绑定哈希(EPA) 转载请注明出处。本站保留一切权力