Delphi의 포스트 양자 암호화

· 컴포넌트
Delphi의 포스트 양자 암호화

오늘 여러분의 애플리케이션이 다루는 데이터에 대해 물어볼 가치가 있는 질문이 있습니다. 그 데이터는 얼마나 오랫동안 민감한 정보로 남아 있을까요? 의료 기록, 계약서, 급여 파일, 인증 정보 등이 그 예입니다. 답이 몇 년 이상이라면, 진짜 주목해야 할 위협은 지금 당장 암호를 깨뜨리려는 공격자가 아닙니다. 지금 트래픽을 기록해 저장해 두었다가, 도구가 따라잡은 미래에 복호화하는 공격자입니다. 이러한 방식에는 harvest now, decrypt later라는 이름이 붙어 있으며, 오늘 당장 누군가 양자 컴퓨터를 만들 필요조차 없습니다. 누군가 언젠가 만들 것이라고 믿게 만드는 것만으로 충분합니다.

그래서 표준이 먼저 움직였습니다. NIST는 2024년에 알고리즘을 발표했고, 2025년 동안 브라우저와 CDN은 하이브리드 키 교환을 활성화했으며, RSA와 타원곡선 키 합의를 폐지하는 기한도 이제 공개 가이드라인에 명시되어 있습니다. sgcWebSockets 2026.10은 이 모든 것을 Delphi에 도입합니다. sgcCrypto 팩 안에 Object Pascal로 작성되어 있으며, 설치하거나 배포해야 할 외부 라이브러리는 없습니다.

3개의 알고리즘, 하나의 팩

이 세 가지 NIST 표준은 서로 다른 역할을 하며, 보통은 앞의 두 가지를 사용하게 됩니다.

ML-KEM으로 키 합의하기

키 캡슐화 메커니즘은 Diffie-Hellman보다 사용하기 간단합니다. 송신자는 수신자의 공개키를 사용해 새로운 공유 비밀과 그것을 전달하는 암호문, 두 가지를 생성합니다. 수신자는 그 암호문을 같은 비밀로 다시 변환합니다.

uses
  sgcCrypto_MLKEM;

var
  oPublicKey, oPrivateKey, oCiphertext, oSecret, oSame: TBytes;
begin
  sgcMLKEM_GenerateKeyPair(mlkem768, oPublicKey, oPrivateKey);

  // the sender, who only ever sees the public key
  sgcMLKEM_Encapsulate(mlkem768, oPublicKey, oCiphertext, oSecret);

  // the recipient, who holds the private key, gets the same 32 bytes
  oSame := sgcMLKEM_Decapsulate(mlkem768, oPrivateKey, oCiphertext);
end;

매개변수 집합은 mlkem512, mlkem768, mlkem1024입니다. FIPS 203이 요구하는 입력 검사가 다른 무엇보다 먼저 실행되므로, 형식이 잘못된 공개키는 조용히 비밀을 생성하는 대신 예외를 발생시킵니다. 또한 복호화되지 않는 암호문은 오류 대신 의사난수 비밀을 반환하는데, 이는 표준이 요구하는 암묵적 거부입니다.

선택하고 싶지 않을 때, X-Wing

새로운 알고리즘을 채택한다는 것은 그것을 신뢰한다는 뜻입니다. 하이브리드 구성은 그 결정을 없애 줍니다. 고전 알고리즘과 포스트 양자 알고리즘을 결합함으로써, 둘 중 어느 한쪽만 안전하면 결과 전체가 안전해지기 때문입니다. X-Wing은 X25519와 ML-KEM-768을 결합해 하나의 키 쌍으로 제공합니다.

uses
  sgcCrypto_MLKEM_Hybrid;

var
  oPublicKey, oPrivateKey, oCiphertext, oSecret: TBytes;
begin
  sgcXWing_GenerateKeyPair(oPublicKey, oPrivateKey);
  sgcXWing_Encapsulate(oPublicKey, oCiphertext, oSecret);
  oSecret := sgcXWing_Decapsulate(oPrivateKey, oCiphertext);
end;

ML-DSA로 서명하기

서명에는 매개변수 집합, 개인키, 메시지, 그리고 보통은 비어 있는 컨텍스트 문자열이 필요합니다.

uses
  sgcCrypto_MLDSA;

var
  oSeed, oPublicKey, oPrivateKey, oSignature: TBytes;
begin
  sgcMLDSA_GenerateKeyPairAndSeed(mldsa65, oSeed, oPublicKey, oPrivateKey);

  oSignature := sgcMLDSA_Sign(mldsa65, oPrivateKey, aMessage, nil);

  if sgcMLDSA_Verify(mldsa65, oPublicKey, aMessage, oSignature, nil) then
    ShowMessage('signature is valid');
end;

