在上一篇中,搭建好了实验环境。完整运行一次 IKE/IPSec 协议,收集相关的输出及抓包,就可以进行协议分析。分析过程中,我们将使用 IKE 进程的屏幕输出和 Wireshark 抓包,结合相关 RFC,利用 python 进行验证计算。先看协议的一次完整运行(过滤掉无关报文,如下图)

下面是 RFC 5996 中对 IKEv2 协议的规范说明

由上可知,IKEv2 协议由两个阶段的交互过程(即两个来回,共四个报文)组成。第一阶段称为 IKE_SA_INIT 交换。第二阶段称为 IKE_AUTH 交换。通观报文格式,前面都是 IKE 报文头 HDR,后跟各类不同类型的载荷。HDR 包含协议发起者和响应者的 Security Parameter Indexes(SPIs),协议版本号,报文长度等一些固定的字段。载荷则是:不同的类型,代表不同的含义。典型载荷说明如下(摘抄自 RFC)

   Notation    Payload
-----------------------------------------
AUTH Authentication
CERT Certificate
CERTREQ Certificate Request
CP Configuration
D Delete
EAP Extensible Authentication
HDR IKE header (not a payload)
IDi Identification - Initiator
IDr Identification - Responder
KE Key Exchange
Ni, Nr Nonce
N Notify
SA Security Association
SK Encrypted and Authenticated
TSi Traffic Selector - Initiator
TSr Traffic Selector - Responder
V Vendor ID

来看第一阶段交互(IKE_SA_INIT),其第一个报文由 Initiator(Windows 7)发出,后面为 Responder(Linux)的响应报文。两个报文的载荷基本相同,包括 SAi1/SAr1,KEi/KEr 和 Ni/Nr。SAi1/SAr1 记号中的数字1表示第一阶段。

SAi1/SAr1 分别表示发起者和响应者可以支持的密码算法套件,其作用类似于 TLS 报文中的 Cipher Suites,详见下图说明。KEi/KEr 和 Ni/Nr 也是密码协议中的常客,分别表示双方的 Diffie–Hellman 密钥交换内容和一次性随机数。可见在 IKE 协议中,强制使用 Diffie–Hellman 密钥交换,从而达到 Perfect Forward Secrecy 效果,在这一点上是略胜 TLS 协议的。此外,第二个报文还包含证书请求载荷(CERTREQ)。因为在 ipsec.conf 配置文件中,启用了证书认证(authby=pubkey)。

再看实际的抓包内容(第一个 IKE_SA_INIT 报文)

我们发现,除了刚才提到的 Security Association/Key Exchange/Nonce 载荷外,还出现了两个 Notify 载荷。
Wireshark 中展开解析协议树,原来是 NAT_DETECTION_SOURCE_IP 和 NAT_DETECTION_DESTINATION_IP 载荷。它们表示支持 NAT 穿越,与密码学核心功能无关,不再讨论。

现在重点看上图中的 SAi1 载荷。SAi1 包含有 6 个 Proposal,具体的 Proposal 内容(展开经过重排后)为

    加密算法      完整性算法        伪随机数生成函数   Diffie-Hellman 组
[1] 3DES_CBC HMAC_SHA1_96 PRF_HMAC_SHA1 MODP_1024
[2] AES_CBC_256 HMAC_SHA1_96 PRF_HMAC_SHA1 MODP_1024
[3] 3DES_CBC HMAC_SHA2_256_128 PRF_HMAC_SHA2_256 MODP_1024
[4] AES_CBC_256 HMAC_SHA2_256_128 PRF_HMAC_SHA2_256 MODP_1024
[5] 3DES_CBC HMAC_SHA2_384_192 PRF_HMAC_SHA2_384 MODP_1024
[6] AES_CBC_256 HMAC_SHA2_384_192 PRF_HMAC_SHA2_384 MODP_1024

每个 Proposal 由密码学相关的四元组构成,即加密算法、完整性算法、伪随机数生成函数、Diffie-Hellman 组。这四元组起什么作用?

