首页 > 代码库 > SSL 通信原理及Tomcat SSL 配置
SSL 通信原理及Tomcat SSL 配置
SSL 通信原理及Tomcat SSL 双向配置
目录
1 参考资料 .................................................................................................................................. 1
2 SSL(Server Socket Layer)简介 ................................................................................................... 2
2.1 工作原理 ....................................................................................................................... 2
2.2 SSL 的具体流程图: .................................................................................................... 4
2.3 SSL 通讯示意图 ............................................................................................................ 5
2.4 SSL 通讯说明 ................................................................................................................ 5
3 keytool 安全证书学习 ............................................................................................................. 6
3.1 验证是否已创建过同名的证书 ................................................................................... 6
3.2 删除已创建的证书 ....................................................................................................... 6
3.3 创建证书 ....................................................................................................................... 6
3.4 常出现的异常: ........................................................................................................... 7
3.4.1 Keytool 是一个Java 数据证书的管理工具。 ................................................. 7
4 为tomcat6 批量生成安全证书 ............................................................................................... 8
4.1.1 第一步:为服务器生成证书 ........................................................................... 8
4.1.2 第二步:为客户端生成证书 ........................................................................... 9
4.1.3 第三步:让服务器信任客户端证书 ............................................................... 9
4.1.4 第四步:配置Tomcat 服务器 ......................................................................... 9
4.1.5 第五步:导入客户端证书 ............................................................................. 10
4.1.6 第六步: 导出服务器证书给客户端 .............................................................. 10
4.1.7 第七步:将导入server.cer,为客户端 创建定制密钥库. ............................ 11
4.1.8 第八步:创建Java 客户端访问: ...................................................................... 11
4.1.9 测试访问: ........................................................................................................ 12
4.1.10 SSL 证书错误信息 .......................................................................................... 12
5 说明: ...................................................................................................................................... 13
6 不同格式证书导入keystore 方法 ......................................................................................... 13
6.1 Java KeyStore 的类型 .................................................................................................. 14
6.2 证书导入 ..................................................................................................................... 14
6.2.1 Der/Cer 证书导入: ....................................................................................... 14
6.2.2 P12 格式证书导入: ...................................................................................... 15
6.3 P7B 格式证书导入: ................................................................................................. 16
6.4 总结:............................................................................................................................ 16
1 参考资料
本文部分内容来源于网络,目的是介绍某些工具及技术的使用与原理。
其中Tomcat 的配置测试及Webservcie 客户端的调用。经过实战测试,
客户端调用Webservice 采用Xfire 框架。仅提供了代码片段。
2 SSL(Server Socket Layer)简介
2.1 工作原理
1、SSL 协议使用不对称加密技术实现会话双方之间信息的安全传递。可以实现信息传
递的保密性、完整性,并且会话双方能鉴别对方身份。不同于常用的http 协议,我们在与
网站建立SSL 安全连接时使用https 协议,即采用https://ip:port/的方式来访问。
当我们与一个网站建立https 连接时,我们的浏览器与Web Server 之间要经过一个握手
的过程来完成身份鉴定与密钥交换,从而建立安全连接。具体过程如下:
用户浏览器将其SSL 版本号、加密设置参数、与session 有关的数据以及其它一些必要
信息发送到服务器。
服务器将其SSL 版本号、加密设置参数、与session 有关的数据以及其它一些必要信息
发送给浏览器,同时发给浏览器的还有服务器的证书。如果配置服务器的SSL 需要验证用户
身份,还要发出请求要求浏览器提供用户证书。
客户端检查服务器证书,如果检查失败,提示不能建立SSL 连接。如果成功,那么继续。
客户端浏览器为本次会话生成pre‐master secret,并将其用服务器公钥加密后发送给服
务器。
如果服务器要求鉴别客户身份,客户端还要再对另外一些数据签名后并将其与客户端证
书一起发送给服务器。
如果服务器要求鉴别客户身份,则检查签署客户证书的CA 是否可信。如果不在信任列
表中,结束本次会话。如果检查通过,服务器用自己的私钥解密收到的pre‐master secret,
并用它通过某些算法生成本次会话的master secret。
客户端与服务器均使用此master secret 生成本次会话的会话密钥(对称密钥)。在双方SSL
握手结束后传递任何消息均使用此会话密钥。这样做的主要原因是对称加密比非对称加密的
运算量低一个数量级以上,能够显著提高双方会话时的运算速度。
客户端通知服务器此后发送的消息都使用这个会话密钥进行加密。并通知服务器客户端
已经完成本次SSL 握手。
服务器通知客户端此后发送的消息都使用这个会话密钥进行加密。并通知客户端服务器
已经完成本次SSL 握手。
本次握手过程结束,会话已经建立。双方使用同一个会话密钥分别对发送以及接受的信
息进行加、解密。
2、SSL(Secure Socket Layer)是一种通信交互协议,由Netscape 公司在1994 年制定,主
要目的就是确保在web 服务器和浏览器之间数据传输安全。SSL 协议层是在TCP/IP 层和应
用层之间。当前TLS(Transport Layer Security)正在逐渐替代SSL(最新版本v3)。
SSL 协议分成以下几部分:
Record Protocal 是SSL 的基础层,SSL 所有的上层操作都是基于这个层次,这层主要负责消
息内容的分段,压缩,加密和数字摘要等操作。
Handshake Protocal 故名思义就是握手协议,也是在正式应用数据传输前双方交换加密
设置以及认证的流程规范协议。
Change Cipher Spec Protocol 是基于record 协议层通知远端服务器修改record 协议层中
安全设置的协议。
Alert Protocol 是基于record 协议发送警告到远端服务器的协议。
2.2 SSL 的具体流程图:
SSL 的流程也体现了对于对称性密钥和非对称性密钥的使用,由于对称性密钥加密比非对称
性密钥加密要快1000 倍,那么对称性密钥被用来做对内容的加密,而非对称性密钥用来做
传递对称性密钥的加密手段。
服务端所需要具备的是一个拥有服务端的标示,公钥私钥对的证书。在握手的流程中,
服务端将带有公钥的证书抽取出来发送给客户端,客户端就首先可以判断证书颁发者是否属
于本机受信的CA,如果不是,就会类似于IE 跳出提示,如果通过了这部分CA 认证,双方
就可以通过非对称性加密算法来交换客户端生成的临时对称密码,进行安全加密信息交互。
2.3 SSL 通讯示意图
2.4 SSL 通讯说明
在该部分,将上图所示的示意图进行说明。为了说明的方便,在本文中称客户端为B,服务
器端为S。
STEP 1: B——〉S(发起对话,协商传送加密算法)
你好,S!我想和你进行安全对话,我的对称加密算法有DES,RC5,我的密钥交换算法有
RSA 和DH,摘要算法有MD5 和SHA。
STEP2: S——〉B(发送服务器数字证书)
你好,B!那我们就使用DES-RSA-SHA 这对组合进行通讯,为了证明我确实是S,现在
发送我的数字证书给你,你可以验证我的身份。
STEP 3: B——〉S(传送本次对话的密钥)
(检查S 的数字证书是否正确,通过CA 机构颁发的证书验证了S 证书的真实有效性后。
生成了利用S 的公钥加密的本次对话的密钥发送给S)
S, 我已经确认了你的身份,现在将我们本次通讯中使用的对称加密算法的密钥发送给
你。
STEP4: S——〉B(获取密钥)
(S 用自己的私钥解密获取本次通讯的密钥)。
B, 我已经获取了密钥。我们可以开始通信了。
STEP5: S<——>B(进行通讯)
说明:一般情况下,当B 是保密信息的传递者时,B 不需要数字证书验证自己身份的真
实性,如电子银行的应用,客户需要将自己的账号和密码发送给银行,因此银行的服务器需
要安装数字证书来表明自己身份的有效性。在某些B2B 应用,服务器端也需要对客户端的
身份进行验证,这时客户端也需要安装数字证书以保证通讯时服务器可以辨别出客户端的身
份,验证过程类似于服务器身份的验证过程。
此外需要说明的是,在一些电子商务的应用中,可能还会使用到电子签名,或者为了信
息交换的更加安全,会增加电子签名和消息校验码(MAC)。
为了便于了解SSL,下面在简要介绍一下信息加密相关知识。使用密钥类型加密信息的加密
算法可以分为以下几类:HASH 编码、对称加密和非对称加密三类。
HASH 编码是使用HASH 算法从任意长度的消息中计算HASH 值的一个过程,HASH 值可
以说是消息的指纹,因为对于任何不同的消息,几乎总有不同的HASH 值。因此在SSL 通讯
过程中,可以对消息的HASH 值进行加密,确保传递的消息在传输过程中没有被修改。
非对称加密或称之为公钥加密使用数学上相关的两个数值来对信息进行编码(加密),
其中一个数字称为公钥,另一个称为私钥。公钥加密的信息可以用私钥解密,私钥加密的信
息可以用公钥解密。由于公钥可以大面积发放,因此公钥加密在SSL 加密通信中应用于对密
钥的加密或者进行数字签名。
对称加密和非对称加密相比的区别在于对称加密中,加密信息和解密信息使用同样的密
钥,因此该密钥无法公开。但是其具有加密、解密快速的特点。
在SSL 通讯中,首先采用非对称加密交换信息,使得服务器获得浏览器端提供的对称加
密的密钥,然后利用该密钥进行通讯过程中信息的加密和解密。为了保证消息在传递过程中
没有被篡改,可以加密HASH 编码来确保信息的完整性。
3 keytool 安全证书学习
3.1 验证是否已创建过同名的证书
keytool -list -v -alias tomcat -keystore "%JAVA_HOME%/JRE/LIB/SECURITY/CACERTS" -storepass changeit
3.2 删除已创建的证书
keytool -delete -alias tomcat -keystore "%JAVA_HOME%/JRE/LIB/SECURITY/CACERTS" -storepass changeit
3.3 创建证书
1.服务器中生成证书:(注:生成证书时,CN要和服务器的域名相同,如果在本地测试,则使用localhost)
keytool -genkey -alias tomcat -keyalg RSA -keystore d:\mykeystore -dname "CN=localhost, OU=localhost, O=localhost, L=SH, ST=SH, C=CN" -keypass changeit -
storepass changeit
2.导出证书,由客户端安装:
keytool -export -alias tomcat -keystore d:\mykeystore -file d:\mycerts.cer -storepass changeit
3.4 常出现的异常:
“未找到可信任的证书”--主要原因为在客户端未将服务器下发的证书导入到JVM中,可以用
keytool -list -alias tomcat -keystore "%JAVA_HOME%/JRE/LIB/SECURITY/CACERTS" -storepass changeit
来查看证书是否真的导入到JVM中。
3.4.1 Keytool 是一个Java 数据证书的管理工具。
keystore
Keytool将密钥(key)和证书(certificates)存在一个称为keystore的文件中
在keystore里,包含两种数据:
密钥实体(Key entity)——密钥(secret key)又或者是私钥和配对公钥(采用非对称加密)
可信任的证书实体(trusted certificate entries)——只包含公钥
Alias(别名)
每个keystore都关联这一个独一无二的alias,这个alias通常不区分大小写
3.4.1.1 keystore 的存储位置
在没有制定生成位置的情况下,keystore会存在与用户的系统默认目录,
如:对于window xp系统,会生成在系统的C:\Documents and Settings\UserName\
文件名为“.keystore”
3.4.1.2 keystore 的生成
引用
keytool -genkey -alias tomcat -keyalg RSA -keystore d:\mykeystore -dname "CN=localhost, OU=localhost, O=localhost, L=SH, ST=SH, C=CN" -keypass changeit -storepass -validity 180
3.4.1.3 参数说明:
-genkey表示要创建一个新的密钥
-dname表示密钥的Distinguished Names,
CN=commonName
OU=organizationUnit
O=organizationName
L=localityName
S=stateName
C=country
Distinguished Names表明了密钥的发行者身份
-keyalg使用加密的算法,这里是RSA
-alias密钥的别名
-keypass私有密钥的密码,这里设置为changeit
-keystore 密钥保存在D:盘目录下的mykeystore文件中
-storepass 存取密码,这里设置为changeit,这个密码提供系统从mykeystore文件中将信息取出
-validity该密钥的有效期为 180天 (默认为90天)
cacerts证书文件(The cacerts Certificates File)
改证书文件存在于java.home\lib\security目录下,是Java系统的CA证书仓库
3.4.1.4 keytool 生成根证书时出现如下错误:
keytool错误:java.io.IOException:keystore was tampered with,or password was incorrect
原因是在你的home目录下是否还有.keystore存在。如果存在那么把他删除掉,后再执行
或者删除"%JAVA_HOME%/JRE/LIB/SECURITY/CACERTS 再执行
4 为tomcat6 批量生成安全证书
4.1.1 第一步:为服务器生成证书
keytool -genkey -v -alias tomcat -keyalg RSA -keystore D:/SSL/keystore/tomcat.keystore -dname "CN=127.0.0.1,OU=风絮,O=风絮,L=昆明,ST=云南,C=CN" -validity 3650 -storepass xiejin -keypass xiejin
如果Tomcat所在服务器的域名不是“localhost”,应改为对应的域名,如“www.sina.com.cn”,否则浏览器会弹出警告窗口,提示用户证书与所在域不匹配。在本地做开发测试时,应填入“localhost”
4.1.2 第二步:为客户端生成证书
(为了能将证书顺利导入至IE和Firefox,证书格式应该是PKCS12)
keytool -genkey -v -alias client -keyalg RSA -storetype PKCS12 -keystore D:/SSL/keystore/client.p12 -dname "CN=client,OU=枫叶,O=枫叶,L=北京,ST=北京,C=CN" -validity 3650 -storepass client -keypass client
对应的证书库存放在“D:/SSL/keystore/client.p12“,客户端的CN可以是任意值。稍候,我们将把这个“client.p12”证书库导入到IE和Firefox中。
4.1.3 第三步:让服务器信任客户端证书
由于是双向SSL认证,服务器必须要信任客户端证书,因此,必须把客户端证书添加为服务器的信任认证。由于不能直接将PKCS12格式的证书库导入,我们必须先把客户端证书导出为一个单独的CER文件,使用如下命令:
keytool -export -alias client -keystore D:/SSL/keystore/xa -storetype PKCS12 -storepass client -rfc -file D:/SSL/keystore/client.cer
通过以上命令,客户端证书就被我们导出到D:/SSL/keystore/client.cer文件了。
下一步,是将该文件导入到服务器的证书库,添加为一个信任证书:
keytool -import -alias client -v -file D:/SSL/keystore/client.cer -keystore D:/SSL/keystore/tomcat.keystore -storepass xiejin
4.1.4 第四步:配置Tomcat 服务器
打开Tomcat根目录下的/conf/server.xml,找到如下配置段,修改如下:
打开注释
<Connector port="8443" protocol="HTTP/1.1" SSLEnabled="true"
maxThreads="150" scheme="https" secure="true"
clientAuth="true" sslProtocol="TLS"
keystoreFile="D:/SSL/keystore/tomcat.keystore" keystorePass="xiejin"
truststoreFile="D:/SSL/keystore/tomcat.keystore" truststorePass="xiejin"
/>
其中,clientAuth指定是否需要验证客户端证书,如果该设置为“false”,
则为单向SSL验证,SSL配置可到此结束。如果clientAuth设置为“true”,表示强制双向SSL验证,必须验证客户端证书。
如果clientAuth设置为“want”,则表示可以验证客户端证书,但如果客户端没有有效证书,也不强制验证。
4.1.5 第五步:导入客户端证书
如果设置了clientAuth="true",则需要强制验证客户端证书。双击client.p12即可将证书导入至IE:导入证书后,即可启动Tomcat,用IE进行访问
4.1.6 第六步: 导出服务器证书给客户端
4.1.6.1 方法1:
导出服务器证书给客户端用:(该步骤是直接使用服务端的tomcat.keystore密匙库导出服务端证书.)
keytool -export -alias tomcat -keystore D:/SSL/keystore/tomcat.keystore -file D:/SSL/keystore/server.cer -storepass xiejin
4.1.6.2 方法2:
直接访问WebService的wsdl文件,然后从浏览器中,下载证书.这样就脱离了服务器端的.也就是说不依赖服务段的密匙库.
将从浏览器中下载的证书命名为:server.cer
4.1.7 第七步:将导入server.cer,为客户端 创建定制密钥库
4.1.7.1 方法1:
server.cer来自于keystore导出.
keytool -import -noprompt -trustcacerts -alias clientInvokeServer -file server_fromTomcat_KeyStore.cer -keystore D:/SSL/keystore/clientInvokeServer.keystore -storepass testpass
4.1.7.2 方法2:
server.cer来自于浏览器导出.
keytool -import -noprompt -trustcacerts -alias clientInvokeServer -file server_fromIEBrowser.cer -keystore D:/SSL/keystore/browserserver.keystore -storepass testpass
4.1.8 第八步:创建Java 客户端访问
示例代码:
- public static void ssl_tomcat () {
- String url = "https://127.0.0.1:8443/Xfire_1.1/services/HelloWorldSimple";
- System.setProperty ( "javax.net.ssl.trustStore", "D:/SSL/keystore/browserserver.keystore" );//关键代码
- System.setProperty ( "javax.net.ssl.trustStorePassword", "testpass" );//关键代码
- Service serviceModel = new ObjectServiceFactory ().create ( IHelloWorld.class );
- try {
- IHelloWorld service = (IHelloWorld) new XFireProxyFactory ().create ( serviceModel, url );
- String result = service.hello ( "恭喜发财" );
- System.out.println ( "result = " + result );
- System.out.println ( "========================================" );
- Float returnValue = service.add ( new Float ( 3.2 ), new Float ( 2.8 ) );
- System.out.println ( "returnValue = " + returnValue );
- }
- catch (MalformedURLException e) {
- e.printStackTrace ();
- }
- }
4.1.9 测试访问:
https://127.0.0.1:8080. 上面配置的是127.0.0.1 就只能用127.0.0.1 来访问,不能有localhost.
如果上面配置的是localhost 就只能用localhost.
4.1.10 SSL 证书错误信息
目前国内用户可选择购买的SSL 证书有两种,一种是直接支持所有浏览器的WoSign/Verisign
等公司颁发的,一种是国内各种认证中心颁发的,但不被浏览器认可,需要另外安装根证书,
同时,在访问网站时会提示 “ 该安全证书由您没有选定信任的公司颁发 ” 或点击锁标志
查询证书时会显示“ 无法将这个证书验证到一个受信任的证书颁发机构 ”。用户应该根据
自己的需要正确选择全球通用的支持所有浏览器的SSL 证书。
且证书是与域名绑定,倘若证书的域名和所访问的网站域名不一致的话,则会提示:
该网站出具的安全证书是为其他网站地址颁发的。若点击继续浏览,查看证书,则会提示:
安全证书上的名称无效或者与站点名词不匹配
4.1.10.1 SSL 证书错误一:
错误情况描述:出现 该安全证书由您没有选定信任的公司颁发 类似情况,说明该
证书没有在浏览器信任的列表里,或该证书的签发机构不在浏览器信任的列表中。
解决办法:将该证书安装到浏览器的 信任列表之中即可 具体 选项‐‐‐内容选项卡
‐‐‐证书‐‐‐‐‐‐导入
4.1.10.2 SSL 证书错误二:
错误情况描述: 证书的域名和所访问的网站域名不一致的话
解决办法:用keytool 产生证书 将证书的cn 设置为域名 即可 解决这样的错误
4.1.10.3 当java 客户端请求实现https 协议的服务时,出现异常:
‘unable to find valid certification path to requested target‘
是因为服务期端的证书没有被认证,需要做的是把服务端证书导入到java keystore。
keytool ‐import ‐file server.crt ‐keystore %java_home%/jre/lib/security/cacerts
如果导入了,还出现这个错误,可能是JDK 的问题.
若Tomcat 是配置在Eclipse 自带的的,所以Tomcat 所用的jre 也当然是Eclipse 配置的jre。
而自己机子上装的jre 一共有3 个之多啊,包括jdk1.5,jdk1.6,eclipse 的jre,系统环境变
量%java_home%指向的是jdk1.5。所以运行
keytool ‐import ‐file server.crt ‐keystore %java_home%/jre/lib/security/cacerts 的时候一直是往
jdk1.5 的jre 添加证书。而Tomcat 用的jre 一直没有添加证书,所以一直有unable to find valid
certification path to requested target 的异常。往eclipse 的jre 添加证书之后,终于运行正
常。!!!!!!!!
不仅仅这个问题,其实有很多问题都是因为系统装了多个各个版本的JDK 而引起的。最好只
保留一个JDK,可以避免很多的稀奇古怪的问题。否则,一定要仔细确认tomcat 等其他依
赖JDk 的应用程序到底用的是哪个JDK.
5 说明:
创建的证书可以导入到JDK 中的, %java_home%/jre/lib/security/cacerts
密匙库,也可以自己生成一个xxx.keystore 密匙库。
6 不同格式证书导入keystore 方法
Java 自带的keytool 工具是个密钥和证书管理工具。它使用户能够管理自己的公钥/私钥对及
相关证书,用于(通过数字签名)自我认证(用户向别的用户/服务认证自己)或数据完整
性以及认证服务。它还允许用户储存他们的通信对等者的公钥(以证书形式)。
keytool 将密钥和证书储存在一个所谓的密钥仓库(keystore)中。缺省的密钥仓库实现将密
钥仓库实现为一个文件。它用口令来保护私钥。
6.1 Java KeyStore 的类型
JKS 和JCEKS 是Java 密钥库(KeyStore)的两种比较常见类型(我所知道的共有5 种,JKS, JCEKS,
PKCS12, BKS,UBER)。
JKS 的Provider 是SUN,在每个版本的JDK 中都有,JCEKS 的Provider 是SUNJCE,1.4 后我们
都能够直接使用它。
JCEKS 在安全级别上要比JKS 强,使用的Provider 是JCEKS(推荐),尤其在保护KeyStore 中的
私钥上(使用TripleDes)。
PKCS#12 是公钥加密标准,它规定了可包含所有私钥、公钥和证书。其以二进制格式存储,
也称为 PFX 文件,在windows 中可以直接导入到密钥区,注意,PKCS#12 的密钥库保护密
码同时也用于保护Key。
BKS 来自BouncyCastle Provider,它使用的也是TripleDES 来保护密钥库中的Key,它能够防
止证书库被不小心修改(Keystore 的keyentry 改掉1 个 bit 都会产生错误),BKS 能够跟JKS
互操作,读者可以用Keytool 去TryTry。
UBER 比较特别,当密码是通过命令行提供的时候,它只能跟keytool 交互。整个keystore 是
通过PBE/SHA1/Twofish 加密,因此keystore 能够防止被误改、察看以及校验。以前,Sun JDK(提
供者为SUN)允许你在不提供密码的情况下直接加载一个Keystore,类似cacerts,UBER 不允
许这种情况。
6.2 证书导入
6.2.1 Der/Cer 证书导入
要从某个文件中导入某个证书,使用keytool工具的-import命令:
keytool -import -file mycert.der -keystore mykeystore.jks
如果在 -keystore 选项中指定了一个并不存在的密钥仓库,则该密钥仓库将被创建。
如果不指定 -keystore 选项,则缺省密钥仓库将是宿主目录中名为 .keystore 的文件。如果该文件并不存在,则它将被创建。
创建密钥仓库时会要求输入访问口令,以后需要使用此口令来访问。可使用-list命令来查看密钥仓库里的内容:
keytool -list -rfc -keystore mykeystore.jks
6.2.2 P12 格式证书导入:
keytool无法直接导入PKCS12文件。
6.2.2.1 第一种方法
是使用IE将pfx证书导入,再导出为cert格式文件。使用上面介绍的方法将其导入到密钥仓库中。这样的话仓库里面只包含了证书信息,没有私钥内容。
6.2.2.2 第二种方法
是将pfx文件导入到IE浏览器中,再导出为pfx文件。
新生成的pfx不能被导入到keystore中,报错:keytool错误: java.lang.Exception: 所输入的不是一个 X.509 认证。新生成的pfx文件可以被当作keystore使用。但会报个错误as unknown attr1.3.6.1.4.1.311.17.1,查了下资料,说IE导出的就会这样,使用Netscape就不会有这个错误.
6.2.2.3 第三种方法
是将pfx文件当作一个keystore使用。但是通过微软的证书管理控制台生成的pfx文件不能直接使用。keytool不认此格式,报keytool错误: java.io.IOException: failed to decrypt safe contents entry。需要通过OpenSSL转换一下:
1)openssl pkcs12 -in mycerts.pfx -out mycerts.pem
2)openssl pkcs12 -export -in mycerts.pem -out mykeystore.p12
通过keytool的-list命令可检查下密钥仓库中的内容:
keytool -rfc -list -keystore mykeystore.p12 -storetype pkcs12
这里需要指明仓库类型为pkcs12,因为缺省的类型为jks。这样此密钥仓库就即包含证书信息也包含私钥信息。