C # 애플리케이션에서 SecureString이 실용적입니까?


224

여기에 내 가정이 틀렸다면 언제든지 바로 고치십시오. 그러나 내가 왜 묻는 지 설명하겠습니다.

MSDN에서 가져온 SecureString:

기밀로 유지해야하는 텍스트를 나타냅니다. 텍스트는 사용시 개인 정보 보호를 위해 암호화되며 더 이상 필요하지 않은 경우 컴퓨터 메모리에서 삭제됩니다.

나는 이것을 얻습니다. 암호 또는 다른 개인 정보를 a SecureString이상 에 저장하는 것이 합리적입니다 System.String. 왜냐하면 실제로 메모리에 저장되는 방법과시기를 제어 할 수 있기 때문입니다 System.String.

불변이며 더 이상 필요하지 않은 경우 프로그래밍 방식으로 가비지 수집을 예약 할 수 없습니다. 즉, 인스턴스는 작성된 후 읽기 전용이며 인스턴스가 컴퓨터 메모리에서 삭제 될시기를 예측할 수 없습니다. 따라서 String 개체에 암호, 신용 카드 번호 또는 개인 데이터와 같은 중요한 정보가 포함 된 경우, 응용 프로그램이 컴퓨터 메모리에서 데이터를 삭제할 수 없으므로 사용 후 정보가 노출 될 위험이 있습니다.

그러나 GUI 응용 프로그램 (예 : ssh 클라이언트)의 경우 SecureString 는에서 빌드해야합니다 System.String . 모든 텍스트 컨트롤 은 기본 데이터 형식으로 문자열을 사용합니다 .

따라서 사용자가 키를 누를 때마다 기존의 문자열이 삭제되고 암호 마스크를 사용하더라도 텍스트 상자 내부의 값을 나타내는 새 문자열이 작성됩니다. 그리고 우리는 그러한 값이 언제 메모리에서 버려지는지 제어 할 수 없습니다 .

이제 서버에 로그인 할 차례입니다. 뭔지 맞춰봐? 인증을 위해 연결을 통해 문자열을 전달해야합니다 . 이제 우리 SecureStringSystem.String... 로 변환합시다 . 이제 우리는 힙에 가비지 수집을 거치지 않고 (또는 버퍼에 0을 쓰도록) 문자열을 가지고 있습니다.

내 요점은 : 당신이 상관없이, 선을 따라 어딘가에 SecureString되어 가고는 로 변환 할 System.String그것을 의미, 어떤 점에서 힙에 이상 존재에서 (가비지 컬렉션의 보증없이) 것입니다.

내 요점은 : ssh 연결에 문자열을 보내는 것을 우회하는 방법이 있는지 또는 컨트롤에 문자열을 저장하는 것을 우회하는 방법이 있는지 여부입니다 (사용자 정의 컨트롤 만들기). 이 질문에 대해 "ssh 연결"을 "로그인 양식", "등록 양식", "지급 양식", "음식-당신은-먹이-당신의 강아지-하지만 당신의 자녀가 아닌 양식", 기타

  • 그렇다면 어느 시점에서 SecureString실제로 사용하는 것이 실용적이됩니까?
  • System.String객체 의 사용을 완전히 근절하는 데 추가 개발 시간이 가치가 있습니까?
  • 힙에 SecureString걸리는 시간을 단순히 줄이는 것이 핵심 System.String입니까 (실제 스왑 파일로 이동할 위험을 줄입니까)?
  • 공격자는 이미 힙 검사하기위한 수단을 가지고 있다면 그 가능성 중 하나 (A)는 이미 이미 키 입력, 또는 (B)를 읽을 수있는 수단이 물리적으로 기계했다 ... 그래서 사용 것이 SecureString받는 점점에서 그를 방지를 어쨌든 데이터?
  • 이것은 단지 "불분명을 통한 보안"입니까?

질문을 너무 두껍게하면 호기심이 더 좋아졌습니다. 내 질문의 일부 또는 전부에 자유롭게 대답하십시오 (또는 내 가정이 완전히 잘못되었다고 말하십시오). :)


