2024 年 PHP Conference Japan

openssl_csr_new

(PHP 4 >= 4.2.0, PHP 5, PHP 7, PHP 8)

openssl_csr_new產生 CSR

說明

openssl_csr_new(
    陣列 $distinguished_names,
    #[\SensitiveParameter] ?OpenSSLAsymmetricKey &$private_key,
    ?陣列 $options = null,
    ?陣列 $extra_attributes = null
): OpenSSLCertificateSigningRequest|布林值

openssl_csr_new() 函式會根據 distinguished_names 參數提供的資訊產生新的 CSR (憑證簽署請求)。

注意 您必須安裝有效的 openssl.cnf 檔案,此函式才能正常運作。詳情請參閱 安裝章節 下的注意事項。

參數

distinguished_names

要在憑證中使用的識別名稱或主旨欄位。

private_key

private_key 參數應設定為先前由 openssl_pkey_new() 函式產生(或透過其他 openssl_pkey 系列函式取得)的私鑰,或是 null 變數。如果其值為 null 變數,則會根據提供的 options 參數產生新的私鑰,並指派給提供的變數。金鑰的對應公鑰部分將用於簽署 CSR

options

預設情況下,系統中的 openssl.conf 檔案資訊會用於初始化請求;您可以透過在 options 參數中設定 config_section_section 鍵值來指定設定檔區段。您也可以透過將 config 鍵值設定為您要使用的檔案路徑,來指定替代的 OpenSSL 設定檔。如果 options 參數中存在以下鍵值,則其作用如同 openssl.conf 中的對應項,如下表所示。

設定覆寫
options 參數鍵值 類型 openssl.conf 對應項 說明
digest_alg 字串 default_md 摘要方法或簽章雜湊,通常為 openssl_get_md_methods() 函式所返回的其中之一
x509_extensions 字串 x509_extensions 選擇在建立 x509 憑證時應使用的擴充功能
req_extensions 字串 req_extensions 選擇在建立 CSR 時應使用的擴充功能
private_key_bits 整數 default_bits 指定用於產生私鑰的位元數
private_key_type 整數 指定要建立的私鑰類型。可以是 OPENSSL_KEYTYPE_DSAOPENSSL_KEYTYPE_DHOPENSSL_KEYTYPE_RSAOPENSSL_KEYTYPE_EC 之一。預設值為 OPENSSL_KEYTYPE_RSA
encrypt_key 布林值 encrypt_key 匯出的金鑰(含密碼)是否應加密?
encrypt_key_cipher 整數 加密常數 之一。
curve_name 字串 openssl_get_curve_names() 函式所返回的其中之一。
config 字串 您自己的替代 openssl.conf 檔案的路徑。

extra_attributes

extra_attributes 參數用於指定 CSR 的其他設定選項。distinguished_namesextra_attributes 都是關聯式陣列,其鍵值會轉換為 OID 並套用至請求的相關部分。

傳回值

成功時傳回 CSR,如果 CSR 建立成功但簽署失敗則傳回 true,失敗時傳回 false

版本異動記錄

版本 說明
8.0.0 成功時,此函數現在會返回一個 OpenSSLCertificateSigningRequest 實例;先前返回的是類型為 OpenSSL X.509 CSR資源
8.0.0 private_key 現在接受一個 OpenSSLAsymmetricKey 實例;先前接受的是類型為 OpenSSL key資源
7.1.0 options 現在也支援 curve_name

範例

範例 #1 建立自簽憑證

<?php
// for SSL server certificates the commonName is the domain name to be secured
// for S/MIME email certificates the commonName is the owner of the email address
// location and identification fields refer to the owner of domain or email subject to be secured
$dn = array(
"countryName" => "GB",
"stateOrProvinceName" => "Somerset",
"localityName" => "Glastonbury",
"organizationName" => "The Brain Room Limited",
"organizationalUnitName" => "PHP Documentation Team",
"commonName" => "Wez Furlong",
"emailAddress" => "wez@example.com"
);

// Generate a new private (and public) key pair
$privkey = openssl_pkey_new(array(
"private_key_bits" => 2048,
"private_key_type" => OPENSSL_KEYTYPE_RSA,
));

// Generate a certificate signing request
$csr = openssl_csr_new($dn, $privkey, array('digest_alg' => 'sha256'));

// Generate a self-signed cert, valid for 365 days
$x509 = openssl_csr_sign($csr, null, $privkey, $days=365, array('digest_alg' => 'sha256'));

