> 通过 [DTLS 协商](https://mp.weixin.qq.com/s/tHW6sWRZUzkOtl2BsRbV1w)后,RTC 通信的双方完成 `MasterKey` 和 `MasterSalt` 的协商。接下来,我们继续分析在 WebRTC 中,如何使用交换的密钥,来对 RTP 和 RTCP 进行加密,实现数据的安全传输。同时,本文会对 libsrtp 使用中,遇到的问题的进行解答,例如,什么是 ROC,ROC 为什么是 32-bits?为什么会返回 error_code=9, error_code=10?交换的密钥有生命周期吗,如果有是多长时间呢?阅读本篇之前建议阅读[ DTLS 协商篇](https://mp.weixin.qq.com/s/tHW6sWRZUzkOtl2BsRbV1w),两者结合,效果更佳哦! 作者|进学 审校|泰一 ## 要解决的问题 `RTP/RTCP` 协议并没有对它的负载数据进行任何保护。因此,如果***者通过抓包工具,如 Wireshark,将音视频数据抓取到后,通过该工具就可以直接将音视频流播放出来,这是非常恐怖的事情。 在 WebRTC 中,为了防止这类事情发生,没有直接使用 `RTP/RTCP` 协议,而是使用了 `SRTP/SRTCP` 协议 ,即安全的 `RTP/RTCP` 协议。WebRTC 使用了非常有名的 libsrtp 库将原来的 `RTP/RTCP` 协议数据转换成 `SRTP/SRTCP` 协议数据。 [SRTP](https://datatracker.ietf.org/doc/html/rfc3711) 要解决的问题: * 对 `RTP/RTCP` 的负载 (payload) 进行加密,保证数据安全; * 保证 `RTP/RTCP` 包的完整性,同时防重放***。 ## SRTP/SRTCP 结构 ### SRTP 结构  从 SRTP 结构图中可以看到: 1. 加密部分 `Encrypted Portion`,由 `payload`, `RTP padding` 和 `RTP pad count` 部分组成。也就是我们通常所说的仅对 RTP 负载数据加密。 2. 需要校验部分 `Authenticated Portion`,由 `RTP Header`, `RTP Header extension` 和 `Encrypted Portion` 部分组成。 通常情况下只需要对 RTP 负载数据进行加密,如果需要对 RTP header extension 进行加密,[RFC6904](https://datatracker.ietf.org/doc/html/rfc6904) 给出了详细方案,在 libsrtp 中也完成了实现。 ### SRTCP 结构  从 `SRTCP` 结构图中可以看到: 1. 加密部分 `Encrypted Portion`,为 `RTCP Header` 之后的部分,对 `Compound RTCP` 也是同样。 2. E-flag 显式给出了 RTCP 包是否加密。(PS:一个 RTP 包怎么判断是加密的?) 3. `SRTCP index` 显示给出了 RTCP 包的序列号,用来防重放***。(PS:一个 RTP 包的 16-bits 的序列号可以防重放***吗?) 4. 待校验部分 `Authenticated Portion`,由 `RTCP Header` 和 `Encrypted Portion` 部分组成。 在初步认识了 `SRTP` 和 `SRTCP` 的结构后,接下来介绍 `Encrypted Portion` 和 `Authenticated Portion` 如何得到了。 ## Key 管理 在 `SRTP/SRTCP` 协议中,使用二元组 `
`的方式来标识一个通信参与者的 `SRTP/SRTCP` 会话,称为 `SRTP/SRTCP Session`。 在 SRTP 协议中使用三元组` <***C, RTP/RTCP目的地址,RTP/RTCP目的端口> `来标识一个 stream,一个 `SRTP/SRTCP Session` 由多个 stream 组成。对每个 stream 的加解密相关参数的描述,称为 `Cryptographic Context`。 每个 stream 的 `Cryptographic Context` 中 中的包含如下参数: * ***C: Stream 使用的 ***C。 * Cipher Parameter:加解密使用的 key, salt,算法描述 (类型,参数等)。 * Authentication Parameter: 完整性使用的 Key, salt,算法描述 (类型,参数等)。 * Anti-Replay Data: 防止重放***缓存的数据信息,例如,ROC,最大序号等。 在 `SRTP/SRTCP Session` 中,每个 Stream 都会使用到属于自己的,加解密 Key,Authentication Key。这些 Key 都是在同一个 Session 中使用到的,称为 `Session Key`。这些 `Session Key` 是通过对 `Master Key` 使用 KDF (Key Derivation Function) 导出的。 `KDF` 是用于导出 `Session Key` 函数,KDF 默认使用是加解密函数。例如,在完成 DTLS 后,协商得到的 SRTP 加密算法的 Profile 为: ``` SRTP_AES128_CM_HMAC_SHA1_80 cipher: AES_128_CM cipher_key_length: 128 cipher_salt_length: 112 maximum_lifetime: 2^31 auth_function: HMAC-SHA1 auth_key_length: 160 auth_tag_length: 80 ``` 对应的 `KDF` 为 `AES128_CM`。`Session Key` 的导出流程如下图所示:  `Session Key` 的导出依赖于如下参数: * `key_label`: 根据导出的 Key 的类型不同,`key_label` 取值如下:  * master_key: DTLS 完成后,协商得到的 Key。 * master_salt: DTLS 完成后,协商得到的 Salt。 * packet_index: RTP/RTCP 的包序号。SRTP 使用 48-bits 的隐式包需要,SRTCP 使用 31-bits 包序号。参考序号管理。 * key_derivation_rate: key 导出速率,记为 kdr。默认取值为 0,执行 1 次 Key 导出。取值范围 `{{1,2,4,...,2^24}`。在 `key_derivation_rate>0` 的情况下,在加密之前,执行一次 key 导出,后续在 `packet_index/key_derivation_rate > 0` 时,执行 key 导出。 ``` r = packet_index / kdr key_id = label || r x = key_id XOR master_salt key = KDF(master_key, x) ``` >'/':表示整除,B=0 时,C = A/B=0。 >||:表示连接的含义。A,B,C 使用网络字节序表示,C = A||B, 则 C 的高字节为 A,低字节位为 B。 >XOR:是异或运算,计算时按照低字节位对齐。 以下使用 `AES128_CM`,举例说明 `Session Key` 的导出过程,假设 DTLS 协商得到: ``` master_key: E1F97A0D3E018BE0D64FA32C06DE4139 // 128-bits master_salt: 0EC675AD498AFEEBB6960B3AABE6 // 112-bits ``` 导出加密 Key (cipher key): ``` packet_index/kdr: 000000000000 label: 00 master_salt: 0EC675AD498AFEEBB6960B3AABE6 ----------------------------------------------- xor: 0EC675AD498AFEEBB6960B3AABE6 (x, KDF input) x*2^16: 0EC675AD498AFEEBB6960B3AABE60000 (AES-CM input) cipher key: C61E7A93744F39EE10734AFE3FF7A087 (AES-CM output) ``` 导出 SALT Key (cipher salt): ``` packet_index/kdr: 000000000000 label: 02 master_salt: 0EC675AD498AFEEBB6960B3AABE6 ---------------------------------------------- xor: 0EC675AD498AFEE9B6960B3AABE6 (x, KDF input) x*2^16: 0EC675AD498AFEE9B6960B3AABE60000 (AES-CM input) 30CBBC08863D8C85D49DB34A9AE17AC6 (AES-CM ouptut) cipher salt: 30CBBC08863D8C85D49DB34A9AE1 ``` 导出校验 Key (auth key),需要 `auth key` 长度为 94 字节: ``` packet_index/kdr: 000000000000 label: 01 master salt: 0EC675AD498AFEEBB6960B3AABE6 ----------------------------------------------- xor: 0EC675AD498AFEEAB6960B3AABE6 (x, KDF input) x*2^16: 0EC675AD498AFEEAB6960B3AABE60000 (AES-CM input) auth key AES input blocks CEBE321F6FF7716B6FD4AB49AF256A15 0EC675AD498AFEEAB6960B3AABE60000 6D38BAA48F0A0ACF3C34E2359E6CDBCE 0EC675AD498AFEEAB6960B3AABE60001 E049646C43D9327AD175578EF7227098 0EC675AD498AFEEAB6960B3AABE60002 6371C10C9A369AC2F94A8C5FBCDDDC25 0EC675AD498AFEEAB6960B3AABE60003 6D6E919A48B610EF17C2041E47403576 0EC675AD498AFEEAB6960B3AABE60004 6B68642C59BBFC2F34DB60DBDFB2 0EC675AD498AFEEAB6960B3AABE60005 ``` >AES-CM 的介绍,参考[ AES-CM](https://www.rfc-editor.org/rfc/rfc3711.html#appendix-B.3)。 至此,我们得到了 `SRTP/SRTCP` 加密和认证需要的 `Session Key`:cipher key,auth key,salt key。 ## 序列号管理 ### SRTP 序列号管理 在 `RTP` 包结构定义中使用 16-bit 来描述序列号。考虑到防重放***,消息完整性校验,加密数据,导出 `SessionKey` 的需要,在 `SRTP` 协议中,SRTP 包的序列号,使用隐式方式来记录包序列号 `packet_index`,使用 i 标识 `packet_index`。 对于发送端来说,i 的计算方式如下: ``` i = 2^16 * ROC + SEQ ``` 其中,SEQ 是 RTP 包中描述的 16-bit 包序号。ROC (rollover couter) 是 RTP 包序号 (SEQ) 翻转计数,也就是每当 `SEQ/2^16=0`, ROC 计数加 1。ROC 初始值为 0。 对于接收端来说,考虑到丢包和乱序因素的影响,除了维护 `ROC`,还需要维护一个当前收到的最大包序号 s_l,当一个新的包到来时候,接收端需要估计出当前包所对应的实际 SRTP 包的序号。ROC 的初始值为 0,s_l 的初始值为收到第一个 SRTP 包的 SEQ。后续通过如下公式,估计接收到的 SRTP 序号 i: ``` i = 2^16 * v + SEQ ``` 其中,`v `可能的取值` { ROC-1, ROC, ROC+1 }`,ROC 是接收端本地维护的 ROC,SEQ 是收到 SRTP 的序号。v 分别取 ROC-1,ROC,ROC+1 计算出 i,与 `2^16*ROC + s_l ` 进行比较,那个更接近,v 就取对应的值。完成 SRTP 解密和完整性校验后,更新 ROC 和 s_l,分如下 3 种情况: 1. v = ROC - 1, ROC 和 s_l 不更新。 2. v = ROC,如果 SEQ > s_1,则更新 s_l = SEQ。 3. v = ROC + 1, ROC = v = ROC + 1,s_l = SEQ。 更直观的代码描述: ``` if (s_l < 32768) if (SEQ - s_l > 32768) set v to (ROC-1) mod 2^32 else set v to ROC endif else if (s_l - 32768 > SEQ) set v to (ROC+1) mod 2^32 else set v to ROC endif endif return SEQ + v*65536 ``` ### SRTCP 序列号管理 `RTCP` 中没有描述序号的字段,`SRTCP` 的序号在 SRTCP 包,使用 `31-bits` 中显示描述,详见 SRTCP 格式,也就是说在 SRTCP 的最大序列号为 2^31。 ### 序列号与通信时长 可以看到 SRTP 的序列号最大值为 2^48, SRTCP 的序列号最大值为 2^16。在大多数应用中(假设每 128000 个 RTP 数据包至少有一个 RTCP 数据包),SRTCP 序号将首先达到上限。以 200 SRTCP 数据包 / 秒的速度, SRTCP 的 2^31 序列号空间足以确保大约 4 个月的通信。 ## 防重放*** ***者将截获的 SRTP/SRTCP 包保存下来,然后重新发送到网络中,实现了包的重放。SRTP 接收者通过维护一个重放列表 (ReplayList) 来防止这种***。理论上 Replay List 应该保存所有接收到并完成校验的包的序列号 index。在实际情况下 ReplayList 使用滑动窗口(sliding window)来实现防重放***。使用 `SRTP-WINDOW-SIZE` 来描述滑动窗口的大小。 ### SRTP 防重放*** 在序列号管理部分,我们详述了接收者,根据接收到的 SRTP 包的 SEQ,ROC,s_l 估算出 SRTP 包的 `packet_index` 的方法。同时,将接收者已经接收到 SRTP 包的最大序列号,记为 `local_packet_index`。计算差值 delta: ``` delta = packet_index - local_packet_index ``` 分如下 3 种情况说明: 1. delta > 0:表示收到了新的包。 2. delta <-(SRTP-WINDOW-SIZE - 1) < 0:表示收到的包的序列号,小于重放窗口要求的最小序号。libSRTP 收到这样的包时,会返回 `srtp_err_status_replay_old=10`, 表示收到旧的重放包。 3. `delta <0, delta>= -(SRTP-WINDOW-SIZE - 1)`: 表示收到了重放窗口之内的包。如果在 ReplayList 找到对应的包,则是一个 index 重复的重放包。libSRTP 收到这样的包时,会返回 `srtp_err_status_replay_fail=9`。否则表示收到一个乱序包。 下图更加直观说明防重放***的三个区域:  >SRTP-WINDOW-SIZE 的取值,最小是 64。应用可以根据需要设置成较大的值,libsrtp 会向上取整为 32 的整数倍。例如,在 WebRTC 中 `SRTP-WINDOW-SIZE = 1024`。使用者可以根据需要进行调整,但要达到防重放***的目的。 ### SRTCP 防重放*** 在 SRTCP 中,packet index 显式给出。在 libsrtp 中,SRTCP 的防重放***的窗口大小为 128。使用 `window_start` 记录防重放***的起始序列号。SRTCP 防重放***的检查步骤如下: 1. index > window_start + 128: 收到新的 `SRTCP` 包。 2. index < window_start: 收到包的序列号在重放窗口的左侧,可以认为我们收到了比较老的包。libsrtp 收到这样的包之后,会返回到 `srtp_err_status_replay_old=10`。 3. replay_list_index = index - windwo_start:在 ReplayList 中 replay_list_index 对应的标识位为 1,表示已经收到包,libsrtp 返回 `srtp_err_status_replay_fail=9`。对应的标识位为 0,表示收到乱序包。 ## 加密和校验算法 在 SRTP 中,使用了 CTR(Counter mode)模式的 AES 加密算法,CTR 模式通过递增一个加密计数器以产生连续的密钥流,计数器可以是任意保证长时间不产生重复输出的密钥。根据计数方式的不同,分为以下两种类型: * `AES-ICM`: ICM 模式(Integer Counter Mode,整数计数模式),使用整数计数运算。 * `AES-GCM`: GCM 模式(Galois Counter Mode,基于伽罗瓦域计数模式),计数运算定义在伽罗瓦域。 在 SRTP 中,使用 `AES-ICM` 完成加密算法,同时使用 `HMAC-SHA1` 完成 `MAC` 计算,对数据进行完整性校验,加密和 `MAC` 计算需要分两步完成。`AES-GCM` 基于 AEAD(Authenticated-Encryption with Associated-Data,关联数据的认证加密)的思想,在对数据进行加密的同时计算 `MAC` 值,实现了一个步骤,完成加密和校验信息的计算。下面分别对这个 `AES-ICM` 和 `AES_GSM` 的用法进行介绍。 ### AEC—ICM  上图描述了 `AES-ICM` 的加密和解密过程,图中的 `K` 是通过 KDF 导出的 `SessionKey`。加密和加密都是通过对 Counter 进行加密,与明文 P 异或运算得到加密数据 C,反之,与密文 C 异或运算得到明文数据 P。考虑到安全性,Counter 生成依赖于 `Session Salt`, 包的索引(packet index)和包的 ***C。Counter 是 128-bits 的计数,生成方式如下定义: ``` one byte <--> 0 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 +--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+ |00|00|00|00| ***C | packet index | b_c |---+ +--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+ | | +--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+ v | salt (k_s) |00|00|->(+) +--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+ | | v +-------------+ encryption key (k_e) -> | AES encrypt | +-------------+ | +--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+ | | keystream block |<--+ +--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+ ``` 其中,`b_c` 是 Counter 的计数,初始 `b_c` 的取值为 0,对应 Counter 0, 每加密 128-bits 数据,`b_c` 增加 1,作为下一个 Counter。根据一个 RTP 包的 index,***C 计算出来的 Counter,组成了 keystream,每个 Counter 是一个 keystream block。 通过使用 `AES-ICM` 算法,对 `RTP/RTCP` 的负载进行加密得到了 `Encrypted Portion Portion` 部分。 ### HMAC—SHA1 散列消息认证码(Hash-based message authentication code,缩写为 HMAC),是一种通过特别计算方式之后产生的消息认证码(MAC),使用密码散列函数,同时结合一个加密密钥,它可以用来保证数据的完整性,同时可以用来作某个消息的身份验证。HMAC 通过一个标准算法,在计算哈希的过程中,把 key 混入计算过程中。HMAC 的加密实现如下: ``` HMAC(K,M) = H ( (K XOR opad ) + H( (K XOR ipad ) + M ) ) ``` * H:hash 算法,比如,MD5,SHA-1,SHA-256。 * B:块字节的长度,块是 hash 操作的基本单位。这里 B=64。 * L:hash 算法计算出来的字节长度。(L=16 for MD5, L=20 for SHA-1)。 * K:共享密钥,K 的长度可以是任意的,但是为了安全考虑,还是推荐 K 的长度 > B。当 K 长度大于 `B` 时候,会先在 K 上面执行 hash 算法,将得到的 L 长度结果作为新的共享密钥。如果 K 的长度 <`B`, 那么会在 K 后面填充 0x00 一直到等于长度 B。 * M:要认证的内容。 * opad:外部填充常量,是 0x5C 重复 B 次。 * ipad:内部填充常量,是 0x36 重复 B 次。 * XOR:异或运算。 * +:代表 "连接" 运算。 计算步骤如下: 1. 将 0x00 填充到 K 的后面,直到其长度等于 B。 2. 将步骤 1 的结果跟 ipad 做异或。 3. 将要加密的信息附在步骤 2 的结果后面。 4. 调用 H 方法。 5. 将步骤 1 的结果跟 opad 做异或。 6. 将步骤 4 的结果附在步骤 5 的结果后面。 7. 调用 H 方法。 `SRTP` 和 `SRTCP` 计算 `Authentication tag`,使用的 `K` 对应 Key 管理部分描述的 `RTP auth key` 和 `RTCP auth key`,使用的 Hash 算法为 `SHA-1`,`Authentication tag` 的长度为 80-bits。 在计算 `SRTP` 的,要认证的内容 M 为: ``` M = Authenticated Portion + ROC ``` 其中,`+` 代表 "连接" 运算,`Authenticated Portion` 在 `SRTP` 的结构图中给出。 在计算 `SRTCP` 时,要认证的内容 M 为: ``` M=Authenticated Portion ``` 其中,`Authenticated Portion` 在 `SRTCP` 的结构图中给出。 通过使用 `Authenticated Portion` 算法,计算得到 `SRTP/SRTCP` 的 `Encrypted Portion Portion` 部分。 ### AES—GCM `AES-GCM` 使用计数器模式来加密数据,该操作可以有效地流水线化,GCM 身份验证使用的操作特别适合于硬件中的有效实现。在 [GCM-SPEC](https://csrc.nist.rip/groups/ST/toolkit/BCM/documents/proposedmodes/gcm/gcm-spec.pdf) 详述了 GCM 的理论知识, [Section4.2 Hardware](https://csrc.nist.rip/groups/ST/toolkit/BCM/documents/proposedmodes/gcm/gcm-spec.pdf) 详述了硬件实现。 `AES-GCM` 在 `SRTP` 加密中的应用,在 [RFC7714](https://datatracker.ietf.org/doc/html/rfc7714) 进行了详细描述。Key 管理和序列号管理与本文中描述的相同,需要注意的是: 1. `AES-GCM` 作为一种 `AEAD(Authenticated Encryption with Associated Data`)加密算法,输入和输出是什么,对应到 `SRTP/SRTCP` 的包结构中理解。 2. `Counter` 的是计算方式和 AES-ICM 中描述的计算方式不同,需要重点关注。 [libsrtp](https://github.com/cisco/libsrtp) 已经实现了 `AES-GCM`,有兴趣的同学,可以结合代码进行研读。 ## libsrtp 的使用 [libsrtp](https://github.com/cisco/libsrtp) 是被广泛使用的 `SRTP/SRTCP` 加密的开源项目。经常用到的 api 如下: 1. `srtp_init`,初始化 srtp 库,初始化内部加密算法,在使用 srtp 前,必须要调用了。 2. `srtp_create`, 创建 srtp_session,可以结合本文中介绍的 session,session key 等概念一起理解。 3. `srtp_unprotect/srtp_protect`,RTP 包加解密接口。 4. `srtp_protect_rtcp/srtp_unprotect_rtcp`,RTCP 包的加解密接口。 5.` srtp_set_stream_roc/srtp_get_stream_roc`, 设置和获取 stream 的 ROC,这两个接口在最新的 2.3 版本加入。 重要的结构 `srtp_policy_t`,用来初始化加解密参数,在 `srtp_create` 中使用这个结构。以下参数需要关注: 1. DTLS 协商后得到的 `MasterKey` 和 `MasterSalt` 通过这个结构传递给 libsrtp,用于 session key 的生成。 2. `window_size`,对应我们之前描述的 srtp 防重放***的窗口大小。 3. `allow_repeat_tx`,是否允许重传相同序号的包。 [SRS](https://github.com/o***s/srs) 是一个新生代实时通信服务器,对 libsrtp 感兴趣的同学,可以快速在本机搭起调试环境,进行相关测试,更加深入理解相关的算法。 ## 总结 本文通过对 `SRTP/SRTCP` 相关原理的深入详细解读,对 libsrtp 使用遇到的问题进行解答,希望能够给实时音视频通信的相关领域的同学以帮助。 ## 参考文献 1. [RFC3711: SRTP](https://www.rfc-editor.org/rfc/rfc3711.html) 1. [RFC6904: Encrypted SRTP Header Extensions](https://datatracker.ietf.org/doc/html/rfc6904) 1. [Integer Counter Mode](https://tools.ietf.org/html/draft-irtf-cfrg-icm-00) 1. [RFC-6188: The Use of AES-192 and AES-256 in Secure RTP](https://tools.ietf.org/html/rfc6188) 1. [RFC7714: AES-GCM for SRTP](https://datatracker.ietf.org/doc/html/rfc7714) 1. [RFC2104: HMAC](https://datatracker.ietf.org/doc/html/rfc2104) 1. [RFC2202: Test Cases for HMAC-MD5 and HMAC-SHA-1](https://datatracker.ietf.org/doc/html/rfc2202) 1. [GCM-SPEC: GCM](https://csrc.nist.rip/groups/ST/toolkit/BCM/documents/proposedmodes/gcm/gcm-spec.pdf) >「视频云技术」你最值得关注的音视频技术公众号,每周推送来自阿里云一线的实践技术文章,在这里与音视频领域一流工程师交流切磋。公众号后台回复【技术】可加入阿里云视频云技术交流群,和作者一起探讨音视频技术,获取更多行业最新信息。
微信关注我们
原文链接:https://blog.51cto.com/u_14968479/2908491
转载内容版权归作者及来源网站所有!
低调大师中文资讯倾力打造互联网数据资讯、行业资源、电子商务、移动互联网、网络营销平台。持续更新报道IT业界、互联网、市场资讯、驱动更新,是最及时权威的产业资讯及硬件资讯报道平台。
相关文章
发表评论
资源下载
更多资源Mario
马里奥是站在游戏界顶峰的超人气多面角色。马里奥靠吃蘑菇成长,特征是大鼻子、头戴帽子、身穿背带裤,还留着胡子。与他的双胞胎兄弟路易基一起,长年担任任天堂的招牌角色。
Oracle
Oracle Database,又名Oracle RDBMS,或简称Oracle。是甲骨文公司的一款关系数据库管理系统。它是在数据库领域一直处于领先地位的产品。可以说Oracle数据库系统是目前世界上流行的关系数据库管理系统,系统可移植性好、使用方便、功能强,适用于各类大、中、小、微机环境。它是一种高效率、可靠性好的、适应高吞吐量的数据库方案。
Eclipse
Eclipse 是一个开放源代码的、基于Java的可扩展开发平台。就其本身而言,它只是一个框架和一组服务,用于通过插件组件构建开发环境。幸运的是,Eclipse 附带了一个标准的插件集,包括Java开发工具(Java Development Kit,JDK)。
JDK
JDK是 Java 语言的软件开发工具包,主要用于移动设备、嵌入式设备上的java应用程序。JDK是整个java开发的核心,它包含了JAVA的运行环境(JVM+Java系统类库)和JAVA工具。