暂且不表,继续看第二个 IKE_SA_INIT 报文,strongSwan 回应的报文内容为:HDR, SAr1, KEr, Nr, N, N, CERTREQ, N。其中 KEr, Nr 和 KEi, Ni 一样,都是一串二进制格式的内容。三个 Notify 我们不关心。只看 SAr1 和 CERTREQ。SAr1 中只包括一个 Proposal,内容为 3DES_CBC/HMAC_SHA1_96/PRF_HMAC_SHA1/MODP_1024,这是 strongSwan 所支持的密码算法套件。
对比上面的 SAi1 可知,双方都同意:(后面要用到的)加密算法/完整性算法/伪随机数生成函数/Diffie-Hellman 组分别为
3DES_CBC/HMAC_SHA1_96/PRF_HMAC_SHA1/MODP_1024。

再看 CERTREQ (证书请求)载荷,其内容如下

Certificate Authority Data,这是个什么东西?需要对方提供证书就罢了,怎么还有一串二进制内容?查看 RFC 5996,其中有这么一段文字

Certification Authority value is a concatenated list of SHA-1 hashes
of the public keys of trusted Certification Authorities (CAs). Each
is encoded as the SHA-1 hash of the Subject Public Key Info element
(see section 4.1.2.7 of [RFC3280]) from each Trust Anchor
certificate. The twenty-octet hashes are concatenated and included
with no other formatting.

原来其内容是一连串的 SHA-1 摘要值,而摘要的输入则是 strongSwan 所信任 CA 证书中的公钥信息。说得再详细,就是 RFC 3280 中的 subjectPublicKeyInfo 部分,见下面

   TBSCertificate  ::=  SEQUENCE  {
version [0] EXPLICIT Version DEFAULT v1,
serialNumber CertificateSerialNumber,
signature AlgorithmIdentifier,
issuer Name,
validity Validity,
subject Name,
subjectPublicKeyInfo SubjectPublicKeyInfo,
issuerUniqueID [1] IMPLICIT UniqueIdentifier OPTIONAL,
-- If present, version MUST be v2 or v3
subjectUniqueID [2] IMPLICIT UniqueIdentifier OPTIONAL,
-- If present, version MUST be v2 or v3
extensions [3] EXPLICIT Extensions OPTIONAL
-- If present, version MUST be v3
} SubjectPublicKeyInfo ::= SEQUENCE {
algorithm AlgorithmIdentifier,
subjectPublicKey BIT STRING }

下图标明了做摘要计算的公钥信息在 CA 文件中的位置:第一个字节是 0x30,后跟 0x81 0x9F ……,直到黄色背景的最后一个字节(注意是 SEQUENCE SubjectPublicKeyInfo 的 DER 表示)

下面是验证过程
D:\>dd if=ca.der bs=1 count=162 skip=195 of=subjectPublicKeyInfo iflag=binary oflag=binary
D:\>openssl dgst -sha1 subjectPublicKeyInfo
SHA1(subjectPublicKeyInfo)= 4f9e47fe96b05611c4d7f66dca3f265b32dde04d

看来 strongSwan 比较严谨:不仅要求对方提供证书,还要求提供的证书是由 strongSwan 认可的 CA 签发的。

至此,IKEv2 的第一阶段完成。在此阶段,双方就后续要用到的一系列密码学算法达成一致,并准备好自己的 Diffie-Hellman 密钥交换值和随机数。称第一阶段协商的结果为 IKE SA。此外 strongSwan 还要求进行基于证书的身份认证。当然这一切都是明文传输。

随后进入到第二阶段,此阶段进行 IKE_AUTH 交换,也包括来回两个报文。第一个报文仍是 Windows 7 发起,第二个报文是 strongSwan 的响应。

