證書鏈設計

加密

加密(英語:Encryption)是將明文信息改變?yōu)殡y以讀取的密文內容郁副,使之不可讀的過程挖滤。

  • 不可逆加密算法
    ??例如:MD4晚缩,MD5,HASH漱受,
  • 可逆加密算法
    • 對稱加密
      ??DES算法络凿,3DES算法,TDEA算法昂羡,Blowfish算法絮记,RC5算法,IDEA算法
    • 非對稱加密
      ??RSA虐先、Elgamal怨愤、背包算法、Rabin蛹批、D-H撰洗、ECC(橢圓曲線加密算法)。
      ??使用最廣泛的是RSA算法般眉,Elgamal是另一種常用的非對稱加密算法了赵。
      RSA原理
      AES原理

證書鏈

20180530103403964.png
  • 授權
    • 生成head
    • 生成證書鏈Message+DN(RSA+SHA:父證書的privateKey)+publicKey(父證書);
    • AES加密+BASE64編碼甸赃;

ROOT證書 check> 中間證書 check> 子證書

圖片1.png

  • 證書格式
    input
  {
    "company": "CompanyNameHere",
    "company_id": CompanyIDHere,
    "type": "redistribute || leaf",
    "product": "ProductNameIncludedInPorductList",
    "model_secret": true,
    "capability": {
      "track": true
    },
    "limit": {
      "uuid": "test_uuid",
      "expiration": [20160501, 20160630],
      "appid": ["com.example.app1", "com.test.*"],
      "cores": 4,
      "_hardware_auth": "athsa204a",
      "_hardware_auth_key": "YWQwYWE0NWVhM2Y5MDNkOWM4ZDQxN2U4NmRmMDU1MzE="
    },
    "counter": {
      "threads": 4
    }
  }

output