25
명심 SecureString정말 보안 문자열이 아닙니다. 누군가가 메모리를 검사하고 중요한 데이터를 성공적으로 얻을 수있는 시간을 줄이는 방법 일뿐입니다. 이것은 방탄이 아니며 의도 된 것이 아닙니다. 그러나 당신이 올리는 포인트는 매우 유효합니다. 관련 : stackoverflow.com/questions/14449579/…
Theodoros Chatzigiannakis 8

1
@TheodorosChatzigiannakis, 예, 그것은 내가 생각한 것입니다. 나는 오늘 하루 종일 응용 프로그램의 수명 동안 암호를 저장하는 안전한 방법을 찾으려고 내 두뇌를 쌓아 놓았습니다.
Steven Jeffries 8

1
아마 가치가 없을 것입니다. 그러나 다시 한 번 극단적 인 시나리오를 방어 할 수 있습니다. 누군가가 귀하의 컴퓨터에 이러한 종류의 액세스 권한을 얻을 가능성이 없다는 의미는 아니지만, 누군가가 이러한 종류의 액세스 권한을 얻는 경우 컴퓨터는 (모든 의도와 목적을 위해) 타협 된 것으로 간주되며 ' t 이것을 완전히 방어하기 위해 사용할 수있는 언어 나 기술이 없다고 생각하십시오.
Theodoros Chatzigiannakis 8

8
그것은 볼 수있는 암호에 대해 보호 모두가 사고가 문제가있을 수 있습니다 중지 긴 후. 폐기 된 하드 드라이브에서 페이징 파일에 저장됩니다. 메모리를 스크러빙하여이 확률이 낮을수록 중요합니다. 불변이므로 System.String을 사용하여이를 수행 할 수 없습니다. Marshal.SecureStringXxx () 메소드가 유용하기 때문에 요즘에는 소수의 프로그램에 액세스 할 수있는 기본 코드와 상호 작용할 수있는 인프라가 필요합니다. 또는 사용자에게 프롬프트를 표시하는 적절한 방법 :)
Hans Passant

1
SecureString은 보안 심층 기술입니다. 대부분의 상황에서 개발 비용과 보안 이득 간의 균형은 바람직하지 않습니다. 좋은 사용 사례는 거의 없습니다.
usr

답변:


237

실제로 매우 실용적인 용도가 SecureString있습니다.

그런 시나리오를 몇 번이나 봤는지 아십니까? (답은 : 많은 것입니다) :

  • 실수로 비밀번호가 로그 파일에 나타납니다.
  • GUI가 실행중인 응용 프로그램의 명령 줄을 표시하면 명령 줄은 암호로 구성됩니다. 죄송합니다 .
  • 메모리 프로파일 러를 사용하여 동료와 소프트웨어 프로파일 링 동료가 메모리에서 비밀번호를 확인합니다. 비현실적인 소리? 전혀.
  • 한때 RedGate예외가 발생했을 때 로컬 변수의 "값"을 포착 할 수있는 소프트웨어를 사용 했는데 놀랍게도 유용했습니다. 그러나 실수로 "문자열 암호"를 기록 할 것이라고 생각할 수 있습니다.
  • 문자열 비밀번호가 포함 된 크래시 덤프.

이러한 모든 문제를 피하는 방법을 알고 있습니까? SecureString. 일반적으로 바보 같은 실수를하지 않도록합니다. 그것을 피하는 방법은 무엇입니까? 암호가 관리되지 않는 메모리에 암호화되어 있는지 확인하고 실제 값은 수행중인 작업을 90 % 확신 할 때만 액세스 할 수 있습니다.

의미에서, SecureString매우 쉽게 작동합니다.

1) 모든 것이 암호화됩니다

2) 사용자 전화 AppendChar

3) 관리되지 않는 메모리의 모든 것을 해독하고 캐릭터를 추가하십시오.

4) UNMANAGED MEMORY에서 모든 것을 다시 암호화하십시오.

사용자가 컴퓨터에 액세스 할 수 있으면 어떻게됩니까? 바이러스가 모든 바이러스에 접근 할 수 SecureStrings있습니까? 예. RtlEncryptMemory메모리를 해독 할 때 자신을 연결 하기 만하면 암호화되지 않은 메모리 주소의 위치를 ​​파악하여 읽을 수 있습니다. 짜잔! 실제로, 바이러스의 사용을 지속적으로 스캔 SecureString하고 모든 활동을 기록 하는 바이러스를 만들 수 있습니다. 나는 그것이 쉬운 일이 될 것이라고 말하지는 않지만 할 수 있습니다. 보시다시피 SecureString시스템에 사용자 / 바이러스가 있으면 "강력" 이 완전히 사라집니다.

