基於TrueLicense實現產品License驗證功能

受朋友所託,須要給產品加上License驗證功能,進行試用期受權,在試用期事後,產品再也不可用。java

經過研究調查,能夠利用Truelicense開源框架實現,下面分享一下如何利用Truelicense實現受權驗證功能。服務器

在此以前先介紹一下License受權和驗證的原理:app

一、  首先須要生成密鑰對,方法有不少,JDK中提供的KeyTool便可生成。框架

二、  受權者保留私鑰,使用私鑰對包含受權信息(如截止日期,MAC地址等)的license進行數字簽名。工具

三、  公鑰交給使用者(放在驗證的代碼中使用),用於驗證license是否符合使用條件。測試

 

實現步驟(代碼參考前賢網上案例實現,再也不贅寫):this

1、使用KeyTool生成密鑰對加密

轉到CMD命令行,切換到%JAVA_HOME%\jre\bin\security\ 目錄(KeyTool工具通常在此目錄),執行命令生成的密鑰對:spa

一、首先利用KeyTool工具來生成私匙庫:(-alias別名 –validity 3650表示10年有效).net

keytool -genkey -alias privatekey -keystoreprivateKeys.store -validity 3650

二、而後把私匙庫內的公匙導出到一個文件當中:

keytool -export -alias privatekey -file certfile.cer -keystore privateKeys.store

三、而後再把這個證書文件導入到公匙庫:

keytool -import -alias publiccert -file certfile.cer -keystore publicCerts.store

最後生成文件privateKeys.store、publicCerts.store拷貝出來備用。

 

2、生成證書(該部分代碼由受權者獨立保管執行)

一、  首先是 LicenseManagerHolder.java 類

package cn.melina.license;
import de.schlichtherle.license.LicenseManager;
import de.schlichtherle.license.LicenseParam;
 
/**
 * LicenseManagerHolder 
 * @author melina
 */
public class LicenseManagerHolder {
    
    private static LicenseManager licenseManager;
 
    public static synchronized LicenseManager getLicenseManager(LicenseParam licenseParams) {
        if (licenseManager == null) {
            licenseManager = new LicenseManager(licenseParams);
        }
        return licenseManager;
    }
}

二、  而後是主要生成 license 的代碼 CreateLicense.java

package cn.melina.license;
 
import java.io.File;
import java.io.IOException;
import java.io.InputStream;
import java.text.DateFormat;
import java.text.ParseException;
import java.text.SimpleDateFormat;
import java.util.Properties;
import java.util.prefs.Preferences;
import javax.security.auth.x500.X500Principal;
import de.schlichtherle.license.CipherParam;
import de.schlichtherle.license.DefaultCipherParam;
import de.schlichtherle.license.DefaultKeyStoreParam;
import de.schlichtherle.license.DefaultLicenseParam;
import de.schlichtherle.license.KeyStoreParam;
import de.schlichtherle.license.LicenseContent;
import de.schlichtherle.license.LicenseParam;
import de.schlichtherle.license.LicenseManager;
 
/**
 * CreateLicense
 * @author melina
 */
public class CreateLicense {
    //common param
    private static String PRIVATEALIAS = "";
    private static String KEYPWD = "";
    private static String STOREPWD = "";
    private static String SUBJECT = "";
    private static String licPath = "";
    private static String priPath = "";
    //license content
    private static String issuedTime = "";
    private static String notBefore = "";
    private static String notAfter = "";
    private static String consumerType = "";
    private static int consumerAmount = 0;
    private static String info = "";
    // 爲了方便直接用的API裏的例子
    // X500Princal是一個證書文件的固有格式,詳見API
    private final static X500Principal DEFAULTHOLDERANDISSUER = new X500Principal(
            "CN=Duke、OU=JavaSoft、O=Sun Microsystems、C=US");
    