// Save your private key, CSR and self-signed cert for later use
openssl_csr_export($csr, $csrout) and var_dump($csrout);
openssl_x509_export($x509, $certout) and var_dump($certout);
openssl_pkey_export($privkey, $pkeyout, "mypassword") and var_dump($pkeyout);

// Show any errors that occurred here
while (($e = openssl_error_string()) !== false) {
echo
$e . "\n";
}
?>

範例 #2 建立自簽 ECC 憑證(PHP 7.1.0 起)

<?php
$subject
= array(
"commonName" => "docs.php.net",
);

// 產生新的私鑰(和公鑰)對
$private_key = openssl_pkey_new(array(
"private_key_type" => OPENSSL_KEYTYPE_EC,
"curve_name" => 'prime256v1',
));

// 產生憑證簽署請求
$csr = openssl_csr_new($subject, $private_key, array('digest_alg' => 'sha384'));

// 產生自簽 EC 憑證
$x509 = openssl_csr_sign($csr, null, $private_key, $days=365, array('digest_alg' => 'sha384'));
openssl_x509_export_to_file($x509, 'ecc-cert.pem');
openssl_pkey_export_to_file($private_key, 'ecc-private.key');
?>

參見

新增註釋

使用者貢獻的註釋 10 則註釋

12
The_Lost_One
15 年前
我不確定我遇到的這個「bug」(未記錄的行為)其他人是否也常遇到,但這則留言或許能幫你省下數小時痛苦的除錯時間。
如果你使用 `openssl_pkey_new()` 或 `openssl_csr_new()` 產生新的私鑰時失敗,你的腳本會在呼叫這些函式時卡住。如果你有指定 `private_key_bits` 參數,請確保將變數強制轉換為整數 (int)。我花了很長時間才注意到這點。

<?php
$SSLcnf
= array('config' => '/usr/local/nessy2/share/ssl/openssl.cnf',
'encrypt_key' => true,
'private_key_type' => OPENSSL_KEYTYPE_RSA,
'digest_alg' => 'sha1',
'x509_extensions' => 'v3_ca',
'private_key_bits' => $someVariable // ---> 錯誤
'private_key_bits' => (int)$someVariable // ---> 正確
'private_key_bits' => 512 // ---> 顯然正確
);
?>
6
james at kirk dot com
9 年前
當你有所疑惑時,就去閱讀 PHP 的原始碼!

`$configargs` 的底層運作方式相當不透明。除非你實際去查看 `/ext/openssl/openssl.c` 中的 `php_openssl_parse_config()` 函式。

SET_OPTIONAL_STRING_ARG("digest_alg", req->digest_name,
CONF_get_string(req->req_config, req->section_name, "default_md"));
SET_OPTIONAL_STRING_ARG("x509_extensions", req->extensions_section,
CONF_get_string(req->req_config, req->section_name, "x509_extensions"));
SET_OPTIONAL_STRING_ARG("req_extensions", req->request_extensions_section,
CONF_get_string(req->req_config, req->section_name, "req_extensions"));
SET_OPTIONAL_LONG_ARG("private_key_bits", req->priv_key_bits,
CONF_get_number(req->req_config, req->section_name, "default_bits"));

SET_OPTIONAL_LONG_ARG("private_key_type", req->priv_key_type, OPENSSL_KEYTYPE_DEFAULT);

我們可以看到,大多數輸入都呼叫了 `SET_OPTIONAL_STRING_ARG()`,但 `'private_key_bits'` 卻呼叫了 `SET_OPTIONAL_LONG_ARG()`。這兩個呼叫都是 C 巨集,它們會展開成強制執行預期輸入類型的程式碼。如果使用了非預期的類型,產生的程式碼會忽略輸入,並且不會發出任何警告或通知,只會使用設定檔中的預設值。這就是為什麼對 `'private_key_bits'` 使用字串會導致非預期行為的原因。

進一步檢視同一個函式中較早的初始化:

SET_OPTIONAL_STRING_ARG("config", req->config_filename, default_ssl_conf_filename);
SET_OPTIONAL_STRING_ARG("config_section_name", req->section_name, "req");
req->global_config = CONF_load(NULL, default_ssl_conf_filename, NULL);
req->req_config = CONF_load(NULL, req->config_filename, NULL);

if (req->req_config == NULL) {
return FAILURE;
}

以及在另一個函式中的其他地方:

