Microsoft MVP성태의 닷넷 이야기
글쓴 사람
정성태 (techsharer at outlook.com)
홈페이지
첨부 파일
(연관된 글이 2개 있습니다.)
(시리즈 글이 10개 있습니다.)
.NET Framework: 292. RSACryptoServiceProvider의 공개키와 개인키 구분
; https://www.sysnet.pe.kr/2/0/1218

.NET Framework: 327. RSAParameters와 System.Numerics.BigInteger 이야기
; https://www.sysnet.pe.kr/2/0/1295

.NET Framework: 329. C# - Rabin-Miller 소수 생성방법을 이용하여 RSACryptoServiceProvider 의 개인키를 직접 채워보자
; https://www.sysnet.pe.kr/2/0/1300

.NET Framework: 356. (공개키를 담은) 자바의 key 파일을 닷넷의 RSACryptoServiceProvider에서 사용하는 방법
; https://www.sysnet.pe.kr/2/0/1401

.NET Framework: 383. RSAParameters의 ToXmlString과 ExportParameters의 결과 비교
; https://www.sysnet.pe.kr/2/0/1491

.NET Framework: 565. C# - Rabin-Miller 소수 생성 방법을 이용하여 RSACryptoServiceProvider의 개인키를 직접 채워보자 - 두 번째 이야기
; https://www.sysnet.pe.kr/2/0/10925

.NET Framework: 566. openssl의 PEM 개인키 파일을 .NET RSACryptoServiceProvider에서 사용하는 방법
; https://www.sysnet.pe.kr/2/0/10926

.NET Framework: 638. RSAParameters와 RSA
; https://www.sysnet.pe.kr/2/0/11140

.NET Framework: 1037. openssl의 PEM 개인키 파일을 .NET RSACryptoServiceProvider에서 사용하는 방법 (2)
; https://www.sysnet.pe.kr/2/0/12598

.NET Framework: 2093. C# - PEM 파일을 이용한 RSA 개인키/공개키 설정 방법
; https://www.sysnet.pe.kr/2/0/13245




openssl의 PEM 개인키 파일을 .NET RSACryptoServiceProvider에서 사용하는 방법

키 파일의 형식이 참 다양합니다. ^^ 예전 글에서 다뤘던,

(공개키를 담은) 자바의 key 파일을 닷넷의 RSACryptoServiceProvider에서 사용하는 방법
; https://www.sysnet.pe.kr/2/0/1401

자바의 키 파일은 완전히 바이너리 형식이었고, openssl의 "genrsa" 옵션으로 생성하는 키 파일은 PEM 형식의 텍스트입니다. 한번 볼까요? ^^ 다음과 같이 명령행에서 "openssl genrsa [keysize]"와 같이 실행하면 화면에 PEM 형식의 개인키가 출력됩니다.

D:\Tools\cert\openssl\bin>openssl genrsa 4096
Loading 'screen' into random state - done
Generating RSA private key, 4096 bit long modulus
......++
.......................++
unable to write 'random state'
e is 65537 (0x10001)
-----BEGIN RSA PRIVATE KEY-----
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
-----END RSA PRIVATE KEY-----

그럼, "-----BEGIN RSA PRIVATE KEY-----"과 "-----END RSA PRIVATE KEY-----"을 포함한 그 사이의 출력 결과를 파일로 저장한 후 닷넷의 RSACryptoServiceProvider로 연결하면 됩니다.

문제는, 닷넷의 BCL에서는 PEM 파일을 읽는 기능이 없다는 것입니다. 따라서 이번에도 "(공개키를 담은) 자바의 key 파일을 닷넷의 RSACryptoServiceProvider에서 사용하는 방법" 글에 소개했던 "Bouncy Castle" 라이브러리를 이용해야 합니다.

그래도 ^^ 세상이 많이 좋아져서 Nuget 콘솔 창에서 다음과 같이 입력만 해주시면 다운로드 및 참조까지 다 되어 한방에 사용 준비가 됩니다.

PM> Install-Package BouncyCastle 

이제 PEM 키 파일 변환은 다음과 같이 해주시면 됩니다.

RSACryptoServiceProvider rsa = LoadKey("key.txt");