    public void setParam(String propertiesPath) {
        // 獲取參數
        Properties prop = new Properties();
        InputStream in = getClass().getResourceAsStream(propertiesPath);
        try {
            prop.load(in);
        } catch (IOException e) {
            // TODO Auto-generated catch block
            e.printStackTrace();
        }
        PRIVATEALIAS = prop.getProperty("PRIVATEALIAS");
        KEYPWD = prop.getProperty("KEYPWD");
        STOREPWD = prop.getProperty("STOREPWD");
        SUBJECT = prop.getProperty("SUBJECT");
        KEYPWD = prop.getProperty("KEYPWD");
        licPath = prop.getProperty("licPath");
        priPath = prop.getProperty("priPath");
        //license content
        issuedTime = prop.getProperty("issuedTime");
        notBefore = prop.getProperty("notBefore");
        notAfter = prop.getProperty("notAfter");
        consumerType = prop.getProperty("consumerType");
        consumerAmount = Integer.valueOf(prop.getProperty("consumerAmount"));
        info = prop.getProperty("info");
        
    }
 
    public boolean create() {        
        try {
            /************** 證書發佈者端執行 ******************/
            LicenseManager licenseManager = LicenseManagerHolder
                    .getLicenseManager(initLicenseParams0());
            licenseManager.store((createLicenseContent()), new File(licPath));    
        } catch (Exception e) {
            e.printStackTrace();
            System.out.println("客戶端證書生成失敗!");
            return false;
        }
        System.out.println("服務器端生成證書成功!");
        return true;
    }
 
    // 返回生成證書時須要的參數
    private static LicenseParam initLicenseParams0() {
        Preferences preference = Preferences
                .userNodeForPackage(CreateLicense.class);
        // 設置對證書內容加密的對稱密碼
        CipherParam cipherParam = new DefaultCipherParam(STOREPWD);
        // 參數1,2從哪一個Class.getResource()得到密鑰庫;參數3密鑰庫的別名;參數4密鑰庫存儲密碼;參數5密鑰庫密碼
        KeyStoreParam privateStoreParam = new DefaultKeyStoreParam(
                CreateLicense.class, priPath, PRIVATEALIAS, STOREPWD, KEYPWD);
        LicenseParam licenseParams = new DefaultLicenseParam(SUBJECT,
                preference, privateStoreParam, cipherParam);
        return licenseParams;
    }
 
    // 從外部表單拿到證書的內容
        public final static LicenseContent createLicenseContent() {
            DateFormat format = new SimpleDateFormat("yyyy-MM-dd");
            LicenseContent content = null;
            content = new LicenseContent();
            content.setSubject(SUBJECT);
            content.setHolder(DEFAULTHOLDERANDISSUER);
            content.setIssuer(DEFAULTHOLDERANDISSUER);
            try {
                content.setIssued(format.parse(issuedTime));
                content.setNotBefore(format.parse(notBefore));
                content.setNotAfter(format.parse(notAfter));
            } catch (ParseException e) {
                // TODO Auto-generated catch block
                e.printStackTrace();
            }
            content.setConsumerType(consumerType);
            content.setConsumerAmount(consumerAmount);
            content.setInfo(info);
            // 擴展
            content.setExtra(new Object());
            return content;
        }
}

三、  測試程序 licenseCreateTest.java

package cn.melina.license;
import cn.melina.license.CreateLicense;
public class licenseCreateTest {
    public static void main(String[] args){
        CreateLicense cLicense = new CreateLicense();
        //獲取參數
        cLicense.setParam("./param.properties");
        //生成證書
        cLicense.create();
    }
}

四、  生成時使用到的 param.properties 文件以下

##########common parameters###########
#alias
PRIVATEALIAS=privatekey
#key(該密碼生成密鑰對的密碼,須要妥善保管,不能讓使用者知道)
KEYPWD=bigdata123456
#STOREPWD(該密碼是在使用keytool生成密鑰對時設置的密鑰庫的訪問密碼)
STOREPWD=abc123456
#SUBJECT
SUBJECT=bigdata
#licPath
licPath=bigdata.lic
#priPath
priPath=privateKeys.store
##########license content###########
#issuedTime
issuedTime=2014-04-01
#notBeforeTime
notBefore=2014-04-01
#notAfterTime
notAfter=2014-05-01
#consumerType
consumerType=user
#ConsumerAmount
consumerAmount=1
#info
info=this is a license

根據properties文件能夠看出,這裏只簡單設置了使用時間的限制,固然能夠自定義添加更多限制。該文件中表示受權者擁有私鑰,而且知道生成密鑰對的密碼。而且設置license的內容。

 

3、驗證證書(使用證書)(該部分代碼結合須要受權的程序一塊兒使用)

 