게시물에 몇 가지 요점이 있습니다. 내부적으로 "문자열 암호"를 보유한 일부 UI 컨트롤을 사용하는 경우 실제를 사용 SecureString하는 것이 그렇게 유용하지 않습니다. 그래도 여전히 위에 나열된 어리 석음으로부터 보호 할 수 있습니다.

또한 다른 사람들이 지적했듯이 WPF는 SecurePassword 속성을SecureString 통해 내부적 으로 사용되는 PasswordBox를 지원 합니다.

결론은 다음과 같습니다. 민감한 데이터 (암호, 신용 카드, ..)가있는 경우을 사용하십시오 SecureString. 이것이 C # Framework가 따르는 것입니다. 예를 들어, NetworkCredentialclass는 password를로 저장합니다 SecureString. 이것을 보면 .NET 프레임 워크에서 ~ 80 가지가 넘는 다른 사용법을 볼 수 있습니다 SecureString.

SecureString일부 API에서는 문자열 을 변환해야하기 때문에 문자열 로 변환해야하는 경우가 많습니다 .

일반적인 문제는 다음 중 하나입니다.

  1. API는 GENERIC입니다. 민감한 데이터가 있다는 것을 모릅니다.
  2. API는 민감한 데이터를 처리하고 "문자열"을 사용한다는 것을 알고 있습니다. 이는 나쁜 설계입니다.

당신은 좋은 점을 제기했습니다 : SecureString로 변환 되면 어떻게됩니까 string? 이것은 첫 번째 지점에서만 발생할 수 있습니다. 예를 들어 API는 민감한 데이터임을 알지 못합니다. 나는 그런 일을 개인적으로 보지 못했습니다. SecureString에서 문자열을 가져 오는 것은 그렇게 간단하지 않습니다.

간단한 이유로 간단하지 않습니다 . 사용자가 SecureString을 문자열로 변환하도록 의도하지 않았습니다 .GC가 시작됩니다. 왜?

내가 본 흥미로운 사건이 하나 있습니다. 즉, WinApi 기능 LogonUser는 LPTSTR을 암호로 사용하므로을 호출해야합니다 SecureStringToGlobalAllocUnicode. 기본적으로 관리되지 않는 메모리에있는 암호화되지 않은 암호를 제공합니다. 완료하자마자이를 제거해야합니다.

// Marshal the SecureString to unmanaged memory.
IntPtr rawPassword = Marshal.SecureStringToGlobalAllocUnicode(password);
try
{
   //...snip...
}
finally 
{
   // Zero-out and free the unmanaged string reference.
   Marshal.ZeroFreeGlobalAllocUnicode(rawPassword);
}

서버의 공개 키를 사용하여 암호화 된 인스턴스 를 제공하는 SecureString등의 확장 메서드를 사용하여 클래스를 항상 확장 할 수 있습니다 . 그런 다음 서버 만 암호를 해독 할 수 있습니다. 문제 해결 : 가비지 수집은 관리되는 메모리에 노출되지 않으므로 "원본"문자열을 볼 수 없습니다. 이것은 정확히 ( ) 에서 수행되고있는 것입니다 .ToEncryptedString(__SERVER__PUBLIC_KEY)stringSecureStringPSRemotingCryptoHelperEncryptSecureStringCore(SecureString secureString)

그리고 거의 관련이 있습니다 : Mono SecureString은 전혀 암호화하지 않습니다 . ..wait 그것을 위해 .. 때문에 구현이 주석하고있다 "그것은 어떻게 든 NUNIT 테스트 파손의 원인" 내 마지막 지점으로 제공합니다 :

SecureString모든 곳에서 지원되지는 않습니다. 플랫폼 / 아키텍처가를 지원하지 않으면 SecureString예외가 발생합니다. 설명서에서 지원되는 플랫폼 목록이 있습니다.


