คู่มือการสร้าง Internal CA (Root & Intermediate) พร้อมออกใบรับรอง .cer และ .p12

คู่มือการสร้าง Internal CA (Root & Intermediate) พร้อมออกใบรับรอง .cer และ .p12

เอกสารนี้สรุปขั้นตอนทั้งหมดตั้งแต่โครงสร้าง 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:
    1. ดับเบิลคลิกไฟล์ ca.cert.pem แล้วคลิก “Install Certificate”
    2. เลือก “Local Machine” → Trusted Root Certification Authorities
    3. เสร็จสิ้นตาม wizard
  • ในเบราว์เซอร์ Firefox:
    1. เปิด Preferences → Privacy & Security → Certificates → View Certificates
    2. Import… แล้วเลือก ca.cert.pem
    3. ติ๊ก “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

Loading