一、  首先 LicenseManagerHolder.java 類,同上。

 

二、  而後是主要驗證 license 的代碼 VerifyLicense.java

package cn.melina.license;
 
import java.io.File;
import java.io.IOException;
import java.io.InputStream;
import java.util.Properties;
import java.util.prefs.Preferences;
 
import de.schlichtherle.license.CipherParam;
import de.schlichtherle.license.DefaultCipherParam;
import de.schlichtherle.license.DefaultKeyStoreParam;
import de.schlichtherle.license.DefaultLicenseParam;
import de.schlichtherle.license.KeyStoreParam;
import de.schlichtherle.license.LicenseParam;
import de.schlichtherle.license.LicenseManager;
 
/**
 * VerifyLicense
 * @author melina
 */
public class VerifyLicense {
    //common param
    private static String PUBLICALIAS = "";
    private static String STOREPWD = "";
    private static String SUBJECT = "";
    private static String licPath = "";
    private static String pubPath = "";
    
    public void setParam(String propertiesPath) {
        // 獲取參數
        Properties prop = new Properties();
        InputStream in = getClass().getResourceAsStream(propertiesPath);
        try {
            prop.load(in);
        } catch (IOException e) {
            // TODO Auto-generated catch block
            e.printStackTrace();
        }
        PUBLICALIAS = prop.getProperty("PUBLICALIAS");
        STOREPWD = prop.getProperty("STOREPWD");
        SUBJECT = prop.getProperty("SUBJECT");
        licPath = prop.getProperty("licPath");
        pubPath = prop.getProperty("pubPath");
    }
 
    public boolean verify() {        
        /************** 證書使用者端執行 ******************/
 
        LicenseManager licenseManager = LicenseManagerHolder
                .getLicenseManager(initLicenseParams());
        // 安裝證書
        try {
            licenseManager.install(new File(licPath));
            System.out.println("客戶端安裝證書成功!");
        } catch (Exception e) {
            e.printStackTrace();
            System.out.println("客戶端證書安裝失敗!");
            return false;
        }
        // 驗證證書
        try {
            licenseManager.verify();
            System.out.println("客戶端驗證證書成功!");
        } catch (Exception e) {
            e.printStackTrace();
            System.out.println("客戶端證書驗證失效!");
            return false;
        }
        return true;
    }
 
    // 返回驗證證書須要的參數
    private static LicenseParam initLicenseParams() {
        Preferences preference = Preferences
                .userNodeForPackage(VerifyLicense.class);
        CipherParam cipherParam = new DefaultCipherParam(STOREPWD);
 
        KeyStoreParam privateStoreParam = new DefaultKeyStoreParam(
                VerifyLicense.class, pubPath, PUBLICALIAS, STOREPWD, null);
        LicenseParam licenseParams = new DefaultLicenseParam(SUBJECT,
                preference, privateStoreParam, cipherParam);
        return licenseParams;
    }
}

三、  驗證測試程序 licenseVerifyTest.java

package cn.melina.license;
 
public class licenseVerifyTest {
    public static void main(String[] args){
        VerifyLicense vLicense = new VerifyLicense();
        //獲取參數
        vLicense.setParam("./param.properties");
        //驗證證書
        vLicense.verify();
    }
}

四、  驗證時使用到的Properties文件

##########common parameters###########
#alias
PUBLICALIAS=publiccert
#STOREPWD(該密碼是在使用keytool生成密鑰對時設置的密鑰庫的訪問密碼)
STOREPWD=abc123456
#SUBJECT
SUBJECT=bigdata
#licPath
licPath=bigdata.lic
#pubPath
pubPath=publicCerts.store

根據該驗證的properties能夠看出,使用者只擁有公鑰,沒有私鑰,而且也只知道訪問密鑰庫的密碼,而不能知道生成密鑰對的密碼。

 

4、參考資料

truelicense官網

https://sourceforge.net/projects/truelicense/

luckymelina專欄

https://blog.csdn.net/luckymelina/article/details/22870665

老張專欄(支持綁定MAC地址)

https://blog.csdn.net/jingshuaizh/article/details/44461289

 

 


 做者:朝雨憶輕塵
出處:https://www.cnblogs.com/xifengxiaoma/ 版權全部,歡迎轉載,轉載請註明原文做者及出處。

相關文章
相關標籤/搜索