用 Kerberos 为 J2ME 应用程序上锁,第 1 部分: Kerberos 数据格式介绍
2010-03-30 00:00:00 来源:WEB开发网许多用户不愿意使用通过无线连接发送敏感数据的应用程序,因为他们不信任无线安全性。但是使传统有线网络上的电子商务的安全成为可能的这些协议,同样也可以使无线交易成为安全的。在这个由三篇文章组成的系列中,我将展示 J2ME 客户机和服务器端 Java 应用程序之间使用 Kerberos 协议进行的安全消息传递。我将开发一个移动银行 MIDlet 应用程序,它可以通过 Internet 安全地发送和接收付款信息。MIDlet 应用程序将使用一个基于 J2ME 的 Kerberos 客户机来进行实际的安全消息传递。在本文中,我首先解释移动银行应用程序的使用模型,然后我将解释 Kerberos 消息交换的顺序,从而为后续的 J2ME 客户机和服务器端 Java 应用程序之间进行的安全消息传递建立安全的上下文。紧接着描述了 Kerberos 消息传递中使用的数据格式。本文的最后一部分简单介绍了 Kerberos 客户机的体系结构,它最终创建并处理 Kerberos 消息和数据格式。
一个用于移动银行的安全 MIDlet
我将首先考虑这样一个用例场景:其中有两个拥有电子银行帐户的移动电话用户 Alice 和 Bob。
如果 Alice 想要付款给 Bob,那么她就向 MIDlet 应用程序提供 Bob 的手机号码(或者他的银行帐户号码)并让 MIDlet 向他付款。MIDlet 安全地与电子银行联系并请求电子银行向 Bob 付款。电子银行完成这笔交易并将确认发送回 Alice。Bob 在他的手机上也安装了 MIDlet,可以用于查看 Alice 的付款是否到达他的帐户。MIDlet 安全地与电子银行进行通信以验证付款状态。
我在本系列文章中将设计并实现的 MIDlet 处理所有与电子银行进行的特定于应用程序的消息传递。当然,MIDlet 必须保证通信是安全的。下面的功能构成了 MIDlet 的安全性需求:
电子银行应该可以确认请求付款和更新帐户状态的用户的身份。这种安全性需求通常称为 身份验证,其中服务器需要验证发出请求的客户的身份。
像 Alice 和 Bob 这样的用户应该可以确保他们是真的与电子银行而不是一些恶意的黑客进行通信。这也是一种身份验证的情况,在这里客户希望对服务器进行身份验证。客户和服务器彼此验证的情况称为 双向身份验证。
所有通信都应该是加密的以维护消息的机密性。即使黑客可以得到在网络上传送的消息字节,他也不能理解这些加密的数据字节的意义。
通信的双方(用户和电子银行)都应该能够验证收到的消息的完整性。如果恶意黑客改变了传输中的消息,那么接收一方应该可以发现这种改变。
我将使用 Kerberos 协议来满足这些安全性需求。我将所有与 Kerberos 相关的功能封装到一个小型的、适合于 J2ME 设备的客户端 Kerberos 实现中。我们的移动银行 MIDlet 只需要负责特定于应用程序的消息传递功能,而 Kerberos 客户机将处理所有安全问题。
这种技术对于读者来说有一个好处。Kerberos 客户机包装了安全功能(身份验证、机密性和消息完整性)。因此,如果要开发自己的 MIDlet,以用于发出和接收付款以外的目的,您也可以使用这里描述的 Kerberos 客户机来使之具有安全功能。
本文的大部分用于讨论 Kerberos 是如何与应用程序一同工作的,以及描述为了建立安全通信而在客户机与服务器之间交换的不同消息。我将在本系列的后续文章中详细描述客户端应用程序本身。
Kerberos 消息交换
本节我将描述以下三个参与者之间的 Kerberos 消息交换:
支持 J2ME 的无线设备
无线设备用户
电子银行
图 1. 为建立安全通信上下文而进行的 Kerberos 消息交换
图 1 给出了三个参与者之间出现的消息交换的概括视图。本节我将讨论这一视图并解释图中显示的消息交换的最终结果。下一节解释每一消息的具体细节(即结构和格式)。
注意,图 1 中支持 J2ME 的无线设备包含两个参与者:MIDlet 和 Kerberos 客户机。与此类似,电子银行系统也包含两个参与者:业务逻辑服务器和 Kerberos 分发中心 (Kerberos Distribution Center,KDC)。
业务逻辑服务器是实现了电子银行业务逻辑的服务器端 Java 应用程序。KDC 是一个管理和分发 Kerberos 票据的 Kerberos 服务器。KDC 又由两个服务器组成:一个 身份验证服务器(AS) 和一个 票据授予服务器(TGS)。AS 和 TGS 都接收客户机请求并发出 Kerberos 票据以响应这些请求。当 AS 接收客户机的票据请求时,它发出一个初始票据。然后客户机向 TGS 展示这个初始票据。TGS 根据这个初始票据发出服务票据。
获得初始票据的主要目的是在以后用它得到一个或者多个服务票据。这就是为什么初始票据也称为 票据授予票据(TGT)。
注意,服务票据是用于客户机与特定服务器之间的安全通信的一种手段。另一方面, TGT 并不是针对任何特定的服务器的。因此,TGT 逻辑上等于一个开放连接,它的一端是客户机,而另一端是未知的。当对一个 TGT 生成一个服务票据时,另一端也就确定了。同一个 TGT 可以用于获得任意数量的服务票据。
下面描述的消息交换步骤揭开了 MIDlet 如何以及为什么获得并使用 Kerberos 票据的神秘面纱。图 1 中的每一个数字都对应于下面讨论的一个步骤。
手机用户向 MIDlet 应用程序提供他或者她的用户名和密码(只由用户和电子银行共享的一个秘密)。这个密码只用于在 J2ME 应用程序内部的处理,它永远也不会进入网络。通过网络传输的只有用户名。
MIDlet 将用户名和密码交给 Kerberos 客户机。由 Kerberos 客户机负责建立与电子银行进行安全通信的上下文。
Kerberos 客户机请求 AS 发出一个 TGT。一个 TGT 请求表示一个安全会话。一个客户机可以在一个安全会话中建立多个子会话。 TGT 请求包含了发出请求的客户的用户名,但是不包括密码(共享的秘密)。
Kerberos 客户机向 AS 发送请求。
当 AS 收到 TGT 请求时,它从请求中提出用户名并从内部数据库中取出相应的密码(共享的秘密)。然后 AS 发布一个 TGT 并将 TGT 包装在回复消息中。这个 TGT 包含一个纯文本部分和一个密码文本(加密的)部分。为了加密 TGT 的密码部分,AS 使用了由用户的密码生成的加密密钥。因此,只有知道密码的用户才能解开加密的部分。TGT 的加密部分还包含一个加密密钥,称为 会话密钥。
AS 向发出请求的 Kerberos 客户机发送回复消息(包括 TGT)。
收到 AS 的回复后,Kerberos 客户机从回复中取出 TGT 并解密 TGT 中加密的部分。 然后 Kerberos 客户机发出一个服务票据请求。这个请求包含了 TGT 和一个称为 鉴别码 (authenticator)的加密结构。客户机用从 TGT 提取出的会话密钥加密鉴别码。鉴别码证明客户机掌握会话密钥。服务票据请求还指定了电子银行业务逻辑服务器的名字。
客户机向 TGS(它是电子银行的 KDC 的一部分)发送服务票据请求。
收到服务票据请求后,TGS 提取客户机向其请求服务票据的服务器的名称,然后授予一个服务票据。服务票据与 TGT 没有很大差别。与 TGT 一样,服务票据包含一个纯文本部分和一个密码文本(加密的)部分。TGS 用服务器的密钥(一个用服务器的共享秘密生成的密钥)加密服务票据的密码文本部分,这样只有那个服务器可以解密这个服务票据的密码文本部分。TGS 在服务票据的密码文本部分中还加入了一个新的加密密钥。这个密钥称为 子会话密钥。注意现在有两个密钥:会话密钥和子会话密钥。
授予了服务票据之后,TGS 将服务票据包装在一个响应消息中。这个响应消息也包含一个密码文本部分,它是用会话密钥加密的。响应消息的密码文本部分包含子会话密钥。
TGS 向客户机发送响应消息。
收到 TGS 响应后,客户机用会话密钥解密密码文本部分,从而提取出子会话密钥。客户机还提取服务票据。
然后客户机向电子银行业务逻辑服务器发出消息,并在消息中包装了服务票据。这个消息请求服务器与客户机建立新的安全会话。
客户机向电子银行的业务逻辑服务器发送消息。
电子银行的业务逻辑服务器从请求中提取服务票据,解密它的密码文本部分,并提取子会话密钥。这样客户机和服务器就都掌握这个密钥了。
电子银行的服务器向客户机发送一个肯定应答。
客户机和电子银行服务器现在可以用子会话密钥进行安全通信了。
Kerberos 消息
那么这些加密是如何工作的呢?在本文的其余部分,我将详细探讨图 1 中步骤 3 到步骤 16 中交换的 Kerberos 消息的结构。
TGT 请求
图 2 图示了在图 1 的步骤 3 中讨论的 TGT 请求消息的表示。
图2. TGT 请求消息的结构
Kerberos 协议定义了在 Kerberos 消息传递中使用的所有数据结构和消息的标题 (title)。注意,图 2 中消息的标题是 AS-REQ --这是一个 AS 请求。
图 2 显示了一种嵌套框的结构。每一个框表示一个数据字段。一些字段又包含了不同的字段,从而构成了一种嵌套的层次结构。
最外面的框标记为 AS-REQ ,包含一个标记为 KDC-REQ 的更小的框。这个 KDC-REQ 框包含四个字段:
pvno :这个数据字段表示 Kerberos 协议版本号。本系列文章的讨论是针对 Kerveros 版本 5 的,它相当稳定,并且在目前是最新的。
msg-type :可以通过消息类型号来识别不同的 Kerberos 消息。TGS 请求消息的类型号是 10。
padata :这是一个可选的字段,在大多数 TGT 请求消息中都没有它。这个字段的目的是加入身份验证信息。在本节后面描述服务票据请求时我将描述 padata 字段的结构。
第四个字段标记为 req-body ,它是 TGT 请求的正文。它又进一步分为几个字段:
kdc-options : 这个字段表示 KDC 选项。Kerveros 定义了客户机可能希望请求的几个选项。例如,客户机可能需要一个 forwardable 票据(可以转发给不同 KDC 服务器的票据)。与此类似,客户机可能会请求一个 renewable 票据(可以在失效后更新的票据)。在本系列文章的后面,我将讨论一些可用的选项,特别是那些与我的移动银行应用程序有关的选项。
cname :客户的用户名。
realm :KDC 领域(realm)。注意,使用 Kerberos 的每一个机构都可以建立自己的领域。领域就像信任域,比如我们的电子银行。一个领域可能跨越或者不跨越企业边界。这意味着一个领域可能需要或者不需要与属于其他企业的用户通信。在移动银行应用程序中,我将尽量保持简单,假定所有用户(如 Alice 和 Bob)是在电子银行自己的企业中注册的。
sname :这是一个标识客户机将向其出示所请求的票据的服务器的名称。对于 TGT 请求, sname 字段指定了 TGS 的名称(因为客户机最终要向 TGS 展示 TGT)。另一方面,对于服务票据请求(您将在本节的后面看到它), sname 字段将指定电子银行服务器的名称(因为客户机最终是要向电子银行的业务逻辑服务器出示服务票据)。
from :这是一个可选的字段,它表明客户机需要一个填迟日期的票据,即其有效性将在将来的某一时刻开始。在移动银行应用程序中我不需要这个功能。
till :这个字段表明 TGT 失效的时间。客户机指定 TGT 在什么时候失效。所有 Kerberos 时间字段都遵循 YearMonthDateHourMinSecZ 格式。例如,1947 年 8 月 14 日早上 3:30 将表示为 19470814033000Z 。
rtime :这个字段指定在什么时刻后票据就不能更新了。这是一个可选的字段,只有当客户机在 kdc-options 字段中选择了 renewable 选项后才会使用。
nonce :这是一个随机生成的整数。尽管这个整数本身没有意义,但是它有助于检测回复攻击。
etype :这个字段指定客户机要使用的加密算法。Kerberos 为常用的加密算法定义了不同的整数,客户机将使用对应的整数值。
addresses :这是一个可选的字段,它包含一组地址,只有从这些地址来的票据才是有效的。客户可以在这里指定从什么网络地址上使用所请求的票据。
enc-authorization-data :这是一个可选字段,它包装了身份验证数据,服务器可以根据这些数据来实施其身份验证策略。我不准备在移动银行应用程序中展示这种功能的使用。
additional-tickets :这是一个可选字段,它使 Kerberos 客户机可以根据客户机已经获得的多个票据请求一个安全会话。我不准备在移动银行应用程序中使用这个功能。
用 ASN.1 定义数据结构
Kerberos 用 Abstract Syntax Notation One (ASN.1) 定义在 Kerberos 通信中使用的各种数据结构和字节格式。ASN.1 是一个 ITU-T (International Telecommunication Union-Telecommunication standardization sector) 标准。它由参考号为 X.680 到 X.699 的不同文档组成(参阅 参考资料中的链接)。
ASN.1 语法和编码细节不是本文的重点。不过,我需要对 ASN.1 的概念做一些讨论以解释 Kerberos 结构和字节格式。我将只讨论那些解释 Kerberos 消息格式所需要的 ASN.1 概念。
Kerberos 消息的字节编码 在讨论 Kerberos 消息的其他内容之前,我将首先描述如何将图 2 中的 TGT 请求消息编码为字节值序列。
清单 1 是图 2 中 TGT 请求消息的 ASN.1 类定义(有关 ASN.1 的更多内容见侧栏)。稍后的表 2 显示了 TGT 请求消息编码的每一字节的格式。为了理解 TGT 请求消息的字节编码,必须将图 2、清单 1 和表 2 联系在一起。
注意,清单 1 中的主要结构标记为 AS-REQ。相同的 AS-REQ 标记出现在图 2 中的外围框中。
现在看一下清单 1 中 AS-REQ 的后面是什么。 AS-REQ 后面的两个冒号和等号 ( ::= ) 表明这一行定义了 AS-REQ 结构。接下来是方括号中的一个字符串 APPLICATION 10 。A APPLICATION 10 表明这个 AS-REQ 结构在这个 APPLICATION 的各种结构中是用编号 10 识别的。我们可以说编号 10 是一个 应用程序级标签号。这个编号在应用程序中是惟一的―― 换句话说,其他 Kerberos 结构将不会使用这个编号。
现在注意在 [APPLICATION 10] 字符串后的 KDC-REQ 字符串。这表明在 AS-REQ 结构的格式后面是另一个名为 KDC-REQ 的结构的定义。这是 ASN.1 中的一种重用机制。 KDC-REQ 结构用在两个地方。因此,Kerberos 定义 KDC-REQ 一次并使用它两次。
总而言之, AS-REQ ::= [APPLICATION 10] KDC-REQ 表明在该应用程序中的不同结构中 AS-REQ 标记为 10,后面是另一个名为 KDC-REQ 的结构的定义。
清单 1. TGT 请求消息的 ASN.1 类定义
AS-REQ ::= [APPLICATION 10] KDC-REQ
KDC-REQ ::= SEQUENCE {
pvno[1] INTEGER,
msg-type[2] INTEGER,
padata[3] SEQUENCE OF PA-DATA OPTIONAL,
req-body[4] KDC-REQ-BODY
}
PA-DATA ::= SEQUENCE {
padata-type[1] INTEGER,
padata-value[2] OCTET STRING,
-- might be encoded AP-REQ
}
KDC-REQ-BODY ::= SEQUENCE {
kdc-options[0] KDCOptions,
cname[1] PrincipalName OPTIONAL,
-- Used only in AS-REQ
realm[2] Realm, -- Server's realm
-- Also client's in AS-REQ
sname[3] PrincipalName OPTIONAL,
from[4] KerberosTime OPTIONAL,
till[5] KerberosTime,
rtime[6] KerberosTime OPTIONAL,
nonce[7] INTEGER,
etype[8] SEQUENCE OF INTEGER, -- EncryptionType,
-- in preference order
addresses[9] HostAddresses OPTIONAL,
enc-authorization-data[10] EncryptedData OPTIONAL,
-- Encrypted AuthorizationData encoding
additional-tickets[11] SEQUENCE OF Ticket OPTIONAL
}
EncryptedData ::= SEQUENCE {
etype[0] INTEGER, -- EncryptionType
kvno[1] INTEGER OPTIONAL,
cipher[2] OCTET STRING -- ciphertext
}
KDCOptions ::= BIT STRING {
reserved(0),
forwardable(1),
forwarded(2),
proxiable(3),
proxy(4),
allow-postdate(5),
postdated(6),
unused7(7),
renewable(8),
unused9(9),
unused10(10),
unused11(11),
renewable-ok(27),
enc-tkt-in-skey(28),
renew(30),
validate(31)
}
PrincipalName ::= SEQUENCE {
name-type[0] INTEGER,
name-string[1] SEQUENCE OF GeneralString
}
KerberosTime ::= GeneralizedTime
-- Specifying UTC time zone (Z)
HostAddresses ::= SEQUENCE OF SEQUENCE {
addr-type[0] INTEGER,
address[1] OCTET STRING
}
现在让我们来看一下 KDC-REQ 结构。
清单 1 中的 KDC-REQ ::= SEQUENCE 这一行表明 KDC-REQ 结构是不同数据结构的序列。在 SEQUENCE 关键词后的一组花括号描述了共同构成 KDC-REQ 结构的数据结构。
花括号中有四行代码。第一行 ( pvno[1] INTEGER ) 定义了 KDC-REQ 结构的第一个元素,它是图 2 中的 pvno 字段。
第二行 ( msg-type[2] INTEGER ) 对应于图 2 中的 msg-type 字段。注意 pvno 和 msg-type 字段的类型为 INTEGER ,这意味着它们是用 INTEGER 数据类型构建的。
还要注意在清单 1 中 pvno 和 msg-type 后面的方括号中的数字 ( pvno[1] and msg-type[2] )。与在前面 AS-REQ ::= [APPLICATION 10] KDC-REQ 一行中见到的应用程序级标签号相反,它们是 上下文特定的标签号。
应用程序级和上下文特定的标签号有什么区别呢?应用程序级标签号在整个应用程序中是惟一的和有效的。例如,在整个 Kerberos 应用程序中编号 10 都是指 AS-REQ 结构。而上下文特定的标签号只在定义它们的上下文中有意义。例如,在 KDC-REQ 的结构内部,上下文特定的标签号 1 表示 pvno 。但是在查看其他结构的内部时,同样的上下文特定的标签号 1 表示的则是一些其他的字段。
在后面讨论将清单 1 编码为表 2 中的字节序列时,我将解释这些应用程序级和上下文特定的标签号的使用。
现在看一看清单 1 中 KDC-REQ 结构中第三行 ( padata[3] SEQUENCE OF PA-DATA ) 和第四行 ( req-body[4] KDC-REQ-BODY )。第三行定义了图 2 中的 padata 字段,它是 PA-DATA 结构的一个 SEQUENCE 。第四行表示图 2 中的 req-body 框。
padata 和 req-body 字段又是由不同字段组成的 Kerberos 结构。例如, req-body 的数据类型是 KDC-REQ-BODY ,而该数据类型自己又是一个带有几个字段(像前面讨论的那样带有 kdc-options、 cname 、 realm 、 sname 、 till 、 nonce 和 etype 等字段)的 Kerberos 结构。
回想一下 pvno 字段是用一个 INTEGER 构建的。另一方面, req-body 字段的数据类型为 KDC-REQ-BODY ,该数据类型本身又是由几个字段构建的一种结构。
还要注意 INTEGER 是基本 ASN.1 数据类型的一个例子,而 KDC-REQ-BODY 是由其他字段构建的派生数据类型。
ASN.1 定义了一些可以被应用程序使用的数据类型,称为 通用数据类型。大多数通用数据类型是基本类型,只有少数是构建的。ASN.1 为通用数据类型定义了 标签号,如表 1 所示。
表 1. 一些通用数据类型和标签号
通用数据类型 | 通用标签号 | 构建类型还是基本类型? |
BOOLEAN | 1 | 基本类型 |
INTEGER | 2 | 基本类型 |
BIT STRING | 3 | 基本类型 |
OCTET STRING | 4 | 基本类型 |
SEQUENCE | 16 | 构建类型 |
GeneralizedTime | 24 | 基本类型 |
GeneralString | 27 | 基本类型 |
表 1 显示 INTEGER 数据类型的通用标签号是 2,并且 INTEGER 数据类型是基本类型。 SEQUENCE 是表 1 中惟一的构建类型的通用标签。这是因为 SEQUENCE 数据类型是使用其他字段普遍定义的, SEQUENCE 总是用其他字段构建的。
在 ASN.1 定义中没有提到通用数据类型的标签号,这是因为这些标签号已经得到普遍定义和理解。不过,在试图将 Kerberos 结构编码为字节序列时需要用到通用标签号。在稍后您会看到这一点。
现在,让我解释图 2 与清单 1 中的 AS-REQ 消息是如何编码为字节序列的。为了展示这个编码过程,我提供了表 2,它显示了客户机发送给 KDC 服务器以请求 TGT 的 TGT 请求(一个 AS-REQ 结构)中实际的字节序列。表 2 中显示的字节序列是我们要分析的 AS-REQ 编码过程的最终结果。
因为它比较长,所以我用一个 单独的文件来提供表 2。在阅读进行下面的讨论时您应该在一个单独的浏览器窗口中打开它。
看一下表 2 中的第一个字节 ( 01101010 )。这是消息的第一个字节,表示清单 1 的 AS-REQ 结构的开始(图 2 中的外围框)。位 8 和位 7 ( 01 ) 表明这是一个应用程序级标签。位 6 ( 1 ) 表明这是一个构建的结构。位 5 到位 1 ( 01010 ) 表示 AS-REQ 的标签号(我在前面的讨论中说过 AS-REQ 结构的应用级标签号为 10,其二进制表示为 01010 )。
可以将标签字节分为三部分:
位 8 和位 7 指定标签的 类型或者类。对于应用程序级标签,位 8 和位 7 应该是 01 ,对于上下文特定的标签,它们应该是 10 ,对于通用标签,它们是 00 。
位 6 指定标签是基本的 ( 0 ) 还是构建的 ( 1 )。
位 5 到位 1 编码标签号。
因为用于表示标签号的位数限制了可以您可以拥有的标签的个数,所以您可能奇怪为什么只有 5 位用于标签号。ASN.1 定义了编码标签号的一个完整机制,与标签号的大小无关。不过,Kerberos 没有定义任何不能用 5 位表达的标签号。因此,为了将讨论集中于 Kerberos,我在这里将不讨论如何处理大的标签号。
现在,看一下表 2 中的第二个字节 ( 10000001 )。在标签字节后,有一个或者多个 长度字节。这些长度字节指定了构成完整标签的总字节数。
有两种定义长度字节的方法。第一种是 单字节长度表示,第二种是 多字节长度表示。对于单字节长度表示,要使第一个长度字节的位 8 为 0 ,并用其余的位指定长度字节的个数。例如,如果您想说这个结构中有 65 个字节,可以将长度值编码为 01000001 (位 8 设置为 0 ,位 7 到位 1――1000001 ――表示 65)。在这种方法中,总是有一个长度字节,下一个字节标记结构内容的开始。用这种方法可编码的最大值是 127。
对于多字节表示法,设置第一个长度字节的位 8 为 1 ,并用位 7 到位 1 指定随后的长度字节的个数。例如,如果要编码值 210,第一个长度字节将是 10000001 (位 8 设置为 1 ,位 7 到位 1 设置为 1 ,即 0000001 表明还有一个长度字节),后面再跟一个字节,其值为 11010010 (表示十进制的 210 )。
看一下表 2 中的字节 2 和字节 3 ,它们分别是 10000001 和 11010010 。这意味着我用多字节长度表示法来指定 AS-REQ 结构的长度,即 210 。
注意在表 2 中共有 213 字节,其中 210 是在第 3 个字节后。在字节 3 后的所有 210 个字节都属于 AS-REQ 结构。因此,第 4 个字节和后面的所有字节构成了 AS-REQ 结构的内容。
注意,清单 1 中 AS-REQ 结构后面是 KDC-REQ 结构的定义,这个结构是一个 SEQUENCE 。回想一下,在表 1 中 SEQUENCE 是 通用标签,它是 构建的数据类型,并且其标签号为 16。这就是为什么表 2 中字节 4 的值为 00110000 。位 8 和位 7 ( 00 ) 指定这是一个通用标签。位 6 ( 1 ) 表明这个结构是构建的。位 5 到位 1 指定标签号,对于 SEQUENCE 来说这是 16(二进制为 10000 )。
字节 5 和字节 6 指定 KDC-REQ 结构中的字节数。字节 5 ( 10000001 ) 指定有一个长度字节。字节 6 ( 11001111 ) 指定 KDC-REQ 序列的实际长度 (207)。我已经解释过长度字节的工作方式。
清单 1 中 KDC-REQ 序列的第一个字段是 pvno ,它是一个 上下文特定的,构建 的字段,其标签号为 1 。表 2 中的字节 7 ( 10100001 ) 表示这个字段。位 8 和位 7 ( 10 ) 表示这是一个上下文特定的标签。位 6 ( 1 ) 表示这是一个构建的字段,位 5 到位 1 ( 00001 ) 指定 pvno 字段的标签号。
字节 8 ( 00000011 ) 指定 pvno 字段的长度。这个长度字节的位 8 是 0 ,这表明我使用单字节长度表示法。位 7 到位 1 ( 0000011 ) 指定长度为 3。这里要注意,正如前面所解释的,当结构的长度小于 127 时,我就使用单字节长度表示法。
pvno 字段的内容从表 2 中的第 9 个字节开始。 pvno 字段是用 INTEGER 基本数据类型构建的,所以我可以期望 pvno 的内容为一个整数值。
字节 9 ( 00000010 ) 表示 INTEGER 标签。位 8 和位 7 ( 00 ) 标识这是一个通用标签,位 6 (0) 说明它是基本类型,位 5 到位 1 ( 00010 ) 表明标签号为 2(见表 1)。
位 10 ( 00000001 ) 提供了这个 INTEGER 数据类型在单字节长度表示法中的长度。 INTEGER 值只由一个字节组成,即下一个字节(第 11 个字节)。
您将注意到字节编码过程遵循一个清晰的模式。我以对应于清单 1 中一个字段的标签值开始。标签值后面是长度字节,长度字节后面是标签的内容。我不断使用嵌套的、分层次的构建结构,直到达到一个基本数据类型。
您可以根据这种模式完成表 2。表 2 的说明栏可以帮助您理解每一字节的作用。对于本文中的要讨论的其他消息,我不会提供逐字节的分析,不过对这个消息的分析可以使您了解它们的工作方式。
包装 TGT 的响应
图 3 是一个 AS 发出的、包装了 TGT 的响应消息的图形表示。图 3 同样使用了图 2 中展示的嵌套框结构。
图 3. AS 的 TGT 响应的结构
图 3 中的主框(外围框)标记为 AS-REP ,它包含一个标记为 KDC-REP 的更小的框。 KDC-REP 是由几个字段组成的一个序列。
pvno :在讨论图 2 时我解释了这个字段。
msg-type :消息的类型。它是一个整数,对于 TGT 响应消息它的值应该为 11。
padata :在讨论图 2 时我对这个字段做过说明。这是一个可选字段,在大多数 TGT 响应消息中没使用它。
crealm 和 cname :在讨论图 2 时对这些字段做过说明。
ticket :实际的 TGT。我将在本文的后面一节中讨论 Kerberos 票据自身的格式。
enc-part :这是一个加密数据的包装器。Kerveros 消息中所有加密的部分都包含三个字段:
etype :指定用于进行密码加密的加密算法的标识符。
kvno :用于加密的加密密钥的版本。AS 使用客户机的密钥进行加密。这个字段指定用于进行加密的密钥的版本。
cipher :一系列字节。这是实际的加密数据。数据解密后,我就有了另一个结构,如图 4 所示。
图 4. TGT 响应消息的加密部分的结构
图 4 显示了对 TGT 响应消息中加密部分进行解密后得到的结构。它包含以下字段:
key :这是会话密钥。当前会话的后续通信将使用这个密钥而不是密码密钥。
last-req :这个字段指定客户机的最后一次票据请求的时间。这个字段有助于通知客户机,它的请求已经收到了。
nonce :在讨论图 2 时我对这个字段做过说明。AS 将包含它在请求中收到的随机数的一个副本。这有助于检测回复攻击。如果黑客取得了 TGT 响应并想反复回复它,那么客户机就可以将响应的 nonce 字段与其请求进行比较以检测回复。
key-expiration :这是一个可选字段,它指定客户机的密钥失效的时间。
flags :这个字段对应于图 2 的 TGT 请求的 kdc-options 字段。这些 flags 表示 Kerberos 客户机可能请求的不同可选功能。AS 将标志发送回客户机,从而允许客户机比较 AS 是否可以提供所请求的可选功能。
authtime :AS 发出票据的时间。
starttime :票据生效的时间。
endtime :票据失效的时间。
renew-till :可更新的票据的最终失效时间。
srealm :服务器的领域。
sname :服务器名,其所带票据是有效的。
caddr :这个字段指定一个地址列表,这些地址给出的相应票据是可以使用的。这个字段的目的是使黑客使用偷来的票据更困难。
清单 2 提供了 TGT 响应消息的 ASN.1 类定义。读者可以将清单 2 中的类定义与图 3 和图 4 中显示的不同字段相对照。
清单 2. TGT 响应消息的 ASN.1 类定义
AS-REP ::= [APPLICATION 11] KDC-REP
KDC-REP ::= SEQUENCE {
pvno[0] INTEGER,
msg-type[1] INTEGER,
padata[2] SEQUENCE OF PA-DATA OPTIONAL,
crealm[3] Realm,
cname[4] PrincipalName,
ticket[5] Ticket,
enc-part[6] EncryptedData
}
EncryptedData ::= SEQUENCE {
etype[0] INTEGER, -- EncryptionType
kvno[1] INTEGER OPTIONAL,
cipher[2] OCTET STRING -- ciphertext
}
EncASRepPart ::= [APPLICATION 25] EncKDCRepPart
EncKDCRepPart ::= SEQUENCE {
key[0] EncryptionKey,
last-req[1] LastReq,
nonce[2] INTEGER,
key-expiration[3] KerberosTime OPTIONAL,
flags[4] TicketFlags,
authtime[5] KerberosTime,
starttime[6] KerberosTime OPTIONAL,
endtime[7] KerberosTime,
renew-till[8] KerberosTime OPTIONAL,
srealm[9] Realm,
sname[10] PrincipalName,
caddr[11] HostAddresses OPTIONAL
}
服务票据请求
收到 TGT 后,客户机发出服务票据请求,如图 5 所示。服务票据请求与我在图 2 中讨论的 TGT 请求非常相似。可以将图 5 中的所有字段与图 2 中的相应字段进行对照。我只需要解释专门针对服务票据请求的 padata 字段。
图 5. 服务票据请求消息的结构
padata 字段是 PA-DATA 结构的序列,它包含 身份验证数据。服务票据请求需要向票据授予服务器发送 TGT。 padata 字段将 TGT 包装到它的一个 PA-DATA 结构中。
Kerberos 用 padata 字段实现不同的目的,包装 TGT 只是其中一项。因此,Kerberos 定义了不同的整数值以指定 PA-DATA 结构包装的是什么类型的数据。
图 5 显示 PA-DATA 序列只包含一个 PA-DATA 结构,它由两个子字段组成,即 padata-type 和 padata-value 。 padata 序列中的每一个 PA-DATA 结构都包含这两个字段。
padata-type 是一个整数值,用于指定所带 padata-value 字段中的数据类型。当在服务票据请求中 padata-value 字段包装了一个 TGT 时, padata-type 字段的值就是 1。
padata-value 字段是一串字节,其中包含 TGT。 padata-value 字段中的字节串实际上是另一个名为 KRB_AP_REQ (或者简称为 AP-REQ )的 Kerberos 结构,也称为 身份验证头。
身份验证头包含 TGT 以及一些其他字段,如下所示:
pvno :在对图 2 的讨论中我已经解释过这个字段。
message-type :这个字段包含一个整数值 (12),用于识别 KRB_TGS_REQ 消息。
ap-options :这是一组选项。第一个选项保留为以后使用。第二个选项指定相应 TGT 是用包装在相应 TGT 中的会话密钥加密的。因为 TGS 已经知道会话密钥,所以它可以使用这把密钥进行解密。如果选择了第三个选项,那么它就指定客户机请求双向身份验证。
ticket :TGT 本身。
authenticator :这是一个加密的结构,其中包含几个字段,允许客户机证明它拥有会话密钥并帮助 TGS 检测回复攻击。在身份验证头中 鉴别码是以加密的(密文)形式出现的。客户机使用会话密钥加密鉴别码。鉴别码结构的字段如下:
authenticator-vno :鉴别码格式的版本号。对于 Kerberos 版本 5,这个字段应该指定 5。
crealm 和 cname: 我在对图 2 的讨论中解释了这些字段。
cksum :一个校验和或者散列值,由图 5 中显示的 req-body 字段的字节编码计算而来。这个字段让 TGS 可以检查请求消息的完整性。因为该校验和位于一个用会话密钥加密的结构中,所以这个字段也证明客户机拥有会话密钥。
cusec 和 ctime :这两个字段共同指定发出 KRB_AP_REQ 消息的客户机时间。 cusec 字段指定时间的微秒部分,而 ctime 字段指定日期和以毫秒计的时间。
subkey :这是一个可选的字段,客户机可以用它指定随后与服务器之间的通信所要使用的密钥。对于移动银行应用程序,我将尽量减少在 J2ME 客户机上的处理负荷,因此,让服务器决定会话密钥和子会话密钥。
seq-number :这是一个可选字段,可以包含消息的一个序列号以检测回复攻击。
authorization-data :这是一个可选字段,带有特定于应用程序的身份验证数据。在移动银行应用程序中我没有使用这个字段。
清单 3 提供了服务票据请求消息的 ASN.1 类定义。读者可以将图 5 中显示的不同字段与清单 3 中的类定义相对照。
清单 3. 服务票据请求消息的 ASN.1 类定义
TGS-REQ ::= [APPLICATION 12] KDC-REQ
KDC-REQ ::= SEQUENCE {
pvno[1] INTEGER,
msg-type[2] INTEGER,
padata[3] SEQUENCE OF PA-DATA OPTIONAL,
req-body[4] KDC-REQ-BODY
}
PA-DATA ::= SEQUENCE {
padata-type[1] INTEGER,
padata-value[2] OCTET STRING,
-- might be encoded AP-REQ
}
KDC-REQ-BODY ::= SEQUENCE {
kdc-options[0] KDCOptions,
realm[2] Realm, -- Server's realm
-- Also client's in AS-REQ
sname[3] PrincipalName OPTIONAL,
from[4] KerberosTime OPTIONAL,
till[5] KerberosTime,
rtime[6] KerberosTime OPTIONAL,
nonce[7] INTEGER,
etype[8] SEQUENCE OF INTEGER, -- EncryptionType,
-- in preference order
addresses[9] HostAddresses OPTIONAL,
enc-authorization-data[10] EncryptedData OPTIONAL,
-- Encrypted AuthorizationData encoding
additional-tickets[11] SEQUENCE OF Ticket OPTIONAL
}
AP-REQ ::= [APPLICATION 14] SEQUENCE {
pvno [0] INTEGER, -- indicates Version 5
msg-type [1] INTEGER, -- indicates KRB_AP_REQ
ap-options[2] APOptions,
ticket[3] Ticket,
authenticator[4] EncryptedData
}
APOptions ::= BIT STRING {
reserved (0),
use-session-key (1),
mutual-required (2)
}
Ticket ::= [APPLICATION 1] SEQUENCE {
tkt-vno [0] INTEGER, -- indicates Version 5
realm [1] Realm,
sname [2] PrincipalName,
enc-part [3] EncryptedData
}
-- Encrypted part of ticket
EncTicketPart ::= [APPLICATION 3] SEQUENCE {
flags[0] TicketFlags,
key[1] EncryptionKey,
crealm[2] Realm,
cname[3] PrincipalName,
transited[4] TransitedEncoding,
authtime[5] KerberosTime,
starttime[6] KerberosTime OPTIONAL,
endtime[7] KerberosTime,
renew-till[8] KerberosTime OPTIONAL,
caddr[9] HostAddresses OPTIONAL,
authorization-data[10] AuthorizationData OPTIONAL
}
-- Unencrypted authenticator
Authenticator ::= [APPLICATION 2] SEQUENCE {
authenticator-vno[0] INTEGER,
crealm[1] Realm,
cname[2] PrincipalName,
cksum[3] Checksum OPTIONAL,
cusec[4] INTEGER,
ctime[5] KerberosTime,
subkey[6] EncryptionKey OPTIONAL,
seq-number[7] INTEGER OPTIONAL,
authorization-data[8] AuthorizationData OPTIONAL
}
响应包含服务票据
当 TGS 收到服务票据的请求时,它就在响应中发出一个服务票据。图 6 显示了包装有服务票据的 TGS 响应。您可以对照图 6 与图 3。您会发现图 3 中显示的字段与在图 6 中显示的一样,只不过图 3 中的 ticket 字段是 TGT,而图 6 的 ticket 字段是服务票据。
还要注意,在产生图 6 的加密部分时,KDC 使用了在以前的消息中与客户机交换的会话密钥。服务票据包装了客户机与电子银行服务器进行安全通信时将会使用的子会话密钥。
图 6. TGS 的服务票据响应的结构
清单 4 提供了服务票据响应消息的 ASN.1 类定义。读者可以将图 6 中不同字段与清单 4 中的类定义进行对照。
清单 4. 服务票据响应消息的 ASN.1 类定义
TGS-REP ::= [APPLICATION 13] KDC-REP
KDC-REP ::= SEQUENCE {
pvno[0] INTEGER,
msg-type[1] INTEGER,
padata[2] SEQUENCE OF PA-DATA OPTIONAL,
crealm[3] Realm,
cname[4] PrincipalName,
ticket[5] Ticket,
enc-part[6] EncryptedData
}
EncryptedData ::= SEQUENCE {
etype[0] INTEGER, -- EncryptionType
kvno[1] INTEGER OPTIONAL,
cipher[2] OCTET STRING -- ciphertext
}
EncTGSRepPart ::= [APPLICATION 26] EncKDCRepPart
EncKDCRepPart ::= SEQUENCE {
key[0] EncryptionKey,
last-req[1] LastReq,
nonce[2] INTEGER,
key-expiration[3] KerberosTime OPTIONAL,
flags[4] TicketFlags,
authtime[5] KerberosTime,
starttime[6] KerberosTime OPTIONAL,
endtime[7] KerberosTime,
renew-till[8] KerberosTime OPTIONAL,
srealm[9] Realm,
sname[10] PrincipalName,
caddr[11] HostAddresses OPTIONAL
}
从客户机到电子银行业务逻辑服务器的消息
现在客户机有了服务票据,可以将它发送到电子银行业务逻辑服务器。客户机发送图 7 中的消息到电子银行服务器。这个消息的目的是请求服务器建立与客户机的新的安全上下文。
图 7. 从 Kerberos 客户机到电子银行服务器的消息的结构
我说过本文的目的是展示基于 J2ME 的安全移动银行应用程序。我准备在服务器端使用 Generic Security Services API(GSS API,或者简称为 GSS)来为电子银行业务逻辑服务器提供安全功能。GSS 是一种一般性的高层安全 API ,它可以在像 Kerberos 这样的不同安全技术上面工作。
我将在本系列的第三篇文章中讨论 GSS API 在电子银行业务逻辑服务器中的使用。现在,只要知道与 Kerberos 一样,GSS 也是一种 IETF 标准。IETF 为客户机与服务器之间相互传递的 Kerveros 消息定义了 GSS 包装器。为了在服务器端使用 GSS,我必须保证 GSS 客户机可以发出并处理 GSS 包装器。
图 7 中的外围框标记为 InitialContextToken ,它实际上是包装了从 GSS 客户机到 GSS 服务器的消息的 GSS 包装器的名字。在 InitialContextToken 包装器中,第一个字段名为 thisMech ,它定义了 GSS 作为低层安全技术使用的安全机制(在这里是 Kerveros)。
InitialContextToken 框中的第二个字段标记为 KRB_AP_REQ ,我在讨论图 5 时分析过它。回想一下前面的讨论中说过 KRB_AP_REQ 结构包装了票据。这就是为什么我可以使用这个结构包装一个服务票据并发送给电子银行服务器。我说过服务票据已经包含了子会话密钥。
清单 5 提供了 Kerberos 客户机发给电子银行业务逻辑服务器的消息的 ASN.1 类定义。您可以将图 7 中的不同字段与清单 5 中的类定义进行对照。
清单 5. 从客户机到服务器的安全上下文请求消息的 ASN.1 类定义
InitialContextToken ::=
[APPLICATION 0] IMPLICIT SEQUENCE {
thisMech MechType
-- MechType is OBJECT IDENTIFIER
-- representing "Kerberos V5"
innerContextToken ANY DEFINED BY thisMech
-- contents mechanism-specific;
-- ASN.1 usage within innerContextToken
-- is not required
}
AP-REQ ::= [APPLICATION 14] SEQUENCE {
pvno [0] INTEGER, -- indicates Version 5
msg-type [1] INTEGER, -- indicates KRB_AP_REQ
ap-options[2] APOptions,
ticket[3] Ticket,
authenticator[4] EncryptedData
}
APOptions ::= BIT STRING {
reserved (0),
use-session-key (1),
mutual-required (2)
}
Ticket ::= [APPLICATION 1] SEQUENCE {
tkt-vno [0] INTEGER, -- indicates Version 5
realm [1] Realm,
sname [2] PrincipalName,
enc-part [3] EncryptedData
}
-- Encrypted part of ticket
EncTicketPart ::= [APPLICATION 3] SEQUENCE {
flags[0] TicketFlags,
key[1] EncryptionKey,
crealm[2] Realm,
cname[3] PrincipalName,
transited[4] TransitedEncoding,
authtime[5] KerberosTime,
starttime[6] KerberosTime OPTIONAL,
endtime[7] KerberosTime,
renew-till[8] KerberosTime OPTIONAL,
caddr[9] HostAddresses OPTIONAL,
authorization-data[10] AuthorizationData OPTIONAL
}
-- Unencrypted authenticator
Authenticator ::= [APPLICATION 2] SEQUENCE {
authenticator-vno[0] INTEGER,
crealm[1] Realm,
cname[2] PrincipalName,
cksum[3] Checksum OPTIONAL,
cusec[4] INTEGER,
ctime[5] KerberosTime,
subkey[6] EncryptionKey OPTIONAL,
seq-number[7] INTEGER OPTIONAL,
authorization-data[8] AuthorizationData OPTIONAL
}
电子银行的响应
当电子银行的业务逻辑服务器收到图 7 中的消息时,它提取出服务票据并解密票据中的加密部分以得到子会话密钥。客户机已经有了同样的子会话密钥。因此,服务器和客户机可以使用这个子会话密钥彼此进行安全通信。
图 8. 电子银行对 Kerberos 客户机的响应的结构
电子银行业务逻辑服务器向客户机发回一个确认消息,如图 8 所示。下面是构成图 8 的消息的字段:
pvno :我在对图 2 的讨论中解释了这个字段。
msg-type :这是具有整数值 14 的消息类型标识符。
enc-part :消息的加密部分。客户机将用子会话密钥解密这个加密部分。在解密时,它将展现另一个带有以下字段的结构:
ctime 和 cusec :我在对图 6 的讨论中解释了这些字段。
subkey :这是一个服务器可能发送给客户机的可选密钥。如果服务器将这个密钥发送给客户机,那么这个密钥将被用于随后客户机与服务器之间的安全通信(取代子会话密钥)。
seq-number :我在对图 5 的讨论中解释了这个字段。
清单 6 提供了电子银行对 Kerveros 客户机的响应的 ASN.1 类定义。读者可以将图 8 中显示的不同字段与清单 6 中的类定义相对照。
清单 6. 从服务器到客户机的安全上下文响应的 ASN.1 类定义
AP-REP ::= [APPLICATION 15] SEQUENCE {
pvno [0] INTEGER, -- represents Kerberos V5
msg-type [1] INTEGER, -- represents KRB_AP_REP
enc-part [2] EncryptedData
}
EncAPRepPart ::= [APPLICATION 27] SEQUENCE {
ctime [0] KerberosTime,
cusec [1] INTEGER,
subkey [2] EncryptionKey OPTIONAL,
seq-number [3] INTEGER OPTIONAL
}
Kerberos 票据
在结束这篇文章之前,我想要展示 Kerberos 票据本身的结构。图 9 显示了 Kerberos 票据的结构。
图 9. Kerberos 票据的结构
它包含 11 个字段:
tkt-vno :票据格式的版本。当前它是 5。
realm 和 sname :我在对图 2 的讨论中解释了这些字段。这两个字段共同指定可以给出有效票据的服务器的完整标识符。对于 TGT,这两个字段标识了 TGS。另一方面,对于服务票据,它们指定电子银行业务逻辑服务器。
enc-part :这是票据的机密部分。这个加密的部分解密后是另一个 Kerberos 结构,它包含如下所描述的一些字段:
flags :这是我在讨论图 2 中的 kdc-options 字段时提到的一组标志。其中一个标志用于说明这是 TGT 还是服务票据。
key :这是会话密钥(对于 TGT)或者子会话密钥(对于服务票据)。
creal 和 cname :我在对图 2 的讨论中解释了这些字段。
transited :正如前面提到的,在不同领域中工作的不同 Kerberos 服务器可以将票据从一个领域转发到另一个领域。这个字段指定在发布这种票据时所涉及的不同领域的名字。在移动银行应用程序中我不需要这种功能。
authtime :这是 KDC 验证请求客户身份的时间。
starttime 和 endtime :票据从 starttime 到 endtime 是有效的。
renew-till :正如前面提到的,Kerberos 票据是可以更新的。这种票据可以包含这个字段,它指定票据的最终失效时间。在这个时间之后,票据将不再是 renewable 的。
caddr :我在对图 4 的讨论中解释了这个字段。
展望:设计 Kerberos 客户机
在本系列的其余部分,我将构建一个 Kerberos 客户机,它为移动银行应用程序提供安全功能。Kerberos 客户机的主要目的是发布并处理这里详细说明的 Kerberos 消息。客户机将可以从客户机向票据或者电子银行服务器发布所有消息(图 2、5 和 7 所示的消息)并处理从服务器发回的消息(图 2、4、6、8 和 9 所示的消息)。
我所开发的 Kerberos 客户机将在资源有限的无线设备上运行。因此,客户机只有很少的资源。我的重点放在高效地使用可用的设备资源上。
安全应用程序通常使设备资源承担繁重的处理负荷。为了提高程序的效率,我必须对良好的面向对象的设计做法做出一些妥协。这对于 重大的 J2ME 应用程序来说是很常见的。本系列的后两篇文章中将展示在移动银行应用程序中是如何做的。
结束语
在本文中,我解释了移动银行应用程序的使用模型和安全性需求。我还描述了在 Kerberos 客户机和一个电子银行服务器之间交换加密密钥以进行安全通信的 Kerberos 消息的序列(以及 Kerberos 数据格式)。然后我简要展望了要在本系列后两篇文章中构建的 J2ME Kerberos 客户机。
我希望本文的内容对于所有希望了解 Kerberos 消息的工作细节的读者可以提供有用的信息。在写作本系列的其余部分时我需要用到所有这些信息。
更多精彩
赞助商链接