############################################################
#  YinLib License
#  License Product : ProductNameIncludedInPorductList
#  Expiration : 20160501 ~ 20160630
#  License SN : 63f09206-1c46-4247-8788-ba9b16bb1994
############################################################
GWfTA0kPlx4+THgLJKZByzgtewJg2AML0Drr33qTzZkTTeuB0K3n6jVVITsY
ocJxd2w45pIX9GVdbHXl/aaeidWWDLcXAYsig05k34PbxarmdJvLJYUTHjk0
EN3r+L6sgutdl974QSX+qHqD60fTNu6F801evs9DXBAs2dWphwG0AxJ9IJIi
npE2nFXJBSfSJ3T7nS157hjF59Y/6xd/1GbGlFZe5onSIZZKvaV3oxHFL6kf
J6a/1mrHnghQwb4D9csXoEK5ky+3lIsMNGKpZeZanEMm/GUN5fDjuZ6TWj6B
FjECjIRtHgFvIaj6+FLskoQgFeuPhMCiAuYqm20e/8UJ8Ay51RMsQ/fgLEI3
v/FAbK6i1B+hKgUk6z8C1XkPeEtF439EF1W+ubj0Z/IOKzyCk87Hcp4GXODD
2N/tlVLjBZ6qhI1jxwbcYYu6HKEM1VpxVJawZgA4YkyNU9LmsKkLLuiKniJz
g1OuqNLkVpgkGiSnpSoZFXTk9OQW7ZXP6mLZA7/HiivILEraP1Wfk4CCayCF
oxxTZpzeQNA5CXWVp4yXNtJdXo2qeX/9AyfQ3PHWdKdYD1aXTldV0tGixOGc
6ayGpbfy6nuZSIFSrko4BEH/VDqMy2orGhyfUMUmfeXa+TnyIi3iFx8yuP2B
1z0U1x/yILc26FePSzdZLWaZLCgBldl9N5nfKvUFrAxHiOK3rtMryokCuS0j
DcKWyPGxiOy5ePFkhrlpDXycngVNF8QhZxy22OLe4VYwWnByGqiChg89t2Gh
x7gWMd+Z9GDmwrnHQ9q/hT8Awo7L1nPNKxY4+xXVq/rg+4ASFeWiXhw0/Q3D
gSluEQy8kmmarnRrgEc1QzZ+lp9Tiemnt64q9u1qSWjqJ1moies/3ynqZA68
7vo0YxGNmKfUW4vQaHfi05AcMtHI2D7c8L8Hnl0=
------------------------------------------------------------
GWfTA0kPlx4+THgLJKZBy9BEimQkYd4jMzDHUnA2hOgCEOSPfmWLlpp7n4eY
5ED5eYLHOB6SXNf+LFyK1GnCgU1bSCzYVA1LRkOmdfGiLvu+iXsTsci8mKea
fsyR9IhbVBhQWqLrBi53QHXIQ9WmF+6F801evs9DXBAs2dWphwG0AxJ9IJIi
npE2nFXJBSfSMZQVOv0nFLV2J4GU5nlAqsebocwKBpJPMlf1FICvADg0imwG
yoTQHuMLaPDWD9kNxl/y8hREC9oUQODmE1yAxl7cPQUpsrCyGcrvDqX/RwYD
R0yqZQLHZrtkqhM1HZ4bmAd6c3ve7Rj/u+/FAl5/LSNvl3vzzUzmAqjytlM3
Gr6BFjECjIRtHgFvIaj6+FLsKSHZE9iSLPXRjcD15LSy44FsbiA9Vg459tKj
YeuyKbbbyq3KvDrGAlMQ0l361YG+puCliofN0P/j13AoElrPYjU4YGp5lXYm
Pn1zYqcAXWMGEVK7iSg4+F4aNqVc5lAcVx8oehIc3zUBsRV6L4fLgtjAWdZB
LNqMn1t0QyQTad44bKZL6m0kYhfhiLJoI9NHQhCDnotyhcV1ELtQtX3hwd+C
3T/HQz8ECurhKA1nvA7TIq8ToKfUUdqspLXDJ+Tk6j4TaZKjqFMrwCNX1tgt
ers05ckKPEX36BSVVCPTkjirVuwkBRp4bLe2Sy4V+NjA8I+jkBOfv9qQkH6w
nYW5C6pkOvPNgZRzMYWk+eDNGG1BB+5ngXxe8u21KhjjUZOdUzXQlA9Gt00O
oF9shltdl3RR1j/Ub9wso23AxuBW4I+zetETUARucq0RJTqWBCuwELfqQM/6
sGmNA5yhj/ggMPG8uJ5kp5nZXz1sx2QvmKK2LIEhYHEMya0/9ml8tmF1RKsK
8lvpzKcSvNIKOlu0EoHn2XGyrGvLDwzHv1ttffN0akqSCvcpl/5vDHCRhA6T
h0u63ELA4QYExYWVNwFY3xXcLfMQfh1BVUbQm3SZIWY=
############################################################

code AES+RSA加密生成License:

public static final String encrypt(String content, String privateKey, String publicKey, String aesKey) throws Exception{
        if(!StringUtils.isNotEmpty(content) || !StringUtils.isNotEmpty(privateKey) || !StringUtils.isNotEmpty(publicKey) || !StringUtils.isNotEmpty(aesKey)){
            return null;
        }
        content = StringUtils.replaceBlank(content);
        String rsaContent = encrypt(content, privateKey, publicKey);
        System.out.println("encrypt rsa content : " + rsaContent.length());
        String encryptContent = AES.encryptToBase64(rsaContent, aesKey);
        System.out.println("encrypt aes content : " + encryptContent);
        return encryptContent;
    }
    
    public static final String encrypt(String content, String privateKey, String publicKey) throws Exception{
        if(!StringUtils.isNotEmpty(content) || !StringUtils.isNotEmpty(privateKey) || !StringUtils.isNotEmpty(publicKey)){
            return null;
        }
        String rsaData = RSA.sign(content, privateKey);
        LicenseContent licenseContent = new LicenseContent();
        licenseContent.setMessage(content);
        licenseContent.setPublicKey(publicKey);
        licenseContent.setMessageDigest(rsaData);
        String objectStr = StringUtils.replaceBlank(sGson.toJson(licenseContent));
        System.out.println("encrypt rsa content : " + objectStr);
        return objectStr;
    }

