첨부 실행 코드는 나눔고딕코딩 폰트를 사용합니다.
728x90
반응형
728x170

■ 라이센스 키를 사용하는 방법을 보여준다.

TestSolution.zip
0.01MB

[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
반응형
그리드형(광고전용)
Posted by icodebroker

댓글을 달아 주세요