

# 使用证书和 Oracle Wallet 配置 UTL\$1HTTP 访问
<a name="Oracle.Concepts.ONA"></a>

Amazon RDS 支持对 RDS for Oracle 数据库实例进行出站网络访问。要将数据库实例连接到网络，您可以使用以下 PL/SQL 软件包：

`UTL_HTTP`  
此软件包从 SQL 和 PL/SQL 发出 HTTP 调用。您可以使用它通过 HTTP 访问 Internet 上的数据。有关详细信息，请参阅 Oracle 文档中的 [UTL\$1HTTP](https://docs.oracle.com/en/database/oracle/oracle-database/19/arpls/UTL_HTTP.html#GUID-A85D2D1F-90FC-45F1-967F-34368A23C9BB)。

`UTL_TCP`  
此软件包在 PL/SQL 中提供 TCP/IP 客户端访问功能。此软件包对于使用 Internet 协议和电子邮件的 PL/SQL 应用程序很有用。有关更多信息，请参阅 Oracle 文档中的 [UTL\$1TCP](https://docs.oracle.com/en/database/oracle/oracle-database/19/arpls/UTL_TCP.html#GUID-348AFFE8-78B2-4217-AE73-384F46A1D292)。

`UTL_SMTP`  
此软件包提供 SMTP 命令的接口，以便客户端能够向 SMTP 服务器发送电子邮件。有关详细信息，请参阅 Oracle 文档中的 [UTL\$1SMTP](https://docs.oracle.com/en/database/oracle/oracle-database/19/arpls/UTL_SMTP.html#GUID-F0065C52-D618-4F8A-A361-7B742D44C520)。

您可以通过完成以下任务来配置 `UTL_HTTP.REQUEST`，以便与 SSL 握手期间需要客户端身份验证证书的网站搭配使用。您还可以通过修改 Oracle Wallet 生成命令和 `DBMS_NETWORK_ACL_ADMIN.APPEND_WALLET_ACE` 过程，为 `UTL_HTTP` 访问网站配置密码身份验证。有关更多信息，请参阅 Oracle Database 文档中的 [DBMS\$1NETWORK\$1ACL\$1ADMIN](https://docs.oracle.com/en/database/oracle/oracle-database/21/arpls/DBMS_NETWORK_ACL_ADMIN.html)。

**注意**  
您可为 `UTL_SMTP` 调整以下任务，以便通过 SSL/TLS（包括 [Amazon Simple Email Service](https://aws.amazon.com/ses/)）发送电子邮件。

**Topics**
+ [配置 UTL\$1HTTP 访问时的注意事项](#utl_http-considerations)
+ [步骤 1：获取网站的根证书](#website-root-certificate)
+ [步骤 2：创建 Oracle Wallet](#create-oracle-wallet)
+ [步骤 3：将 Oracle Wallet 下载到 RDS for Oracle 实例](#upload-wallet-to-instance)
+ [步骤 4：授予用户 Oracle Wallet 权限](#config-oracle-wallet-user)
+ [步骤 5：配置从数据库实例访问网站](#config-website-access)
+ [步骤 6：测试从数据库实例到网站的连接](#test_utl_http)

## 配置 UTL\$1HTTP 访问时的注意事项
<a name="utl_http-considerations"></a>

在配置访问之前，请考虑以下事项：
+ 您可以将 SMTP 与 UTL\$1MAIL 选项结合使用。有关更多信息，请参阅 [Oracle UTL\$1MAIL](Oracle.Options.UTLMAIL.md)。
+ 远程主机的域名服务器 (DNS) 名称满足以下所有条件：
  + 可公开解析。
  + Amazon RDS 数据库实例的终端节点。
  + 可通过自定义 DNS 服务器解析。有关更多信息，请参阅“[设置自定义 DNS 服务器](Appendix.Oracle.CommonDBATasks.System.md#Appendix.Oracle.CommonDBATasks.CustomDNS)”。
  + 同一 VPC 或对等 VPC 中的 Amazon EC2 实例的私有 DNS 名称。在这种情况下，请确认该名称可通过自定义 DNS 服务器解析。或者，要使用 Amazon 提供的 DNS，您可以启用 VPC 设置中的 `enableDnsSupport` 属性，并启用对 VPC 对等连接的 DNS 解析支持。有关更多信息，请参阅 [VPC 中的 DNS 支持](https://docs.aws.amazon.com/vpc/latest/userguide/vpc-dns.html#vpc-dns-support)和[修改 VPC 对等连接](https://docs.aws.amazon.com/vpc/latest/peering/working-with-vpc-peering.html#modify-peering-connections)。
  + 若要安全地连接到远程 SSL/TLS 资源，建议创建和上传自定义 Oracle Wallet。使用与 Amazon RDS for Oracle 功能集成的 Amazon S3，您可以将 Wallet 从 Amazon S3 下载到 Oracle 数据库实例。有关 Oracle 的 Amazon S3 集成的信息，请参阅[Amazon S3 集成](oracle-s3-integration.md)。
+ 如果为每个实例配置了 Oracle SSL 选项，您可以在 SSL/TLS 终端节点上的 Oracle 数据库实例之间建立数据库连接。无需作进一步配置。有关更多信息，请参阅 [Oracle 安全套接字层](Appendix.Oracle.Options.SSL.md)。

## 步骤 1：获取网站的根证书
<a name="website-root-certificate"></a>

要在 RDS for Oracle 数据库实例与网站之间建立安全连接，请添加根 CA 证书。Amazon RDS 使用根证书将网站证书签署到 Oracle Wallet。

您可以通过各种方式获取根证书。例如，您可以执行以下操作：

1. 使用 Web 服务器访问受证书保护的网站。

1. 下载用于签名的根证书。

对于 AWS 服务，根证书通常位于 [Amazon Trust Services 存储库](https://www.amazontrust.com/repository/)中。

## 步骤 2：创建 Oracle Wallet
<a name="create-oracle-wallet"></a>

创建包含 Web 服务器证书和客户端身份验证证书的 Oracle Wallet。RDS Oracle 实例使用 Web 服务器证书建立与网站的安全连接。网站需要客户端证书才能对 Oracle 数据库用户进行身份验证。

您可能希望在不使用客户端证书进行身份验证的情况下配置安全连接。在这种情况下，您可以跳过以下过程中的 Java 密钥库步骤。

**创建 Oracle Wallet**

1. 将根证书和客户端证书放在一个目录中，然后更改到此目录。

1. 将 .p12 客户端证书转换为 Java 密钥库。
**注意**  
如果没有使用客户端证书进行身份验证，可以跳过此步骤。

   以下示例将名为 *client\$1certificate.p12* 的客户端证书转换为名为 *client\$1keystore.jks* 的 Java 密钥库。该密钥库随后会包含在 Oracle Wallet 中。密钥库密码为 *P12PASSWORD*。

   ```
   orapki wallet pkcs12_to_jks -wallet ./client_certificate.p12 -jksKeyStoreLoc ./client_keystore.jks -jksKeyStorepwd P12PASSWORD
   ```

1. 为 Oracle Wallet 创建与证书目录不同的目录。

   以下示例会创建 `/tmp/wallet` 目录。

   ```
   mkdir -p /tmp/wallet
   ```

1. 在 Wallet 目录中创建 Oracle Wallet。

   以下示例将 Oracle Wallet 密码设置为 *P12PASSWORD*，这与之前步骤中 Java 密钥库使用的密码相同。虽然使用相同的密码很方便，但没有必要这样做。`-auto_login` 参数会打开自动登录功能，无需在每次访问时指定密码。
**注意**  
作为安全最佳实践，请指定除此处所示提示以外的密码。

   ```
   orapki wallet create -wallet /tmp/wallet -pwd P12PASSWORD -auto_login
   ```

1. 将 Java 密钥库添加到 Oracle Wallet 中。
**注意**  
如果没有使用客户端证书进行身份验证，可以跳过此步骤。

   以下示例将密钥库 *client\$1keystore.jks* 添加到名为 */mp/wallet* 的 Oracle Wallet 中。在此示例中，您为 Java 密钥库和 Oracle Wallet 指定了相同密码。

   ```
   orapki wallet jks_to_pkcs12 -wallet /tmp/wallet -pwd P12PASSWORD -keystore ./client_keystore.jks -jkspwd P12PASSWORD
   ```

1. 将目标网站的根证书添加到 Oracle Wallet。

   以下示例将添加名为 *Root\$1CA.cer* 的证书。

   ```
   orapki wallet add -wallet /tmp/wallet -trusted_cert -cert ./Root_CA.cer -pwd P12PASSWORD
   ```

1. 添加任何中间证书。

   以下示例将添加名为 *Intermediate.cer* 的证书。根据需要多次重复此步骤，以便加载所有中间证书。

   ```
   orapki wallet add -wallet /tmp/wallet -trusted_cert -cert ./Intermediate.cer -pwd P12PASSWORD
   ```

1. 确认新创建的 Oracle Wallet 具有所需证书。

   ```
   orapki wallet display -wallet /tmp/wallet -pwd P12PASSWORD
   ```

## 步骤 3：将 Oracle Wallet 下载到 RDS for Oracle 实例
<a name="upload-wallet-to-instance"></a>

在此步骤中，将 Oracle Wallet 上传到 Amazon S3，然后将 Wallet 从 Amazon S3 下载到 RDS for Oracle 实例。

**将 Oracle Wallet 下载到 RDS for Oracle 数据库实例**

1. 完成 Amazon S3 与 Oracle 集成的先决条件，然后将 `S3_INTEGRATION` 选项添加到您的 Oracle 数据库实例。确保该选项的 IAM 角色具有您正在使用的 Amazon S3 存储桶的访问权限。

   有关更多信息，请参阅 [Amazon S3 集成](oracle-s3-integration.md)。

1. 以主用户身份登录数据库实例，然后创建 Oracle 目录来保存 Oracle Wallet。

   以下示例将创建名为 *WALLET\$1DIR* 的 Oracle 目录。

   ```
   EXEC rdsadmin.rdsadmin_util.create_directory('WALLET_DIR');
   ```

   有关更多信息，请参阅 [在主数据存储空间中创建和删除目录](Appendix.Oracle.CommonDBATasks.Misc.md#Appendix.Oracle.CommonDBATasks.NewDirectories)。

1. 将 Oracle Wallet 上传到 Amazon S3 存储桶。

   您可以使用任何受支持的上传技术。

1. 如果要重新上传 Oracle Wallet，请删除现有 Wallet。否则，请跳到下一步。

   以下示例将删除名为 *cwallet.sso* 的现有 Wallet。

   ```
   EXEC UTL_FILE.FREMOVE ('WALLET_DIR','cwallet.sso');
   ```

1. 将 Oracle Wallet 从 Amazon S3 存储桶下载到 Oracle 数据库实例。

   以下示例会将名为 *cwallet.sso* 的 Wallet 从名为 *my\$1s3\$1bucket* 的 Amazon S3 存储桶下载到名为 *WALLET\$1DIR* 的数据库实例目录。

   ```
   SELECT rdsadmin.rdsadmin_s3_tasks.download_from_s3(
         p_bucket_name    =>  'my_s3_bucket', 
         p_s3_prefix      =>  'cwallet.sso', 
         p_directory_name =>  'WALLET_DIR') 
      AS TASK_ID FROM DUAL;
   ```

1. （可选）下载受密码保护的 Oracle Wallet。

   仅在每次使用 Wallet 都需要密码时，才下载此 Wallet。以下示例将下载受密码保护的 Wallet *ewallet.p12*。

   ```
   SELECT rdsadmin.rdsadmin_s3_tasks.download_from_s3(
         p_bucket_name    =>  'my_s3_bucket', 
         p_s3_prefix      =>  'ewallet.p12', 
         p_directory_name =>  'WALLET_DIR') 
      AS TASK_ID FROM DUAL;
   ```

1. 检查数据库任务的状态。

   在以下示例中，将上述步骤返回的任务 ID 替换为 *dbtask-1234567890123-4567.log*。

   ```
   SELECT TEXT FROM TABLE(rdsadmin.rds_file_util.read_text_file('BDUMP','dbtask-1234567890123-4567.log'));
   ```

1. 检查用于存储 Oracle Wallet 的目录的内容。

   ```
   SELECT * FROM TABLE(rdsadmin.rds_file_util.listdir(p_directory => 'WALLET_DIR'));
   ```

   有关更多信息，请参阅 [列出数据库实例目录中的文件](Appendix.Oracle.CommonDBATasks.Misc.md#Appendix.Oracle.CommonDBATasks.ListDirectories)。

## 步骤 4：授予用户 Oracle Wallet 权限
<a name="config-oracle-wallet-user"></a>

您可以创建新的数据库用户或配置现有用户。无论采用哪种方法，都必须配置用户使用证书访问 Oracle Wallet，以便实现安全连接和客户端身份验证。

**授予用户 Oracle Wallet 权限**

1. 以主用户身份登录 RDS for Oracle 数据库实例。

1. 如果不想配置现有数据库用户，可以选择创建新用户。否则，请跳到下一步。

   以下示例将创建名为 *my-user* 的数据库用户。

   ```
   CREATE USER my-user IDENTIFIED BY my-user-pwd;
   GRANT CONNECT TO my-user;
   ```

1. 授予数据库用户对包含 Oracle Wallet 的目录的权限。

   以下示例将授予用户 *my-user* 对目录 *WALLET\$1DIR* 的读取访问权限。

   ```
   GRANT READ ON DIRECTORY WALLET_DIR TO my-user;
   ```

1. 授予数据库用户对 `UTL_HTTP` 软件包的使用权限。

   以下 PL/SQL 计划授予用户 *my-user* 对 `UTL_HTTP` 的访问权限。

   ```
   BEGIN 
     rdsadmin.rdsadmin_util.grant_sys_object('UTL_HTTP', UPPER('my-user')); 
     END;
   /
   ```

1. 授予数据库用户对 `UTL_FILE` 软件包的使用权限。

   以下 PL/SQL 计划授予用户 *my-user* 对 `UTL_FILE` 的访问权限。

   ```
   BEGIN 
     rdsadmin.rdsadmin_util.grant_sys_object('UTL_FILE', UPPER('my-user')); 
     END;
   /
   ```

## 步骤 5：配置从数据库实例访问网站
<a name="config-website-access"></a>

在此步骤中，您将配置 Oracle 数据库用户，以便其可以使用 `UTL_HTTP`、上传的 Oracle Wallet 和客户端证书连接到目标网站。有关更多信息，请参阅 Oracle Database 文档中的[配置对 Oracle Wallet 的访问控制](https://docs.oracle.com/en/database/oracle/oracle-database/19/dbseg/managing-fine-grained-access-in-pl-sql-packages-and-types.html#GUID-0BCB5925-A40F-4507-95F9-5DA4A1919EBD)。

**配置从 RDS for Oracle 数据库实例访问网站**

1. 以主用户身份登录 RDS for Oracle 数据库实例。

1. 在安全端口上为用户和目标网站创建主机访问控制条目 (ACE)。

   以下示例将 *my-user* 配置为在安全端口 443 上访问 *secret.encrypted-website.com*。

   ```
   BEGIN
     DBMS_NETWORK_ACL_ADMIN.APPEND_HOST_ACE(
       host       => 'secret.encrypted-website.com', 
       lower_port => 443,
       upper_port => 443,
       ace        => xs$ace_type(privilege_list => xs$name_list('http'),
                                 principal_name => 'my-user',
                                 principal_type => xs_acl.ptype_db)); 
                              -- If the program unit results in PLS-00201, set
                              -- the principal_type parameter to 2 as follows:
                              -- principal_type => 2));
   END;
   /
   ```
**重要**  
前面的程序单元可能会导致以下错误：`PLS-00201: identifier 'XS_ACL' must be declared`。如果返回此错误，请将为 `principal_type` 分配值的行替换为以下行，然后重新运行程序单元：  

   ```
   principal_type => 2));
   ```
有关 PL/SQL 软件包 `XS_ACL` 中常量的更多信息，请参阅 Oracle Database 文档中的 [Real Application Security Administrator's and Developer's Guide](https://docs.oracle.com/en/database/oracle/oracle-database/19/dbfsg/XS_ACL-package.html#GUID-A157FB28-FE23-4D30-AAEB-8224230517E7)**。

   有关更多信息，请参阅 Oracle Database 文档中的[为外部网络服务配置访问控制](https://docs.oracle.com/en/database/oracle/oracle-database/19/dbseg/managing-fine-grained-access-in-pl-sql-packages-and-types.html#GUID-3D5B66BC-0277-4887-9CD1-97DB44EB5213)。

1. （可选）在标准端口上为用户和目标网站创建主机访问控制条目。

   如果是标准 Web 服务器端口 (80) 而不是安全端口 (443) 提供的网页，则可能需要使用标准端口。

   ```
   BEGIN
     DBMS_NETWORK_ACL_ADMIN.APPEND_HOST_ACE(
       host       => 'secret.encrypted-website.com', 
       lower_port => 80,
       upper_port => 80,
       ace        => xs$ace_type(privilege_list => xs$name_list('http'),
                                 principal_name => 'my-user',
                                 principal_type => xs_acl.ptype_db)); 
                              -- If the program unit results in PLS-00201, set
                              -- the principal_type parameter to 2 as follows:
                              -- principal_type => 2));
   END;
   /
   ```

1. 确认存在访问控制条目。

   ```
   SET LINESIZE 150
   COLUMN HOST FORMAT A40
   COLUMN ACL FORMAT A50
   
   SELECT HOST, LOWER_PORT, UPPER_PORT, ACL
     FROM DBA_NETWORK_ACLS
   ORDER BY HOST;
   ```

1. 授予数据库用户对 `UTL_HTTP` 软件包的使用权限。

   以下 PL/SQL 计划授予用户 *my-user* 对 `UTL_HTTP` 的访问权限。

   ```
   BEGIN 
     rdsadmin.rdsadmin_util.grant_sys_object('UTL_HTTP', UPPER('my-user')); 
     END;
   /
   ```

1. 确认存在相关的访问控制列表。

   ```
   SET LINESIZE 150
   COLUMN ACL FORMAT A50
   COLUMN PRINCIPAL FORMAT A20
   COLUMN PRIVILEGE FORMAT A10
   
   SELECT ACL, PRINCIPAL, PRIVILEGE, IS_GRANT,
          TO_CHAR(START_DATE, 'DD-MON-YYYY') AS START_DATE,
          TO_CHAR(END_DATE, 'DD-MON-YYYY') AS END_DATE
     FROM DBA_NETWORK_ACL_PRIVILEGES
   ORDER BY ACL, PRINCIPAL, PRIVILEGE;
   ```

1. 授予数据库用户使用证书进行客户端身份验证和 Oracle Wallet 进行连接的权限。
**注意**  
如果没有使用客户端证书进行身份验证，可以跳过此步骤。

   ```
   DECLARE
     l_wallet_path all_directories.directory_path%type;
   BEGIN
     SELECT DIRECTORY_PATH 
       INTO l_wallet_path 
       FROM ALL_DIRECTORIES
      WHERE UPPER(DIRECTORY_NAME)='WALLET_DIR';
     DBMS_NETWORK_ACL_ADMIN.APPEND_WALLET_ACE(
       wallet_path => 'file:/' || l_wallet_path,
       ace         =>  xs$ace_type(privilege_list => xs$name_list('use_client_certificates'),
                                   principal_name => 'my-user',
                                   principal_type => xs_acl.ptype_db));
   END;
   /
   ```

## 步骤 6：测试从数据库实例到网站的连接
<a name="test_utl_http"></a>

在此步骤中，您将配置数据库用户，以便其可以使用 `UTL_HTTP`、上传的 Oracle Wallet 和客户端证书连接到网站。

**配置从 RDS for Oracle 数据库实例访问网站**

1. 以具有 `UTL_HTTP` 权限的数据库用户身份登录 RDS for Oracle 数据库实例。

1. 确认与目标网站的连接可以解析主机地址。

   以下示例将从 *secret.encrypted-website.com* 获取主机地址。

   ```
   SELECT UTL_INADDR.GET_HOST_ADDRESS(host => 'secret.encrypted-website.com')
     FROM DUAL;
   ```

1. 测试失败连接。

   以下查询失败，因为 `UTL_HTTP` 需要带有证书的 Oracle Wallet 的位置。

   ```
   SELECT UTL_HTTP.REQUEST('secret.encrypted-website.com') FROM DUAL;
   ```

1. 使用 `UTL_HTTP.SET_WALLET` 并从 `DUAL` 中进行选择来测试网站访问。

   ```
   DECLARE
     l_wallet_path all_directories.directory_path%type;
   BEGIN
     SELECT DIRECTORY_PATH
       INTO l_wallet_path 
       FROM ALL_DIRECTORIES
      WHERE UPPER(DIRECTORY_NAME)='WALLET_DIR';
     UTL_HTTP.SET_WALLET('file:/' || l_wallet_path);
   END;
   /
   
   SELECT UTL_HTTP.REQUEST('secret.encrypted-website.com') FROM DUAL;
   ```

1. （可选）将查询存储在变量中并使用 `EXECUTE IMMEDIATE` 来测试网络访问。

   ```
   DECLARE
     l_wallet_path all_directories.directory_path%type;
     v_webpage_sql VARCHAR2(1000);
     v_results     VARCHAR2(32767);
   BEGIN
     SELECT DIRECTORY_PATH
       INTO l_wallet_path 
       FROM ALL_DIRECTORIES
      WHERE UPPER(DIRECTORY_NAME)='WALLET_DIR';
     v_webpage_sql := 'SELECT UTL_HTTP.REQUEST(''secret.encrypted-website.com'', '''', ''file:/' ||l_wallet_path||''') FROM DUAL';
     DBMS_OUTPUT.PUT_LINE(v_webpage_sql);
     EXECUTE IMMEDIATE v_webpage_sql INTO v_results;
     DBMS_OUTPUT.PUT_LINE(v_results);
   END;
   /
   ```

1. （可选）查找 Oracle Wallet 目录的文件系统位置。

   ```
   SELECT * FROM TABLE(rdsadmin.rds_file_util.listdir(p_directory => 'WALLET_DIR'));
   ```

   使用上一个命令的输出来发出 HTTP 请求。例如，如果目录为 *rdsdbdata/userdirs/01*，则运行以下查询。

   ```
   SELECT UTL_HTTP.REQUEST('https://secret.encrypted-website.com/', '', 'file://rdsdbdata/userdirs/01') 
   FROM   DUAL;
   ```