개발/C#

C# 암호화된 키 만들기(Create and Share (with a client app) a Random Encryption Key)

FA1976 2021. 4. 25. 21:42

 

암호화 키 만드는것은 다양하지만 그래도 필요할때, 급할때 가져다 쓰자.

 

 

소개

지난주에 누군가가 임의의 암호화 키 생성에 대한 질문을 올렸고, 이전에 그 라인을 따라 무언가를했기 때문에 과거의 노력을 재현하고 이에 대한 기사를 게시 할 것이라고 생각했습니다.

일반

아이디어는 완전히 임의의 키를 만들고 원하는 데이터를 암호화 한 다음 어떻게 든 소비자와 키를 공유하여 데이터를 해독하여 키와 데이터를 소비자에게 전달하는 것입니다. 이 기사에서는 키를 생성하는 코드를 제공하고 실제 암호화와 데이터 전송은 프로그래머의 상상력에 맡깁니다.

키 생성

문자열 배열로 시작했습니다. 보다 구체적으로, 문자열로 변환 된 guid의 배열입니다. 예를 들어, 배열의 크기는 정상적인 크기로 유지되었지만 코드의 특성은 코드를 리버스 엔지니어링하려는 사람들에게 훨씬 더 "흥미로운"배열을 만들기 위해 배열을 쉽게 확장 할 수 있다는 것입니다. 않습니다. 유일한 제한은 각 배열 요소의 크기가 정확히 동일하다는 것입니다 (이 예에서는 문자열 길이가 모두 32 자임).

씨#

코드 복사

protected string[] m_markers = new string[11] {"E388e564Bb4040E78fB6268120b5521D", "84525762d2Ef46178BbEe9CA60cFaBa7", "245Aa8A0cDe44342A9F0f3E598fD41cA", "dCc0A41302c345F59698d5CaE32a4AcE", "08C134fE645847108f5A09a4B7310c10", "071dBf900Ec1413FaA8bC42f153E1648", "6A189FdAeD894DaB93590a413CdA1fB6", "28aCe9C59cE04925815cCfBeA9eC4b04", "E83a43D7d48E40e5B6b20Df8B72b2708", "25277a837B554306847dAfDc285E455b", "2A06dF9f35E04F54a783D4bE6cF00d0B", };

또한 프로그래머가 키 생성을 제어 할 수 있도록 몇 가지 속성을 설정했습니다.

씨#

축소 ▲   코드 복사

public int KeyMinLength { get { return m_keyMinLength; } set { m_keyMinLength = value; } } /// <summary /> /// Get/set the maximum possible key length /// </summary /> public int KeyMaxLength { get { return m_keyMaxLength; } set { m_keyMaxLength = value; } } /// <summary /> /// Get/set the key direction (how the marker array is traversed while /// building the key) /// </summary /> public Directions Direction { get { return m_direction; } set { m_direction = value; } } /// <summary /> /// Get/set the actual length of the key /// </summary /> public int ActualKeyLength { get { return m_keyActualLength; } set { m_keyActualLength = value; } } /// <summary /> /// Get/set the descriptor length /// </summary /> public int StaticDescriptorLength { get { return m_staticDescriptorLength; } set { m_staticDescriptorLength = value; } }

이들 중 가장 흥미로운 것은 Direction재산입니다. 키를 구성하는 동안 마커 배열을 가로 지르는 방향과 방향을 지시하는 플래그 집합을 나타냅니다. 다음 방향을 지정할 수 있습니다.

  • 왼쪽
  • 권리
  • 쪽으로
  • 하위
  • 수평
  • 세로
  • 대각선

물론 왼쪽과 오른쪽 또는 위아래로 이동하는 것은 이치에 맞지 않으므로 클래스는 ReasonableDirection실제로 키를 빌드하기 전에 메서드를 호출하여 잘못된 방향이 지정되는 것을 방지하기 위해 합리적인 예방 조치를 취 합니다. 프로그래머가 수평 방향에서 왼쪽과 오른쪽을 모두 지정하는 것과 같이 이상한 일을하지 않았는지 확인하기 위해 온 전성 검사를 수행 할뿐입니다.

씨#

축소 ▲   코드 복사

//-------------------------------------------------------------------------------- /// <summary /> /// Determines if the direction specified is "reasonable" /// </summary /> /// <returns /> public bool ReasonableDirection() { bool reasonable = true; int orientations = 0; int horizontals = 0; int verticals = 0; orientations += HasDirection(Directions.Horizontal) ? 1 : 0; orientations += HasDirection(Directions.Vertical) ? 1 : 0; orientations += HasDirection(Directions.Diagonal) ? 1 : 0; reasonable = (orientations == 1); if (reasonable) { horizontals += HasDirection(Directions.Left) ? 1 : 0; horizontals += HasDirection(Directions.Right) ? 1 : 0; verticals += HasDirection(Directions.Up) ? 1 : 0; verticals += HasDirection(Directions.Down) ? 1 : 0; if (HasDirection(Directions.Horizontal)) { reasonable = (horizontals == 1); } else if (HasDirection(Directions.Vertical)) { reasonable = (verticals == 1); } else { reasonable = (horizontals == 1 && verticals == 1); } } return reasonable; }

위의 이미지는 Direction속성 이 가능하게하는 몇 가지 가능성을 보여줍니다 .

  • 청록색 선은 Right|Down|Diagonal(0 행, 1 열 Left|Up|Diagonal에서 시작 ) 또는 (위치 7, 8 열에서 시작 두 가지 가능한 방향을 나타냅니다 .
  • 빨간색 선은 Right|Horizontal(5 행 12 열 Left|Horizontal에서 시작 ) 또는 (5 행, 20 열에서 시작 두 가지 가능한 길 찾기를 나타냅니다 .
  • 녹색 선은 두 가지 가능한 길 찾기- Up|Vertical(3 행, 24 열 Down|Vertical에서 시작 ) 또는 (위치 7 행, 24 열에서 시작)을 나타냅니다. 이렇게하면 코드가 먼 가장자리까지 줄 바꿈됩니다.
  • 노란색 선은 한 가지 가능한 방향을 나타냅니다 Right|Horizontal(9 행, 28 열에서 시작). 코드는 줄 바꿈 할뿐만 아니라 다음 행으로 이동합니다. 이는 Direction속성에 RowIncrement플래그 도 포함되어 있음을 나타냅니다 이 플래그는 Horizontal방향에 대해서만 합리적이며 지정된 수평 방향에 관계없이 코드가 배열에서 다음으로 낮은 행으로 줄 바꿈되도록합니다.

클래스가 방향을 무작위로 선택하도록 허용하면 모든 캐릭터가 동일한 키로 끝날 수 있습니다. (가) 경우에 발생할 것이다 Direction었죠 Diagonal배열의 모서리 중 하나에서 시작 위치로. 이것이 신경 쓰이는 경우 항상 임의 위치 선택 코드를 변경하여 극단적 인 모서리를 생략 할 수 있습니다. 개인적으로 나는 각 데이터 항목이 다른 암호화 키로 전송 될 수 있기 때문에 가끔씩 이런 일이 발생하는 것을 전혀 보지 못합니다.

마지막으로 ConstructKey방법에 도달합니다 이 메서드는 Direction속성을 사용 하여 마커 배열을 순회하는 방법을 결정합니다. 이것은 배열과 배열의 항목으로 인덱싱하는 방법을 제어하는 ​​조정자를 사용하여 제어됩니다. 먼저 합리성 검사를 실행합니다. 문제가 있으면 예외를 던집니다.

씨#

코드 복사

private void ConstructKey() { if (!ReasonableDirection()) { throw new Exception("The specified Direction value indicates either both left and right, or both up and down."); }

다음으로 인덱싱 조정자를 설정합니다.

씨#

코드 복사

// assume horizontal left-to-right int rowAdjuster = 0; int colAdjuster = 1; bool wrapToNextRow = false; // establish our adjusters so we can index to the correct spot in the markers array if (HasDirection(Directions.Horizontal)) { colAdjuster = HasDirection(Directions.Left) ? -1 : 1; wrapToNextRow = HasDirection(Directions.RowIncrement); // wrapping to next row will always wrap down do the next row, even // if you're going left } else if (HasDirection(Directions.Vertical)) { rowAdjuster = HasDirection(Directions.Up) ? -1 : 1; colAdjuster = 0; } else // Directions.Diagonal { rowAdjuster = HasDirection(Directions.Up) ? -1 : 1; colAdjuster = HasDirection(Directions.Left) ? -1 : 1; }

그런 다음 타이핑을 쉽게하기 위해 줄 바꿈 위치와 줄 바꿈 위치를 결정합니다. "pivot"이라는 단어를 좋아하기 때문에 "pivot"이라는 용어를 사용합니다.

씨#

코드 복사

int rowPivotAt = (rowAdjuster == -1) ? 0 : m_markers.Length - 1; int rowPivotTo = (rowAdjuster == -1) ? m_markers.Length - 1 : 0; int colPivotAt = (colAdjuster == -1) ? 0 : m_markers[0].Length - 1; int colPivotTo = (colAdjuster == -1) ? m_markers[0].Length - 1 : 0; // this is where the magic will start int currRow = ArrayIndex; int currCol = MarkerIndex;

마지막으로 우리는 실제로 배열을 순회합니다. 위의 모든 제어 변수를 설정했기 때문에 루프가 깨끗하고 단단하며 읽기 쉽습니다.

씨#

코드 복사

StringBuilder key = new StringBuilder(""); for (int keyCount = 0; keyCount < ActualKeyLength; keyCount++) { // snatch the character at the current row/column key.Append(m_markers[currRow][currCol]); // adjust our row/column based on the adjusters (and where we are i the array) currCol = (currCol == colPivotAt) ? colPivotTo : currCol + colAdjuster; currRow = (currRow == rowPivotAt) ? rowPivotTo : currRow + rowAdjuster; // if we need to, wrap to the next row if (wrapToNextRow && currCol == colPivotTo) { // remember, we always wrap down to the next row currRow++; currRow = (currRow == rowPivotAt) ? rowPivotTo : currRow; } } // set our Key property with the results Key = key.ToString(); }

설명자

지금 쯤이면 아마도 "좋아요! 우리는 무작위 키를 가지고 있습니다. 이제 어떻게해야합니까?"라고 생각할 것입니다.

보안 정보를 전송하는 것에 대해 아는 사람은이 정보를 단순히 전송하지 않는다는 것을 알고 있습니다. 내가 보는 방식은 전체 shebang을 포기하지 않고 보내는 것을 "설명"하는 것이 완벽하게 실행 가능하다는 것입니다. "설명"을 입력하십시오.

실제로 랜덤 키를 만드는 데 사용 된 멋진 제어 변수를 모두 기억하십니까? 키를 빌드하는 데 사용 된 것과 동일한 어셈블리를 참조하는 클라이언트 응용 프로그램에 해당 정보를 제공하기 만하면 해당 값을 기반으로 키를 디코딩 할 수 있습니다.

자, 우리의 키가 14 자 었죠, 그리고 시작 마커 배열 인덱스는 5이었다 가정 해 봅시다, (우리가 배열 요소 5의 마커로 색인) 마커 지수는 28이었고, 우리는 Direction했다 Diagonal | Up | Left간단히 GetKeyDescriptor메서드를 호출하여 설명자 문자열을 만듭니다.

씨#

코드 복사

public string GetKeyDescriptor() { StringBuilder descriptor = new StringBuilder(); descriptor.AppendFormat("{0},", ActualKeyLength); descriptor.AppendFormat("{0},", ArrayIndex); descriptor.AppendFormat("{0},", MarkerIndex); descriptor.AppendFormat("{0},", (int)Direction); descriptor.Append(MakePadding(descriptor.Length)); KeyDescriptor = descriptor.ToString(); return KeyDescriptor; }

에 대한 마지막 전화를 눈치 채 셨을 것 Append입니다. 이것은 두 가지 목적을 제공합니다. a) 디스크립터 문자열의 페이로드를 확인하는 방법으로 작동하고 해커가 그 거대한 바이트 블록이 의미하는 바를 알아 내려고 절대적으로 공격하도록 유도합니다. 방법은 다음과 같습니다.

씨#

코드 복사

private string MakePadding(int currLength) { int maxLength = StaticDescriptorLength - currLength; if (maxLength <= 0) { // if you get this exception, you need to specify a higher value for // the StaticDescriptorLength property. throw new Exception("Descriptor string is already longer than the value indicated by the static descriptor length."); } StringBuilder padding = new StringBuilder(""); while (padding.Length < maxLength) { padding.Append(Guid.NewGuid().ToString("N")); } return (padding.ToString().Substring(0, maxLength)); }

이 메서드의 목적은 문자열을 지정된 길이로 채우기에 충분한 임의의 패딩 문자를 만드는 것입니다. 보시다시피 새 guid를 만들고 필요한 길이를 초과 할 때까지 패딩 문자열에 추가 한 다음 Substring필요하지 않은 모든 항목을 잘라내 는 메서드를 호출 합니다.

마지막으로 설명자 문자열을 수신하는 클라이언트 응용 프로그램은 끝에서 키를 다시 만드는 방법이 필요합니다. BuildKey설명자 문자열을 받아들이고 해당 문자열을 구문 분석하는 버전으로 메서드를 오버로드하여이를 수행 합니다.

씨#

축소 ▲   코드 복사

public void BuildKey(string descriptor) { Key = ""; KeyDescriptor = descriptor; ActualKeyLength = 0; ArrayIndex = -1; MarkerIndex = -1; Direction = Directions.None; if (descriptor.Length == StaticDescriptorLength) { string[] parts = descriptor.Split(','); int temp; if (Int32.TryParse(parts[0], out temp)) { ActualKeyLength = temp; } if (Int32.TryParse(parts[1], out temp)) { ArrayIndex = temp; } if (Int32.TryParse(parts[2], out temp)) { MarkerIndex = temp; } if (Int32.TryParse(parts[3], out temp)) { Direction = (Directions)(temp); } if (ActualKeyLength > 0 && ArrayIndex >= 0 && MarkerIndex >= 0 && Direction != Directions.None) { ConstructKey(); } } }

웹 사이트 (또는 웹 서비스)와 클라이언트 응용 프로그램이 모두이 어셈블리 (또는이를 기반으로하는 어셈블리)를 사용하는 경우 모든 개별 데이터 블록에 대해 서로 다른 암호화 키를 사용하여 데이터를 보낼 수 있으므로 암호화를 깨는 경우 무작위로 만들 수 있습니다. 사실상 불가능 해집니다. 해커를 더욱 흔들기 위해 30 일 정도마다 완전히 다른 마커 문자열 세트가 포함 된 새 어셈블리를 게시 할 수 있습니다. 클라이언트 측 애플리케이션의 클릭 한 번 배포와 결합하면 뒤로 구부리지 않아도 모든 것이 동기화 상태를 유지할 수 있습니다.

샘플 애플리케이션

샘플 앱을 사용하면 몇 가지 기준을 지정한 다음 설명자 문자열과 함께 임의의 키를 생성 할 수 있습니다. 양식의 마지막 컨트롤은 설명자 키의 정보에서 다시 빌드 된 키를 보여줍니다 (작동을 증명하기 위해).

마무리 중

이 코드는 데이터 무결성을 희생하지 않고 안전한 데이터 전송을 허용하는 더 큰 코드 모음의 일부일 뿐이며 애플리케이션 식별자, 어셈블리 난독 화 및 서명, 기타 기술과 같은 다른 측정과 결합되어야합니다. 설명자 문자열을 사용하면 사용할 암호화 알고리즘을 지정할 수도 있고 설명자 문자열을 암호화하여 보안을 강화할 수도 있습니다.

몇 년 전에이 코드를 작성할 때 decsriptor 문자열을 암호화하고 내가 보내는 암호화 된 데이터 앞에 추가했습니다. 클라이언트 측의 코드는 동일한 어셈블리를 가지고 있기 때문에 무엇을해야하는지 알고있었습니다.

역사

  • 2010 년 10 월 4 일- 원본 버전.

첨부파일 다운은 아래 블로그에서...

blog.naver.com/kazewa00/222321148524

 

C# 암호화된 키 만들기(Create and Share (with a client app) a Random Encryption Key)

https://www.codeproject.com/Articles/115435/Create-and-Share-with-a-client-app-a-Random-Encry...

blog.naver.com