콘텐츠로 이동
getnextpdf.com

Enterprise 에디션

HSM 서명 — 심층 참조

이 페이지는 NextPDF Enterprise HSM 서명 표면에 대한 심층 레퍼런스입니다. 세 개의 공개 타입을 다룹니다. NextPDF\Enterprise\Security\Signature\Hsm\Pkcs11Signerext-pkcs11 확장을 통해 PKCS#11 토큰으로 서명합니다. NextPDF\Enterprise\Security\Signature\Hsm\OpenSslCliSigner는 PHP ext-openssl이 로드할 수 없는 provider 또는 engine 기반 키를 위해 서브프로세스에서 openssl 바이너리를 통해 서명합니다. NextPDF\Enterprise\Security\Signature\Hsm\Provider\HsmSignerProviderAdapter는 두 구현체 중 하나를 통합된 SignerProviderInterface로 노출합니다. 모든 경로에서 개인 키는 토큰 경계 안에 머무릅니다. NextPDF는 서명할 바이트를 넘기고 서명을 돌려받습니다. 포스트 퀀텀 경로(signPqs)는 미리보기입니다. 기본적으로 비활성화되어 있고, 어떠한 적합성 주장도 하지 않으며, 현재 PDF 검증기에서 지원되는 검증 경로가 없습니다. NextPDF는 어떠한 인증도 보유하지 않으며 부여하지도 않습니다. 지원은 적합성과 같지 않고, 적합성은 인증과 같지 않습니다.

이 기능은 NextPDF Enterprise(nextpdf/enterprise)에 포함되어 있으며 Enterprise 등급 라이선스 봉투로 활성화됩니다. 해당 권한이 없는 배포는 이 기능의 클래스를 로드하지 않습니다. 에디션을 비교하고 라이선스를 받으세요.

세 타입 모두 NextPDF\Enterprise\Security\Signature\Hsm에 있으며, 어댑터는 그 하위 네임스페이스 Provider에 위치합니다. 두 서명자 모두 Core NextPDF\Contracts\HsmSignerInterface 계약을 구현합니다.