回顾第一阶段,经过 IKE_SA_INIT 交换后,双方对后续通信具备了基本的保护能力,包括:报文加密、报文完整性保护。这是为什么?因为经过 SAi1/SAr1 比较,已经协商出四元组:加密算法、完整性算法、伪随机数生成函数,及 Diffie-Hellman 组。其中,加密算法(即 3DES_CBC)用于报文的加密保护。HMAC_SHA1_96 算法则用于报文的完整性保护。算法达成一致后,下一个问题就是,密钥从何而来?我们看到,四元组中还有 Diffie-Hellman 组,再加上 IKE_SA_INIT 交换中的 KEi 和 KEr 载荷。熟悉密码协议的朋友能够猜到,密钥应该来自 KEi/KEr 的交换结果。在实际协议中,Diffie-Hellman 密钥的交换结果,更多是充当“密钥种子”的作用,而不会去直接参与加、解密等密码学的基本运算。在这一点上,IKEv2 协议也不例外。重要的是,一旦有了“密钥种子”,所有直接参与基本运算的密钥就可以源源不断地产生。那么如何产生?这就要靠上述四元组中的最后一个元素:伪随机数生成函数。

思路厘清后,现在就看看 RFC 是如何将理论付诸实践。先看密钥种子的生成,在 IKEv2 中,其生成公式为

SKEYSEED = prf(Ni | Nr, g^ir)

上述公式中,g^ir 是 Diffie-Hellman 密钥交换值,prf 就是四元组中的伪随机数生成函数(即 PRF_HMAC_SHA1,参见 RFC 2104)。而且 SKEYSEED 并没有直接把 g^ir 拿过来用,还加上了 Ni、Nr 的影响。协议设计者的思考可谓周全。当然要注意,g^ir 是个秘密值,除了真正的通信双方,第三方是不知道的。(如果第三方发起中间人攻击,知晓密钥交换值,则无法通过后面的数字签名,故中间人攻击不予考虑)。

下面是计算 SKEYSEED 的 python 脚本(g^ir 来自 strongSwan 的屏幕调试输出)

# SKEYSEED = prf(Ni | Nr, g_ir)
import binascii
from Crypto.Hash import HMAC, SHA
Ni = binascii.a2b_hex('305243E21BB674F3EBDA3A370C7688C446F5C189391F55F7C26A91F82D03F2689114AE822042ECD8E6CEDCB6128F09FB')
Nr = binascii.a2b_hex('D7E075DB89009F3788B30D2FE6DC77F7CB527D8E2CE091B496A0253767C75188')
# Ni、Nr 的长度不一样
g_ir = binascii.a2b_hex([Diffie-Hellman 密钥交换结果]) # []用实际内容代替
key = Ni + Nr
data = g_ir
SKEYSEED = binascii.a2b_hex(HMAC.new(key, data, SHA).hexdigest())
print HMAC.new(key, data, SHA).hexdigest()

密钥种子 SKEYSEED 得到后,利用伪随机数生成函数,就可以产生实际参与加解密运算的密钥。这和 TLS 协议中的思路是一致的。但是,单个伪随机数生成函数,输出长度有限,而实际要用到的密钥,数量比较多,长度会不够用。所以经常利用计数循环递增的概念,做到:用一个伪随机数生成函数,源源不断地产生需要的密钥。这是怎么得到的?只要看下 RFC 中的密钥生成公式,就会明白

   {SK_d | SK_ai | SK_ar | SK_ei | SK_er | SK_pi | SK_pr }
= prf+ (SKEYSEED, Ni | Nr | SPIi | SPIr ) prf+ (K,S) = T1 | T2 | T3 | T4 | ... where:
T1 = prf (K, S | 0x01)
T2 = prf (K, T1 | S | 0x02)
T3 = prf (K, T2 | S | 0x03)
T4 = prf (K, T3 | S | 0x04)
...
其中
SK_d: 生成 IPSec 密钥材料的密钥种子
SK_ai:后续 IKEv2 报文的认证密钥 -- 用于协议发起者
SK_ar:后续 IKEv2 报文的认证密钥 -- 用于协议响应者
SK_ei:后续 IKEv2 报文的加密密钥 -- 用于协议发起者
SK_er:后续 IKEv2 报文的加密密钥 -- 用于协议响应者
SK_pi:生成身份认证(AUTH)载荷时用到 -- 用于协议发起者
SK_pr:生成身份认证(AUTH)载荷时用到 -- 用于协议响应者

按上述公式,函数 prf+() 生成一连串的密钥流,然后 SK_d、SK_ai、SK_ar、SK_ei、SK_er、SK_pi、SK_pr 依次从密钥流中取各自所需长度的密钥。