개별 문자에 액세스하는 메커니즘이 있다면 SecureString훨씬 실용적 이라고 생각 합니다. 이러한 메커니즘의 효율성은 SecureStringTempBuffer공용 멤버가없는 구조체 유형을 사용하여 향상시킬 수 있습니다.이 형식 refSecureString"한 문자 읽기"방법 으로 전달 될 수 있습니다 ( 암호화 / 암호 해독이 8 자 청크로 처리되는 경우 이러한 구조는 데이터를 보유 할 수 있음) 8 자 및 그 내의 문자의 처음의 위치 SecureString].
supercat

2
나는 내가 수행하는 모든 소스 코드 감사에서 이것을 표시합니다. 또한 사용하는 경우 SecureString스택 전체에 걸쳐 사용해야한다는 점을 반복 해야합니다.
Casey

1
인증서를 사용하여 .ToEncryptedString () 확장을 구현했습니다. 이걸 잘못하고 있는지 알려 주시겠습니까? 안전한 engouhg gist.github.com/sturlath/99cfbfff26e0ebd12ea73316d0843330
Sturla

@Sturla — EngineSettings확장 방법에는 무엇이 있습니까?
InteXX


15

가정에 몇 가지 문제가 있습니다.

우선 모든 SecureString 클래스에는 String 생성자가 없습니다. 객체를 만들려면 객체를 할당 한 다음 문자를 추가하십시오.

GUI 또는 콘솔의 경우, 누른 각 키를 보안 문자열에 매우 쉽게 전달할 수 있습니다.

이 클래스는 실수로 저장된 값에 액세스 할 수 없도록 설계되었습니다. 즉 string, 비밀번호를 직접 비밀번호로 얻을 수 없습니다 .

예를 들어 웹을 통해 인증하기 위해이를 사용하려면 안전한 적절한 클래스를 사용해야합니다.

.NET 프레임 워크에는 SecureString을 사용할 수있는 몇 가지 클래스가 있습니다.

  • WPF의 PasswordBox 컨트롤은 암호를 내부적으로 SecureString으로 유지합니다.
  • System.Diagnostics.ProcessInfo의 Password 속성은 SecureString입니다.
  • X509Certificate2의 생성자는 비밀번호에 SecureString을 사용합니다.

(더)

결론적으로 SecureString 클래스는 유용 할 수 있지만 개발자의주의가 더 필요합니다.

이 모든 것은 예제와 함께 MSDN의 SecureString 설명서에 잘 설명되어 있습니다.


10
나는 당신의 대답의 마지막 세 번째 단락을 이해하지 못합니다. 네트워크를 통해 직렬화하지 않고 어떻게SecureString 네트워크를 통해 전송 string합니까? 문제의 OP의 요점은 실제로 안전하게 유지 된 값을 사용 하고 싶을 때가 있다는 것입니다. 즉, 값을 가져 와야하며 SecureString더 이상 안전하지 않습니다. 모든 프레임 워크 클래스 라이브러리에는 실제로 SecureString입력으로 직접 입력 할 수있는 메소드가 없으므로 (내가 볼 수있는 한) SecureString먼저 안에 값을 보유하는 요점은 무엇 입니까?
stakx-더 이상

@ DamianLeszczyński-Vash, SecureString에는 문자열 생성자가 없다는 것을 알고 있지만 요점은 (A) SecureString을 만들고 각 문자열을 문자열에서 추가하거나 (B) 어느 시점에서 사용해야합니다. 일부 API에 전달하기 위해 SecureString에 문자열로 저장된 값 즉, 당신은 몇 가지 좋은 점을 제시하고 매우 구체적인 경우에 그것이 어떻게 유용 할 수 있는지 알 수 있습니다.
Steven Jeffries

1
@stakx 프로세스를 통해 가비지 수집 가능한 개체를 만들지 않고 소켓을 통해 char[]각각 을 가져 와서 네트워크를 통해 보냅니다 char.
user253751

Char []는 수집 가능하고 변경 가능하므로 덮어 쓸 수 있습니다.
피터 리치

물론 해당 배열을 덮어 쓰는 것도 잊어 버리기 쉽습니다. 어느 쪽이든, 문자를 전달하는 API도 사본을 만들 수 있습니다.
cHao

10

