本文还有配套的精品资源,点击获取
简介:在Android开发中确保HTTPS传输安全性是关键,特别是涉及敏感数据时。HTTPS通过SSL/TLS协议为HTTP提供加密处理、服务器验证及数据完整性检查,保证数据传输安全。本文将介绍如何在Android应用中实现HTTPS传输安全的Demo,包括SSL/TLS工作原理、自定义
TrustManager
、
SSLSocketFactory
配置、
HostnameVerifier
实现、处理
SSLHandshakeException
、证书管理以及安全最佳实践等步骤。开发者通过这些步骤和示例代码,可以加深对Android HTTPS安全通信流程的理解,并在项目中应用安全的数据传输技术。
1. HTTPS传输安全性
1.1 网络安全的重要性
随着网络技术的快速发展,数据传输的安全性变得尤为重要。HTTPS(全称:Hyper Text Transfer Protocol Secure)是HTTP的安全版,它通过使用SSL/TLS协议为网站提供加密通道,确保数据在客户端和服务器之间传输时不会被窃取或篡改。
1.2 HTTPS与传统HTTP的区别
HTTPS在HTTP的基础上加入了SSL/TLS层,用于数据的加密与解密、身份的验证以及数据完整性校验。简单来说,HTTPS在传输数据前通过SSL/TLS协议对数据进行加密,再传输至服务器,服务器收到后解密得到原始数据。这有效防止了数据在互联网上的传输被窃听或篡改,提高了通信的安全性。
1.3 HTTPS传输过程中的安全性
在HTTPS的传输过程中,客户端与服务器间首先进行一次握手操作,这个过程中涉及服务器的证书验证、密钥交换、加密算法的选择等安全措施。只有握手成功后,客户端和服务器才能开始加密的通信过程。这些机制共同作用,确保了信息传输的安全性和隐私性。
通过以上内容的介绍,我们已经对HTTPS在网络安全方面的重要作用有了初步了解。下一章节,我们将深入探讨SSL/TLS协议的原理和握手过程,来更全面地理解HTTPS的工作机制。
2. SSL/TLS工作原理和握手过程
2.1 SSL/TLS协议概述
2.1.1 SSL与TLS的关系
SSL(Secure Sockets Layer)是最早由Netscape公司开发的一种安全传输协议,旨在在互联网上提供数据加密和身份验证。SSL协议经过几个版本的迭代,最后发展为SSL 3.0。然而,由于SSL 3.0存在一些安全漏洞,互联网工程任务组(IETF)基于SSL 3.0开发了TLS(Transport Layer Security),即传输层安全协议。TLS 1.0实际上是对SSL 3.0的一个改进版本,提供更严密的安全性。尽管如此,在日常对话中,人们仍然经常将SSL和TLS混用,提及的"SSL"很多时候实际上是指TLS。
2.1.2 加密通信的基本要求
要实现加密通信,必须满足以下基本要求:
- ** 机密性 ** :通过加密算法,确保传输过程中的数据不被窃听。
- ** 完整性 ** :通过消息摘要算法验证数据在传输过程中未被篡改。
- ** 身份验证 ** :通过证书和密钥验证通信双方的真实身份。
为了实现上述要求,SSL/TLS协议使用了多种密码学技术,包括非对称加密、对称加密、哈希算法和数字签名等。
2.2 SSL/TLS握手过程详解
2.2.1 握手过程中的关键步骤
SSL/TLS握手是建立安全通信的关键步骤,这一过程通常包含以下几个关键步骤:
- ** 客户端Hello ** :客户端向服务器发送一个“Hello”消息,提供支持的加密算法列表和随机数(ClientHello)。
- ** 服务器Hello ** :服务器响应一个“Hello”消息,选择双方都支持的加密算法和随机数(ServerHello)。
- ** 证书传输 ** :服务器发送其SSL证书给客户端。
- ** 密钥交换 ** :客户端验证服务器的证书有效性后,使用服务器的公钥加密一个预主密钥(Pre-Master Secret)发送给服务器。
- ** 证书验证 ** :在需要双向认证的情况下,客户端也会发送自己的证书给服务器进行验证。
- ** 主密钥生成 ** :客户端和服务器使用预主密钥和其他信息,生成会话密钥(Master Secret)。
- ** 客户端ChangeCipherSpec ** :客户端发送ChangeCipherSpec消息,告知服务器后续消息将采用加密传输。
- ** 客户端Finished ** :客户端发送Finished消息,该消息是第一个使用新协商密钥加密的消息。
- ** 服务器ChangeCipherSpec ** :服务器也发送ChangeCipherSpec消息。
- ** 服务器Finished ** :服务器发送Finished消息,建立连接完成。
2.2.2 密钥交换机制
SSL/TLS协议的密钥交换机制是实现安全通信的关键环节。主要的密钥交换算法包括:
- ** RSA ** :服务器发送其公钥给客户端,客户端用此公钥加密预主密钥后发送给服务器。
- ** DH(Diffie-Hellman) ** :双方通过交换公钥和私钥计算出共享的秘密。
- ** ECDH(Elliptic-Curve Diffie-Hellman) ** :与DH类似,但使用椭圆曲线,提高了安全性。
- ** PSK(Pre-shared key) ** :双方共享一个密钥进行加密通信,适用于已存在密钥的情况。
2.2.3 认证和加密细节
在SSL/TLS握手过程中,身份认证和加密的细节是保证安全通信不可或缺的部分:
- ** 客户端验证 ** :可选步骤,客户端向服务器提供其证书,服务器验证客户端证书的有效性。
- ** 服务器验证 ** :服务器必须向客户端提供证书,并由客户端验证证书的有效性。
- ** 加密套件 ** :客户端和服务器在Hello消息中商定一个加密套件,包括加密算法、密钥交换算法、消息认证码(MAC)算法和加密散列函数。
- ** 会话密钥 ** :双方通过密钥交换机制生成的会话密钥,用于对称加密整个通信过程中的数据。
在本章节中,我们介绍了SSL/TLS协议的基本概念和握手过程中的关键步骤,包括密钥交换机制和认证以及加密的细节。下一章,我们将探讨在Android平台上如何实现HTTPS通信。
3. Android HTTPS实现步骤
3.1 Android网络通信概述
3.1.1 HTTP与HTTPS的区别
在移动应用中,网络通信是与服务器交互的基础。Android 应用中常用的两种协议是 HTTP(超文本传输协议)和 HTTPS(HTTP 安全版)。尽管它们都用于客户端和服务器之间的通信,但在安全性方面存在显著差异。
HTTP 是一种无状态的协议,运行在 TCP/IP 协议之上,它允许数据以明文形式传输。HTTP 的主要优点是简单易用,但是它不具备加密功能,数据在传输过程中容易被截获,存在中间人攻击和数据篡改的安全隐患。
HTTPS 在 HTTP 的基础上增加了 SSL/TLS 协议,为数据传输提供了端到端的加密。通过使用 SSL/TLS,HTTPS 能够有效防止数据被窃听、篡改和伪造。HTTPS 还提供身份验证功能,确保客户端与服务器之间建立连接的是预期的服务器,而不是伪造的服务器。
3.1.2 Android网络权限配置
要使 Android 应用支持网络通信,首先需要在应用的
AndroidManifest.xml
文件中声明相应的网络权限。对于使用 HTTP 的应用,需要添加
INTERNET
权限:
<uses-permission android:name="android.permission.INTERNET" />
如果应用需要使用 HTTPS,除了上述权限外,不需要额外声明其他权限,因为 HTTPS 使用的端口通常是 443,这是为安全通信预留的标准端口,不需要特别的网络访问权限。
3.2 Android HTTPS基本实现流程
3.2.1 HTTPS URL连接的建立
在 Android 中,建立 HTTPS 连接的典型做法是使用
HttpURLConnection
类或者第三方库如 OkHttp。下面以
HttpURLConnection
为例,展示如何建立一个 HTTPS 连接。
URL url = new URL("***");
HttpURLConnection conn = (HttpURLConnection) url.openConnection();
conn.setRequestMethod("GET");
conn.setConnectTimeout(15000); // 设置连接超时时间
conn.setReadTimeout(15000); // 设置读取超时时间
3.2.2 数据传输和会话管理
建立连接后,可以通过输入/输出流进行数据的读写操作。HTTPS 支持会话恢复机制,即基于前一个连接的某些参数来创建新的安全连接,减少握手次数,提高性能。
InputStream in = new BufferedInputStream(conn.getInputStream());
OutputStream out = new BufferedOutputStream(conn.getOutputStream());
// 使用输入输出流进行数据的读取和写入
// 示例代码省略具体读写逻辑
在进行数据传输时,需要合理处理网络异常,例如
SSLHandshakeException
,这通常是由于 SSL/TLS 握手过程失败导致的。
对于会话管理,可以使用
HttpsURLConnection
的
getSSLSocketFactory()
方法获取 SSLSocketFactory,并通过它来管理 SSL 会话。对于第三方库,通常有现成的方法来管理会话。
以上就是 Android HTTPS 实现的基本步骤,接下来将详细介绍如何在 Android 中配置自定义的 TrustManager,以及创建和应用 SSLSocketFactory,这些是实现安全 HTTPS 通信不可或缺的部分。
4. 自定义TrustManager配置
4.1 TrustManager的职责和作用
4.1.1 证书验证的必要性
在SSL/TLS握手过程中,客户端和服务器之间的加密通信需要一个可信的第三方来确保双方的身份。这个第三方通常是证书颁发机构(CA),它通过发布证书来认证服务器的身份。证书中包含了服务器的公钥、证书颁发机构的信息以及证书的有效期等信息。
证书验证是HTTPS通信安全的基础。当客户端与服务器建立连接时,如果服务器提供了一个证书,客户端会使用CA的公钥来验证这个证书的有效性。如果证书是可信的,客户端就可以继续通信,并使用该证书中包含的公钥加密后续数据。如果证书无效或者无法验证,客户端会立即中断通信,以防止中间人攻击(MITM)等安全威胁。
4.1.2 TrustManager接口的介绍
在Java编程中,
TrustManager
接口扮演着重要的角色。它位于SSL协议栈的安全层中,用于管理信任的证书库,以及在握手过程中验证服务器证书的合法性。当SSL/TLS握手发生时,
TrustManager
会使用本地存储的可信证书来验证远程服务器提供的证书链。
TrustManager
接口包含三个方法:
getAcceptedIssuers()
:返回一个包含所有可信证书的数组。checkClientTrusted(X509Certificate[] chain, String authType)
:验证客户端证书是否可信。checkServerTrusted(X509Certificate[] chain, String authType)
:验证服务器证书是否可信。
在Android平台上,我们通常需要实现自定义的
TrustManager
来处理特定的证书验证逻辑,尤其是当使用非标准证书或者需要动态更新证书时。
4.2 自定义TrustManager的实践
4.2.1 实现自定义TrustManager
要实现一个自定义的
TrustManager
,你需要创建一个实现了
***.ssl.X509TrustManager
接口的类。以下是一个简单的示例代码:
public class CustomTrustManager implements X509TrustManager {
@Override
public void checkClientTrusted(X509Certificate[] chain, String authType) throws CertificateException {
// 客户端证书验证逻辑
}
@Override
public void checkServerTrusted(X509Certificate[] chain, String authType) throws CertificateException {
// 服务器证书验证逻辑
}
@Override
public X509Certificate[] getAcceptedIssuers() {
// 返回可接受的证书链
return new X509Certificate[0];
}
}
在上述代码中,checkServerTrusted
方法是关键,你在这里编写服务器证书的验证逻辑。例如,你可以在这个方法中实现校验证书是否被吊销的逻辑。
4.2.2 证书的加载和校验过程
为了实现自定义的证书验证逻辑,我们首先需要加载证书。这通常涉及到读取本地文件系统中的证书文件,将其转换为X509Certificate
对象,然后存储在一个数组中以供使用。以下是一个加载证书并进行校验的代码示例:
***.ssl.X509TrustManager;
import java.security.cert.CertificateException;
import java.security.cert.X509Certificate;
import java.io.File;
import java.io.FileInputStream;
import java.security.cert.CertificateFactory;
import java.security.KeyStore;
public class CustomTrustManager implements X509TrustManager {
private X509Certificate[] trustedCerts;
public CustomTrustManager(String certsFilePath) throws CertificateException, Exception {
trustedCerts = loadCertificates(certsFilePath);
}
private X509Certificate[] loadCertificates(String certsFilePath) throws Exception {
CertificateFactory cf = CertificateFactory.getInstance("X.509");
FileInputStream is = new FileInputStream(new File(certsFilePath));
KeyStore trustStore = KeyStore.getInstance(KeyStore.getDefaultType());
trustStore.load(null, null);
int index = 0;
while (is.available() > 0) {
X509Certificate cert = (X509Certificate) cf.generateCertificate(is);
String alias = Integer.toString(index);
trustStore.setCertificateEntry(alias, cert);
index++;
}
is.close();
return (X509Certificate[]) trustStore.getCertificateChain(alias);
}
@Override
public void checkClientTrusted(X509Certificate[] chain, String authType) throws CertificateException {
// 客户端证书验证逻辑
}
@Override
public void checkServerTrusted(X509Certificate[] chain, String authType) throws CertificateException {
// 服务器证书验证逻辑
}
@Override
public X509Certificate[] getAcceptedIssuers() {
return trustedCerts;
}
}
请注意,在
loadCertificates
方法中,我们首先创建了一个
KeyStore
实例,用于存储从文件中加载的证书。然后我们使用
CertificateFactory
来解析PEM格式的证书文件,并将它们添加到
KeyStore
中。最后,我们从
KeyStore
中获取所有证书,并将其作为
X509Certificate
数组返回。
在
checkServerTrusted
方法中,你需要实现具体的证书校验逻辑。比如,你可以检查证书是否在有效期内,是否被吊销,以及证书颁发机构是否是预期的CA等。
实现自定义的
TrustManager
允许开发者更加灵活地控制证书的加载和验证过程,这对于企业内部系统或私有证书颁发机构尤其有用。通过这种方式,你可以确保只有经过授权的证书才能用于SSL/TLS握手过程,从而提高系统的整体安全性。
5. SSLSocketFactory的创建和应用
5.1 SSLSocketFactory核心原理
5.1.1 SSLSocketFactory与SocketFactory的关系
SSLSocketFactory
是 Java 网络编程中的一个关键接口,它继承自
SocketFactory
接口,扩展了原始套接字的功能以支持 SSL/TLS 加密协议。简而言之,
SSLSocketFactory
允许你在使用套接字通信时确保数据传输的安全性。
SocketFactory
本身是一个抽象类,用于创建套接字(Socket),而
SSLSocketFactory
则专门处理安全套接字(SSL/TLS)。使用
SSLSocketFactory
,开发者可以在建立连接时协商和实施加密和认证协议,从而增强通信的隐私性和完整性。它是实现 HTTPS 客户端连接中的重要一环,能够保证客户端与服务器之间的数据传输不被未授权访问和篡改。
5.1.2 SSLContext的构建过程
SSLContext
是 Java 中 SSL/TLS 协议的上下文,用于初始化 SSL/TLS 连接。它在
SSLSocketFactory
中扮演着核心角色。构建一个
SSLContext
需要几个关键步骤,下面是一个典型的构建流程:
- 创建
SSLContext
实例:java SSLContext sslContext = SSLContext.getInstance("TLS");
- 初始化
SSLContext
,需要使用KeyManager
和TrustManager
数组。KeyManager
管理客户端的密钥材料,而TrustManager
管理信任的证书。这里通常使用TrustManagerFactory
和KeyManagerFactory
从存储中加载密钥和信任证书:java KeyManagerFactory kmf = KeyManagerFactory.getInstance(KeyManagerFactory.getDefaultAlgorithm()); TrustManagerFactory tmf = TrustManagerFactory.getInstance(TrustManagerFactory.getDefaultAlgorithm()); // 从输入源加载密钥和信任证书 kmf.init(keyStore, keyPassword.toCharArray()); tmf.init(trustStore); // 初始化 SSLContext sslContext.init(kmf.getKeyManagers(), tmf.getTrustManagers(), new SecureRandom());
- 一旦
SSLContext
被初始化,就可以使用它来创建SSLSocketFactory
实例:java SSLSocketFactory sslSocketFactory = sslContext.getSocketFactory();
这个
SSLSocketFactory
实例随后可以用来创建安全的
SSLSocket
对象,用于安全的网络通信。
5.2 SSLSocketFactory的应用实践
5.2.1 安装自定义的SSLSocketFactory
在 Android 或其他 Java 应用中,安装自定义的
SSLSocketFactory
涉及将该工厂应用到网络连接中。以下是一个示例,展示如何在一个自定义的
SSLSocketFactory
中初始化 SSL/TLS:
public class CustomSSLSocketFactory extends SSLSocketFactory {
private final SSLSocketFactory delegate;
public CustomSSLSocketFactory(SSLSocketFactory delegate) {
this.delegate = delegate;
}
@Override
public String[] getDefaultCipherSuites() {
return delegate.getDefaultCipherSuites();
}
@Override
public String[] getSupportedCipherSuites() {
return delegate.getSupportedCipherSuites();
}
@Override
public Socket createSocket(Socket s, String host, int port, boolean autoClose) throws IOException {
return delegate.createSocket(s, host, port, autoClose);
}
// 其他方法实现...
}
// 在自定义工厂中初始化 SSLContext
SSLContext sslContext = SSLContext.getInstance("TLS");
sslContext.init(null, null, new SecureRandom());
// 获取 SSLSocketFactory
SSLSocketFactory defaultFactory = sslContext.getSocketFactory();
CustomSSLSocketFactory customFactory = new CustomSSLSocketFactory(defaultFactory);
// 应用到连接中
URL url = new URL("***");
HttpURLConnection connection = (HttpURLConnection) url.openConnection();
connection.setSSLSocketFactory(customFactory);
5.2.2 HTTPS客户端的建立与数据交换
一旦自定义的
SSLSocketFactory
被安装,就可以用来建立一个安全的 HTTPS 客户端连接。以下是一个简单的例子,展示了如何使用自定义的
SSLSocketFactory
来与 HTTPS 服务端进行数据交换:
// 继续上面的代码...
// 设置请求方法和头部信息
connection.setRequestMethod("GET");
connection.setRequestProperty("Accept", "text/html");
// 打开连接
connection.connect();
// 获取输入流
InputStream inputStream = connection.getInputStream();
BufferedReader reader = new BufferedReader(new InputStreamReader(inputStream));
// 读取响应内容
StringBuilder response = new StringBuilder();
String line;
while ((line = reader.readLine()) != null) {
response.append(line).append("\n");
}
// 打印结果
System.out.println(response.toString());
// 关闭连接
reader.close();
inputStream.close();
connection.disconnect();
上述代码展示了如何使用自定义的
SSLSocketFactory
来执行 HTTPS 请求。自定义
SSLSocketFactory
允许你进行额外的 SSL/TLS 配置,比如指定支持的加密套件,或者在建立连接之前进行服务器身份验证。这种灵活性使得开发者可以根据自己的需求调整 SSL/TLS 行为,从而实现更高级别的安全性和兼容性。
6. Android HTTPS高级配置与安全
6.1 HttpClient和OkHttp的HTTPS配置
在Android应用中使用HTTPS,常见的HTTP客户端库是Apache的HttpClient和Square的OkHttp。它们在配置HTTPS时有一些细微的差别,但是主要的目的都是为了保证数据传输的安全性。
6.1.1 HttpClient的HTTPS设置
要使***lient支持HTTPS,你需要为其创建一个特定的SSLSocketFactory。以下是配置HttpClient以使用自定义的SSLSocketFactory的步骤:
import org.apache.http.conn.ssl.SSLSocketFactory;
import org.apache.http.conn.scheme.Scheme;
import org.apache.http.conn.scheme.SchemeRegistry;
import org.apache.http.impl.client.DefaultHttpClient;
import org.apache.http.impl.conn.tsccm.ThreadSafeClientConnManager;
import org.apache.http.params.HttpParams;
public void configureHttpsOnHttpClient(DefaultHttpClient httpClient) {
HttpParams params = httpClient.getParams();
SSLSocketFactory sf = new MySSLSocketFactory();
sf.setHostnameVerifier(SSLSocketFactory.STRICT_HOSTNAME_VERIFIER);
SchemeRegistry registry = new SchemeRegistry();
registry.register(new Scheme("https", 443, sf));
httpClient.getConnectionManager().getSchemeRegistry().register(registry);
}
// MySSLSocketFactory 类的实现代码被省略了,需要自定义实现SSLContext和TrustManager
6.1.2 OkHttp的安全配置
OkHttp提供了较为简洁的API来配置HTTPS。它使用OkHttpClient类,并通过Builder模式配置证书管理器。示例如下:
import okhttp3.OkHttpClient;
import okhttp3.OkHttpClient.Builder;
import java.util.concurrent.TimeUnit;
***.ssl.X509TrustManager;
OkHttpClient client = new Builder()
.connectTimeout(30, TimeUnit.SECONDS)
.readTimeout(30, TimeUnit.SECONDS)
.writeTimeout(30, TimeUnit.SECONDS)
.sslSocketFactory(new CustomSSLContext().getSocketFactory(), (X509TrustManager)x509TrustManager)
.hostnameVerifier(NoopHostnameVerifier.INSTANCE) // 不执行主机名验证
.build();
在以上示例中,
CustomSSLContext
类需要实现SSLContext的创建,并设置相应的TrustManager。
6.2 hostnameVerifier的自定义处理
6.2.1 hostnameVerifier的作用与实现
hostnameVerifier用于验证服务器的证书中的主机名是否与服务器的实际主机名匹配。在某些情况下,例如使用自签名证书时,标准的hostnameVerifier可能会导致连接被拒绝。
public class MyHostnameVerifier implements HostnameVerifier { @Override public boolean verify(String hostname, SSLSession session) { // 自定义实现,可以根据实际情况返回true或false return "expected-hostname".equalsIgnoreCase(hostname); } }
6.2.2 SSLSession的校验过程
SSLSession包含连接中使用的安全参数。校验SSLSession通常需要检查证书中的主机名与连接中的主机名是否一致。
// 示例代码,检查SSLSession中的主机名
SSLSession session = sslSocket.getSession();
String sessionHostname = session.getPeerPrincipal().getName();
boolean isValidHostname = hostnameVerifier.verify("expected-hostname", session);
6.3 SSLHandshakeException异常处理
6.3.1 异常发生的原因分析
SSLHandshakeException是在SSL/TLS握手过程中遇到错误时抛出的异常。它可能由多种原因引起,包括证书验证失败、不支持的协议版本等。
6.3.2 异常处理和解决方案
为了有效地处理SSLHandshakeException,你需要先捕获它,然后根据异常信息中的细节进行调试。常见做法是在catch块中打印堆栈跟踪,或者使用日志记录详细信息。
try {
// HTTPS连接代码
} catch (SSLHandshakeException e) {
e.printStackTrace();
// 根据实际情况进行处理,如提示用户证书有问题等
}
6.4 证书管理及动态更新策略
6.4.1 证书存储与更新机制
Android提供了KeyStore系统用于存储证书。为了确保应用的安全性,应该定期更新证书库,以包含最新的信任锚。
6.4.2 动态更新证书的流程和注意事项
在Android应用中动态更新证书需要确保所有的网络请求都使用最新的证书。你可能需要在应用启动时或在后台线程中检查并更新证书。
public void updateCertificates() {
// 从服务器获取最新的证书列表
// 将新的证书存储到KeyStore中
}
6.5 安全最佳实践和安全漏洞处理
6.5.1 安全最佳实践的总结
最佳实践包括使用最新的安全库、定期更新证书、使用强加密算法和协议、以及进行安全审计。
6.5.2 常见安全漏洞的识别与防范
识别和防范常见的安全漏洞,例如POODLE、Heartbleed、CRIME等,需要对这些漏洞有所了解,并采取适当的防护措施。
以上内容介绍了如何在Android平台上配置和管理HTTPS连接,包括处理常见问题如SSLHandshakeException异常以及更新证书和安全漏洞的防护。要保证应用的安全性,就需要深入理解HTTPS的工作原理,并结合实际开发中的情况做出合理配置。
本文还有配套的精品资源,点击获取
简介:在Android开发中确保HTTPS传输安全性是关键,特别是涉及敏感数据时。HTTPS通过SSL/TLS协议为HTTP提供加密处理、服务器验证及数据完整性检查,保证数据传输安全。本文将介绍如何在Android应用中实现HTTPS传输安全的Demo,包括SSL/TLS工作原理、自定义
TrustManager
、
SSLSocketFactory
配置、
HostnameVerifier
实现、处理
SSLHandshakeException
、证书管理以及安全最佳实践等步骤。开发者通过这些步骤和示例代码,可以加深对Android HTTPS安全通信流程的理解,并在项目中应用安全的数据传输技术。
本文还有配套的精品资源,点击获取
版权归原作者 初雪CH 所有, 如有侵权,请联系我们删除。