证书链设计

加密

加密(英语:Encryption)是将明文信息改變為難以讀取的密文內容,使之不可读的过程。

  • 不可逆加密算法
      例如: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是本人签发,没有被篡改

    private static boolean checkLicenseChain(String[] licenseChain) {
        if(licenseChain == null || licenseChain.length == 0){
            return false;
        }
        int length = licenseChain.length;
        List licenseContents = new ArrayList(length); 
        List licenseInfos = new ArrayList(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 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

你可能感兴趣的:(证书链设计)