다음과 같은 경우 SecureString이 유용합니다.

  • 문자별로 (예 : 콘솔 입력) 빌드하거나 관리되지 않는 API에서 가져옵니다

  • 관리되지 않는 API (SecureStringToBSTR)에 전달하여 사용합니다.

관리되는 문자열로 변환하면 그 목적을 달성 한 것입니다.

의견에 대한 답변으로 업데이트

... 또는 당신이 언급 한 것처럼 더 안전하지 않은 BSTR

BSTR로 변환 된 후 BSTR을 사용하는 관리되지 않는 구성 요소는 메모리를 0으로 만들 수 있습니다. 관리되지 않는 메모리는 이러한 방식으로 재설정 할 수 있다는 점에서 더 안전합니다.

그러나 .NET Framework에는 SecureString을 지원하는 API가 거의 없으므로 오늘날 가치가 매우 제한적이라고 말할 수 있습니다.

내가 볼 수있는 주요 사용 사례는 사용자가 매우 민감한 코드 또는 암호를 입력 해야하는 클라이언트 응용 프로그램입니다. 사용자 입력을 문자별로 사용하여 SecureString을 빌드 한 다음 관리되지 않는 API로 전달하여 사용 후 수신 한 BSTR을 0으로 만들 수 있습니다. 후속 메모리 덤프에는 민감한 문자열이 포함되지 않습니다.

서버 응용 프로그램에서는 그것이 유용한 위치를 찾기가 어렵습니다.

업데이트 2

SecureString을 허용하는 .NET API의 한 가지 예 는 X509Certificate 클래스에 대한이 생성자입니다 . ILSpy 또는 이와 유사한 기능을 사용하는 경우 SecureString이 내부적으로 관리되지 않는 버퍼 ( Marshal.SecureStringToGlobalAllocUnicode) 로 변환 된 다음 ( Marshal.ZeroFreeGlobalAllocUnicode)로 끝나면 0이됩니다 .


1
+1, 그러나 항상 "목적을 훼손"하지는 않습니까? Framework 클래스 라이브러리에서 거의 모든 메소드가 SecureString입력을 허용하지 않는다면 어떻게 사용합니까? 항상 string조만간 다시 변환해야합니다 (또는 BSTR더 안전하지 않은 언급). 그래서 왜 SecureString전혀 귀찮게 합니까?
stakx-더 이상

5

SecureString최신 코드 에는 사용하지 않는 것이 좋습니다 .

SecureString 클래스의 문서에서 :

중대한

SecureString새로운 개발에 클래스를 사용하지 않는 것이 좋습니다 . 자세한 내용 은 사용해서는 안되는 것을 참조하십시오SecureString

어떤 것이 좋습니다 :

사용하지 마십시오 SecureString새로운 코드 . 코드를 .NET Core로 이식 할 때 배열의 내용이 메모리에 암호화되어 있지 않은 것을 고려하십시오.

자격 증명을 처리하는 일반적인 방법은 자격 증명을 피하고 대신 인증서 또는 Windows 인증과 같은 다른 인증 방법을 사용하는 것입니다. GitHub에서.


4

정확하게 식별했듯이 결정 론적 소거에 SecureString비해 하나의 특별한 이점이 string있습니다. 이 사실에는 두 가지 문제가 있습니다.

  1. 다른 사람들이 언급했듯이, 당신이 스스로 알아 차린 것처럼, 이것만으로는 충분하지 않습니다. 프로세스의 모든 단계 (입력 검색, 문자열 구성, 사용법, 삭제, 운송 등 포함)가 사용 목적을 상실하지 않고 발생하는지 확인해야합니다 SecureString. 즉, GC 관리 불변 string또는 민감한 정보를 저장할 다른 버퍼를 만들지 않도록주의해야합니다 (또는 그 정보도 추적 해야 함). 실제로 많은 API가 string,SecureString . 그리고 모든 것을 올바르게 관리하더라도 ...
  2. SecureString매우 특정한 종류의 공격으로부터 보호합니다 (일부는 신뢰할 수 없습니다). 예를 들어 SecureString공격자가 프로세스 메모리를 덤프하고 중요한 정보 (정확하게 지적한대로)를 추출 할 수있는 시간 범위를 축소 할 수 있지만 공격자가하기에는 창 크기가 너무 작기를 바라고 있습니다. 메모리의 스냅 샷을 찍는 것은 전혀 보안으로 간주되지 않습니다.

