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]

... 46  47  48  49  50  51  52  53  [54]  55  56  57  58  59  60  ...
NoWriterDateCnt.TitleFile(s)
12270정성태7/16/202011333오류 유형: 633. SSL_CTX_use_certificate_file - error:140AB18F:SSL routines:SSL_CTX_use_certificate:ee key too small
12269정성태7/16/20208410오류 유형: 632. .NET Core 웹 응용 프로그램 - The process was terminated due to an unhandled exception.
12268정성태7/15/202010512오류 유형: 631. .NET Core 웹 응용 프로그램 오류 - HTTP Error 500.35 - ANCM Multiple In-Process Applications in same Process
12267정성태7/15/202012177.NET Framework: 927. C# - 윈도우 프로그램에서 Credential Manager를 이용한 보안 정보 저장파일 다운로드1
12266정성태7/14/20209829오류 유형: 630. 사용자 계정을 지정해 CreateService API로 서비스를 등록한 경우 "Error 1069: The service did not start due to a logon failure." 오류발생
12265정성태7/10/20209054오류 유형: 629. Visual Studio - 웹 애플리케이션 실행 시 "Unable to connect to web server 'IIS Express'." 오류 발생
12264정성태7/9/202018060오류 유형: 628. docker: Error response from daemon: Conflict. The container name "..." is already in use by container "...".
12261정성태7/9/202010976VS.NET IDE: 148. 윈도우 10에서 .NET Core 응용 프로그램을 리눅스 환경에서 실행하는 2가지 방법 - docker, WSL 2 [5]
12260정성태7/8/20209425.NET Framework: 926. C# - ETW를 이용한 ThreadPool 스레드 감시파일 다운로드1
12259정성태7/8/20208965오류 유형: 627. nvlddmkm.sys의 BAD_POOL_HEADER BSOD 문제 [1]
12258정성태7/8/202012130기타: 77. DataDog APM 간략 소개
12257정성태7/7/20209187.NET Framework: 925. C# - ETW를 이용한 Monitor Enter/Exit 감시파일 다운로드1
12256정성태7/7/20209598.NET Framework: 924. C# - Reflection으로 변경할 수 없는 readonly 정적 필드 [4]
12255정성태7/6/20209998.NET Framework: 923. C# - ETW(Event Tracing for Windows)를 이용한 Finalizer 실행 감시파일 다운로드1
12254정성태7/2/20209900오류 유형: 626. git - REMOTE HOST IDENTIFICATION HAS CHANGED!
12253정성태7/2/202010961.NET Framework: 922. C# - .NET ThreadPool의 Local/Global Queue파일 다운로드1
12252정성태7/2/202012887.NET Framework: 921. C# - I/O 스레드를 사용한 비동기 소켓 서버/클라이언트파일 다운로드2
12251정성태7/1/202010843.NET Framework: 920. C# - 파일의 비동기 처리 유무에 따른 스레드 상황 [1]파일 다운로드2
12250정성태6/30/202013490.NET Framework: 919. C# - 닷넷에서의 진정한 비동기 호출을 가능케 하는 I/O 스레드 사용법 [1]파일 다운로드1
12249정성태6/29/20209595오류 유형: 625. Microsoft SQL Server 2019 RC1 Setup - 설치 제거 시 Warning 26003 오류 발생
12248정성태6/29/20208050오류 유형: 624. SQL 서버 오류 - service-specific error code 17051
12247정성태6/29/20209579.NET Framework: 918. C# - 불린 형 상수를 반환값으로 포함하는 3항 연산자 사용 시 단축 표현 권장(IDE0075) [2]파일 다운로드1
12246정성태6/29/202010414.NET Framework: 917. C# - USB 관련 ETW(Event Tracing for Windows)를 이용한 키보드 입력을 감지하는 방법
12245정성태6/24/202010913.NET Framework: 916. C# - Task.Yield 사용법 (2) [2]파일 다운로드1
12244정성태6/24/202010656.NET Framework: 915. ETW(Event Tracing for Windows)를 이용한 닷넷 프로그램의 내부 이벤트 활용 [1]파일 다운로드1
12243정성태6/23/20208313VS.NET IDE: 147. Visual C++ 프로젝트 - .NET Core EXE를 "Debugger Type"으로 지원하는 기능 추가
... 46  47  48  49  50  51  52  53  [54]  55  56  57  58  59  60  ...