下面是生成这些密钥的 python 脚本

SPIi = binascii.a2b_hex('75dd3961203ca3b1')
SPIr = binascii.a2b_hex('3cc3328025824d2d')
K = SKEYSEED
S = Ni + Nr + SPIi + SPIr
T = ''
TotalKey = ''
for i in range(1, 10): # 10 次循环足够生成所需密钥
count_byte = binascii.a2b_hex('%02d' % i) # 0x01 0x02 0x03 ...
data = T + S + count_byte
T = HMAC.new(K, data, SHA).hexdigest()
T = binascii.a2b_hex(T)
TotalKey += T SK_d = TotalKey[0:20]
SK_ai = TotalKey[20:20+20]
SK_ar = TotalKey[40:40+20]
SK_ei = TotalKey[60:60+24]
SK_er = TotalKey[84:84+24]
SK_pi = TotalKey[108:108+20]
SK_pr = TotalKey[128:128+20] print 'SK_d = ' + binascii.hexlify(SK_d)
print 'SK_ai = ' + binascii.hexlify(SK_ai)
print 'SK_ar = ' + binascii.hexlify(SK_ar)
print 'SK_ei = ' + binascii.hexlify(SK_ei)
print 'SK_er = ' + binascii.hexlify(SK_er)
print 'SK_pi = ' + binascii.hexlify(SK_pi)
print 'SK_pr = ' + binascii.hexlify(SK_pr)

到现在为止,各种算法和密钥都已算出。注意,这是在第一阶段 IKE_SA_INIT 交换后就发生的事。然后进入第二阶段(IKE_AUTH 交换)。自然,第二阶段的报文就可以应用上面协商出的各种密码学要素进行保护。具体是如何保护的?先看交互的第一个 IKE_AUTH 报文

IKE_AUTH 报文中除去报文头,其余所有的载荷都已经受到保护,并且专门用一种 Encrypted and Authenticated 类型的载荷来表示。该载荷的内部格式参见下图

果然,除了 SK_d(后面将讨论),前面讨论的所有密钥(SK_ai/SK_ar、SK_ei/SK_er、SK_pi/SK_pr)都在这里派上用场。黄色背景中的内容表示加密之前的明文。当然仅仅加密还是不够的,还要对报文进行完整性保护(目前更流行的是加密认证一体化处理,比如用在块加密中的 AEAD)。密文后跟称为 Integrity Checksum Data(ICD) 的字段,起到完整性保护作用。需要注意,ICD 是用前面协商的 HMAC_SHA1_96 算法计算得到,并且其保护内容是除去最后的 ICD 字段的整个 IKEv2 报文。现在按此逻辑解开加密报文的面纱,相应 python 脚本如下(以第一个 IKE_AUTH 报文为例)

