From 1469d0d752e37851c436c167951e8b183d9ebbaf Mon Sep 17 00:00:00 2001 From: "icksishu@gmail.com" Date: Fri, 19 Dec 2025 16:42:44 +0900 Subject: [PATCH] =?UTF-8?q?=EC=82=AC=EC=84=A4=20=EC=9D=B8=EC=A6=9D?= =?UTF-8?q?=EC=84=9C=EB=A5=BC=20=ED=86=B5=ED=95=9C=20https=20=ED=86=B5?= =?UTF-8?q?=EC=8B=A0=20=EA=B8=B0=EB=8A=A5=20#4=20=20=EC=A7=84=ED=96=89?= =?UTF-8?q?=EC=A4=91=20-=20=ED=85=8C=EC=8A=A4=ED=8A=B8=EC=9A=A9=20?= =?UTF-8?q?=EC=9D=B8=EC=A6=9D=EC=84=9C=20=EC=83=9D=EC=84=B1=20=EB=B0=8F=20?= =?UTF-8?q?=EC=9D=B8=EC=A6=9D=20=EB=A1=9C=EC=A7=81?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../create-leaf-bsm-lab-postgres.md | 109 ++++++++++++++++++ src/certificate/create-rootca.md | 7 +- .../pki/intermediate/intermediate-kdn.srl | 1 + .../dfxagent-bsm-lab-postgres-leaf-ext.cnf | 11 ++ .../dfxagent-bsm-lab-postgres-req.cnf | 19 +++ .../dfxagent-bsm-lab-postgres.crt | 28 +++++ .../dfxagent-bsm-lab-postgres.csr | 17 +++ .../dfxagent-bsm-lab-postgres.key | 28 +++++ .../dfxagent-bsm-lab-postgres.p12 | Bin 0 -> 5976 bytes .../truststore-bsm-lab-postgres.jks | Bin 0 -> 1471 bytes 10 files changed, 217 insertions(+), 3 deletions(-) create mode 100644 src/certificate/create-leaf-bsm-lab-postgres.md create mode 100644 src/certificate/pki/intermediate/intermediate-kdn.srl create mode 100644 src/certificate/pki/leaf-dfxagent-bsm-lab-postgres/dfxagent-bsm-lab-postgres-leaf-ext.cnf create mode 100644 src/certificate/pki/leaf-dfxagent-bsm-lab-postgres/dfxagent-bsm-lab-postgres-req.cnf create mode 100644 src/certificate/pki/leaf-dfxagent-bsm-lab-postgres/dfxagent-bsm-lab-postgres.crt create mode 100644 src/certificate/pki/leaf-dfxagent-bsm-lab-postgres/dfxagent-bsm-lab-postgres.csr create mode 100644 src/certificate/pki/leaf-dfxagent-bsm-lab-postgres/dfxagent-bsm-lab-postgres.key create mode 100644 src/certificate/pki/leaf-dfxagent-bsm-lab-postgres/dfxagent-bsm-lab-postgres.p12 create mode 100644 src/certificate/pki/leaf-dfxagent-bsm-lab-postgres/truststore-bsm-lab-postgres.jks diff --git a/src/certificate/create-leaf-bsm-lab-postgres.md b/src/certificate/create-leaf-bsm-lab-postgres.md new file mode 100644 index 0000000..87e9e38 --- /dev/null +++ b/src/certificate/create-leaf-bsm-lab-postgres.md @@ -0,0 +1,109 @@ +# 개발용 leaf 인증서 생성 + +## 0) 사전 설치 +scoop install openssl + +## 1) leaf 개인키 생성 (dfxagent-bsm-lab-postgres.json 대상) + +```bash +cd pki +mkdir leaf-dfxagent-bsm-lab-postgres +cd leaf-dfxagent-bsm-lab-postgres +openssl genrsa -out dfxagent-bsm-lab-postgres.key 2048 +``` + +--- + +## 2) CSR 생성 + SAN(도메인/IP) 넣기 + +### 2-1) CSR용 설정 파일 만들기: `dfxagent-bsm-lab-postgres-req.cnf` + +```ini +[ req ] +default_bits = 2048 +prompt = no +default_md = sha256 +distinguished_name = dn +req_extensions = req_ext + +[ dn ] +C = KR +O = KDN +OU = DFX +CN = settings.json의 myHostId 값 기재 (mTLS에 따른 클라이언트 검증의 확인 문자로 사용함) + +[ req_ext ] +subjectAltName = @alt_names + +[ alt_names ] +IP.1 = 로컬PC 아이피 기재 +``` + +### 2-2) CSR 생성 + +```bash +openssl req -new -key dfxagent-bsm-lab-postgres.key -out dfxagent-bsm-lab-postgres.csr -config dfxagent-bsm-lab-postgres-req.cnf +``` + +--- + +## 3) Intermediate로 leaf 인증서 서명(발급) + +### 3-1) leaf 확장 파일 만들기: `dfxagent-bsm-lab-postgres-leaf-ext.cnf` + +#### ✅ 서버용(HTTPS), mTLS 클라이언트 겸용 + +```ini +[ v3_server ] +basicConstraints = critical, CA:false +keyUsage = critical, digitalSignature, keyEncipherment +extendedKeyUsage = serverAuth, clientAuth +subjectKeyIdentifier = hash +authorityKeyIdentifier = keyid,issuer +subjectAltName = @alt_names + +[ alt_names ] +IP.1 = 로컬PC 아이피 기재 +``` + +### 3-2) Intermediate로 서명 + +```bash +openssl x509 -req -in dfxagent-bsm-lab-postgres.csr -CA ..\intermediate\intermediate-kdn.crt -CAkey ..\intermediate\intermediate-kdn.key -CAcreateserial -out dfxagent-bsm-lab-postgres.crt -days 825 -sha256 -extfile dfxagent-bsm-lab-postgres-leaf-ext.cnf -extensions v3_server +``` +Certificate request self-signature ok +subject=C=KR, O=KDN, OU=DFX, CN=agent-bsm-lab-postgres +Enter pass phrase for ..\intermediate\intermediate-kdn.key: 백세민1! +> `-days`는 운영 정책에 맞춰 조정(예: 365, 730 등). + +--- + +## 4) 체인 검증(중요) + +```bash +openssl verify -CAfile ..\intermediate\ca-chain-kdn.crt dfxagent-bsm-lab-postgres.crt +``` + +`OK`가 나오는지 확인 + +--- + +## 5) (Java/톰캣용) PKCS12 keystore(p12) 만들기 + +DFXAgent가 Spring Boot(내장 톰캣)이므로 `p12`를 keystore로 사용 + +```bash +openssl pkcs12 -export -inkey dfxagent-bsm-lab-postgres.key -in dfxagent-bsm-lab-postgres.crt -certfile ..\intermediate\ca-chain-kdn.crt -out dfxagent-bsm-lab-postgres.p12 -name agent-bsm-lab-postgres +``` +Enter Export Password: 백세민1! + +--- + +## 6) (클라이언트 검증용) truststore 만들기 - JKS truststore (Java에서 흔함) + +```bash +keytool -importcert -alias bsm-ca-chain -file ../intermediate/ca-chain-kdn.crt -keystore truststore-bsm-lab-postgres.jks -storepass changeit -noprompt +``` + +--- + diff --git a/src/certificate/create-rootca.md b/src/certificate/create-rootca.md index af0ebbb..c3e9a9c 100644 --- a/src/certificate/create-rootca.md +++ b/src/certificate/create-rootca.md @@ -71,6 +71,8 @@ authorityKeyIdentifier = keyid:always,issuer cd ../root openssl x509 -req -in ../intermediate/intermediate-kdn.csr -CA rootca-kdn.crt -CAkey rootca-kdn.key -CAcreateserial -out ../intermediate/intermediate-kdn.crt -days 1825 -sha256 -extfile root-ext-kdn.cnf -extensions v3_intermediate_ca ``` +이후 intermediate-kdn.srl 파일이 생성됨 +이는 -CAcreateserial 옵션에 따른 결과로 다음 발급할 인증서에 쓸 serial 값이 저장되어 있음. serial이 중복되지 않도록 하는 역할임 ### 2-4) CA 체인 파일 만들기 고객사 설치용 CA 체인 생성. 추후 truststore 저장 @@ -82,13 +84,12 @@ cat ../intermediate/intermediate-kdn.crt rootca-kdn.crt > ../intermediate/ca-cha ## 3) 다음 단계(참고): leaf(에이전트/웹서버) 발급은 Intermediate로 -CA 체인이 준비되면, leaf는 보통 이런 흐름입니다. +이후 leaf 인증서 발급 순서 1. (에이전트/웹서버) 개인키 생성 2. CSR 생성(CN/SAN 포함) 3. **Intermediate로 서명**해서 leaf cert 발급 -4. `leaf cert + private key`는 keystore(p12), `ca-chain`은 truststore에 +4. `leaf cert + private key`는 keystore(p12), `ca-chain`은 truststore에 저장 --- -원하시면, 위걸 “고객사 A/B별로 자동으로 디렉토리 생성해서 CA 체인 뽑는 스크립트(Windows PowerShell / Linux bash)”로 만들어드릴게요. 그리고 이어서 **leaf 인증서 발급 시 SAN(호스트/IP) 넣는 방법**까지 같이 붙이면 실제 배포에 바로 쓸 수 있습니다. diff --git a/src/certificate/pki/intermediate/intermediate-kdn.srl b/src/certificate/pki/intermediate/intermediate-kdn.srl new file mode 100644 index 0000000..ca2d29f --- /dev/null +++ b/src/certificate/pki/intermediate/intermediate-kdn.srl @@ -0,0 +1 @@ +77169B7FC8B412B10E189B971C35B057DE5EE5C4 diff --git a/src/certificate/pki/leaf-dfxagent-bsm-lab-postgres/dfxagent-bsm-lab-postgres-leaf-ext.cnf b/src/certificate/pki/leaf-dfxagent-bsm-lab-postgres/dfxagent-bsm-lab-postgres-leaf-ext.cnf new file mode 100644 index 0000000..1f6ac23 --- /dev/null +++ b/src/certificate/pki/leaf-dfxagent-bsm-lab-postgres/dfxagent-bsm-lab-postgres-leaf-ext.cnf @@ -0,0 +1,11 @@ +[ v3_server ] +basicConstraints = critical, CA:false +keyUsage = critical, digitalSignature, keyEncipherment +extendedKeyUsage = serverAuth, clientAuth +subjectKeyIdentifier = hash +authorityKeyIdentifier = keyid,issuer +subjectAltName = @alt_names + +[ alt_names ] +IP.1 = 192.168.0.41 +IP.2 = 172.22.1.4 diff --git a/src/certificate/pki/leaf-dfxagent-bsm-lab-postgres/dfxagent-bsm-lab-postgres-req.cnf b/src/certificate/pki/leaf-dfxagent-bsm-lab-postgres/dfxagent-bsm-lab-postgres-req.cnf new file mode 100644 index 0000000..97fd33d --- /dev/null +++ b/src/certificate/pki/leaf-dfxagent-bsm-lab-postgres/dfxagent-bsm-lab-postgres-req.cnf @@ -0,0 +1,19 @@ +[ req ] +default_bits = 2048 +prompt = no +default_md = sha256 +distinguished_name = dn +req_extensions = req_ext + +[ dn ] +C = KR +O = KDN +OU = DFX +CN = agent-bsm-lab-postgres + +[ req_ext ] +subjectAltName = @alt_names + +[ alt_names ] +IP.1 = 192.168.0.41 +IP.2 = 172.22.1.4 diff --git a/src/certificate/pki/leaf-dfxagent-bsm-lab-postgres/dfxagent-bsm-lab-postgres.crt b/src/certificate/pki/leaf-dfxagent-bsm-lab-postgres/dfxagent-bsm-lab-postgres.crt new file mode 100644 index 0000000..1a1b561 --- /dev/null +++ b/src/certificate/pki/leaf-dfxagent-bsm-lab-postgres/dfxagent-bsm-lab-postgres.crt @@ -0,0 +1,28 @@ +-----BEGIN CERTIFICATE----- +MIIEtTCCAp2gAwIBAgIUdxabf8i0ErEOGJuXHDWwV95e5cQwDQYJKoZIhvcNAQEL +BQAwRTELMAkGA1UEBhMCS1IxEDAOBgNVBAoMB0JTTS1MQUIxJDAiBgNVBAMMG0JT +TS1MQUIgS0ROIEludGVybWVkaWF0ZSBDQTAeFw0yNTEyMTkwNjQ4MzlaFw0yODAz +MjMwNjQ4MzlaMEoxCzAJBgNVBAYTAktSMQwwCgYDVQQKDANLRE4xDDAKBgNVBAsM +A0RGWDEfMB0GA1UEAwwWYWdlbnQtYnNtLWxhYi1wb3N0Z3JlczCCASIwDQYJKoZI +hvcNAQEBBQADggEPADCCAQoCggEBAKu0KwbfwED1yF57MzVkcPR3sWWY44NxW4z3 +F3uGg5ToPBFep4PUOi7ut7R5Gqa26xbl/zfifE/kwBbIjzzWe/5xV3mclElw8ntl +eyUY3xQn1/vEqvui6Qu7x3P7nKTsn9+HYmbVicJzP9aHgOZJvjYxLsJXMm8ioG8a +6vCwZM3RjENc4Ymn+52BxEDiBv+TEOO/LXyhJ72TltPdYvXwyzv4n/lpYbSwh/Nm +GgRHJh/7C8vWhplWKfgX4Gg6JRXPWiVmuShVH2n53DlnApL8b2otc46GS6TybaWd +SIm/hR1PRVMc/AIdGaKJh85QhpYmmcsvEtRYia0bJwGW+YIN2W8CAwEAAaOBlzCB +lDAMBgNVHRMBAf8EAjAAMA4GA1UdDwEB/wQEAwIFoDAdBgNVHSUEFjAUBggrBgEF +BQcDAQYIKwYBBQUHAwIwHQYDVR0OBBYEFAXaov383xvuFINt9+udgBjL5nnUMB8G +A1UdIwQYMBaAFFlMwDCEbM1mlKMWE8D/6GMTOMUaMBUGA1UdEQQOMAyHBMCoACmH +BKwWAQQwDQYJKoZIhvcNAQELBQADggIBAFAkigkU6CcSKKvZgULf7ZdW1Xskc3Ux +d0KXH4kVt5PN0rhZz5QBxdJM3xdft0TdAixN/i1Hd7RjBAWuMzSveiBprYKahwbP +So6eqXYSFlMSPis2hMdy+OXb87zibdekzJ4VNG+CwDY6lvBxSY77rdZmoAxdu3GN +x/OfoVTZ/vBpnTDSeMi/Z/lAwvfwdvB5Ou0kQvxrdek/Yt8uu1/XDG76IKrimS1+ +z2aRSSTHPZJCYJb1goRDkhdpxkYXQTnm8D3/VFuZMtqxpIpESn/7OQu5st1SdR2r +FV8Y+h8f5NHfAz77bHAQqzPL4ahAHvFbm33fkEZ5/lkp5N3yj7JmEcjvenIXv39L +Grp0IVLDlOqIRC0454ZuQsA83InCgtdwHom2YVy/11HqP/QUBGA6yFPvmpq3OcTd +mP/6lySU+2JvDnfaHW7GTHVp4EXrngqKCbHY1WU9Of5zBRF4gvCmHPe1KxUDCbLS +aVmJqaZBcGFC8a1SHxIEHs7w9WOke78iLRqkkkTyfdkgztH8lXhlqQ2vdXH3myU5 +4ZoeR2wKksg0Pg/y6/DJxycoVu0BnTwX41de5WJtmEG8gB21P08+s15lJxU2f+ob +I/R1FPhjywq71iih55Cqn2Qw+NjX6lbBCQWKLctAjW10lhdWjZ3VmP0mE/vfuoc2 +U5L6dhQhwob/ +-----END CERTIFICATE----- diff --git a/src/certificate/pki/leaf-dfxagent-bsm-lab-postgres/dfxagent-bsm-lab-postgres.csr b/src/certificate/pki/leaf-dfxagent-bsm-lab-postgres/dfxagent-bsm-lab-postgres.csr new file mode 100644 index 0000000..bbc2e4e --- /dev/null +++ b/src/certificate/pki/leaf-dfxagent-bsm-lab-postgres/dfxagent-bsm-lab-postgres.csr @@ -0,0 +1,17 @@ +-----BEGIN CERTIFICATE REQUEST----- +MIICtzCCAZ8CAQAwSjELMAkGA1UEBhMCS1IxDDAKBgNVBAoMA0tETjEMMAoGA1UE +CwwDREZYMR8wHQYDVQQDDBZhZ2VudC1ic20tbGFiLXBvc3RncmVzMIIBIjANBgkq +hkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEAq7QrBt/AQPXIXnszNWRw9HexZZjjg3Fb +jPcXe4aDlOg8EV6ng9Q6Lu63tHkaprbrFuX/N+J8T+TAFsiPPNZ7/nFXeZyUSXDy +e2V7JRjfFCfX+8Sq+6LpC7vHc/ucpOyf34diZtWJwnM/1oeA5km+NjEuwlcybyKg +bxrq8LBkzdGMQ1zhiaf7nYHEQOIG/5MQ478tfKEnvZOW091i9fDLO/if+WlhtLCH +82YaBEcmH/sLy9aGmVYp+BfgaDolFc9aJWa5KFUfafncOWcCkvxvai1zjoZLpPJt +pZ1Iib+FHU9FUxz8Ah0ZoomHzlCGliaZyy8S1FiJrRsnAZb5gg3ZbwIDAQABoCgw +JgYJKoZIhvcNAQkOMRkwFzAVBgNVHREEDjAMhwTAqAAphwSsFgEEMA0GCSqGSIb3 +DQEBCwUAA4IBAQA2FZCgRR9mmnhGuDFNeQQBMguEWCV67LWYAEhJYEwte4DmKR6q +VACFU8qHYpXIuz0z3XJGj6h2GkdT7kizFXGgKNXsnH7Wn2a3NBZ+zTnUGpKGRiGl +XEGT9lPpY48tYDPJrk33nv9kRYiNL5ZMFoBJLZUPNZWr7pgxn6vtvB0oqUdibFxv +AqoKxH9K0lswA2ccejvt7u1faLh0dIDmD33xDgR4yTkqAdMxJGXFRGiG3+2X4ZRc +Iy1xvUhwYF1DyTpY0pHbjbtLTVIXt5nOv/h9BwHIMhxzQd08pHOcbAMN62dvB+Dc +bPTKplyGX3mdlyz8GhIIl9fYE7k48HOJEGBQ +-----END CERTIFICATE REQUEST----- diff --git a/src/certificate/pki/leaf-dfxagent-bsm-lab-postgres/dfxagent-bsm-lab-postgres.key b/src/certificate/pki/leaf-dfxagent-bsm-lab-postgres/dfxagent-bsm-lab-postgres.key new file mode 100644 index 0000000..496b1d7 --- /dev/null +++ b/src/certificate/pki/leaf-dfxagent-bsm-lab-postgres/dfxagent-bsm-lab-postgres.key @@ -0,0 +1,28 @@ +-----BEGIN PRIVATE KEY----- +MIIEvAIBADANBgkqhkiG9w0BAQEFAASCBKYwggSiAgEAAoIBAQCrtCsG38BA9che +ezM1ZHD0d7FlmOODcVuM9xd7hoOU6DwRXqeD1Dou7re0eRqmtusW5f834nxP5MAW +yI881nv+cVd5nJRJcPJ7ZXslGN8UJ9f7xKr7oukLu8dz+5yk7J/fh2Jm1YnCcz/W +h4DmSb42MS7CVzJvIqBvGurwsGTN0YxDXOGJp/udgcRA4gb/kxDjvy18oSe9k5bT +3WL18Ms7+J/5aWG0sIfzZhoERyYf+wvL1oaZVin4F+BoOiUVz1olZrkoVR9p+dw5 +ZwKS/G9qLXOOhkuk8m2lnUiJv4UdT0VTHPwCHRmiiYfOUIaWJpnLLxLUWImtGycB +lvmCDdlvAgMBAAECggEAB8TMGZQTqY6KpxdFCKbZTfEBVsPI6F6UwGV6h/Yj/Uo7 +U8AWSccB39AvOoplFb+CFmXroiLlVapJXxr4nz9HU1/4VulGnonSDQvnZepn1X4u +rd2jV0jksHP/IQafQhsLIvkynVtkQXxf4WNTBkMLrH2VaMzzo1UvojdJPHJrJhKH +xsc+wfWCo8S4C1InA9jJzsZ5DzBa/1eEsouLQguclIGggXdLlzUJcoVu7JWmkuiU +fELpm5qOkImdu4QbdHFzwm349egkjW9vxqzvue4sfU0T/Qjo03EtZ27rlACYxW82 +KSUybKf9Esx9BbswaOHhxSNpgSmUZGE36KYdldOmeQKBgQDa9dZ7Nh5HgDpzmPe/ +DCyhhBOhejPYSKyaLlCW8NYXtSm5isEzV0A4AD1s80z1GJcwDoezDkiwsZFOFXFH +Yr3tM3qXdXpC+Mz93hYySOYkqtZrzPeaQlywxq+e/ECcpqcazu96nxXnYjIxuc/+ +aM6Tpu6LDl+Y3d6HNvrIXZceQwKBgQDIv94xk+QrID2TUldNPIXhhDGqvDrzrkeS +f4XebOYZOkdw1ktZCbA4Rl3Wr/vnjuSFEtwISYLCMODpTtsarj3LHfTuzPn6fni/ +ZqjtFqtYYrZCu1S6C7QkBCYV47zxvxyn/wd4HSEBOOgCZz/f6k3d10sc93uLj+4Q +X4F0xCljZQKBgFIJ6Dmz9jZeAgiL2M95FUPTA7Pt4Hz6Bcmi7skPJXguhZqiNW8y +ErqoxFsM8dmnRAZae1eIU434ifPSruXLRlQYhKc4+f0b0VqRGonurGQyqjIr0t22 +XZpSZzzPULog6t1tiWbNMlzGev4Mm7S7uiKyWhA563GQN68710y5XESXAoGAb7EI +v87H4RK0D7Z9ajSlTH7PX24Q4qlxmtmmssUmFJ0vSGGCVIymZfkIlr0dS41eKYf/ +sgCsZrzpNgWwtByDtvH457BV2P/q0Jsem6LEPI3XWDOABW8jj/Ja+kzWQC6TlAi5 +sCOMzHBL7aJikIN9RVNWsEwlidXPn35zuXK2kF0CgYBGGPqAuO/o0zO+3eTItMSh +wS0B3l0GwywgvvDuss8OFoyFXiZmOblVy1csrUvzpt40+HP+glB2eLKXLP710++a +0TId02P5NO3PUog03tzLkSV23qXWTG6oBU7Kp99+4ZfVkeoqwc1vvuApqbDiri6S +lXZmpfShjr/B1anMzojuSA== +-----END PRIVATE KEY----- diff --git a/src/certificate/pki/leaf-dfxagent-bsm-lab-postgres/dfxagent-bsm-lab-postgres.p12 b/src/certificate/pki/leaf-dfxagent-bsm-lab-postgres/dfxagent-bsm-lab-postgres.p12 new file mode 100644 index 0000000000000000000000000000000000000000..420c52bb8a769edfe1645c75b7447b999e653848 GIT binary patch literal 5976 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 literal 0 HcmV?d00001