Microsoft MVP성태의 닷넷 이야기
글쓴 사람
정성태 (techsharer at outlook.com)
홈페이지
첨부 파일
(연관된 글이 2개 있습니다.)
닷넷 System.ComponentModel.LicenseManager를 이용한 라이선스 적용

찾아보니, 다음과 같은 글이 눈에 띄는군요.

Licensed Applications using the .NET Framework
; http://www.codeguru.com/columns/experts/article.php/c5469

구현 방식은 예상외로 무척 간단했습니다. 라이선스를 걸어 보호하려는 클래스가 있다면 아래와 같이 LicenseProvider 특성을 걸어주고,

[LicenseProvider(typeof(MyLicenseProvider))]
public class Class1
{
    public Class1()
    {
        LicenseManager.Validate(typeof(Class1), this);
    }
}

다음으로, LicenseProvider를 우리가 구현해 주면 됩니다.

public class MyLicenseProvider : System.ComponentModel.LicenseProvider
{
    public override License GetLicense(LicenseContext context, 
        Type type, object instance, bool allowExceptions) 
    {
        return null;
    }
}

GetLicense 내의 코드 구현은 그야말로 천차만별이라고 보면 되겠습니다. 중요한 것은, 라이선스가 올바르다고 판단이 되면 GetLicense 메서드에서 License 개체(추상 클래스이므로 그것을 상속받은 개체)를 반환해주면 됩니다.




위의 글에서 보면 닷넷 BCL에서 기본 제공되는 LicFileLicenseProvider를 설명해 주고 있는데요. 이건 매우 간단한 거라서, 해당 클래스가 라이선스가 걸린 것임을 나타내는 정도로만 쓰일 뿐 어떤 강제적인 수단을 동원하는 것은 아닙니다. 이 때문에, LicFileLicenseProvider로 보호된 클래스가 있다면 실행 파일과 같은 위치에 "{네임스페이스를 포함한 클래스 이름}.lic"라는 파일명을 두는 것으로 '라이선스 검사'에 통과할 수 있습니다.

제가 테스트 한 예제를 기준으로 한번 살펴볼까요?

우선, 다음과 같은 예제 클래스를 두고,

===== CommercialLibrary 라이브러리 프로젝트의 Class1.cs 파일 내용 =====

namespace CommercialLibrary
{
    [LicenseProvider(typeof(LicFileLicenseProvider))]
    public class LicFileLicensedClass1
    {
        public LicFileLicensedClass1()
        {
            License lic = LicenseManager.Validate(typeof(LicFileLicensedClass1), this);
            if (lic == null)
            {
                Console.WriteLine("lic == null");
            }
            else
            {
                Console.WriteLine(lic);
            }
        }

        public void Do()
        {
            Console.WriteLine("licensed library is activated!");
        }
    }
}

이런 경우, CommercialLibrary.dll을 사용하는 ConsoleApplication1.exe 측에서는 다음과 같은 이름의 lic 파일을 exe 파일과 같은 폴더에 두어야 합니다.

CommercialLibrary.LicFileLicensedClass1.lic

그리고, 그 파일의 내용에는 "{네임스페이스를 포함한 클래스 이름} is a licensed component." 같은 형식의 문자열이 포함되어 있어야 합니다. 예제에서는 다음과 같습니다.

CommercialLibrary.LicFileLicensedClass1 is a licensed component.

보시는 것처럼, 다소 허무하긴 하지만 LicFileLicenseProvider 파일은 상속이 가능하고 IsKeyValid와 GetKey 메서드를 재정의할 수 있기 때문에 약간의 부가적인 코드만 구현해 준다면 상용 라이브러리를 보호하기 위한 어느 정도의 기준은 만족시킬 수 있습니다.




상용 컴포넌트를 사용해 보신 분들은 lc.exe 컴파일러와 친숙하실 텐데요.

System.ComponentModel.LicenseException (MSBuild의 lc.exe 빌드 과정 생략)
; https://www.sysnet.pe.kr/2/0/518

64비트 OS에서의 ChartFX 라이선스 문제
; https://www.sysnet.pe.kr/2/0/537