Format

    public static String generateLicense() {
        GenerateService.generateRootKeyPare();
        GenerateService.generateSubKeyPare();
        String rootContent = FileUtils.read("root.lic");
        String rootLic = generateRootLicense(rootContent);
        String subContent = FileUtils.read("sub.lic");
        String subLic = generateSubLicense(subContent);
        String head = generateHead(subContent);
        String content = head + StringUtils.formatContent(rootLic) + StringUtils.contentSplit() + StringUtils.formatContent(subLic) + StringUtils.headSplit();
        System.out.println("License format : \n" + content);
        return content;
    }
  • 驗證
    • 解析
    • 驗證簽名
    • 驗證權限

解析:License解析
AES:

    public static String decryptFromBase64(String data, String key){
        try {
            byte[] originalData = Base64.decode(data.getBytes());
            byte[] valueByte = decrypt(originalData, key.getBytes(ConfigureEncryptAndDecrypt.CHAR_ENCODING));
            return new String(valueByte, ConfigureEncryptAndDecrypt.CHAR_ENCODING);
        } catch (UnsupportedEncodingException e) {
            throw new RuntimeException("decrypt fail!", e);
        }
    }

解析后的內容:數據+數據簽名+publicKey

{
    "messageDigest":"N4JeSlY1uaBlFggO96O6OdLmkekkAs0QTClSbb4fEvcs7cQiTw7vHctmGsyMvz1laLx7hXRPEdUB2b9oeZJilQ\u003d\u003d",
    "message":"{\"company\":\"yinlib\",\"company_id\":10001,\"type\":\"redistribute\",\"product\":\"rsatest\",\"model_secret\":false,\"capability\":{\"track\":true},\"limit\":{\"uuid\":\"test_uuid\",\"expiration\":[20160501,20160630],\"appid\":[\"com.example.app1\",\"com.test.*\"],\"cores\":4,\"_hardware_auth\":\"athsa204a\",\"_hardware_auth_key\":\"YWQwYWE0NWVhM2Y5MDNkOWM4ZDQxN2U4NmRmMDU1MzE\u003d\"},\"counter\":{\"threads\":4}}",
    "publicKey":"MFwwDQYJKoZIhvcNAQEBBQADSwAwSAJBAIH+jdyjYL7wBeea08YRYky5mV4mSlVx6rIfMECyeb9JE4lUaPBza3p1sq4oFUp01Ke4ei+XKJRIR7rjD6PrfWUCAwEAAQ\u003d\u003d"
}

{
    "messageDigest":"TnLSOAzv+UVFxw+7htXiBToQlw3SLj7PnBSwY2Y0SWW8Kl9ITg94gsQ1LXBok5JWEAkhdpH0fQGrI/AYdyR97g\u003d\u003d",
    "message":"{\"company\":\"CompanyNameHere\",\"company_id\":CompanyIDHere,\"type\":\"redistribute||leaf\",\"product\":\"ProductNameIncludedInPorductList\",\"model_secret\":true,\"capability\":{\"track\":true},\"limit\":{\"uuid\":\"test_uuid\",\"expiration\":[20160501,20160630],\"appid\":[\"com.example.app1\",\"com.test.*\"],\"cores\":4,\"_hardware_auth\":\"athsa204a\",\"_hardware_auth_key\":\"YWQwYWE0NWVhM2Y5MDNkOWM4ZDQxN2U4NmRmMDU1MzE\u003d\"},\"counter\":{\"threads\":4}}",
    "publicKey":"MFwwDQYJKoZIhvcNAQEBBQADSwAwSAJBAIH+jdyjYL7wBeea08YRYky5mV4mSlVx6rIfMECyeb9JE4lUaPBza3p1sq4oFUp01Ke4ei+XKJRIR7rjD6PrfWUCAwEAAQ\u003d\u003d"
}