그래서 언제 사용해야합니까? SecureString모든 요구에 대해 작업 할 수있는 작업을 수행하는 경우에만 특정 상황에서만 안전하다는 점을 명심해야합니다.


2
공격자가 특정 시점에 프로세스 메모리를 스냅 샷 할 수 있다고 가정합니다. 반드시 그런 것은 아닙니다. 크래시 덤프를 고려하십시오. 민감한 데이터로 응용 프로그램을 수행 한 후 충돌이 발생한 경우 충돌 덤프에는 중요한 데이터가 포함되어서는 안됩니다.

4

아래 텍스트는 HP Fortify 정적 코드 분석기에서 복사 한 것입니다

Abstract : PassGenerator.cs의 PassString () 메서드는 중요한 데이터를 안전하지 않은 방식 (예 : 문자열)으로 저장하여 힙 검사를 통해 데이터를 추출 할 수 있습니다.

설명: 메모리에 저장된 민감한 데이터 (예 : 비밀번호, 주민등록번호, 신용 카드 번호 등)는 관리되는 String 객체에 저장된 경우 유출 될 수 있습니다. 문자열 객체는 고정되지 않으므로 가비지 수집기는 이러한 객체를 마음대로 재배치하고 여러 복사본을 메모리에 남겨 둘 수 있습니다. 이러한 객체는 기본적으로 암호화되지 않으므로 프로세스의 메모리를 읽을 수있는 사람은 누구나 내용을 볼 수 있습니다. 또한 프로세스의 메모리가 디스크로 스왑 아웃되면 암호화되지 않은 문자열 내용이 스왑 파일에 기록됩니다. 마지막으로 String 객체는 변경할 수 없으므로 메모리에서 String 값을 제거하는 것은 CLR 가비지 수집기 만 수행 할 수 있습니다. CLR의 메모리가 부족하지 않으면 가비지 수집기를 실행할 필요가 없으므로 가비지 수집이 수행되는 시점에 대해서는 보장 할 수 없습니다.

권장 사항 : 문자열과 같은 개체에 중요한 데이터를 저장하는 대신 SecureString 개체에 저장하십시오. 각 객체는 그 내용을 항상 메모리에 암호화 된 형식으로 저장합니다.


3

이 점을 해결하고 싶습니다.

공격자가 이미 힙 검사하기위한 수단, 그들은 가장 가능성 중 하나 (A)가 이미 키 입력을 읽을 수있는 수단을 가지고, 또는 (B)가 이미있는 경우 물리적 머신이를 ... 그래서는 사용 할 SecureString받는 얻는 것을 방지 어쨌든 데이터?

공격자는 컴퓨터와 응용 프로그램에 대한 전체 액세스 권한이 없지만 프로세스 메모리의 일부에 액세스 할 수 있습니다. 특수하게 구성된 입력으로 인해 응용 프로그램이 메모리의 일부를 노출 시키거나 덮어 쓸 수있는 경우 버퍼 오버런과 같은 버그로 인해 발생합니다.

HeartBleed 누출 메모리

예를 들어 Heartbleed를 보자. 특수하게 구성된 요청으로 인해 코드가 프로세스 메모리의 임의 부분을 공격자에게 노출시킬 수 있습니다. 공격자는 메모리에서 SSL 인증서를 추출 할 수 있지만 필요한 유일한 조작은 잘못된 요청을 사용하는 것입니다.

관리되는 코드의 세계에서 버퍼 오버런은 훨씬 덜 문제가됩니다. 그리고 WinForms의 경우, 데이터는 이미 안전하지 않은 방식으로 저장되며 이에 대해 아무것도 할 수 없습니다. 이것은 SecureString거의 쓸모없는 보호를 제공합니다 .

그러나 GUI 를 사용하도록 프로그래밍 할 수 있으며 SecureString,이 경우 메모리에서 암호 사용 가능 기간을 줄이는 것이 좋습니다. 예를 들어 WPF의 PasswordBox.SecurePassword 는 유형 SecureString입니다.