SymbolParametersDefault behaviorReturnsThrows or fails withNotes
Pkcs11Signer::__construct()string $libraryPath, int $slotId, string $pin, string $certLabel, ?string $keyLabel = null, array $chainDer = [], bool $enablePostQuantum = false, ?FipsSignatureEnforcer $fipsEnforcer = null벤더 라이브러리를 열고, 슬롯에 로그인하며, 토큰에서 인증서와 키 알고리즘 메타데이터를 로드합니다ext-pkcs11가 없거나 토큰 접근이 실패하면 HsmOperationException라이브러리 경로별로 프로세스당 하나의 모듈 핸들이 캐시됩니다. PIN과 레이블은 #[SensitiveParameter]입니다
Pkcs11Signer::sign()string $data, string $algorithm = 'sha256WithRSAEncryption'토큰에서 서명하며, 원시 ECDSA 출력은 DER ECDSA-Sig-Value로 변환됩니다string 원시 서명 바이트HsmOperationException(키 미발견, 토큰 실패); InvalidArgumentException(매핑되지 않은 알고리즘); enforcer가 연결된 경우 서명 전 FIPS 게이트 예외닫힌 알고리즘 집합. Behavior contract 참조
Pkcs11Signer::signPqs()string $data, Pkcs11PqsAlgorithm $algorithm, string $context = '', bool $randomized = true$enablePostQuantum가 설정되지 않으면 거부되며, 잠정적인 PKCS#11 PQ 메커니즘으로 디스패치합니다string 원시 서명 바이트HsmOperationException(비활성화, 토큰 실패, 서명 길이 불일치); InvalidArgumentException(255바이트 초과 컨텍스트)미리보기. 적합성 주장 없음. 메커니즘 식별자는 잠정적입니다
Pkcs11Signer::isPostQuantumEnabled()없음생성자의 opt-in 플래그를 보고합니다bool없음
Pkcs11Signer::getCertificateDer()없음토큰에서 읽은 서명자 인증서를 반환합니다string (DER)없음생성 시 한 번 로드됩니다
Pkcs11Signer::getCertificateChainDer()없음생성자가 제공한 중간 인증서를 반환합니다array<string> (DER)없음서명자 인증서는 제외됩니다
OpenSslCliSigner::__construct()string $keyUri, string $certPath, string $pin, array $extraCertPaths = [], OpenSslCliBackend $backend = OpenSslCliBackend::Auto, string $opensslBinary = 'openssl', int $timeoutSeconds = 30, ?string $modulePath = null, ?string $configPath = null, bool $legacyPinDelivery = false, ?FipsSignatureEnforcer $fipsEnforcer = nullproc_open을 검증하고, 바이너리와 버전을 탐지하며, 백엔드를 해석하고, 인증서를 로드합니다HsmOperationException(proc_open 비활성화, 모듈/설정/인증서 파일 누락, 바이너리 실패, 백엔드 없음); InvalidArgumentException($keyUri 내부의 pin-value)OpenSslCliBackend::Auto는 OpenSSL 3.x provider를 먼저 선호한 다음 engine을 선호합니다
OpenSslCliSigner::sign()string $data, string $algorithm = 'sha256WithRSAEncryption'서브프로세스에서 openssl dgst를 실행하며, 기본적으로 PIN은 임시 0600 pin-source 파일을 통해 전달됩니다string 원시 서명 바이트HsmOperationException(타임아웃, PIN 거부, 키 미발견, 모듈 로드 실패, 빈 출력, pin-file 실패); InvalidArgumentException(매핑되지 않은 알고리즘); 서명 전 FIPS 게이트 예외서브프로세스는 $timeoutSeconds 후에 종료됩니다. stderr는 메시지에 도달하기 전에 편집됩니다
OpenSslCliSigner accessor surface없음읽기 전용 생성 결과string / array<string> / OpenSslCliBackend없음getCertificateDer, getCertificateChainDer, getPublicKeyAlgorithm, getCertificatePem, getResolvedBackend, getOpensslVersion
HsmSignerProviderAdapter::__construct()HsmSignerInterface $hsm, string $providerId, SignatureAlgorithm $algorithm = SignatureAlgorithm::Pkcs1v15HSM 구현체를 SignerProviderInterface로 래핑합니다없음Provider id 규칙: pkcs11-{module-id}, openssl-cli
HsmSignerProviderAdapter::providerId()없음생성자가 제공한 id를 반환합니다non-empty-string없음
HsmSignerProviderAdapter::supportsAlgorithm()SignatureAlgorithm $algo열거형을 OpenSSL 스타일 이름으로 매핑한 다음, 백엔드 허용 집합과 교집합을 취합니다bool없음다이제스트 전용 알고리즘은 거부합니다. openssl-engine id는 아무것도 광고하지 않습니다
HsmSignerProviderAdapter::sign()string $data, ?string $keyVersion = null구성된 알고리즘으로 래핑된 서명자를 통해 디스패치합니다non-empty-stringKeyManagementException(non-null $keyVersion); SignatureFailedException(매핑 불가 알고리즘, 드라이버 실패, 빈 서명)Fail-closed SPI 계약. 모든 드라이버 오류가 타입으로 표면화됩니다
public function __construct(private readonly string $libraryPath, private readonly int $slotId, #[SensitiveParameter] private readonly string $pin, #[SensitiveParameter] private readonly string $certLabel, #[SensitiveParameter] private readonly ?string $keyLabel = null, array $chainDer = [], private readonly bool $enablePostQuantum = false, ?FipsSignatureEnforcer $fipsEnforcer = null)
public function sign(string $data, string $algorithm = 'sha256WithRSAEncryption'): string
public function signPqs(string $data, Pkcs11PqsAlgorithm $algorithm, string $context = '', bool $randomized = true): string
public function isPostQuantumEnabled(): bool
public function getCertificateDer(): string
public function getCertificateChainDer(): array
public function __construct(private string $keyUri, string $certPath, #[SensitiveParameter] private string $pin, array $extraCertPaths = [], private OpenSslCliBackend $backend = OpenSslCliBackend::Auto, private string $opensslBinary = 'openssl', private int $timeoutSeconds = 30, private ?string $modulePath = null, private ?string $configPath = null, private bool $legacyPinDelivery = false, private ?FipsSignatureEnforcer $fipsEnforcer = null)
public function sign(string $data, string $algorithm = 'sha256WithRSAEncryption'): string
public function getCertificateDer(): string
public function getCertificateChainDer(): array
public function getPublicKeyAlgorithm(): string
public function getCertificatePem(): string
public function getResolvedBackend(): OpenSslCliBackend
public function getOpensslVersion(): string
public function __construct(private HsmSignerInterface $hsm, private string $providerId, private SignatureAlgorithm $algorithm = SignatureAlgorithm::Pkcs1v15)
public function providerId(): string
public function supportsAlgorithm(SignatureAlgorithm $algo): bool
public function sign(string $data, ?string $keyVersion = null): string
  • 키 보관. 개인 키는 토큰 경계를 절대 벗어나지 않습니다. Pkcs11Signer는 작업을 토큰에 위임합니다. OpenSslCliSigner는 키 참조 — PKCS#11 URI — 를 openssl 서브프로세스에 전달합니다. 두 서명자 모두 키를 내보낼 수 없습니다.
  • 세션과 로그인. Pkcs11Signer는 라이브러리 경로별로 프로세스당 하나의 PKCS#11 모듈 핸들을 캐시합니다. 토큰 인터페이스는 프로세스당 정확히 한 번 초기화되어야 하기 때문입니다. 각 작업은 세션을 열고 PIN으로 로그인합니다. 로그인은 개인 키 사용 전에 사용자를 인증합니다(PKCS#11 v3.1 §5.6.8). 슬롯이 기존 로그인을 보고하면 서명자는 로그아웃한 다음 다시 로그인하므로, 작업마다 새 PIN을 요구하는 토큰도 새 PIN을 받습니다.
  • 알고리즘 집합(닫힘). 두 서명자 모두 정확히 다음을 받아들입니다: sha256WithRSAEncryption, sha384WithRSAEncryption, sha512WithRSAEncryption; RSASSA-PSS, RSASSA-PSS-SHA256, RSASSA-PSS-SHA384, RSASSA-PSS-SHA512; ecdsa-with-SHA256, ecdsa-with-SHA384, ecdsa-with-SHA512. Pkcs11Signer는 추가로 ecdsa-raw를 받아들입니다. 다른 식별자는 InvalidArgumentException을 일으킵니다 — 대체 알고리즘은 결코 서명되지 않습니다.
  • PSS salt 바인딩. 모든 PSS 변형에서 salt 길이는 다이제스트 길이와 같습니다 — 32, 48, 또는 64바이트 — 그리고 hash와 MGF 파라미터는 선택된 다이제스트와 일치합니다. 이는 salt 길이가 일반적으로 메시지 해시 길이인 PSS 메커니즘 파라미터 구조를 따릅니다(PKCS#11 v3.1 §6.1.9). 두 서명자 모두 동일한 쌍을 적용하므로, 한 백엔드에서 유효한 구성은 다른 백엔드에서도 유효합니다.
  • ECDSA 변환. 토큰은 ECDSA 서명을 rs의 원시 zero-padded 연결로 반환합니다(PKCS#11 v3.1 §6.3.1). Pkcs11Signer::sign()은 그 출력을 PDF 검증기와 OpenSSL이 기대하는 DER 인코딩 ECDSA-Sig-Value 형식으로 변환합니다. 호출자는 원시 형식을 결코 다루지 않습니다.
  • PIN 전달(CLI 경로). 안전한 기본값에서는 PIN이 소유자 전용 권한으로 배타적으로 생성된 임시 파일에 기록되고, PKCS#11 URI pin-source 속성을 통해 참조되며, 서브프로세스 종료 후 링크가 해제됩니다. 이 모드에서는 PIN이 명령줄에 배치되지 않고 서브프로세스 환경으로 내보내지지 않습니다. $legacyPinDelivery = true이면 PIN이 URI에 pin-value로 포함되며, 이는 프로세스 명령줄에서 관찰 가능합니다. 이 모드는 opt-in 전용입니다.
  • 서브프로세스 규율. OpenSslCliSigner는 인수 배열로 바이너리를 스폰하고 — 셸 보간 없음 — $timeoutSeconds를 강제하며, 만료 시 서브프로세스를 종료하고, stderr를 타입 오류로 분류합니다. 비밀은 예외 메시지에 인용되기 전에 stderr에서 편집됩니다.
  • 어댑터 의미론. HSM 토큰에는 관리되는 키 버전 개념이 없습니다. 토큰의 키가 곧 버전입니다. 따라서 HsmSignerProviderAdapter::sign()은 null이 아닌 $keyVersion을 무시하는 대신 KeyManagementException으로 거부합니다. supportsAlgorithm()은 열거형 매핑과 래핑된 백엔드의 허용 집합의 교집합을 취하므로, 어댑터는 백엔드가 서명 시점에 거부할 메커니즘을 결코 광고하지 않습니다. 드라이버로부터의 빈 서명은 SignatureFailedException을 일으킵니다.
  • 포스트 퀀텀 미리보기. signPqs()$enablePostQuantum 생성자 플래그 뒤에 게이팅되며 그 외에는 실행을 거부합니다. 컨텍스트 문자열은 255바이트로 제한되며, 이는 ML-DSA 컨텍스트 한계와 일치합니다(FIPS 204). 반환된 서명은 선택된 Pkcs11PqsAlgorithm 파라미터 세트의 정확한 바이트 길이와 일치해야 하며, 그렇지 않으면 호출이 실패합니다. 메커니즘 식별자는 잠정적인 PKCS#11 PQ 확장을 따르며 최종이 아닙니다. PAdES 프로파일은 포스트 퀀텀 스위트를 인식하지 못하고, 대부분의 PDF 검증기는 그러한 서명을 거부하며, NextPDF는 이에 대한 검증 경로를 제공하지 않습니다. 어떠한 적합성도 주장하지 않습니다.
  • ext-pkcs11 없이 Pkcs11Signer를 생성하면 즉시 HsmOperationException을 일으킵니다. 이 확장은 표준 PHP 배포판에 번들되지 않습니다.
  • 토큰의 어떤 객체와도 일치하지 않는 인증서 또는 개인 키 레이블은 누락된 객체 클래스를 지목하는 HsmOperationException을 일으킵니다. 일부 토큰에서는 키 레이블이 인증서 레이블과 정당하게 다를 수 있습니다.
  • 반복된 로그인 실패는 토큰에서 PIN을 잠글 수 있습니다. 그 정책을 강제하는 것은 NextPDF가 아니라 토큰입니다. 사용할 때마다 인증이 필요한 키를 가진 토큰은 로그아웃-후-재시도 경로를 통해 새 로그인을 받습니다(PKCS#11 v3.1, always-authenticate 의미론).
  • OpenSslCliSigner는 생성 시 이미 pin-value를 포함하는 $keyUri를 fail-closed로 거부합니다. 그러한 전달은 안전한 PIN 경로를 우회하기 때문입니다.
  • Windows에서는 안전한 pin-file 모드가 HsmOperationException으로 fail-closed됩니다. 그곳에서는 파일 권한 비트가 ACL 읽기 권한을 제한할 수 없으므로, 서명자는 temp 디렉터리 ACL에 평문 PIN을 남기기를 거부합니다. 레거시 PIN 전달은 신뢰할 수 있는 Windows 호스트를 위한 문서화된 opt-in 대안입니다.
  • 백엔드 자동 탐지는 provider 경로에 OpenSSL 3.x를 요구합니다. LibreSSL은 결코 provider로 해석되지 않습니다. provider도 engine 탐지도 성공하지 못하면, 실패를 서명 시점으로 미루는 대신 생성이 HsmOperationException으로 실패합니다.
  • $timeoutSeconds를 초과하는 서브프로세스는 종료되고 타임아웃으로 보고됩니다. 빈 출력으로 깨끗하게 종료된 서브프로세스는 빈 서명 실패로 보고됩니다. 어느 조건도 부분적으로 서명된 문서를 생성할 수 없습니다.
  • 바이트 길이가 선택된 파라미터 세트와 일치하지 않는 포스트 퀀텀 서명은 CMS 인코딩에 도달하기 전에 거부됩니다.
  • 폐기된 openssl-engine provider id를 가진 HsmSignerProviderAdapter는 어떠한 알고리즘도 광고하지 않으므로, 오래된 구성은 서명 시점이 아니라 provider 선택 시점에 실패합니다.

두 서명자 모두 선택적 FipsSignatureEnforcer를 받아들입니다. 하나가 연결되면 해당 서명자에 대해 FIPS 모드가 활성화됩니다: sign()은 어떤 토큰이나 서브프로세스 서명이 발생하기 전에 허용되지 않는 서명 알고리즘이나 하한 미달 키를 거부합니다. 하한은 서명 생성 표를 따릅니다 — 2048비트 미만의 RSA 모듈러스와 224비트 미만의 ECDSA order는 허용되지 않습니다(NIST SP 800-131A Rev.2 §3 Table 2). enforcer가 없으면 동작은 변경되지 않습니다. 게이트는 전통적 sign() 경로만 다룹니다. signPqs()는 자체 미리보기 플래그로 관리됩니다. 이것들은 NextPDF 코드에 대한 기능 주장입니다: FIPS 140-3 검증은 CMVP를 통해 암호화 모듈에 부착되며, 이 배포에서 그것은 운영자의 HSM 또는 provider입니다 — NextPDF는 검증된 모듈이 아니고, 어떠한 인증도 보유하지 않으며, 부여하지도 않습니다.

ClaimStandardClause
로그인은 개인 키 작업 전에 사용자를 토큰에 인증하며, 잘못된 PIN은 접근을 거부합니다.PKCS#11 v3.1§5.6.8
Always-authenticate 키는 사용마다 새 로그인이 필요하며, 반복된 재인증 실패는 PIN을 잠글 수 있습니다.PKCS#11 v3.1CKA_ALWAYS_AUTHENTICATE re-authentication
토큰 ECDSA 서명은 원시 r‖s 연결이며, 서명자는 이를 PDF 상호운용을 위해 DER로 변환합니다.PKCS#11 v3.1§6.3.1
PSS 파라미터는 hash, MGF, salt 길이를 바인딩하며, 서명자는 salt를 다이제스트 길이와 같게 설정합니다.PKCS#11 v3.1§6.1.9
FIPS 게이트는 2048비트 미만의 RSA 또는 224비트 미만의 ECDSA order로 서명 생성을 거부합니다.NIST SP 800-131A Rev.2§3 Table 2
포스트 퀀텀 컨텍스트 문자열은 255바이트로 제한됩니다.FIPS 204HashML-DSA context handling
FIPS 140-3 검증은 CMVP를 통해 암호화 모듈에 부착됩니다.FIPS 140-3CMVP program scope

모든 조항은 의역되었으며, 규범 텍스트는 재현되지 않습니다. NextPDF는 어떠한 인증 주장도 하지 않습니다. 서명자는 자신의 동작을 인용된 조항에 하나의 기능으로서 정렬합니다. 생성된 서명이 검증되는지 여부는 자체 신뢰 앵커에 대한 검증기의 결정입니다. 키 보안은 토큰, HSM, 운영자에 달려 있으며 — NextPDF만으로 결정되지 않습니다.

  • PIN 전달 메커니즘은 PKCS#11 URI pin-source 규칙(RFC 7512)을 따릅니다. 그 RFC는 인용 코퍼스 밖에 있으므로, 위 동작은 스펙 인용이 아니라 제품 소스에서 근거합니다.

  • Pkcs11Signer를 생성하기 전에 런타임이 ext-pkcs11을 로드하는지 확인하세요. 확장이 없으면 생성이 빠르게 실패합니다. CLI 서명자는 proc_open이 활성화되어 있고 PKCS#11 provider 또는 engine이 설치된 openssl 바이너리가 필요합니다.

  • PIN, 인증서 레이블, 키 레이블은 #[SensitiveParameter]이므로 스택 트레이스에서 제외됩니다. PIN은 시크릿 매니저에서 공급하세요. 소스, 버전 관리에 커밋된 구성, 또는 로그에 절대 기록하지 마세요.

  • 생성은 두 서명자 모두에서 비용이 큰 단계입니다: PKCS#11 경로는 로그인하고 인증서를 읽으며, CLI 경로는 바이너리와 백엔드를 탐지합니다. 한 번 생성하고 인스턴스를 재사용하세요. 라이브러리별 모듈 캐시가 동일한 라이브러리에 대한 반복 생성을 안전하게 만듭니다.

  • 호출자가 SignerProviderInterface를 통해 작동할 때 서명자를 HsmSignerProviderAdapter로 래핑하세요. 래핑된 클래스에 대한 표준 provider id — pkcs11-{module-id} 또는 openssl-cli — 를 전달하여 기능 검사가 올바른 백엔드 허용 집합을 사용하도록 하세요.

  • 포스트 퀀텀 미리보기를 활성화하기 전에, NextPDF가 등록하는 잠정 값에 대해 토큰 펌웨어의 메커니즘 식별자를 확인하세요. 불일치는 서명 시점에 실패합니다. 프로덕션 PAdES 출력을 위해 미리보기를 활성화하지 마세요.

  • getResolvedBackend()getOpensslVersion()은 증거 기록을 위해 존재합니다. 컴플라이언스 프로그램이 재현성을 요구할 때 서명 증거와 함께 이들을 영속화하세요.

이 페이지는 외부에서 관찰 가능한 동작과 지원되는 공개 API 표면만 문서화합니다. 내부 네임스페이스 경로, 헬퍼 클래스, 메커니즘 테이블, 런북 파일명, 티켓 접두사는 범위 밖입니다.