เอกสารนี้สรุปขั้นตอนทั้งหมดตั้งแต่โครงสร้าง CA ตอนต้น ไปจนถึงการออกใบรับรองและแปลงเป็น .p12 สำหรับใช้งานจริง
1. โครงสร้างโฟลเดอร์
สร้างโฟลเดอร์และไฟล์ฐานข้อมูลตามนี้:
/opt/myca/
├── root/ ← Root CA (self-signed)
│ ├── certs/ ← ใบรับรอง Root CA (PEM)
│ ├── crl/ ← CRL ของ Root CA
│ ├── newcerts/ ← ใบรับรองที่ออกแล้วตาม serial
│ ├── private/ ← private key ของ Root CA (chmod 700)
│ ├── index.txt ← ฐานข้อมูลใบรับรองที่ออก
│ ├── serial ← serial number ปัจจุบัน
│ └── openssl.cnf ← config ของ Root CA
└── intermediate/ ← Intermediate CA
├── certs/ ← ใบรับรอง Intermediate CA
├── crl/ ← CRL ของ Intermediate CA
├── csr/ ← CSR ของผู้ใช้งาน
├── newcerts/ ← ใบรับรองที่ออกตาม serial
├── private/ ← private key ของ Intermediate CA
├── index.txt ← ฐานข้อมูลใบรับรองที่ออก
├── serial ← serial number ปัจจุบัน
└── openssl.cnf ← config ของ Intermediate CA
2. สร้างโฟลเดอร์และไฟล์ฐานข้อมูล
sudo mkdir -p /opt/myca/root/{certs,crl,newcerts,private}
sudo mkdir -p /opt/myca/intermediate/{certs,crl,csr,newcerts,private}
sudo chmod 700 /opt/myca/root/private /opt/myca/intermediate/private
sudo touch /opt/myca/root/index.txt /opt/myca/intermediate/index.txt
echo 1000 | sudo tee /opt/myca/root/serial /opt/myca/intermediate/serial
3. เตรียมไฟล์ /opt/myca/root/openssl.cnf
(Root CA)
[ ca ]
default_ca = CA_default
[ CA_default ]
dir = /opt/myca/root
certs = $dir/certs
crl_dir = $dir/crl
new_certs_dir = $dir/newcerts
database = $dir/index.txt
serial = $dir/serial
RANDFILE = $dir/private/.rand
# Certificate policy
default_days = 3650
default_md = sha256
policy = policy_strict
email_in_dn = optional
random_serial = yes
private_key = $dir/private/ca.key.pem
certificate = $dir/certs/ca.cert.pem
[ policy_strict ]
countryName = match
stateOrProvinceName = optional
organizationName = match
organizationalUnitName = optional
commonName = supplied
emailAddress = optional
[ req ]
default_bits = 4096
prompt = no
default_md = sha256
distinguished_name = dn
[ dn ]
C = TH
ST = Bangkok
L = Bangkok
O = ExampleCorp Ltd.
OU = Root Authority
CN = ExampleCorp Root CA
[ v3_ca ]
subjectKeyIdentifier = hash
authorityKeyIdentifier = keyid,issuer
basicConstraints = critical, CA:true, pathlen:1
keyUsage = critical, digitalSignature, cRLSign, keyCertSign
# Extension ใช้ตอนเซ็น Intermediate CSR
[ v3_intermediate_ca ]
subjectKeyIdentifier = hash
authorityKeyIdentifier = keyid,issuer
basicConstraints = critical, CA:true, pathlen:0
keyUsage = critical, digitalSignature, cRLSign, keyCertSign
# หมายเหตุ: ส่วน [ usr_cert ] ใช้เฉพาะใน /opt/myca/intermediate/openssl.cnf
# ไม่ต้องใส่ใน /opt/myca/root/openssl.cnf
4. สร้าง Root Key & Self-Signed Certificate
cd /opt/myca/root
# สร้าง private key
sudo openssl genrsa -aes256 \
-out private/ca.key.pem 4096
sudo chmod 400 private/ca.key.pem
# สร้าง self-signed cert
sudo openssl req -config /opt/myca/root/openssl.cnf \
-key private/ca.key.pem \
-new -x509 -days 7300 -sha256 \
-extensions v3_ca \
-out certs/ca.cert.pem
sudo chmod 444 certs/ca.cert.pem
5. เตรียมไฟล์ /opt/myca/intermediate/openssl.cnf
(Intermediate CA)
[ ca ]
default_ca = CA_default
[ CA_default ]
dir = /opt/myca/intermediate
certs = $dir/certs
crl_dir = $dir/crl
new_certs_dir = $dir/newcerts
database = $dir/index.txt
serial = $dir/serial
RANDFILE = $dir/private/.rand
# Certificate policy
default_days = 1825
default_md = sha256
policy = policy_loose
email_in_dn = optional
random_serial = yes
private_key = $dir/private/intermediate.key.pem
certificate = $dir/certs/intermediate.cert.pem
chain_cert = /opt/myca/root/certs/ca.cert.pem
[ policy_loose ]
countryName = optional
stateOrProvinceName = optional
organizationName = optional
organizationalUnitName = optional
commonName = supplied
emailAddress = optional
[ req ]
default_bits = 4096
prompt = no
default_md = sha256
distinguished_name = dn
[ dn ]
C = TH
ST = Bangkok
L = Bangkok
O = ExampleCorp Ltd.
OU = Intermediate Authority
CN = ExampleCorp Intermediate CA
[ v3_intermediate_ca ]
subjectKeyIdentifier = hash
authorityKeyIdentifier = keyid,issuer
basicConstraints = critical, CA:true, pathlen:0
keyUsage = critical, digitalSignature, cRLSign, keyCertSign
[ usr_cert ]
basicConstraints = CA:FALSE
nsCertType = client, email
keyUsage = nonRepudiation, digitalSignature, keyEncipherment
6. สร้าง Intermediate Key & CSR
cd /opt/myca/intermediate
# สร้าง private key
sudo openssl genrsa -aes256 \
-out private/intermediate.key.pem 4096
sudo chmod 400 private/intermediate.key.pem
# สร้าง CSR
sudo openssl req -config /opt/myca/intermediate/openssl.cnf \
-new -sha256 \
-key private/intermediate.key.pem \
-out csr/intermediate.csr.pem
7. ใช้ Root CA เซ็น Intermediate CSR
cd /opt/myca/root
# เซ็น CSR ของ Intermediate
sudo openssl ca -config /opt/myca/root/openssl.cnf \
-extensions v3_intermediate_ca \
-days 3650 -notext -md sha256 \
-in ../intermediate/csr/intermediate.csr.pem \
-out ../intermediate/certs/intermediate.cert.pem
sudo chmod 444 ../intermediate/certs/intermediate.cert.pem
8. สร้าง Chain File (Root+Intermediate)
cd /opt/myca/intermediate/certs
# ใช้ path relative
sudo cat intermediate.cert.pem \
../../root/certs/ca.cert.pem \
> ca-chain.cert.pem
sudo chmod 444 ca-chain.cert.pem
# หรือใช้ path แบบเต็ม
# sudo cat /opt/myca/intermediate/certs/intermediate.cert.pem \
# /opt/myca/root/certs/ca.cert.pem \
# > /opt/myca/intermediate/certs/ca-chain.cert.pem
9. ออก Leaf Certificate & แปลงเป็น .p12
9.1 สร้าง CSR สำหรับผู้ใช้ (Leaf)
# สร้าง key+CSR ใน intermediate
sudo openssl req -new -nodes \
-out /opt/myca/intermediate/csr/user1.csr.pem \
-newkey rsa:2048 \
-keyout /opt/myca/intermediate/private/user1.key.pem \
-subj "/C=TH/ST=Bangkok/O=ExampleCorp Ltd./OU=Users/CN=user1"
9.2 เซ็น Leaf CSR ด้วย Intermediate CA
cd /opt/myca/intermediate
# ตรวจสอบไฟล์ CSR
ls -l csr/user1.csr.pem
# เซ็น CSR
sudo openssl ca -config /opt/myca/intermediate/openssl.cnf \
-extensions usr_cert \
-days 825 -notext -md sha256 \
-in csr/user1.csr.pem \
-out certs/user1.cert.pem
sudo chmod 444 certs/user1.cert.pem
9.3 แปลงเป็น .p12
cd /opt/myca/intermediate/certs
sudo openssl pkcs12 -export \
-inkey ../private/user1.key.pem \
-in user1.cert.pem \
-certfile ca-chain.cert.pem \
-name "user1" \
-out user1.p12
สรุปไฟล์สำคัญ
/opt/myca/root/private/ca.key.pem
— Root private key (ห้ามเผยแพร่)/opt/myca/root/certs/ca.cert.pem
— Root certificate (นำไปติดตั้งในเครื่องลูกข่ายเพื่อเชื่อถือ CA)/opt/myca/intermediate/private/intermediate.key.pem
— Intermediate private key/opt/myca/intermediate/certs/intermediate.cert.pem
— Intermediate certificate/opt/myca/intermediate/certs/ca-chain.cert.pem
— Chain file (Root + Intermediate)/opt/myca/intermediate/private/user1.key.pem
,/opt/myca/intermediate/certs/user1.cert.pem
,/opt/myca/intermediate/certs/user1.p12
— Leaf key/cert/p12
10. ติดตั้ง Root CA Certificate ในเครื่องลูกข่าย
เพื่อให้ระบบและเบราว์เซอร์เชื่อถือใบรับรองที่ออกโดย CA ภายใน ต้องนำใบรับรอง Root CA (ca.cert.pem
) ไปติดตั้งเป็น Trusted Root ดังนี้:
- บน Ubuntu / Debian:
sudo cp /opt/myca/root/certs/ca.cert.pem /usr/local/share/ca-certificates/examplecorp-root.crt sudo update-ca-certificates
- บน CentOS / RHEL:
sudo cp /opt/myca/root/certs/ca.cert.pem /etc/pki/ca-trust/source/anchors/examplecorp-root.crt sudo update-ca-trust extract
- บน Windows:
- ดับเบิลคลิกไฟล์
ca.cert.pem
แล้วคลิก “Install Certificate” - เลือก “Local Machine” → Trusted Root Certification Authorities
- เสร็จสิ้นตาม wizard
- ดับเบิลคลิกไฟล์
- ในเบราว์เซอร์ Firefox:
- เปิด Preferences → Privacy & Security → Certificates → View Certificates
- Import… แล้วเลือก
ca.cert.pem
- ติ๊ก “Trust this CA to identify websites”
หลังติดตั้งแล้ว ใบรับรองทั้งหมดที่ออกโดย Intermediate CA และ Leaf CA จะได้รับความน่าเชื่อถือจากระบบ
11. การรวมกับ PHP เพื่อสร้างไฟล์ .p12 อัตโนมัติ (ใช้ openssl_csr_sign)
ใช้สคริปต์ PHP ด้านล่างนี้ โดยกำหนด $dn
ให้มีทั้ง commonName
และ emailAddress
จากฟอร์ม:
<?php
putenv('RANDFILE=/opt/myca/intermediate/private/.rand');
// config.php เหมือนเดิม (INT_OPENSSL_CNF, INT_PRIVATE_KEY, INT_CERT, CA_CHAIN)
define('INT_OPENSSL_CNF', '/opt/myca/intermediate/openssl.cnf');
define('INT_PRIVATE_KEY', '/opt/myca/intermediate/private/intermediate.key.pem');
define('INT_CERT', '/opt/myca/intermediate/certs/intermediate.cert.pem');
define('CA_CHAIN', '/opt/myca/intermediate/certs/ca-chain.cert.pem');
if ($_SERVER['REQUEST_METHOD']==='POST') {
$cn = trim($_POST['common_name']);
$email = trim($_POST['email']);
$password = $_POST['p12_pass'];
$passphrase = '123456'; // intermediate key passphrase
// 1. สร้าง CSR ชั่วคราว
$dn = [
"countryName" => "TH",
"stateOrProvinceName" => "Bangkok",
"localityName" => "Bangkok",
"organizationName" => "ExampleCorp Ltd.",
"organizationalUnitName" => "Users",
"commonName" => $cn,
"emailAddress" => $email
];
$privkey = openssl_pkey_new(["private_key_bits"=>2048]);
$csr = openssl_csr_new($dn, $privkey, ["digest_alg"=>"sha256"]);
// 2. โหลด Intermediate CA key+cert
$caKey = openssl_pkey_get_private(
"file://".INT_PRIVATE_KEY,
$passphrase
);
$caCert = file_get_contents(INT_CERT);
// 3. เซ็น CSR (PHP เรียก openssl_csr_sign แทน openssl ca)
$serial = time(); // จะใช้เลขใดก็ได้ แค่ไม่ซ้ำ
$x509 = openssl_csr_sign(
$csr,
$caCert,
$caKey,
825, // วันหมดอายุ
[
"digest_alg" => "sha256",
"x509_extensions" => "usr_cert",
"config" => INT_OPENSSL_CNF
],
$serial
);
if (!$x509) {
die("Signing CSR failed: " . openssl_error_string());
}
// 4. ส่งออกเป็นไฟล์ PEM
$tmp = sys_get_temp_dir().'/cert_'.uniqid();
$certFile = "$tmp.cert.pem";
openssl_x509_export_to_file($x509, $certFile);
// 5. สร้าง .p12
$p12 = null;
$certChain = file_get_contents(CA_CHAIN);
$certBody = file_get_contents($certFile);
$fullCert = $certBody . "\n" . $certChain;
$privKeyPem = '';
openssl_pkey_export($privkey, $privKeyPem);
if (!openssl_pkcs12_export(
$fullCert,
$p12,
$privKeyPem,
$password
)) {
die("Export .p12 failed: " . openssl_error_string());
}
// 6. ส่ง .p12 ให้ดาวน์โหลด
header('Content-Type: application/x-pkcs12');
header('Content-Disposition: attachment; filename="' . basename($cn) . '.p12"');
echo $p12;
// 7. ลบไฟล์ชั่วคราว
@unlink($certFile);
exit;
}
?>
**Form HTML** ตัวอย่าง:
<form method="post">
<label>Common Name (CN):<input name="common_name" required></label><br>
<label>Email Address:<input type="email" name="email" required></label><br>
<label>Password for .p12:<input type="password" name="p12_pass" required></label><br>
<button>Generate .p12</button>
</form>
อย่าลืมปรับสิทธิ์โฟลเดอร์ private ให้ PHP อ่านได้ เพื่อรัน openssl_csr_sign สำเร็จ:
sudo chown -R www-data:www-data /opt/myca/intermediate/private
sudo chmod 750 /opt/myca/intermediate/private
sudo chmod 640 /opt/myca/intermediate/private/intermediate.key.pem