private static RSACryptoServiceProvider LoadKey(string keyFilePath)
{
    Org.BouncyCastle.OpenSsl.PemReader pem = new Org.BouncyCastle.OpenSsl.PemReader(File.OpenText(keyFilePath));
    Org.BouncyCastle.Crypto.AsymmetricCipherKeyPair rsaParameters = pem.ReadObject() as Org.BouncyCastle.Crypto.AsymmetricCipherKeyPair;
    Org.BouncyCastle.Crypto.Parameters.RsaPrivateCrtKeyParameters key = rsaParameters.Private as Org.BouncyCastle.Crypto.Parameters.RsaPrivateCrtKeyParameters;

    RSAParameters netrsa = DotNetUtilities.ToRSAParameters(key);
    RSACryptoServiceProvider rsa = new RSACryptoServiceProvider();
    rsa.ImportParameters(netrsa);

    return rsa;
}

정상적으로 로드되었다면 다음과 같은 암호화/복호화 테스트가 잘 수행이 됩니다. ^^

// RSACryptoServiceProvider로 암/복호화 테스트
UnicodeEncoding ByteConverter = new UnicodeEncoding();

byte[] dataToEncrypt = ByteConverter.GetBytes("Data to Encrypt");
byte[] encryptedData;
byte[] decryptedData;

encryptedData = RSAEncrypt(dataToEncrypt, rsa.ExportParameters(false), false);
decryptedData = RSADecrypt(encryptedData, rsa.ExportParameters(true), false);

Console.WriteLine("Decrypted plaintext: {0}", ByteConverter.GetString(decryptedData));

static public byte[] RSAEncrypt(byte[] DataToEncrypt, RSAParameters RSAKeyInfo, bool DoOAEPPadding)
{
    try
    {
        byte[] encryptedData;
        using (RSACryptoServiceProvider RSA = new RSACryptoServiceProvider())
        {
            RSA.ImportParameters(RSAKeyInfo);
            encryptedData = RSA.Encrypt(DataToEncrypt, DoOAEPPadding);
        }
        return encryptedData;
    }
    catch (CryptographicException e)
    {
        Console.WriteLine(e.Message);
        return null;
    }
}

static public byte[] RSADecrypt(byte[] DataToDecrypt, RSAParameters RSAKeyInfo, bool DoOAEPPadding)
{
    try
    {
        byte[] decryptedData;
        using (RSACryptoServiceProvider RSA = new RSACryptoServiceProvider())
        {
            RSA.ImportParameters(RSAKeyInfo);
            decryptedData = RSA.Decrypt(DataToDecrypt, DoOAEPPadding);
        }
        return decryptedData;
    }
    catch (CryptographicException e)
    {
        Console.WriteLine(e.ToString());
        return null;
    }
}

(첨부한 파일은 이 글의 예제 코드를 포함합니다.)

참고로, PEM 파일로부터 .pfx 파일 만드는 것과 Java .keystore 파일에 저장된 개인키가 pem 형식으로 나오는 등의 내용을 다음의 글에서 상세하게 보실 수 있습니다. ^^

.keystore 파일에 저장된 개인키 추출방법과 인증기관으로부터 온 공개키를 합친 pfx 파일 만드는 방법
; https://www.sysnet.pe.kr/2/0/1262

JKS(Java Key Store)에 저장된 인증서를 ActiveX 코드 서명에 사용하는 방법
; https://www.sysnet.pe.kr/2/0/882




[이 글에 대해서 여러분들과 의견을 공유하고 싶습니다. 틀리거나 미흡한 부분 또는 의문 사항이 있으시면 언제든 댓글 남겨주십시오.]

[연관 글]






[최초 등록일: ]
[최종 수정일: 4/14/2021]

Creative Commons License
이 저작물은 크리에이티브 커먼즈 코리아 저작자표시-비영리-변경금지 2.0 대한민국 라이센스에 따라 이용하실 수 있습니다.
by SeongTae Jeong, mailto:techsharer at outlook.com

비밀번호

댓글 작성자
 



