OpenAI X.509 身份联合配置
X.509 身份联合将 TLS 客户端证书身份换为短期 OpenAI 访问令牌。之后 API 请求同时发送令牌和被接受的客户端证书,替换的是 API 密钥,而非客户端证书。
仅支持 OpenAI API,Codex 不支持此路径,须用 OIDC 或 SPIFFE JWT-SVID。交换字段见交换参考,权限、证书、激活、主机与轮换见 mTLS 指南。
工作原理
- 在现有 mTLS 设置上传并激活可信根证书。
- X.509 提供商从已验证证书派生
openai.*,必须有一个非空openai.subject。 - 映射授权该身份使用某项目的一个服务账户。
- 工作负载向
mtls.auth.openai.com的 X.509 交换端点呈交 TLS 证书,取得短期 bearer;证书来自 TLS 连接,请求体没有subject_token。 - 向
mtls.api.openai.com的 API 路径同时发送 bearer 和证书。
API 请求独立检查令牌与证书,单凭证书不能调用 API。
开始前
需要管理组织 mTLS 与身份提供商的权限、目标项目和服务账户、客户端证书/私钥及必要中间证书、已在组织或项目激活的可信根。私钥不入版本库,仅工作负载可访问;不要记录私钥、证书或返回令牌。
配置 mTLS 证书信任
复用组织现有 mTLS,不单独上传或建立信任库。先读 mTLS 指南,再到组织 mTLS 设置上传 PEM 信任证书,在组织或每个目标项目激活。
客户端经中间证书链到根时,配置稳定锚,TLS 握手依次提交叶证书及当前中间证书。OpenAI 使用请求提供的链,不从证书 URL 补取缺失部分。
配置 X.509 提供商
- 在组织工作负载身份设置选择 Create identity provider。
- Provider type 选 X.509,输入名称和可选描述;不使用 OIDC issuer、audience、discovery 或 JWKS,创建后不能改类型。
- Advanced 可加 Attribute conditions CEL,在映射前拒绝不合格证书。
- Attribute transformations 为必需
openai.subject输入非空表达式。选 X.509 后界面自动出现 subject 行,显示并应用openai.前缀,选择稳定识别工作负载的证书事实。 - 按需增加唯一的
openai.*派生项并创建。
示例用 common name 作为标准主体、组织单元作为附加属性:
来源示例 1(json):
中文说明:来源配置或结果示例,按所在小节的字段、作用域与权限要求解释;JSON保持合法格式,CEL/声明匹配文本不是可独立运行的应用。
[
{
"attribute": "openai.subject",
"expression": "assertion.subject.common_name"
},
{
"attribute": "openai.environment",
"expression": "assertion.subject.organizational_unit"
}
]
证书事实在 assertion.subject、assertion.subject_alt_names;用于映射的转换须产生标量,附加名称须唯一。仅允许生产证书的条件示例:
来源示例 2(text):
中文说明:来源配置或结果示例,按所在小节的字段、作用域与权限要求解释;JSON保持合法格式,CEL/声明匹配文本不是可独立运行的应用。
assertion.subject.organizational_unit == "Production"
创建服务账户映射
在提供商详情选 Create mapping;选择目标项目和账户,仅授予必要 API 权限;Key/Value 优先要求精确 openai.subject。X.509 映射可以无断言(空对象 {}),或断言键都以 openai. 开头,然后创建。
| 键 | 值 |
|---|---|
openai.subject |
payments-service-prod |
它只匹配派生属性,不匹配 JWT 的 sub、iss、aud。提供商列表显示 provider ID,映射详情显示 service account ID,记录两者用于交换。
在 SDK 中使用 X.509 身份
设置证书链、私钥、提供商和服务账户环境变量:
来源示例 3(bash):
## [中文] X.509交换和后续API均携带客户端证书;Bearer替代API密钥,不替代证书,不打印返回的短期令牌。
export OPENAI_MTLS_CERT_CHAIN="/path/to/client-chain.pem"
export OPENAI_MTLS_KEY="/path/to/client-key.pem"
export OPENAI_IDENTITY_PROVIDER_ID="idp_example"
export OPENAI_SERVICE_ACCOUNT_ID="svc_acct_example"
链文件先叶证书后中间证书,HTTP 请求体不包含证书或 subject_token。SDK 在交换与 API 调用中呈交证书,使用 mTLS 主机并自动续期:
来源示例 4(javascript):
// [中文] X.509交换和后续API均携带客户端证书;Bearer替代API密钥,不替代证书,不打印返回的短期令牌。
import { readFile } from "node:fs/promises";
import OpenAI from "openai";
import { workloadIdentity } from "openai/auth/x509-transport";
const certificatePath = process.env.OPENAI_MTLS_CERT_CHAIN;
const privateKeyPath = process.env.OPENAI_MTLS_KEY;
const identityProviderId = process.env.OPENAI_IDENTITY_PROVIDER_ID;
const serviceAccountId = process.env.OPENAI_SERVICE_ACCOUNT_ID;
if (
!certificatePath ||
!privateKeyPath ||
!identityProviderId ||
!serviceAccountId
) {
throw new Error(
"Set OPENAI_MTLS_CERT_CHAIN, OPENAI_MTLS_KEY, OPENAI_IDENTITY_PROVIDER_ID, and OPENAI_SERVICE_ACCOUNT_ID"
);
}
const credential = workloadIdentity.fromX509({
certificateChain: await readFile(certificatePath, "utf8"),
privateKey: await readFile(privateKeyPath, "utf8"),
identityProviderId,
serviceAccountId,
});
try {
const client = new OpenAI({ credential });
const response = await client.responses.create({
model: "gpt-5.6-terra",
input: "Say hello from X.509 workload identity federation.",
});
console.log(response.output_text);
} finally {
await credential.close();
}
来源示例 5(python):
## [中文] X.509交换和后续API均携带客户端证书;Bearer替代API密钥,不替代证书,不打印返回的短期令牌。
import os
import ssl
from openai import DefaultHttpx2Client, OpenAI
from openai.auth import x509_workload_identity
tls_context = ssl.create_default_context()
tls_context.load_cert_chain(
certfile=os.environ["OPENAI_MTLS_CERT_CHAIN"],
keyfile=os.environ["OPENAI_MTLS_KEY"],
)
with OpenAI(
base_url="https://mtls.api.openai.com/v1",
workload_identity=x509_workload_identity(
identity_provider_id=os.environ["OPENAI_IDENTITY_PROVIDER_ID"],
service_account_id=os.environ["OPENAI_SERVICE_ACCOUNT_ID"],
),
http_client=DefaultHttpx2Client(verify=tls_context, follow_redirects=False),
) as client:
response = client.responses.create(
model="gpt-5.6-terra",
input="Say hello from X.509 workload identity federation.",
)
print(response.output_text)
来源示例 6(go):
// [中文] X.509交换和后续API均携带客户端证书;Bearer替代API密钥,不替代证书,不打印返回的短期令牌。
package main
import (
"context"
"crypto/tls"
"fmt"
"log"
"net/http"
"os"
"github.com/openai/openai-go/v3"
"github.com/openai/openai-go/v3/auth"
"github.com/openai/openai-go/v3/option"
"github.com/openai/openai-go/v3/responses"
)
func main() {
certificate, err := tls.LoadX509KeyPair(
os.Getenv("OPENAI_MTLS_CERT_CHAIN"),
os.Getenv("OPENAI_MTLS_KEY"),
)
if err != nil {
log.Fatal(err)
}
transport, err := auth.NewX509Transport(&http.Transport{
TLSClientConfig: &tls.Config{
Certificates: []tls.Certificate{certificate},
MinVersion: tls.VersionTLS12,
},
})
if err != nil {
log.Fatal(err)
}
defer transport.Close()
client := openai.NewClient(
option.WithX509WorkloadIdentity(auth.X509WorkloadIdentity{
IdentityProviderID: os.Getenv("OPENAI_IDENTITY_PROVIDER_ID"),
ServiceAccountID: os.Getenv("OPENAI_SERVICE_ACCOUNT_ID"),
Transport: transport,
}),
)
response, err := client.Responses.New(context.Background(), responses.ResponseNewParams{
Model: "gpt-5.6-terra",
Input: responses.ResponseNewParamsInputUnion{
OfString: openai.String("Say hello from X.509 workload identity federation."),
},
})
if err != nil {
log.Fatal(err)
}
fmt.Println(response.OutputText())
}
来源示例 7(java):
// [中文] X.509交换和后续API均携带客户端证书;Bearer替代API密钥,不替代证书,不打印返回的短期令牌。
import com.openai.client.OpenAIClient;
import com.openai.client.okhttp.OpenAIOkHttpClient;
import com.openai.client.okhttp.X509Transport;
import com.openai.client.okhttp.X509WorkloadIdentity;
import com.openai.models.responses.ResponseCreateParams;
import java.io.InputStream;
import java.nio.file.Files;
import java.nio.file.Paths;
import java.security.KeyStore;
import java.time.Duration;
import java.util.Arrays;
import javax.net.ssl.KeyManagerFactory;
import javax.net.ssl.TrustManagerFactory;
import javax.net.ssl.X509ExtendedKeyManager;
import javax.net.ssl.X509TrustManager;
char[] password = System.getenv("OPENAI_X509_KEYSTORE_PASSWORD").toCharArray();
try {
KeyStore keyStore = KeyStore.getInstance("PKCS12");
try (InputStream input =
Files.newInputStream(Paths.get(System.getenv("OPENAI_X509_KEYSTORE_PATH")))) {
keyStore.load(input, password);
}
KeyManagerFactory keyManagers =
KeyManagerFactory.getInstance(KeyManagerFactory.getDefaultAlgorithm());
keyManagers.init(keyStore, password);
X509ExtendedKeyManager keyManager =
Arrays.stream(keyManagers.getKeyManagers())
.filter(X509ExtendedKeyManager.class::isInstance)
.map(X509ExtendedKeyManager.class::cast)
.findFirst()
.orElseThrow(() -> new IllegalStateException("No X.509 key manager available"));
TrustManagerFactory trustManagers =
TrustManagerFactory.getInstance(TrustManagerFactory.getDefaultAlgorithm());
trustManagers.init((KeyStore) null);
X509TrustManager trustManager =
Arrays.stream(trustManagers.getTrustManagers())
.filter(X509TrustManager.class::isInstance)
.map(X509TrustManager.class::cast)
.findFirst()
.orElseThrow(() -> new IllegalStateException("No X.509 trust manager available"));
X509Transport transport =
X509Transport.builder()
.keyManager(keyManager)
.certificateAlias(System.getenv("OPENAI_X509_CERTIFICATE_ALIAS"))
.trustManager(trustManager)
.build();
X509WorkloadIdentity identity =
X509WorkloadIdentity.builder()
.identityProviderId(System.getenv("OPENAI_IDENTITY_PROVIDER_ID"))
.serviceAccountId(System.getenv("OPENAI_SERVICE_ACCOUNT_ID"))
.transport(transport)
.refreshBuffer(Duration.ofMinutes(10))
.build();
OpenAIClient client = OpenAIOkHttpClient.builder().x509WorkloadIdentity(identity).build();
try {
ResponseCreateParams params =
ResponseCreateParams.builder()
.model("gpt-5.6-terra")
.input("Say hello from X.509 workload identity federation.")
.build();
client.responses().create(params).output().stream()
.flatMap(item -> item.message().stream())
.flatMap(message -> message.content().stream())
.flatMap(content -> content.outputText().stream())
.forEach(outputText -> System.out.println(outputText.text()));
} finally {
client.close();
}
} finally {
Arrays.fill(password, '\0');
}
来源示例 8(ruby):
## [中文] X.509交换和后续API均携带客户端证书;Bearer替代API密钥,不替代证书,不打印返回的短期令牌。
require "openai"
require "openssl"
certificate_chain = OpenSSL::X509::Certificate.load(
File.binread(ENV.fetch("OPENAI_MTLS_CERT_CHAIN"))
)
certificate, *intermediates = certificate_chain
private_key = OpenSSL::PKey.read(File.binread(ENV.fetch("OPENAI_MTLS_KEY")))
http_client = OpenAI::NetHTTPClient.new do |connection|
connection.cert = certificate
connection.extra_chain_cert = intermediates
connection.key = private_key
end
workload_identity = OpenAI::Auth::X509WorkloadIdentity.new(
identity_provider_id: ENV.fetch("OPENAI_IDENTITY_PROVIDER_ID"),
service_account_id: ENV.fetch("OPENAI_SERVICE_ACCOUNT_ID"),
http_client: http_client
)
begin
client = OpenAI::Client.new(api_key: nil, workload_identity: workload_identity)
response = client.responses.create(
model: "gpt-5.6-terra",
input: "Say hello from X.509 workload identity federation."
)
puts(response.output_text)
ensure
http_client.close
end
此配置要求 JavaScript 7.8.0+ 且安装 undici peer dependency,Python 3.6.0+,Go 3.54.0+,Java 4.55.0+,Ruby 0.83.0+。
Java 示例从 PKCS12 keystore 构建 X509ExtendedKeyManager,平台默认 trust store 构建 X509TrustManager。设置 OPENAI_X509_KEYSTORE_PATH、OPENAI_X509_KEYSTORE_PASSWORD、OPENAI_X509_CERTIFICATE_ALIAS,也可向 SDK 提供 PEM 或硬件支持的 manager。
手动交换证书
为检查或自行实现协议,向精确的 X.509 端点呈交证书:
来源示例 9(bash):
## [中文] X.509交换和后续API均携带客户端证书;Bearer替代API密钥,不替代证书,不打印返回的短期令牌。
curl --cert "$OPENAI_MTLS_CERT_CHAIN" \
--key "$OPENAI_MTLS_KEY" \
--request POST "https://mtls.auth.openai.com/oauth/token" \
--header "Content-Type: application/json" \
--data @- <<JSON
{
"grant_type": "urn:ietf:params:oauth:grant-type:token-exchange",
"subject_token_type": "urn:openai:params:oauth:token-type:x509",
"identity_provider_id": "${OPENAI_IDENTITY_PROVIDER_ID}",
"service_account_id": "${OPENAI_SERVICE_ACCOUNT_ID}"
}
JSON
成功返回普通短期 bearer:
来源示例 10(json):
中文说明:来源配置或结果示例,按所在小节的字段、作用域与权限要求解释;JSON保持合法格式,CEL/声明匹配文本不是可独立运行的应用。
{
"access_token": "eyJ...",
"issued_token_type": "urn:ietf:params:oauth:token-type:access_token",
"token_type": "Bearer",
"expires_in": 3600,
"expires_at": 1789045200,
"scope": "api.model.read api.model.request"
}
仅匹配映射设有 Permissions 时返回 scope。到期值只是示例,证书更早到期时实际令牌会更短。单位见响应字段。将 access_token 安全加载到应用凭证存储或 OPENAI_WIF_ACCESS_TOKEN 等环境变量,不打印、记录或提交版本库。
手动调用 API
OPENAI_MODEL 设为来源当前默认 gpt-6-astra 或目标项目可用的其他模型,携带 bearer 与证书请求 mTLS API:
来源示例 11(bash):
## [中文] X.509交换和后续API均携带客户端证书;Bearer替代API密钥,不替代证书,不打印返回的短期令牌。
curl --request POST \
--cert "$OPENAI_MTLS_CERT_CHAIN" \
--key "$OPENAI_MTLS_KEY" \
--header "Authorization: Bearer $OPENAI_WIF_ACCESS_TOKEN" \
--header "Content-Type: application/json" \
--data "{\"model\":\"$OPENAI_MODEL\",\"input\":\"Say hello in one sentence.\"}" \
"https://mtls.api.openai.com/v1/responses"
Bearer 替代 API 密钥,证书继续必需。令牌没有密码学绑定到证书。复用交换证书最直接,但也可使用另一张独立满足当前 API mTLS 政策的证书。
令牌寿命与续期
最长一小时,且不超过已验证证书的到期时间。不返回刷新令牌,须再次交换。手动管理时保留 expires_at,提前安排交换并留出时钟、网络余量,见续期指南。轮换中间证书无须改根,后续请求提交新完整链。
交换故障排查
返回通用 OAuth 错误,不暴露证书、根、提供商或映射详情:
| 结果 | 常见原因 |
|---|---|
HTTP 403 |
未使用 mtls.auth.openai.com 上精确 POST /oauth/token 的方法或路径 |
invalid_subject_token |
客户端证书缺失/无效、链不达活动根、证书不在有效期或mTLS准入规则拒绝 |
invalid_grant |
提供商/映射无效或停用、Attribute conditions拒绝、无活动适用根或无匹配映射 |
| 服务器错误 | 暂时服务器错误,按暂时故障策略重试 |
X.509 交换不会回退到 OIDC 或普通 OAuth 流程。
限制
- 无独立证书信任库。
- Bearer 非证书绑定,不使用 DPoP 或
cnf。 - 不是仅证书 API 授权,后续仍需 bearer 和证书。
- 不从 AIA 补取中间证书,握手须提交完整链。
- 不做 CRL/OCSP 检查,应围绕 mTLS 根、提供商、映射控制及令牌短寿命设计证书事件响应。
- 不增加 SPIFFE X.509-SVID 支持,SPIFFE 路径继续使用 JWT-SVID。

TRAE-AI编程


更多建议: