728x90
반응형
728x170
■ 라이센스 키를 사용하는 방법을 보여준다.
[TestLibrary.Common 프로젝트]
▶ KeyByteSet.cs
namespace TestLibrary.Common;
/// <summary>
/// 키 바이트 세트
/// </summary>
public class KeyByteSet
{
//////////////////////////////////////////////////////////////////////////////////////////////////// Propery
////////////////////////////////////////////////////////////////////////////////////////// Public
#region 키 바이트 번호 - KeyByteNumber
/// <summary>
/// 키 바이트 번호
/// </summary>
public int KeyByteNumber { get; }
#endregion
#region 키 바이트 A - KeyByteA
/// <summary>
/// 키 바이트 A
/// </summary>
public byte KeyByteA { get; }
#endregion
#region 키 바이트 B - KeyByteB
/// <summary>
/// 키 바이트 B
/// </summary>
public byte KeyByteB { get; }
#endregion
#region 키 바이트 C - KeyByteC
/// <summary>
/// 키 바이트 C
/// </summary>
public byte KeyByteC { get; }
#endregion
//////////////////////////////////////////////////////////////////////////////////////////////////// Constructor
////////////////////////////////////////////////////////////////////////////////////////// Public
#region 생성자 - KeyByteSet(keyByteNumber, keyByteA, keyByteB, keyByteC)
/// <summary>
/// 생성자
/// </summary>
/// <param name="keyByteNumber">키 바이트 번호</param>
/// <param name="keyByteA">키 바이트 A</param>
/// <param name="keyByteB">키 바이트 B</param>
/// <param name="keyByteC">키 바이트 C</param>
/// <exception cref="ArgumentException"></exception>
public KeyByteSet(int keyByteNumber, byte keyByteA, byte keyByteB, byte keyByteC)
{
if(keyByteNumber < 1)
{
throw new ArgumentException($"{nameof(keyByteNumber)} should be greater or equal to 1", nameof(keyByteNumber));
}
KeyByteNumber = keyByteNumber;
KeyByteA = keyByteA;
KeyByteB = keyByteB;
KeyByteC = keyByteC;
}
#endregion
}
▶ LincenseKeyBase.cs
namespace TestLibrary.Common;
/// <summary>
/// 라이센스 키 베이스
/// </summary>
public class LincenseKeyBase
{
//////////////////////////////////////////////////////////////////////////////////////////////////// Method
////////////////////////////////////////////////////////////////////////////////////////// Static
//////////////////////////////////////////////////////////////////////////////// Protected
#region 키 정규화하기 - NormalizeKey(key)
/// <summary>
/// 키 정규화하기
/// </summary>
/// <param name="key">키</param>
/// <returns>정규화 키</returns>
protected static string NormalizeKey(string key)
{
if(key == null)
{
key = string.Empty;
}
return key.Trim().ToUpper().Replace("-", string.Empty).Replace(" ", string.Empty);
}
#endregion
////////////////////////////////////////////////////////////////////////////////////////// Instance
//////////////////////////////////////////////////////////////////////////////// Protected
#region 키 바이트 구하기 - GetKeyByte(seed, a, b, c)
/// <summary>
/// 키 바이트 구하기
/// </summary>
/// <param name="seed">시드</param>
/// <param name="a">바이트 A</param>
/// <param name="b">바이트 B</param>
/// <param name="c">바이트 C</param>
/// <returns>키 바이트</returns>
/// <remarks>
/// 시드와 일부 입력 바이트가 주어지면 반환할 단일 바이트를 생성한다.
/// 이것은 동일한 키를 검색하기 위해 나타낼 수 있는 무작위 데이터와 함께 사용해야 한다.
/// </remarks>
protected byte GetKeyByte(long seed, byte a, byte b, byte c)
{
int temporaryA = a % 25;
int temporaryB = b % 3;
long result;
if((a % 2) == 0)
{
result = ((seed >> temporaryA) & 0xFF) ^ ((seed >> temporaryB) | c);
}
else
{
result = ((seed >> temporaryA) & 0xFF) ^ ((seed >> temporaryB) & c);
}
return (byte)result;
}
#endregion
#region 체크썸 구하기 - GetChecksum(key)
/// <summary>
/// 체크썸 구하기
/// </summary>
/// <param name="key">키</param>
/// <returns>체크썸</returns>
protected string GetChecksum(string key)
{
ushort left = 0x56;
ushort right = 0xaf;
if(key.Length > 0)
{
for(int count = 0; count < key.Length; count++)
{
right = (ushort)(right + Convert.ToByte(key[count]));
if(right > 0xff)
{
right -= 0xff;
}
left += right;
if(left > 0xff)
{
left -= 0xff;
}
}
}
ushort checksum = (ushort)((left << 8) + right);
return checksum.ToString("X4");
}
#endregion
}
[TestLibrary.Generator 프로젝트]
▶ KeyByteSetComparer.cs
using System.Collections;
using TestLibrary.Common;
namespace TestLibrary.Generator;
/// <summary>
/// 키 바이트 세트 비교자
/// </summary>
public class KeyByteSetComparer : IComparer
{
//////////////////////////////////////////////////////////////////////////////////////////////////// Method
////////////////////////////////////////////////////////////////////////////////////////// Public
#region 비교하기 - Compare(key1, key2)
/// <summary>
/// 비교하기
/// </summary>
/// <param name="key1">키 1</param>
/// <param name="key2">키 2</param>
/// <returns>비교 결과</returns>
public int Compare(object key1, object key2)
{
KeyByteSet keyByteSet1 = (KeyByteSet)key1;
KeyByteSet keyByteSet2 = (KeyByteSet)key2;
if(keyByteSet1 == null)
{
throw new ArgumentNullException(nameof(keyByteSet1));
}
if(keyByteSet2 == null)
{
throw new ArgumentNullException(nameof(keyByteSet2));
}
if(keyByteSet1.KeyByteNumber > keyByteSet2.KeyByteNumber)
{
return 1;
}
if(keyByteSet1.KeyByteNumber < keyByteSet2.KeyByteNumber)
{
return -1;
}
return 0;
}
#endregion
}
▶ LicenseKeyGenerator.cs
using TestLibrary.Common;
namespace TestLibrary.Generator;
/// <summary>
/// 라이센스 키 제너레이터
/// </summary>
public class LicenseKeyGenerator : LincenseKeyBase
{
//////////////////////////////////////////////////////////////////////////////////////////////////// Method
////////////////////////////////////////////////////////////////////////////////////////// Public
#region 키 생성하기 - GenerateKey(seed, keyByteSetArray)
/// <summary>
/// 키 생성하기
/// </summary>
/// <param name="seed">시드</param>
/// <param name="keyByteSetArray">키 바이트 세트 배열</param>
/// <returns>키</returns>
public string GenerateKey(int seed, KeyByteSet[] keyByteSetArray)
{
if(keyByteSetArray.Length < 2)
{
throw new InvalidOperationException("The KeyByteSet array must be of length 2 or greater.");
}
Array.Sort(keyByteSetArray, new KeyByteSetComparer());
bool allKeyByteNumbersDistinct = true;
List<int> keyByteNumberList = new List<int>();
int maximumKeyByteNumber = 0;
foreach(KeyByteSet keyByteSet in keyByteSetArray)
{
if(!keyByteNumberList.Contains(keyByteSet.KeyByteNumber))
{
keyByteNumberList.Add(keyByteSet.KeyByteNumber);
if(keyByteSet.KeyByteNumber > maximumKeyByteNumber)
{
maximumKeyByteNumber = keyByteSet.KeyByteNumber;
}
}
else
{
allKeyByteNumbersDistinct = false;
break;
}
}
if(!allKeyByteNumbersDistinct)
{
throw new InvalidOperationException("The KeyByteSet array contained at least 1 item with a duplicate KeyByteNumber value.");
}
if(maximumKeyByteNumber != keyByteSetArray.Length)
{
throw new InvalidOperationException("The values for KeyByteNumber in each KeyByteSet item must be sequential and start with the number 1.");
}
byte[] keyByteArray = new byte[keyByteSetArray.Length];
for(int i = 0; i < keyByteSetArray.Length; i++)
{
keyByteArray[i] = GetKeyByte
(
seed,
keyByteSetArray[i].KeyByteA,
keyByteSetArray[i].KeyByteB,
keyByteSetArray[i].KeyByteC
);
}
string result = seed.ToString("X8");
for(int i = 0; i < keyByteArray.Length; i++)
{
result = result + keyByteArray[i].ToString("X2");
}
result = result + GetChecksum(result);
int startPosition = 7;
while(startPosition < (result.Length - 1))
{
result = result.Insert(startPosition, "-");
startPosition = startPosition + 7;
}
return result;
}
#endregion
}
[TestLibrary.Validator 프로젝트]
▶ LicenseKeyValidationResult.cs
namespace TestLibrary.Validator;
/// <summary>
/// 라이센스 키 검증 결과
/// </summary>
public enum LicenseKeyValidationResult
{
/// <summary>
/// KEY IS VALID
/// </summary>
KeyIsValid = 0,
/// <summary>
/// KEY IS INVALID
/// </summary>
KeyIsInvalid = 1,
/// <summary>
/// KEY BLACK LISTED
/// </summary>
KeyBlackListed = 2,
/// <summary>
/// KEY PHONEY
/// </summary>
KeyPhoney = 3
}
▶ LicenseKeyValidator.cs
using System.Globalization;
using TestLibrary.Common;
namespace TestLibrary.Validator;
/// <summary>
/// 라이센스 키 검증자
/// </summary>
public class LicenseKeyValidator : LincenseKeyBase
{
//////////////////////////////////////////////////////////////////////////////////////////////////// Method
////////////////////////////////////////////////////////////////////////////////////////// Public
#region 키 검증하기 - ValidateKey(key, keyByteSetArray, totalKeyByteSetCount, keyListBlackListed)
/// <summary>
/// 키 검증하기
/// </summary>
/// <param name="key">키</param>
/// <param name="keyByteSetArray">키 바이트 세트 배열</param>
/// <param name="totalKeyByteSetCount">전체 키 바이트 세트 수</param>
/// <param name="keyListBlackListed">블랙 리스트 키 리스트</param>
/// <returns>라이센스 키 검증 결과</returns>
public LicenseKeyValidationResult ValidateKey(string key, KeyByteSet[] keyByteSetArray, int totalKeyByteSetCount, string[] keyListBlackListed)
{
key = NormalizeKey(key);
LicenseKeyValidationResult validationResult = LicenseKeyValidationResult.KeyIsInvalid;
bool passChecksum = ValidateKeyChecksum(key, totalKeyByteSetCount);
if(passChecksum)
{
if(keyListBlackListed != null && keyListBlackListed.Length > 0)
{
for(int i = 0; i < keyListBlackListed.Length; i++)
{
if(key.StartsWith(keyListBlackListed[i]))
{
validationResult = LicenseKeyValidationResult.KeyBlackListed;
}
}
}
if(validationResult != LicenseKeyValidationResult.KeyBlackListed)
{
validationResult = LicenseKeyValidationResult.KeyPhoney;
int seed;
bool seedParsed = int.TryParse(key.Substring(0, 8), NumberStyles.HexNumber, null, out seed);
if(seedParsed)
{
foreach(KeyByteSet keyByteSet in keyByteSetArray)
{
int pattern = GetPattern(keyByteSet.KeyByteNumber);
if(pattern - 1 > key.Length)
{
throw new InvalidOperationException
(
"The KeyByte check position is out of range. " +
"You may have specified a check KeyByteNumber that did not exist in the original key generation."
);
}
string pattenValue = key.Substring(pattern, 2);
byte keyByte = GetKeyByte(seed, keyByteSet.KeyByteA, keyByteSet.KeyByteB, keyByteSet.KeyByteC);
if(pattenValue != keyByte.ToString("X2"))
{
return validationResult;
}
}
validationResult = LicenseKeyValidationResult.KeyIsValid;
}
}
}
return validationResult;
}
#endregion
////////////////////////////////////////////////////////////////////////////////////////// Private
#region 패턴 구하기 - GetPattern(keyByteNumber)
/// <summary>
/// 패턴 구하기
/// </summary>
/// <param name="keyByteNumber">키 바이트 번호</param>
/// <returns>패턴</returns>
private int GetPattern(int keyByteNumber)
{
return (keyByteNumber * 2) + 6;
}
#endregion
#region 키 체크썸 검증하기 - ValidateKeyChecksum(key, totalKeyByteSetCount)
/// <summary>
/// 키 체크썸 검증하기
/// </summary>
/// <param name="key">키</param>
/// <param name="totalKeyByteSetCount">전체 키 바이트 세트 수</param>
/// <returns>키 체크썸 검증 결과</returns>
private bool ValidateKeyChecksum(string key, int totalKeyByteSetCount)
{
bool result = false;
string keyNormalized = NormalizeKey(key);
if(keyNormalized.Length == (8 + 4 + (2 * totalKeyByteSetCount)))
{
int keyLessChecksumLength = keyNormalized.Length - 4;
string checksum = keyNormalized.Substring(keyLessChecksumLength, 4);
string keyWithoutChecksum = keyNormalized.Substring(0, keyLessChecksumLength);
result = GetChecksum(keyWithoutChecksum) == checksum;
}
return result;
}
#endregion
}
[TestProject.Generator 프로젝트]
▶ Program.cs
using TestLibrary.Common;
using TestLibrary.Generator;
namespace TestProject.Generator
{
/// <summary>
/// 프로그램
/// </summary>
class Program
{
//////////////////////////////////////////////////////////////////////////////////////////////////// Method
////////////////////////////////////////////////////////////////////////////////////////// Static
//////////////////////////////////////////////////////////////////////////////// Private
#region 프로그램 시작하기 - Main()
/// <summary>
/// 프로그램 시작하기
/// </summary>
private static void Main()
{
while(true)
{
KeyByteSet[] keyByteSetArray = new[]
{
new KeyByteSet(keyByteNumber : 1, keyByteA : 58 , keyByteB : 6 , keyByteC : 97 ),
new KeyByteSet(keyByteNumber : 2, keyByteA : 96 , keyByteB : 254, keyByteC : 23 ),
new KeyByteSet(keyByteNumber : 3, keyByteA : 11 , keyByteB : 185, keyByteC : 69 ),
new KeyByteSet(keyByteNumber : 4, keyByteA : 2 , keyByteB : 93 , keyByteC : 41 ),
new KeyByteSet(keyByteNumber : 5, keyByteA : 62 , keyByteB : 4 , keyByteC : 234),
new KeyByteSet(keyByteNumber : 6, keyByteA : 200, keyByteB : 56 , keyByteC : 49 ),
new KeyByteSet(keyByteNumber : 7, keyByteA : 89 , keyByteB : 45 , keyByteC : 142),
new KeyByteSet(keyByteNumber : 8, keyByteA : 6 , keyByteB : 88 , keyByteC : 32 )
};
int seed = new Random().Next(0, int.MaxValue);
Console.WriteLine($"시드 (예를 들어 사용자 ID) : {seed}");
Console.WriteLine();
LicenseKeyGenerator licenseKeyGenerator = new LicenseKeyGenerator();
string licenceKey = licenseKeyGenerator.GenerateKey(seed, keyByteSetArray);
Console.WriteLine($"생성된 라이센스 키 : {licenceKey}");
Console.WriteLine();
Console.WriteLine("라이센스 키 검증을 위해서 TestProject.Validator 프로그램에 이 값을 복사한다.");
Console.WriteLine();
Console.WriteLine("다른 라이센스 키 생성을 위해 아무 키나 눌러 주시기 바랍니다.");
Console.WriteLine();
Console.ReadKey(true);
Console.WriteLine();
}
}
#endregion
}
}
[TestProject.Validator 프로젝트]
▶ Program.cs
using TestLibrary.Common;
using TestLibrary.Validator;
namespace TestProject.Validator;
/// <summary>
/// 프로그램
/// </summary>
class Program
{
//////////////////////////////////////////////////////////////////////////////////////////////////// Method
////////////////////////////////////////////////////////////////////////////////////////// Static
//////////////////////////////////////////////////////////////////////////////// Private
#region 프로그램 시작하기 - Main()
/// <summary>
/// 프로그램 시작하기
/// </summary>
private static void Main()
{
while (true)
{
KeyByteSet[] keyByteSetArray = new[]
{
new KeyByteSet(keyByteNumber: 1, keyByteA: 58, keyByteB: 6, keyByteC: 97),
new KeyByteSet(keyByteNumber: 5, keyByteA: 62, keyByteB: 4, keyByteC: 234),
new KeyByteSet(keyByteNumber: 8, keyByteA: 6, keyByteB: 88, keyByteC: 32)
};
Console.WriteLine("TestProject.Generator 프로그램에서 생성한 라이센스 키를 입력한다 :");
string licenseKey = Console.ReadLine();
Console.WriteLine();
LicenseKeyValidator licenseKeyValidator = new LicenseKeyValidator();
LicenseKeyValidationResult licenseKeyValidationResult = licenseKeyValidator.ValidateKey
(
key : licenseKey?.Trim(),
keyByteSetArray : keyByteSetArray,
totalKeyByteSetCount : 8,
keyListBlackListed : null
);
Console.WriteLine($"검증 결과 : {licenseKeyValidationResult}");
Console.WriteLine();
Console.WriteLine("다른 라이센스 키 검증을 위해 아무 키나 눌러 주시기 바랍니다.");
Console.ReadKey(true);
Console.WriteLine();
}
}
#endregion
}
728x90
반응형
그리드형(광고전용)
'C# > Common' 카테고리의 다른 글
[C#/COMMON/.NET6] HashAlgorithm 클래스 : ComputeHash 메소드를 사용해 파일 해시값 비교하기 (0) | 2022.10.20 |
---|---|
[C#/COMMON/.NET6] FileStream 클래스 : 파일 동일 여부 구하기 (0) | 2022.10.20 |
[C#/COMMON/.NET6] DateTime 구조체 : ToFileTime 메소드를 사용해 타임스탬프 구하기 (0) | 2022.10.20 |
[C#/COMMON/.NET6] XmlDocument 클래스 : WriteTo 메소드를 사용해 XML 문자열 구하기 (0) | 2022.10.20 |
[C#/COMMON/.NET6] XmlDocument 클래스 : OuterXml 속성을 사용해 XML 문자열 구하기 (0) | 2022.10.20 |
[C#/COMMON/.NET6] String 클래스 : Substring 메소드를 사용해 문자열을 특정 크기로 나누기 (기능 개선) (0) | 2022.10.19 |
[C#/COMMON/.NET6] String 클래스 : Substring 메소드를 사용해 문자열을 특정 크기로 나누기 (0) | 2022.10.19 |
[C#/COMMON/.NET6] Enumerable 클래스 : Range 정적 메소드/Select 확장 메소드를 사용해 문자열을 특정 크기로 나누기 (0) | 2022.10.19 |
[C#/COMMON/.NET6] 규칙 엔진 만들기 (0) | 2022.10.19 |
[C#/COMMON/.NET6] Marshal 클래스 : WriteByte 정적 메소드를 사용해 비관리 메모리에 바이트 쓰기 (0) | 2022.10.19 |
댓글을 달아 주세요