기왕 여기까지 온 김에, 그동안 무심코 써왔던 lc.exe에 대해 "Licensed Applications using the .NET Framework" 글을 통해서 이해를 해보는 것도 좋겠지요.

사실, lc.exe를 굳이 사용하지 않아도 상관없습니다. 위에서 설명한 것처럼 lic 파일을 실행 파일과 같이 두는 것은 그리 어려운 일이 아니기 때문입니다. 문제는, lic 파일에 2개 이상의 구성요소 정의가 불가능하다는 데 있습니다. 즉, 라이선스를 걸어놓은 구성 요소 10개를 사용중이라면 10개의 lic 파일들이 exe 파일과 동일한 폴더에 배포되어야 한다는 약간의 불편함 정도가 있는 것입니다.

바로 이런 문제를 해결하기 위해 lc.exe를 사용합니다. lc.exe를 이용하면 라이선스 정보를 '하나의 파일에 여러개 정의하는 것'이 가능하고, 게다가 그렇게 정의된 파일을 어셈블리 내의 리소스로 포함하는 것이 가능합니다. 즉, 배포할 때 lic 파일들이 전혀 필요없고 오로지 exe 파일 하나만을 복사하는 것으로 끝낼 수 있다는 장점이 있습니다.

lc.exe 실행 방법을 알아보기 위해 MSDN 도움말을 보면,

License Compiler (Lc.exe)
; https://docs.microsoft.com/en-us/previous-versions/dotnet/netframework-2.0/ha0k3c9f(v=vs.80)

/complist 옵션에 주는 파일(ConsoleApplication1Lic.txt)의 내용을 다음과 같이 구성하도록 설명하고 있는데요. (바로 이 파일 내에 여러 개의 구성 요소를 지정해 주는 것이 가능합니다.)

CommercialLibrary.LicFileLicensedClass1, CommercialLibrary.dll

그리고, 위의 파일에 들어가는 구성요소에 대한 lic 파일(이 예제에서는 CommercialLibrary.LicFileLicensedClass1.lic)이 exe 파일과 같은 폴더에 있어야 합니다.

애석하게도 그렇게 모든 준비를 마치고, 실제로 실행해 보면 다음과 같은 식으로 LC0003 오류가 발생합니다.

D:\...[생략]...\bin\Debug>lc /target:ConsoleApplication1.exe /complist:ConsoleApplication1Lic.txt /i:CommercialLibrary.DLL
Microsoft (R) .NET License Compiler
[Microsoft .Net Framework, Version 4.0.30319.225]
Copyright (c) Microsoft Corporation.  All rights reserved.


Processing complist 'ConsoleApplication1Lic.txt'...
ConsoleApplication1Lic.txt (1) : error LC0003 : Unable to resolve type 'CommercialLibrary.LicFileLicensedClass1, Commerc
ialLibrary.DLL'

이거 원인 찾느라고 엄청난 시간을 소비했는데요. ^^; 아래의 예제에 소개된 것을 보고서야 오류를 바로 잡을 수 있었습니다.

.NET Licensing
; http://windowsforms.net/articles/Licensing.aspx

즉, ConsoleApplication1Lic.txt 파일의 내용에서 DLL 확장자를 지정해서는 안되는 것이었습니다.

CommercialLibrary.LicFileLicensedClass1, CommercialLibrary

아래는 실행 결과입니다.

D:\...[생략]...\bin\Debug>lc /target:ConsoleApplication1.exe /complist:ConsoleApplication1Lic.txt /i:CommercialLibrary.DLL
Microsoft (R) .NET License Compiler
[Microsoft .Net Framework, Version 4.0.30319.225]
Copyright (c) Microsoft Corporation.  All rights reserved.


Processing complist 'ConsoleApplication1Lic.txt'...
System.ComponentModel.LicFileLicenseProvider+LicFileLicense
Creating Licenses file consoleapplication1.exe.licenses...

이렇게 생성된 "consoleapplication1.exe.licenses" 파일은 닷넷 어셈블리에 '리소스' 자원으로 포함될 수 있습니다. 하지만, 이렇게 되면 빌드 절차가 복잡해지기 때문에 사실 이런 유형으로는 거의 사용되질 않고 있습니다.