시드에 주목하세요. ML-DSA 개인키는 생성 원본인 32바이트 시드로 저장할 수도 있고, 확장된 키로 저장할 수도 있으며, 둘 다로 저장할 수도 있습니다. 이 라이브러리는 세 가지 형태를 모두 읽고 쓸 수 있습니다. 설정 파일에는 시드 형태가 적합합니다. 크기가 작고, 확장된 키는 시드로부터 결정론적으로 유도되기 때문입니다.

이동 가능한 키

아무와도 키를 교환할 수 없는 알고리즘은 그다지 쓸모가 없습니다. 그래서 인코딩은 공개된 프로필을 따릅니다. SubjectPublicKeyInfo와 PKCS#8을 DER 또는 PEM 형식으로, ML-KEM은 RFC 9935, ML-DSA는 RFC 9881, SLH-DSA는 RFC 9909에 따라 인코딩합니다.

var
  vPublicPEM, vPrivatePEM: string;
begin
  vPublicPEM := sgcMLDSA_ExportPublicKeyPEM(mldsa65, oPublicKey);
  vPrivatePEM := sgcMLDSA_ExportPrivateKeyPEM(mldsa65, oSeed, nil);
end;

이는 다른 구현체에서도 읽을 수 있는 일반적인 BEGIN PUBLIC KEYBEGIN PRIVATE KEY 블록입니다. 개인키는 가져올 때 검증됩니다. 공개키 부분을 다시 유도해 비교하므로, 인증서와 일치하지 않는 키는 아무도 검증할 수 없는 서명을 만들어내기 전에 그 자리에서 거부됩니다.

인증서와 포스트 양자 CA

인증서와 인증서 요청은 포스트 양자 키를 담을 수 있으며, 인증 기관 역시 마찬가지이므로 체인 전체를 포스트 양자로 구성할 수 있습니다.

var
  vKey: TsgcX509SignKey;
  vOptions: TsgcX509Options;
  oCertDER: TBytes;
begin
  vKey := sgcX509_MLDSAKey(mldsa65, oPrivateKey, oPublicKey);
  vOptions.Subject.CommonName := 'My Post-Quantum CA';
  vOptions.KeyUsage := [kuKeyCertSign, kuCRLSign];
  oCertDER := sgcX509_CreateSelfSignedEx(vKey, vOptions);
end;

프로필의 키 사용 규칙은 서명이 이루어지기 전에 적용되므로, 키 암호화 용도를 요청받은 포스트 양자 키는 자신의 프로필을 위반하는 인증서를 만드는 대신 예외를 발생시킵니다. 체인 검증은 이제 발급 인증서가 선언한 경로 길이와 이름 제약도 적용합니다.

이미 적용되어 있는 곳

라이브러리 안의 두 곳에서는 이 모든 것을 이미 사용하고 있어, 직접 암호화 코드를 작성할 필요가 없습니다.

정확성을 어떻게 확인하는가

표준의 구현은 무언가가 검증하기 전까지는 하나의 주장에 불과합니다. 세 가지 알고리즘 모두 NIST 기지답 벡터로 검증되며, 이 벡터들은 데이터시트의 문장이 아니라 실행 가능한 테스트 세트로 라이브러리에 포함되어 있습니다. ML-DSA 서명기는 결정론적 방식이므로, 같은 키와 같은 메시지는 항상 같은 서명을 만들어내며, 이 덕분에 RFC에 실린 예제를 바이트 단위로 재현할 수 있습니다.

어떤 것을 사용해야 할까

키 합의에는 하이브리드를 사용하세요. 단독으로는 X-Wing, 트래픽이 TLS라면 하이브리드 TLS 그룹입니다. 거의 아무것도 포기하지 않으면서, 어느 쪽 가정이 틀리더라도 대비할 수 있습니다. 서명의 경우 ML-DSA-65가 무난한 기본값이며, 크기가 중요할 때는 ML-DSA-44를, 그렇지 않을 때는 ML-DSA-87을 사용합니다. 펌웨어처럼 업데이트할 수 없는 대상이 먼 훗날 서명을 검증해야 하고 서명 크기를 감수할 수 있다면 SLH-DSA를 선택하세요.

업그레이드

이 모든 기능은 sgcCrypto 팩 안에 있으며 어떠한 의존성도 추가하지 않습니다. OpenSSL도, DLL도 필요 없고, 애플리케이션을 실행하는 컴퓨터에 설치할 것도 없습니다. 기존 코드는 호출하기 전까지는 그대로 유지됩니다.

다음 읽을거리

영상으로 보기

eSeGeCe 채널에 이 내용을 담은 짧은 영상이 있습니다.

질문이나 피드백, 마이그레이션 지원이 필요하신가요? 문의하기—코드를 직접 작성한 사람에게서 답변을 받으실 수 있습니다.