驗證簽名:驗證License是本人簽發(fā)柿汛,沒有被篡改

    private static boolean checkLicenseChain(String[] licenseChain) {
        if(licenseChain == null || licenseChain.length == 0){
            return false;
        }
        int length = licenseChain.length;
        List<LicenseContent> licenseContents = new ArrayList<LicenseContent>(length); 
        List<LicenseInfo> licenseInfos = new ArrayList<LicenseInfo>(length); 
        for(int i = 0; i < length; i++){
            String content = AES.decryptFromBase64(licenseChain[i], AESKEY);
            LicenseContent licenseContent = sGson.fromJson(content, LicenseContent.class);
            System.out.println(content);
            if(licenseContent == null){
                return false;
            }
            licenseContents.add(licenseContent);
            boolean isSignCheckPass = RSA.checkSign(licenseContent.getMessage(), licenseContent.getMessageDigest(), licenseContent.getPublicKey());
            if(!isSignCheckPass){
                return false;
            }
            LicenseInfo licenseInfo = sGson.fromJson(licenseContent.getMessage(), LicenseInfo.class);
            if(licenseInfo == null){
                return false;
            }
            licenseInfos.add(licenseInfo);
            System.out.println("[" + i + "] : " + isSignCheckPass);
        }
        boolean status = checkLicenseChainPermission(licenseInfos);
        return status;
    }

驗證權限:驗證License的權限

    private static boolean checkLicenseChainPermission(
            List<LicenseInfo> licenseInfos) {
        //check head time / product
        //check child license time/appid/count and so on permission
        return true;
    }

缺陷

  • AES Key是固定的,容易泄露埠对;
  • AES Key泄露后容易被偽造络断;

改進策略

  • AES Key隨機,并由父證書的PrivateKey加密项玛,Root的publicKey隱藏(不公開)貌笨,解析時,由root publickey開始逐級解析出AES Key和publickey;
  • AES Key隨機秘鑰 + 內容的MD5或者其他hash值襟沮,重新生成秘鑰锥惋,驗證是驗證新秘鑰是否包含內容的MD5或者Hash值;
  • 建立父子證書依賴關系开伏,互相驗證關系膀跌;

證書鏈設計Code:GitHub