MSbuild에서는 이런 작업을 간단하게 할 수 있도록 프로젝트에 licx라는 특별한 확장자를 가진 파일이 있다면 그것을 '/complist'에 지정되는 파일이라고 여기고 자동으로 lc.exe 컴파일러를 호출하여 .licenses 파일을 생성하고 어셈블리의 리소스로 포함까지 시켜줍니다.

실제로 LicFileLicenseProvider를 기준으로 테스트를 원한다면, 다음과 같은 식으로 솔루션을 구성해 주면 됩니다.

license_manager_compiler_1.png

즉, 사용하려는 응용 프로그램(ConsoleApplication1.exe)측에서 licx 파일을 추가하고 내용은 다음과 같이 구성합니다.

CommercialLibrary.LicFileLicensedClass1, CommercialLibrary

위에서 lc.exe 컴파일을 명령행으로 사용하는 방법을 설명하면서 언급했지만, lc.exe는 /complist 옵션에 지정될 파일 뿐만 아니라, 원래의 구성요소에 대한 lic 파일들이 필요하므로, 라이브러리(CommercialLibrary.dll) 측에서는 CommercialLibrary.LicFileLicensedClass1.lic 파일의 "Copy to output Directory" 속성을 "Copy if newer" 설정으로 해두시면 '편리합니다.' (물론, 배포시에는 lic 파일들은 없어도 됩니다.)

결국, 빌드된 ConsoleApplication1.exe 파일을 .NET Reflector로 확인해 보면 다음과 같이 명령행에서 lc.exe를 실행했을 때 처럼 "consoleapplication1.exe.licenses 리소스 파일이 들어있는 것을 확인할 수 있습니다.

license_manager_compiler_2.png

LicenseProvider와 lc.exe에 대해서 이 정도 알아봤으니, 이제 자신만의 라이선스 체계를 만들어서 LicFileLicenseProvider를 확장하고 싶어질 텐데요. 소스 코드를 구현하기 전에 아래의 글들 중에서 자신이 생각하고 있던 것이 있다면,... ^^ 그대로 가져다 쓰는 것도 좋겠지요.

C#: LicenseManager (and Other uses): Get a Mobile Devices Unique ID 
; http://skysigal.xact-solutions.com/Blog/tabid/427/entryid/1962/C-LicenseManager-and-Other-uses-Get-a-Mobile-Devices-Unique-ID.aspx

C#: Developing a LicenseProvider/License: Getting the Motherboard ID. 
; http://skysigal.xact-solutions.com/Blog/tabid/427/entryid/1963/C-Developing-a-LicenseProvider-License-Getting-the-Motherboard-ID.aspx

C#: Developing a LicenseProvider/License: Getting the Manufacturer’s UID.
; http://skysigal.xact-solutions.com/Blog/tabid/427/entryid/1964/C-Developing-a-LicenseProvider-License-Getting-the-Manufacturer-rsquo-s-UID.aspx

C#: Developing a LicenseProvider/License: Getting the CPU’s ID. 
; http://skysigal.xact-solutions.com/Blog/tabid/427/entryid/1965/C-Developing-a-LicenseProvider-License-Getting-the-CPU-rsquo-s-ID.aspx

참고로, 첨부한 파일은 제가 테스트한 프로젝트 소스입니다.



[이 글에 대해서 여러분들과 의견을 공유하고 싶습니다. 틀리거나 미흡한 부분 또는 의문 사항이 있으시면 언제든 댓글 남겨주십시오.]

[연관 글]






[최초 등록일: ]
[최종 수정일: 7/9/2021]

Creative Commons License
이 저작물은 크리에이티브 커먼즈 코리아 저작자표시-비영리-변경금지 2.0 대한민국 라이센스에 따라 이용하실 수 있습니다.
by SeongTae Jeong, mailto:techsharer at outlook.com

비밀번호

댓글 작성자
 



2012-08-08 02시26분
[전호진] 유용한 정보 도움 많이 되었습니다 ^^
[guest]

