■ 라이센스 키를 사용하는 방법을 보여준다.
[TestLibrary.Common 프로젝트]
▶ KeyByteSet.cs
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 |
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
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 |
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
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 |
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
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 |
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
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 |
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
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 |
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
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 |
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
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 |
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 } |