POSA_LEAKSMS/firmware/certs/gen_ca_header.ps1
유창욱 90f121e14c chore: import codebase with security hardening
SHT30 온습도 모니터링 시스템 전체 소스(서버 PHP, STM32 펌웨어, SQL, 테스트).
전체 코드리뷰에서 도출된 보안 하드닝 10건 반영:
- 요청 서명 HMAC-SHA256 전환(펌웨어 sig.c/서버 config.php/호스트 패리티 동시)
- 재전송 방어 + 기본 API_KEY fail-closed + 디바이스 문자열 정제(api/sensor_data.php)
- 오프라인 SMS 중복 발송 경합 제거(cron_heartbeat.php, 원자적 선점)
- CSV 수식 주입 방지(monthly_report.php), 감사로그 회전 락(retention_cleanup.php)
- 브루트포스 카운터 원자화(login.php), 예시 TOTP 비밀키 무효화, 마이그레이션 멱등화

_backup/(하드코딩 실 비밀값 포함)·config.local.php·런타임 상태는 .gitignore 제외.
2026-06-20 09:37:40 +09:00

113 lines
4.3 KiB
PowerShell

<#
.SYNOPSIS
PEM 인증서 파일로부터 firmware/certs/server_ca.c 를 재생성한다.
.DESCRIPTION
Cafe24 서버 루트 CA(필요 시 intermediate+root 를 연결한 체인) PEM 을 읽어,
mbedTLS 가 그대로 파싱할 수 있는 C 문자열 리터럴 형태로 server_ca.c 에 임베드한다.
- 각 PEM 라인을 "....\n" C 리터럴로 변환(끝의 종결 NUL 은 sizeof 에 포함).
- SERVER_CA_PEM_LEN = sizeof(SERVER_CA_PEM) 로 정의(NUL 포함 → mbedTLS 요구).
- 폐쇄망: 이 스크립트는 *빌드 머신*에서만 실행. 타깃은 결과 .c 만 컴파일.
.PARAMETER PemPath
입력 PEM 파일 경로(.pem/.crt). 하나 이상의 -----BEGIN CERTIFICATE----- 블록 포함.
.PARAMETER OutPath
출력 C 파일 경로. 기본값: 이 스크립트와 같은 폴더의 server_ca.c
.EXAMPLE
./gen_ca_header.ps1 -PemPath cafe24_root.pem
.EXAMPLE
# 체인 추출 → 헤더 생성 (한 번에)
openssl s_client -showcerts -connect your-domain.example:443 -servername your-domain.example </dev/null `
| openssl x509 -out cafe24_root.pem
./gen_ca_header.ps1 -PemPath cafe24_root.pem
.NOTES
위험 R3(docs/stm32f407_migration_plan.md): 인증서 만료 시 펌웨어 재빌드/재배포 필요.
#>
[CmdletBinding()]
param(
[Parameter(Mandatory = $true, Position = 0)]
[string]$PemPath,
[Parameter(Position = 1)]
[string]$OutPath = (Join-Path $PSScriptRoot 'server_ca.c')
)
$ErrorActionPreference = 'Stop'
# --- 입력 검증 ----------------------------------------------------------------
if (-not (Test-Path -LiteralPath $PemPath)) {
throw "PEM 파일을 찾을 수 없습니다: $PemPath"
}
# CRLF/CR 을 LF 로 정규화하고 라인 분해. 빈 끝줄은 제거.
$raw = Get-Content -LiteralPath $PemPath -Raw
$normalized = $raw -replace "`r`n", "`n" -replace "`r", "`n"
$lines = $normalized -split "`n"
# 최소한 BEGIN/END CERTIFICATE 마커가 있어야 한다.
if (-not ($normalized -match '-----BEGIN CERTIFICATE-----')) {
throw "유효한 PEM 인증서 블록(-----BEGIN CERTIFICATE-----)이 없습니다: $PemPath"
}
if (-not ($normalized -match '-----END CERTIFICATE-----')) {
throw "PEM 종료 마커(-----END CERTIFICATE-----)가 없습니다: $PemPath"
}
# 인증서 블록 개수(intermediate+root 연결이면 2개 이상).
$certCount = ([regex]::Matches($normalized, '-----BEGIN CERTIFICATE-----')).Count
# --- C 문자열 리터럴로 변환 ---------------------------------------------------
# 후행 빈 라인 제거(마지막 -----END----- 뒤의 공백 라인).
$body = New-Object System.Collections.Generic.List[string]
foreach ($line in $lines) {
$body.Add($line) | Out-Null
}
while ($body.Count -gt 0 -and [string]::IsNullOrEmpty($body[$body.Count - 1])) {
$body.RemoveAt($body.Count - 1)
}
# 각 라인을 C escape: 백슬래시/큰따옴표 이스케이프 후 "...\n" 형태로.
$escapedLines = foreach ($line in $body) {
$esc = $line -replace '\\', '\\' -replace '"', '\"'
' "' + $esc + '\n"'
}
$generatedAt = (Get-Date).ToString('yyyy-MM-dd HH:mm:ss zzz')
$pemFileName = Split-Path -Leaf $PemPath
$header = @"
/* =============================================================================
* server_ca.c - CA (PEM, flash ) [ ]
*
* gen_ca_header.ps1 . .
* source PEM : $pemFileName
* : $generatedAt
* : $certCount
*
* mbedTLS PEM : SERVER_CA_PEM_LEN NUL
* sizeof(SERVER_CA_PEM) .
* R3: / + .
* ===========================================================================*/
#include "server_ca.h"
const char SERVER_CA_PEM[] =
"@
$footer = @"
;
/* : NUL (mbedTLS PEM ). */
const unsigned int SERVER_CA_PEM_LEN = sizeof(SERVER_CA_PEM);
"@
$content = $header + "`n" + ($escapedLines -join "`n") + $footer + "`n"
# UTF-8 (BOM 없음) 로 기록 — arm-none-eabi-gcc 가 깔끔히 읽도록.
$utf8NoBom = New-Object System.Text.UTF8Encoding($false)
[System.IO.File]::WriteAllText($OutPath, $content, $utf8NoBom)
Write-Host "[OK] $OutPath 생성 완료 (인증서 블록 $certCount 개, source: $pemFileName)"