... 46  47  48  49  50  51  52  53  [54]  55  56  57  58  59  60  ...
NoWriterDateCnt.TitleFile(s)
12588정성태4/7/202119819개발 환경 구성: 565. PowerShell - New-SelfSignedCertificate를 사용해 CA 인증서 생성 및 인증서 서명 방법
12587정성태4/6/202121095개발 환경 구성: 564. Windows 10 - ClickOnce 배포처럼 사용할 수 있는 MSIX 설치 파일 [1]
12586정성태4/5/202117962오류 유형: 710. Windows - Restart-Computer / shutdown 명령어 수행 시 Access is denied(E_ACCESSDENIED)
12585정성태4/5/202116930개발 환경 구성: 563. 기본 생성된 kubeconfig 파일의 내용을 새롭게 생성한 인증서로 구성하는 방법
12584정성태4/1/202118103개발 환경 구성: 562. kubeconfig 파일 없이 kubectl 옵션만으로 실행하는 방법
12583정성태3/29/202119021개발 환경 구성: 561. kubectl 수행 시 다른 k8s 클러스터로 접속하는 방법
12582정성태3/29/202118417오류 유형: 709. Visual C++ - 컴파일 에러 error C2059: syntax error: '__stdcall'
12581정성태3/28/202118387.NET Framework: 1031. WinForm/WPF에서 Console 창을 띄워 출력하는 방법 (2) - Output 디버깅 출력을 AllocConsole로 우회 [2]
12580정성태3/28/202116228오류 유형: 708. SQL Server Management Studio - Execution Timeout Expired.
12579정성태3/28/202116819오류 유형: 707. 중첩 가상화(Nested Virtualization) - The virtual machine could not be started because this platform does not support nested virtualization.
12578정성태3/27/202117254개발 환경 구성: 560. Docker Desktop for Windows 기반의 Kubernetes 구성 (2) - WSL 2 인스턴스에 kind가 구성한 k8s 서비스 위치
12577정성태3/26/202118903개발 환경 구성: 559. Docker Desktop for Windows 기반의 Kubernetes 구성 - WSL 2 인스턴스에 kind 도구로 k8s 클러스터 구성
12576정성태3/25/202116941개발 환경 구성: 558. Docker Desktop for Windows에서 DockerDesktopVM 기반의 Kubernetes 구성 (2) - k8s 서비스 위치
12575정성태3/24/202115513개발 환경 구성: 557. Docker Desktop for Windows에서 DockerDesktopVM 기반의 Kubernetes 구성 [1]
12574정성태3/23/202121029.NET Framework: 1030. C# Socket의 Close/Shutdown 동작 (동기 모드)
12573정성태3/22/202118428개발 환경 구성: 556. WSL 인스턴스 초기 설정 명령어 [1]
12572정성태3/22/202117765.NET Framework: 1029. C# - GC 호출로 인한 메모리 압축(Compaction)을 확인하는 방법파일 다운로드1
12571정성태3/21/202115796오류 유형: 706. WSL 2 기반으로 "Enable Kubernetes" 활성화 시 초기화 실패 [1]
12570정성태3/19/202121107개발 환경 구성: 555. openssl - CA로부터 인증받은 새로운 인증서를 생성하는 방법
12569정성태3/18/202121474개발 환경 구성: 554. WSL 인스턴스 export/import 방법 및 단축 아이콘 설정 방법
12568정성태3/18/202114798오류 유형: 705. C# 빌드 - Couldn't process file ... due to its being in the Internet or Restricted zone or having the mark of the web on the file.
12567정성태3/17/202116866개발 환경 구성: 553. Docker Desktop for Windows를 위한 k8s 대시보드 활성화 [1]
12566정성태3/17/202116675개발 환경 구성: 552. Kubernetes - kube-apiserver와 REST API 통신하는 방법 (Docker Desktop for Windows 환경)
12565정성태3/17/202113443오류 유형: 704. curl.exe 실행 시 dll not found 오류
12564정성태3/16/202114311VS.NET IDE: 160. 새 프로젝트 창에 C++/CLI 프로젝트 템플릿이 없는 경우
12563정성태3/16/202117192개발 환경 구성: 551. C# - JIRA REST API 사용 정리 (3) jira-oauth-cli 도구를 이용한 키 관리
... 46  47  48  49  50  51  52  53  [54]  55  56  57  58  59  60  ...