2016-03-25 01시06분
[spowner] 어떤 일을 하시길래 정리하시는 내용들이 평범하지가 않네요 (사실 거의 대부분의 글들이 그렇습니다 TT). 키 관련 최근 포스팅 잘 보고 있습니다. 감사합니다
[guest]
2016-03-25 12시10분
그냥 이거저거 Q&A 하다 보니... 그렇게 된 것 같습니다. ^^
정성태
2018-08-27 12시50분
정성태
2022-01-11 09시32분
[apple] 일반적으로 RSA 은 암호화 복호화 키가 서로 달라야 하는데, 예제 상의 암, 복호하는 privete.txt 의 키만 가지고 수행하는데,
관련 설명을 요청드려도 될런지요.
[guest]
2022-01-11 09시58분
공개키의 정보는 개인키에 이미 포함되어 있습니다.

본문에서 LoadKey를 이용해 RSACryptoServiceProvider를 반환받은 후 ExportParameters 메서드를 호출하고 있는데요. ExportParameters의 인자로 true를 주면 개인키를 포함한 RSAParameters를 반환하고, false를 주면 개인키를 포함하지 않은, 즉 공개키를 반환합니다.

따라서, RSA 암호화 시에 원래는 ExportParameters(false)로 반환받은 공개키 정보를 상대방에게 전달하고 그쪽에서 공개키로 데이터를 암호화하면, 그 암호 데이터를 이쪽에서 가지고 있는 개인키로 복호화를 하는 것입니다.

(단지, 본문의 예제는 그것들을 별도의 프로젝트로 분리하지 않은 것입니다.)
정성태
2022-06-28 07시17분
[sheoito] 안녕하세요
PEM, PCKS8 format으로 된 key로 동작하는 예제를 요청드려도 될까요?
구글링으로 관련 내용 찾기가 어렵네요.
BouncyCastle은 PCKS1를 Default로 하는 것 같지만, Pkcs8EncryptedPrivateKeyInfo 라는 Class를 가지고 있는 것 보면, 지원을 하는 것 같긴 한데,,
이쪽으로는 지식이 거의 없어서 헤메는 중입니다;;
[guest]
2022-06-28 09시20분
이 글에 있는 내용이 그대로 적용이 될 텐데요. Org.BouncyCastle.OpenSsl.PemReader로 안 읽혀지던가요?
정성태
2022-06-29 09시50분
[sheoito] 네..
Org.BouncyCastle.Crypto.AsymmetricCipherKeyPair rsaParameters = pem.ReadObject() as Org.BouncyCastle.Crypto.AsymmetricCipherKeyPair;
이 구문에서 rsaParameters가 null이 됩니다.

개인키는 아래 구문으로 만들었습니다.
openssl genrsa -out rsaprivkey.pem 2048
openssl pkcs8 -topk8 -inform PEM -outform PEM -nocrypt -in rsaprivkey.pem -out rsaprivkey8.pem

웹 프로그램(ASP.NET MVC)에서 사용하고 있는데,
서버에서 복호화를 못하니, 급한대로 Client(임시 페이지)에 개인키를 넘겨주고 JSEncrypt library로 복호화 해서 다시 서버로 넘겨주고 있습니다;
임시 페이지가 짧게 지나가고, 복호화된 대칭키로 서버에서 실제 정보를 다시 복호화하는 과정이 있긴 하지만, 그래도 이건 아니지 싶습니다 ㅠㅜ
[guest]
2022-06-29 10시25분
본문의 예제는 openssl genrsa로 생성한 "-----BEGIN RSA PRIVATE KEY-----" / "-----END RSA PRIVATE KEY-----" PEM 파일에 대해서 적용할 수 있습니다.

그 파일을 pkcs8 PEM 형식으로 전환한 경우에는 그냥 다음과 같이 AsymmetricCipherKeyPair 절차 없이 곧바로 RsaPrivateCrtKeyParameters로 읽으면 됩니다.

-------------------------------------------
string keyFilePath = "rsaprivkey8.pem";

Org.BouncyCastle.OpenSsl.PemReader pem = new Org.BouncyCastle.OpenSsl.PemReader(File.OpenText(keyFilePath));
var key = pem.ReadObject() as Org.BouncyCastle.Crypto.Parameters.RsaPrivateCrtKeyParameters;
-------------------------------------------

그건 그렇고... (암호화 데이터의 보안 등급에 따라 다르겠지만) 개인키를 클라이언트로 보내 구현한 것은 단순히 급했다는 핑계로 넘어갈 수 있는 문제는 아닙니다.
정성태
2022-07-05 11시57분
[sheoito] 출장 때문에 답변 주신걸 이제야....
덕분에 해결했네요. 정말 감사합니다.