最后編輯于
?著作權歸作者所有,轉載或內容合作請聯(lián)系作者
  • 序言:七十年代末,一起剝皮案震驚了整個濱河市固灵,隨后出現的幾起案子捅伤,更是在濱河造成了極大的恐慌,老刑警劉巖巫玻,帶你破解...
    沈念sama閱讀 212,222評論 6 493
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件丛忆,死亡現場離奇詭異祠汇,居然都是意外死亡,警方通過查閱死者的電腦和手機熄诡,發(fā)現死者居然都...
    沈念sama閱讀 90,455評論 3 385
  • 文/潘曉璐 我一進店門可很,熙熙樓的掌柜王于貴愁眉苦臉地迎上來,“玉大人凰浮,你說我怎么就攤上這事根穷。” “怎么了导坟?”我有些...
    開封第一講書人閱讀 157,720評論 0 348
  • 文/不壞的土叔 我叫張陵屿良,是天一觀的道長。 經常有香客問我惫周,道長尘惧,這世上最難降的妖魔是什么? 我笑而不...
    開封第一講書人閱讀 56,568評論 1 284
  • 正文 為了忘掉前任递递,我火速辦了婚禮喷橙,結果婚禮上,老公的妹妹穿的比我還像新娘登舞。我一直安慰自己贰逾,他們只是感情好,可當我...
    茶點故事閱讀 65,696評論 6 386
  • 文/花漫 我一把揭開白布菠秒。 她就那樣靜靜地躺著疙剑,像睡著了一般。 火紅的嫁衣襯著肌膚如雪践叠。 梳的紋絲不亂的頭發(fā)上言缤,一...
    開封第一講書人閱讀 49,879評論 1 290
  • 那天,我揣著相機與錄音禁灼,去河邊找鬼管挟。 笑死,一個胖子當著我的面吹牛弄捕,可吹牛的內容都是我干的僻孝。 我是一名探鬼主播,決...
    沈念sama閱讀 39,028評論 3 409
  • 文/蒼蘭香墨 我猛地睜開眼守谓,長吁一口氣:“原來是場噩夢啊……” “哼穿铆!你這毒婦竟也來了?” 一聲冷哼從身側響起分飞,我...
    開封第一講書人閱讀 37,773評論 0 268
  • 序言:老撾萬榮一對情侶失蹤悴务,失蹤者是張志新(化名)和其女友劉穎睹限,沒想到半個月后譬猫,有當地人在樹林里發(fā)現了一具尸體讯檐,經...
    沈念sama閱讀 44,220評論 1 303
  • 正文 獨居荒郊野嶺守林人離奇死亡,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內容為張勛視角 年9月15日...
    茶點故事閱讀 36,550評論 2 327
  • 正文 我和宋清朗相戀三年染服,在試婚紗的時候發(fā)現自己被綠了别洪。 大學時的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片。...
    茶點故事閱讀 38,697評論 1 341
  • 序言:一個原本活蹦亂跳的男人離奇死亡柳刮,死狀恐怖挖垛,靈堂內的尸體忽然破棺而出,到底是詐尸還是另有隱情秉颗,我是刑警寧澤痢毒,帶...
    沈念sama閱讀 34,360評論 4 332
  • 正文 年R本政府宣布,位于F島的核電站蚕甥,受9級特大地震影響哪替,放射性物質發(fā)生泄漏。R本人自食惡果不足惜菇怀,卻給世界環(huán)境...
    茶點故事閱讀 40,002評論 3 315
  • 文/蒙蒙 一凭舶、第九天 我趴在偏房一處隱蔽的房頂上張望。 院中可真熱鬧爱沟,春花似錦帅霜、人聲如沸。這莊子的主人今日做“春日...
    開封第一講書人閱讀 30,782評論 0 21
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽。三九已至括享,卻和暖如春闽铐,著一層夾襖步出監(jiān)牢的瞬間,已是汗流浹背奶浦。 一陣腳步聲響...
    開封第一講書人閱讀 32,010評論 1 266
  • 我被黑心中介騙來泰國打工兄墅, 沒想到剛下飛機就差點兒被人妖公主榨干…… 1. 我叫王不留,地道東北人澳叉。 一個月前我還...
    沈念sama閱讀 46,433評論 2 360
  • 正文 我出身青樓隙咸,卻偏偏與公主長得像,于是被迫代替她去往敵國和親成洗。 傳聞我的和親對象是個殘疾皇子五督,可洞房花燭夜當晚...
    茶點故事閱讀 43,587評論 2 350

推薦閱讀更多精彩內容

  • 之前的項目中接觸過一些加密的方法,也沒有太仔細的進行記錄和研究瓶殃。最近在寫SDK時充包,加密模塊的占比相當之大;借此時機...
    過半_e764閱讀 569評論 0 0
  • 文中首先解釋了加密解密的一些基礎知識和概念,然后通過一個加密通信過程的例子說明了加密算法的作用基矮,以及數字證書的出現...
    梅_梅閱讀 279評論 0 0
  • 本文摘自 騰訊bugly 的文章《全站 HTTPS 來了》,內容有修改钢悲。 大家在使用百度点额、谷歌或淘寶的時候,是否注...
    bnotes閱讀 3,642評論 1 9
  • iOS 側滑返回詳解 BBGestureBack iOS 全屏手勢返回 滑動返回 pop 動畫效果 這種手勢主流A...
    Bonway_Huang閱讀 816評論 6 0
  • 我就像冬日里的殘枝莺琳,渴望著來春的重生还棱。可我卻不能惭等,因為枝頭已被折斷诱贿,掉落在冰冷的土壤上,被肆意的踐踏咕缎,成為了一根朽木珠十。
    LOSEFIND閱讀 290評論 0 0