from Crypto.Cipher import DES, DES3
import binascii
IV = binascii.a2b_hex([Initialization Vector]) # []用实际内容代替
cipher = DES3.new(SK_ei, DES.MODE_CBC, IV)
ciphertext = binascii.a2b_hex([Encrypted Data]) # []用实际内容代替
plaintext = cipher.decrypt(ciphertext

对比下面 strongSwan 的输出,完全一致

13[ENC] data after decryption with padding => 1872 bytes @ 0xaf0036a0
13[ENC] 0: 25 00 00 47 09 00 00 00 30 3D 31 0B 30 09 06 03 %..G....0=1.0...
13[ENC] 16: 55 04 06 13 02 43 4E 31 0B 30 09 06 03 55 04 08 U....CN1.0...U..
13[ENC] 32: 0C 02 48 5A 31 0C 30 0A 06 03 55 04 0A 0C 03 56 ..HZ1.0...U....V
13[ENC] 48: 50 4E 31 13 30 11 06 03 55 04 03 0C 0A 56 50 4E PN1.0...U....VPN
13[ENC] 64: 20 43 6C 69 65 6E 74 26 00 02 77 04 30 82 02 6E Client&..w.0..n

得到明文后,就可以直接计算 Integrity Checksum Data,脚本如下

# 验证 IKE_AUTH 的 Integrity Checksum Data
# 计算范围:从 IKE_AUTH 报文头一直延续到报文末尾(除去 Integrity Checksum Data 字段)
from Crypto.Hash import HMAC, SHA
import binascii
data = binascii.a2b_hex([IKE_AUTH 报文除去 ICD 字段]) # []用实际内容代替
IntegrityChecksumData = HMAC.new(SK_ai, data, SHA).hexdigest()
print IntegrityChecksumData

得到 IKE_AUTH 明文后,我们来看下它到底是什么内容?这就可以求助强大的 Wireshark,它自带解密 IKE 载荷的功能,前提是你要告诉它密钥。下图是开启解密功能的界面(Wireshark Version 1.8.5)

解密后的报文如下

故实际报文的结构为:HDR, SK{IDi, CERT, CERTREQ, AUTH, N, CP, SAi2, TSi, TSr},我们来看这些载荷代表什么?

IDi -- 表示发起者的身份信息,展开 Wireshark 协议树,其内容是发起者证书中的主题字段(Subject: C=CN, ST=HZ, O=VPN, CN=VPN Client),并采用 DER_ASN1_DN 类型编码

CERT -- 不言而喻,承载内容是发起者的证书。

CERTREQ -- 发起者要求响应者提供证书(用于验证对方的数字签名),前面已经讨论过细节。

AUTH -- 认证载荷,这是重点

回忆一下,目前只对 IKE_AUTH 报文进行了加密和完整性保护,这做到了 CIA 中的 C(Confidentiality) 和 I(Integrity)。还有一个我个人认为更重要的 A(Authentication,即身份认证。另一种解释是 Availability,这里采用前者解释)没有体现。因为身份认证,或者说敌我识别,应该是在不安全环境中要考虑的首要问题。作为经典的安全协议,IKEv2 自然也考虑了这点,它在 AUTH 载荷中包含了身份认证的信息,只有 AUTH 载荷通过了验证,才能认为是真正的对方在和自己通信。那么 AUTH 载荷究竟包含什么内容?容易猜到,应该是数字签名。签名密钥是证书对应的私钥,剩下签名算法和签名内容需要确定。对于 RSA 密钥,签名算法采用 RFC 3447 中的 RSASSA-PKCS1-v1_5 标准。而签名内容,则比较复杂,仍以 Initiator 发送的 IKE_AUTH 报文为例,其计算过程如下

RealMessage1 = 发起者的 IKE_SA_INIT 消息 # 完整的 IKE_SA_INIT 报文,从 IKE 头到报文末尾
InitIDPayload = IDType | RESERVED | IdentificationDataOfInitiator # 见上图,就是发起者 Identification 载荷的实际内容部分
MACedIDForI = prf(SK_pi, InitIDPayload) # 使用 prf 生成发起者身份信息的 HMAC 值
InitiatorSignedOctets = RealMessage1 | NonceRData | MACedIDForI # 签名内容

由上可见,签名内容包括:发起者的 IKE_SA_INIT 报文、响应者的 Nonce 和发起者身份信息的校验值。最后一项还涉及从双方 Diffie–Hellman 密钥交换结果 g_ir 衍生出来的 SK_pi 密钥。所有这些消息组合起来,如果其签名正确,足以让对方相信发起者的身份。下面是最终的计算脚本。

from Crypto.Hash import HMAC, SHA
from Crypto.Signature import PKCS1_v1_5
from Crypto.PublicKey import RSA
# InitIDPayload = IDType + RESERVED + IdentificationDataOfInitiator
InitIDPayload = binascii.a2b_hex([IDi 载荷的实际内容]) # []用实际内容代替
MACedIDForI = binascii.a2b_hex(HMAC.new(SK_pi, InitIDPayload, SHA).hexdigest())
RealMessage1 = binascii.a2b_hex([发起者的 IKE_SA_INIT 报文]) # []用实际内容代替
InitiatorSignedOctets = RealMessage1 + Nr + MACedIDForI
key = RSA.importKey(open('clientkey.pem').read(), '')
hash = SHA.new(InitiatorSignedOctets)
signer = PKCS1_v1_5.new(key)
AuthenticationPayloadOfInitiator = signer.sign(hash) # PKCS1_v1_5 签名
print binascii.hexlify(AuthenticationPayloadOfInitiator

与认证载荷相比,剩下的几个载荷就简单多了。

N -- 通知载荷:MOBIKE_SUPPORTED

CP -- 配置载荷:Windows 7 向对端请求分配 IP/DNS 等地址

SAi2 -- 这是第二阶段交互(IKE_AUTH)中的 Security Association,用 SAi2 表示。此时,双方还没有商定如何保护后续的业务流量,所以它们在各自的 IKE_AUTH 交换报文中发送 SAi2 和 SAr2,再次进行协商。这次协商的结果称为 IPSec SA。可以想像,IPSec SA 的内容,也是相关密码的要素,包括算法、密钥等关键信息。在抓包文件中,SAi2/SAr2 的主要内容如下

                加密算法       完整性算法
SAi1/Proposal-1 ENCR_AES_CBC AUTH_HMAC_SHA1_96
SAi1/Proposal-2 ENCR_3DES AUTH_HMAC_SHA1_96
SAr2/Proposal-1 ENCR_3DES AUTH_HMAC_SHA1_96

最终,双方同意使用 3DES 和 HMAC_SHA1_96 作为加密和完整性算法。由于双方身份已经得到确认,所以后续报文的保护,只需要这两者就可以了。如果你比较细心,会问密钥从哪里来?没有密钥,IPSec SA 是不完整的。答案是密钥来自 SK_d,后面将讨论。

TSi 和 TSr -- 流选择载荷,与本篇主旨无关,不展开。

基于同样思路,可以分析响应者发出的 IKE_AUTH 报文。在此不再赘述。

最后归纳 IKEv2 的协议过程:通信双方在第一阶段进行 IKE_SA_INIT 交换,协商的结果为 IKE SA。第二阶段 IKE_AUTH 交换受 IKE SA 中定义的密码学要素保护,双方同时完成身份认证,并协商出 IPSec SA。IPSec SA 保护后续的通信流量。

至此,IKEv2 协议分析完毕。


参考

RFC 2104 -- HMAC: Keyed-Hashing for Message Authentication
RFC 3280 -- Internet X.509 Public Key Infrastructure Certificate and Certificate Revocation List (CRL) Profile
RFC 3447 -- Public-Key Cryptography Standards (PKCS) #1: RSA Cryptography Specifications Version 2.1
RFC 5996 -- Internet Key Exchange Protocol Version 2 (IKEv2)
RFC 4718 -- IKEv2 Clarifications and Implementation Guidelines

安全协议系列(五)---- IKE 与 IPSec(中)的更多相关文章

  1. SQL Server 2008空间数据应用系列五:数据表中使用空间数据类型

    原文:SQL Server 2008空间数据应用系列五:数据表中使用空间数据类型 友情提示,您阅读本篇博文的先决条件如下: 1.本文示例基于Microsoft SQL Server 2008 R2调测 ...

  2. 【Kubernetes 系列五】在 AWS 中使用 Kubernetes:EKS

    目录 1. 概述 2. 版本 3. 预备 3.1. 操作环境 3.2. 角色权限 3.2.1. CloudFormation 完全权限 3.2.2. EKS 读写权限 3.2.3. EC2 相关权限 ...

  3. 安全协议系列(五)---- IKE 与 IPSec(上)

    IKE/IPSec 属于网络层安全协议,保护 IP 及上层的协议安全.自上个世纪末面世以来,关于这两个协议的研究.应用,已经非常成熟.协议本身,也在不断地进化.仅以 IKE 为例,其对应的 RFC 编 ...

  4. Dubbo(五):Dubbo中的URL统一资源模型与Dubbo协议

    一.URL简介 URL也就是Uniform Resource Locator,中文叫统一资源定位符.Dubbo中无论是服务消费方,或者服务提供方,或者注册中心.都是通过URL进行定位资源的.所以今天来 ...

  5. 安全协议系列(四)----SSL与TLS

    当今社会,电子商务大行其道,作为网络安全 infrastructure 之一的 -- SSL/TLS 协议的重要性已不用多说.OpenSSL 则是基于该协议的目前应用最广泛的开源实现,其影响之大,以至 ...

  6. Netty4.x中文教程系列(五)编解码器Codec

    Netty4.x中文教程系列(五)编解码器Codec 上一篇文章详细解释了ChannelHandler的相关构架设计,版本和设计逻辑变更等等. 这篇文章主要在于讲述Handler里面的Codec,也就 ...

  7. WCF编程系列(五)元数据

    WCF编程系列(五)元数据   示例一中我们使用了scvutil命令自动生成了服务的客户端代理类: svcutil http://localhost:8000/?wsdl /o:FirstServic ...

  8. CSS 魔法系列:纯 CSS 绘制各种图形《系列五》

    我们的网页因为 CSS 而呈现千变万化的风格.这一看似简单的样式语言在使用中非常灵活,只要你发挥创意就能实现很多比人想象不到的效果.特别是随着 CSS3 的广泛使用,更多新奇的 CSS 作品涌现出来. ...

  9. ASP.NET 5系列教程 (六): 在 MVC6 中创建 Web API

    ASP.NET 5.0 的主要目标之一是统一MVC 和 Web API 框架应用. 接下来几篇文章中您会了解以下内容: ASP.NET MVC 6 中创建简单的web API. 如何从空的项目模板中启 ...

  10. JVM系列五:JVM监测&工具

    JVM系列五:JVM监测&工具[整理中]  http://www.cnblogs.com/redcreen/archive/2011/05/09/2040977.html 前几篇篇文章介绍了介 ...

随机推荐

  1. 在ie7中overflow:hidden失效问题及解决方案

    css兼容ie7: 做页面的时候用负边距居中的时候在IE7下面,父节点中的overflow:hiden失效的问题,查阅了一些资料,总结一下解决方法. 问题原因: 当父元素的直接子元素或者下级子元素的样 ...

  2. Student elective system (VF)

    博客插N+文件有些麻烦,索性PDF上传到百度文库 点击获取<数据库系统原理与应用>也有相应的word版本 word版加密密码:(博客链接加密后) 六.附录 数据库设计的基本步骤,按照规范设 ...

  3. 常见HTML的!DOCTYPE声明

    HTML或者XHTML版本 !DOCTYPE声明 HTML 2.0 <!DOCTYPE HTML PUBLIC "-//IETF//DTD HTML//EN"> HTM ...

  4. sicily 1934. 移动小球

    Description 你有一些小球,从左到右依次编号为1,2,3,...,n. 你可以执行两种指令(1或者2).其中, 1 X Y表示把小球X移动到小球Y的左边, 2 X Y表示把小球X移动到小球Y ...

  5. applicationContext配置文件中的属性说明

    lazy-init:设置只对scop属性为singleton的bean起作用. 1.true:延迟加载:这时在第一次向容器通过getBean索取bean时实例化的. 2.false:表示spring启 ...

  6. Swift 06.Closures

    Closures --闭包 看了好些文章.由于自己也是刚开始学习swift,闭包还是不是很明白.暂时先放放.等看完后面的.加深感触后,在回头总结闭包的概念. 数组中常用的闭包函数 在Swift的数组中 ...

  7. ie浏览器兼容问题汇总

    对兼容ie浏览器所遇到的问题及总结 互联快谈 2016-10-28 05:51 1,若直接给一个元素设置absolute定位.在浏览器缩放的时候.位置会错位.解决的方法是给外层的元素设置为relati ...

  8. EXE加锁器 只是思路

    代码有点乱 但是我不想整理 // AddBoxDlg.cpp : 实现文件 // #include "stdafx.h" #include "AddBox.h" ...

  9. daydayup3 codeforces144C

    上古的c还是很简单的,一直逗比忘记加EOF了,直到看了数据才发现 题意:给你两个字符串a,b,求问字符串a里有多少个子串排列后可以生成字符串b,‘?’可以替换为任意小写字母 思路:统计第一个子字符串小 ...

  10. LeetCode 201 Bitwise AND of Numbers Range 位运算 难度:0

    https://leetcode.com/problems/bitwise-and-of-numbers-range/ [n,m]区间的合取总值就是n,m对齐后前面一段相同的数位的值 比如 5:101 ...