# 서버 루트 CA 인증서 (`certs/`) STM32F407 펌웨어는 폐쇄망(air-gapped)에서 동작하므로 런타임에 CA 인증서를 다운로드할 수 없다. 따라서 서버(Cafe24) TLS 검증에 사용할 **루트 CA PEM 을 flash 에 임베드**한다(`server_ca.c`). mbedTLS(`tls_mbedtls.c`)가 부팅 시 `mbedtls_x509_crt_parse()` 로 이 PEM 을 파싱하고, 매 핸드셰이크마다 서버 인증서 체인을 검증한다. | 파일 | 역할 | |------|------| | `server_ca.h` | `SERVER_CA_PEM[]` / `SERVER_CA_PEM_LEN` 선언 | | `server_ca.c` | PEM 본문(현재 **PLACEHOLDER**, 반드시 교체) | | `gen_ca_header.ps1` | PEM 파일 → `server_ca.c` 재생성 스크립트 | > ⚠️ **현재 `server_ca.c` 는 자리표시자(PLACEHOLDER)다.** 컴파일은 되지만 실제 > 서버 인증서 검증은 실패한다. 배포 전 반드시 실제 Cafe24 루트 CA 로 교체할 것. --- ## 1. 서버 인증서 체인 추출 배포 대상 호스트(`app_config.h` 의 `APP_API_HOST`, 기본 `your-domain.example`)에 대해 **빌드 머신에서** 다음을 실행한다. (`-servername` 으로 SNI 지정 필수 — 가상호스팅 환경에서 올바른 인증서를 받기 위함.) ```sh HOST=your-domain.example # = APP_API_HOST 와 동일하게 # 전체 체인(leaf + intermediate + root) 보기 openssl s_client -showcerts -connect "$HOST:443" -servername "$HOST" chain_all.pem # chain_all.pem 에서 intermediate + root 를 골라 cafe24_root.pem 으로 연결. # (leaf[0] 는 제외. intermediate/root 만 trust 파일에 둔다.) # - 가장 확실: CA 벤더 공식 사이트에서 root/intermediate PEM 다운로드 후 cat 으로 연결 cat intermediate.pem root.pem > cafe24_root.pem ``` > 팁: root 만으로 검증이 되면 root 한 장이면 충분하다. intermediate 까지 넣으면 > 서버가 intermediate 를 안 보내는 경우에도 검증이 안정적이다(여분은 무해). --- ## 3. 체인 / cipher 사전 검증 (위험 R3 사전조사) 펌웨어에 넣기 전에, 추출한 trust PEM 으로 **검증이 실제로 통과**하는지, 그리고 mbedTLS 클라이언트가 쓸 **TLS1.2 ECDHE-RSA-AES-GCM** 을 서버가 받아주는지 확인한다. ```sh # (a) 우리 trust PEM 으로 체인 검증 통과 여부 openssl s_client -connect "$HOST:443" -servername "$HOST" \ -CAfile cafe24_root.pem /dev/null \ | grep -E "Verify return code|Verification" # 기대: "Verify return code: 0 (ok)" # (b) mbedTLS 가 협상할 cipher/프로토콜 강제 확인 (TLS1.2 + ECDHE-RSA-AES-GCM) openssl s_client -connect "$HOST:443" -servername "$HOST" \ -tls1_2 -cipher 'ECDHE-RSA-AES128-GCM-SHA256:ECDHE-RSA-AES256-GCM-SHA384' \ -CAfile cafe24_root.pem /dev/null \ | grep -E "Protocol|Cipher|Verify return code" # 기대: Protocol = TLSv1.2, Cipher = ECDHE-RSA-AES(128|256)-GCM-... # (c) 인증서 만료일 확인 (leaf + CA) openssl s_client -connect "$HOST:443" -servername "$HOST" /dev/null \ | openssl x509 -noout -dates -subject -issuer openssl x509 -in cafe24_root.pem -noout -dates -subject ``` 펌웨어 `mbedtls_config.h` 가 ECDHE-RSA + AES-GCM(TLS1.2)만 켜므로, 위 (b) 가 실패하면(서버가 해당 cipher 미지원) **TLS 연결 불가**다 → 서버 측 cipher 설정을 맞추거나 `mbedtls_config.h` 의 허용 cipher/곡선을 조정해야 한다. --- ## 4. `server_ca.c` 재생성 검증을 통과한 `cafe24_root.pem` 으로 C 소스를 재생성한다(빌드 머신, PowerShell). ```powershell # firmware/certs 디렉터리에서 ./gen_ca_header.ps1 -PemPath cafe24_root.pem # 또는 출력 경로 지정: ./gen_ca_header.ps1 -PemPath C:\path\cafe24_root.pem -OutPath .\server_ca.c ``` 스크립트는 PEM 각 라인을 `"...\n"` C 리터럴로 변환하고, `SERVER_CA_PEM_LEN = sizeof(SERVER_CA_PEM)` 로 정의한다(종결 NUL 포함 — mbedTLS PEM 파싱 요구사항). 생성 후 재빌드하면 새 CA 가 적용된다. 생성 결과를 빠르게 sanity-check: ```sh # 생성된 .c 안의 PEM 이 다시 openssl 로 파싱되는지(따옴표/escape 깨짐 검출용) grep -oE 'BEGIN CERTIFICATE|END CERTIFICATE' server_ca.c ``` --- ## 5. 만료 → 펌웨어 업데이트 (위험 R3) 폐쇄망이라 인증서를 OTA 로 갱신할 수 없다. 임베드한 root/intermediate CA 가 **만료**되거나 서버가 **발급기관/체인을 교체**하면 TLS 핸드셰이크가 실패하고 보고가 중단된다. 대응: 1. **만료일 모니터링**: 위 3-(c) 로 CA `notAfter` 를 기록·달력 알림 설정. 2. **사전 교체**: 만료 1~3개월 전 새 CA 로 `gen_ca_header.ps1` 재생성 → 재빌드. 3. **재배포 절차**: 폐쇄망 반입 절차(USB/오프라인 번들)로 새 `.bin/.hex` 플래싱(`sht30_fw` 단일 타깃). `firmware/VERSION` 갱신. 4. **여유 신뢰 앵커**: 가능하면 차기 root 까지 함께 임베드(여러 CA 연결)하여 교체 윈도를 넓힌다(여분 CA 는 검증에 무해). > 참고: `docs/stm32f407_migration_plan.md` 위험표 **R3** — > "Cafe24 인증서 cipher/체인/만료 → TLS 연결 불가 → 사전 `openssl s_client` 로 > 체인·cipher 확인, 루트 CA 임베드, 만료 시 펌웨어 업데이트 절차".