/* 如果沒有設定環境變數,則預設為 'openssl.cnf' */
if (config_filename == NULL) {
snprintf(default_ssl_conf_filename, sizeof(default_ssl_conf_filename), "%s/%s",
X509_get_default_cert_area(),
"openssl.cnf");
} else {
strlcpy(default_ssl_conf_filename, config_filename, sizeof(default_ssl_conf_filename));
}

揭示了 `$configargs` 中的 `config` 會覆蓋其他地方的任何預設設定。這實際上否定了文件中「注意:您需要安裝有效的 openssl.cnf 才能使此函數正常運作。有關更多資訊,請參閱安裝章節下的注意事項。」這段註釋。更正確的句子應該是「注意:您需要設定有效的 openssl.cnf 或使用 `$configargs` 指向有效的 openssl.cnf 檔案,才能使此函數正常運作。」

所有這些都表明,查看 PHP 原始碼是了解實際情況的唯一真正方法。這樣做可以節省時間和精力。
3
匿名
9 年前
對於使用基於 Debian 系統的用戶,openssl 設定檔位於:/etc/ssl/openssl.cnf
2
main ATT jokester DOTT fr
16 年前
要將「basicConstraints」設定為「critical,CA:TRUE」,您必須在 openssl_csr_sign() 函數中定義 configargs!

這是我簽署「子」憑證的程式碼範例

`$CAcrt = "file://ca.crt";`
`$CAkey = array("file://ca.key", "myPassWord");`

`$clientKeys = openssl_pkey_new();`
`$dn = array(`
`"countryName" => "FR",`
`"stateOrProvinceName" => "Finistere",`
`"localityName" => "Plouzane",`
`"organizationName" => "Ecole Nationale d'Ingenieurs de Brest",`
`"organizationalUnitName" => "Enib Students",`
`"commonName" => "www.enib.fr",`
`"emailAddress" => "ilovessl@php.net"`
);
`$csr = openssl_csr_new($dn, $clientPrivKey);`

`$configArgs = array("x509_extensions" => "v3_req");`
`$cert = openssl_csr_sign($csr, $CAcrt, $CAkey, 100, $configArgs);`

`openssl_x509_export_to_file($cert, "childCert.crt");`

然後,如果您想新增更多選項,您可以編輯「/etc/ssl/openssl.cnf」ssl 設定檔(debian 路徑),並在 [ v3_req ] 標記後新增這些選項。
1
Richard Lynch
11 年前
似乎沒有 openssl_csr_free 函數。

至少這裡沒有。

如果它在原始碼中,則可以直接呼叫它。

如果它不在原始碼中,則可能應該新增它。
2
alex at nodex dot co dot uk
9 年前
在上面的 PHP 範例中,它使用「UK」作為國家名稱,這是錯誤的,國家名稱必須是「GB」。
0
匿名
19 年前
如果您收到錯誤訊息

error:0D11A086:asn1 encoding routines:ASN1_mbstring_copy:string too short

那麼請查看 `$dn`(識別名稱)陣列中的鍵值對。

如果您將一個值(例如「organizationalUnitName」= "")設定為空字串,則會拋出上述錯誤。

透過完全從 `$dn` 中刪除該陣列元素或使用空格「 」代替空字串來修復錯誤。
0
robertliu AT wiscore DOT com
19 年前
我正在使用 PHP-4.3.11。
configargs--private_key_bits 的類型是整數,而不是字串。
設定範例
<?php
$config
= array(
"digest_alg" => "sha1",
"private_key_bits" => 2048,
"private_key_type" => OPENSSL_KEYTYPE_DSA,
"encrypt_key" => false
);
?>
-1
dankybastard at hotmail
19 年前
如同您可能從範例中猜到的,文件資訊有誤。 openssl_csr_new 會回傳一個 CSR 資源,若失敗則回傳 FALSE。

mixed openssl_csr_new (assoc_array dn, resource_privkey, [...])
-3
@operator
7 年前
使用一個指令建立包含 4 個 SAN 子網域的現代憑證請求。
根據 RFC,您可以更改 CN(通用名稱)和 subjectAltName。 驗證憑證時會在 CN 和 subjectAltName 中搜尋。

openssl req -new -nodes -config <( cat <<-EOF
[req]
default_bits = 2048
prompt = no
default_md = sha256
req_extensions = re
distinguished_name = dn
[ dn ]
CN = my.tld
C = 國家
ST = 州/省
L = 城市
O = 組織
[ re ]
subjectAltName = DNS.1: www.my.tld, DNS.2: www2.my.tld, DNS.3: www3.my.tld, DNS.4: www4.my.tld
EOF
) -keyout secret.key -out req.csr
To Top