흠, 확실히 흥미 롭습니다. 솔직히 말해서, 실제로 메모리에서 System.String 값을 얻는 데 필요한 공격 유형에 대해 걱정하지는 않습니다. 순수한 호기심을 요구합니다. 그러나 정보를 주셔서 감사합니다! :)
Steven Jeffries

2

얼마 전에 Java 신용 카드 결제 게이트웨이에 대해 AC # 인터페이스를 만들어야했고 하나는 호환 가능한 보안 통신 키 암호화가 필요했습니다. Java 구현은 다소 구체적이기 때문에 주어진 방식으로 보호 된 데이터로 작업해야했습니다.

이 디자인은 SecureString을 사용하는 것보다 사용하기 쉽고 훨씬 쉽다는 것을 발견했습니다. 이 클래스는 내부 클래스이므로 공개해야합니다.

namespace Cardinity.Infrastructure
{
    using System.Security.Cryptography;
    using System;
    enum EncryptionMethods
    {
        None=0,
        HMACSHA1,
        HMACSHA256,
        HMACSHA384,
        HMACSHA512,
        HMACMD5
    }


internal class Protected
{
    private  Byte[] salt = Guid.NewGuid().ToByteArray();

    protected byte[] Protect(byte[] data)
    {
        try
        {
            return ProtectedData.Protect(data, salt, DataProtectionScope.CurrentUser);
        }
        catch (CryptographicException)//no reason for hackers to know it failed
        {
#if DEBUG
            throw;
#else
            return null;
#endif
        }
    }

    protected byte[] Unprotect(byte[] data)
    {
        try
        {
            return ProtectedData.Unprotect(data, salt, DataProtectionScope.CurrentUser);
        }
        catch (CryptographicException)//no reason for hackers to know it failed
        {
#if DEBUG
            throw;
#else
            return null;
#endif
        }
    }
}


    internal class SecretKeySpec:Protected,IDisposable
    {
        readonly EncryptionMethods _method;

        private byte[] _secretKey;
        public SecretKeySpec(byte[] secretKey, EncryptionMethods encryptionMethod)
        {
            _secretKey = Protect(secretKey);
            _method = encryptionMethod;
        }

        public EncryptionMethods Method => _method;
        public byte[] SecretKey => Unprotect( _secretKey);

        public void Dispose()
        {
            if (_secretKey == null)
                return;
            //overwrite array memory
            for (int i = 0; i < _secretKey.Length; i++)
            {
                _secretKey[i] = 0;
            }

            //set-null
            _secretKey = null;
        }
        ~SecretKeySpec()
        {
            Dispose();
        }
    }

    internal class Mac : Protected,IDisposable
    {
        byte[] rawHmac;
        HMAC mac;
        public Mac(SecretKeySpec key, string data)
        {

            switch (key.Method)
            {
                case EncryptionMethods.HMACMD5:
                    mac = new HMACMD5(key.SecretKey);
                    break;
                case EncryptionMethods.HMACSHA512:
                    mac = new HMACSHA512(key.SecretKey);
                    break;
                case EncryptionMethods.HMACSHA384:
                    mac = new HMACSHA384(key.SecretKey);
                    break;
                case EncryptionMethods.HMACSHA256:
                    mac = new HMACSHA256(key.SecretKey);

                break;
                case EncryptionMethods.HMACSHA1:
                    mac = new HMACSHA1(key.SecretKey);
                    break;

                default:                    
                    throw new NotSupportedException("not supported HMAC");
            }
            rawHmac = Protect( mac.ComputeHash(Cardinity.ENCODING.GetBytes(data)));            

        }

        public string AsBase64()
        {
            return System.Convert.ToBase64String(Unprotect(rawHmac));
        }

        public void Dispose()
        {
            if (rawHmac != null)
            {
                //overwrite memory address
                for (int i = 0; i < rawHmac.Length; i++)
                {
                    rawHmac[i] = 0;
                }

                //release memory now
                rawHmac = null;

            }
            mac?.Dispose();
            mac = null;

        }
        ~Mac()
        {
            Dispose();
        }
    }
}
당사 사이트를 사용함과 동시에 당사의 쿠키 정책개인정보 보호정책을 읽고 이해하였음을 인정하는 것으로 간주합니다.
Licensed under cc by-sa 3.0 with attribution required.