빨리 부끄러운 코드 수정하러 가야겠습니다. ;;
다시 한번 감사드립니다.
[guest]

1  2  3  4  5  6  7  8  9  10  [11]  12  13  14  15  ...
NoWriterDateCnt.TitleFile(s)
13347정성태5/10/20233929.NET Framework: 2118. C# - Semantic Kernel의 Prompt chaining 예제파일 다운로드1
13346정성태5/10/20233775오류 유형: 858. RDP 원격 환경과 로컬 PC 간의 Ctrl+C, Ctrl+V 복사가 안 되는 문제
13345정성태5/9/20235043.NET Framework: 2117. C# - (OpenAI 기반의) Microsoft Semantic Kernel을 이용한 자연어 처리 [1]파일 다운로드1
13344정성태5/9/20236322.NET Framework: 2116. C# - OpenAI API 사용 - 지원 모델 목록 [1]파일 다운로드1
13343정성태5/9/20234200디버깅 기술: 192. Windbg - Hyper-V VM으로 이더넷 원격 디버깅 연결하는 방법
13342정성태5/8/20234123.NET Framework: 2115. System.Text.Json의 역직렬화 시 필드/속성 주의
13341정성태5/8/20233909닷넷: 2114. C# 12 - 모든 형식의 별칭(Using aliases for any type)
13340정성태5/8/20233928오류 유형: 857. Microsoft.Data.SqlClient.SqlException - 0x80131904
13339정성태5/6/20234617닷넷: 2113. C# 12 - 기본 생성자(Primary Constructors)
13338정성태5/6/20234102닷넷: 2112. C# 12 - 기본 람다 매개 변수파일 다운로드1
13337정성태5/5/20234626Linux: 59. dockerfile - docker exec로 container에 접속 시 자동으로 실행되는 코드 적용
13336정성태5/4/20234382.NET Framework: 2111. C# - 바이너리 출력 디렉터리와 연관된 csproj 설정
13335정성태4/30/20234514.NET Framework: 2110. C# - FFmpeg.AutoGen 라이브러리를 이용한 기본 프로젝트 구성 - Windows Forms파일 다운로드1
13334정성태4/29/20234166Windows: 250. Win32 C/C++ - Modal 메시지 루프 내에서 SetWindowsHookEx를 이용한 Thread 메시지 처리 방법
13333정성태4/28/20233626Windows: 249. Win32 C/C++ - 대화창 템플릿을 런타임에 코딩해서 사용파일 다운로드1
13332정성태4/27/20233721Windows: 248. Win32 C/C++ - 대화창을 위한 메시지 루프 사용자 정의파일 다운로드1
13331정성태4/27/20233744오류 유형: 856. dockerfile - 구 버전의 .NET Core 이미지 사용 시 apt update 오류
13330정성태4/26/20233414Windows: 247. Win32 C/C++ - CS_GLOBALCLASS 설명
13329정성태4/24/20233622Windows: 246. Win32 C/C++ - 직접 띄운 대화창 템플릿을 위한 Modal 메시지 루프 생성파일 다운로드1
13328정성태4/19/20233259VS.NET IDE: 184. Visual Studio - Fine Code Coverage에서 동작하지 않는 Fake/Shim 테스트
13327정성태4/19/20233684VS.NET IDE: 183. C# - .NET Core/5+ 환경에서 Fakes를 이용한 단위 테스트 방법
13326정성태4/18/20235054.NET Framework: 2109. C# - 닷넷 응용 프로그램에서 SQLite 사용 (System.Data.SQLite) [1]파일 다운로드1
13325정성태4/18/20234402스크립트: 48. 파이썬 - PostgreSQL의 with 문을 사용한 경우 연결 개체 누수
13324정성태4/17/20234237.NET Framework: 2108. C# - Octave의 "save -binary ..."로 생성한 바이너리 파일 분석파일 다운로드1
13323정성태4/16/20234134개발 환경 구성: 677. Octave에서 Excel read/write를 위한 io 패키지 설치
13322정성태4/15/20234936VS.NET IDE: 182. Visual Studio - 32비트로만 빌드된 ActiveX와 작업해야 한다면?
1  2  3  4  5  6  7  8  9  10  [11]  12  13  14  15  ...