Atlassian系列软件破解研究记录

声明

仅供个人学习、研究使用,请勿用于商业用途。

基于的版本

产品 版本
Confluence 6.0.6
Jira Software 7.3.3
Bitbucket 4.14.2
Bamboo 5.15.3
Crucible & FishEye 4.3.1

License 明文内容

Confluence

Description=Confluence (Data Center)
ServerID=BSVW-4XDC-RWIW-M083
Organisation=Your Organisation
ContactEMail=example@example.com
LicenseTypeName=COMMERCIAL
NumberOfUsers=-1
Subscription=true
Evaluation=false
PurchaseDate=2017-03-14
CreationDate=2017-03-14
LicenseExpiryDate=unlimited
MaintenanceExpiryDate=253402257599855
LicenseID=LIDSEN-L9418768
SEN=SEN-L9418768
licenseVersion=2

conf.LicenseEdition=ENTERPRISE
conf.DataCenter=false
conf.active=true
conf.NumberOfUsers=-1
conf.Starter=false
conf.LicenseTypeName=COMMERCIAL

注意:

  1. conf.DataCenter=trueLicenseExpiryDate不能设置为unlimited
    参见以下LicenseValidator
    confluence-${version}.jar com.atlassian.confluence.license.validator.DataCenterLicenseExpiryValidator
  2. conf.DataCenter=true 只能用于生产环境,使用这种License,Confluence不支持再使用内置的数据库,需要配置受支持的其他数据库。

Jira Software

Description=JIRA Software (Data Center)
ServerID=BSVW-4XDC-RWIW-M083
Organisation=Your Organisation
ContactEMail=example@example.com
LicenseTypeName=COMMERCIAL
NumberOfUsers=-1
Subscription=true
Evaluation=false
PurchaseDate=2017-03-15
CreationDate=2017-03-15
LicenseExpiryDate=253402257599855
MaintenanceExpiryDate=253402257599855
LicenseID=LIDSEN-L9423264
SEN=SEN-L9423264
licenseVersion=2

greenhopper.LicenseEdition=ENTERPRISE
jira.NumberOfUsers=-1
jira.product.jira-software.active=true
jira.product.jira-software.DataCenter=false
jira.LicenseEdition=ENTERPRISE
greenhopper.LicenseTypeName=COMMERCIAL
jira.product.jira-software.NumberOfUsers=-1
jira.DataCenter=false
jira.product.jira-software.Starter=false
greenhopper.enterprise=true
jira.active=true
jira.LicenseTypeName=COMMERCIAL
greenhopper.active=true

Bitbucket (原 Stash)

Description=Bitbucket (Data Center)
ServerID=BSVW-4XDC-RWIW-M083
Organisation=Your Organisation
ContactEMail=example@example.com
LicenseTypeName=COMMERCIAL
NumberOfUsers=-1
Subscription=true
Evaluation=false
PurchaseDate=2017-03-14
CreationDate=2017-03-14
LicenseExpiryDate=unlimited
MaintenanceExpiryDate=253402257599855
LicenseID=LIDSEN-L9418768
SEN=SEN-L9418768
licenseVersion=2

stash.LicenseTypeName=COMMERCIAL
stash.DataCenter=false
stash.active=true
stash.NumberOfUsers=-1
stash.Starter=false

Bamboo

Description=Bamboo (Server) unlimited Remote Agents
ServerID=BSVW-4XDC-RWIW-M083
Organisation=Your Organisation
ContactEMail=example@example.com
LicenseTypeName=COMMERCIAL
NumberOfUsers=-1
Subscription=true
Evaluation=false
PurchaseDate=2017-03-14
CreationDate=2017-03-14
LicenseExpiryDate=unlimited
MaintenanceExpiryDate=253402257599855
LicenseID=LIDSEN-L9418768
SEN=SEN-L9418768
licenseVersion=2

bamboo.LicenseEdition=ENTERPRISE
bamboo.NumberOfBambooLocalAgents=-1
bamboo.active=true
bamboo.NumberOfBambooRemoteAgents=-1
bamboo.NumberOfBambooPlans=-1
bamboo.LicenseTypeName=COMMERCIAL

Crucible & FishEye

Description=Crucible (Server)
ServerID=B164-53DN-93PE-5MRN
Organisation=Your Organisation
ContactEMail=example@example.com
LicenseTypeName=COMMERCIAL
NumberOfUsers=-1
Subscription=true
Evaluation=false
PurchaseDate=2017-03-14
CreationDate=2017-03-14
LicenseExpiryDate=unlimited
MaintenanceExpiryDate=4102401599852
LicenseID=LIDSEN-L9418768
SEN=SEN-L9418768
licenseVersion=2

crucible.LicenseTypeName=COMMERCIAL
crucible.Starter=false
crucible.NumberOfUsers=-1
crucible.active=true
Description=FishEye (Server)
ServerID=B164-53DN-93PE-5MRN
Organisation=Your Organisation
ContactEMail=example@example.com
LicenseTypeName=COMMERCIAL
NumberOfUsers=-1
Subscription=true
Evaluation=false
PurchaseDate=2017-03-14
CreationDate=2017-03-14
LicenseExpiryDate=unlimited
MaintenanceExpiryDate=4102401599852
LicenseID=LIDSEN-L9418768
SEN=SEN-L9418768
licenseVersion=2

fisheye.active=true
fisheye.Starter=false
fisheye.LicenseTypeName=COMMERCIAL
fisheye.NumberOfUsers=-1

License 结构

License是一个basee64字符串,由三部分组成

第一部分:License
第二部分:License版本号 固定值 X02
第三部分:第一部分长度 31进制形式

上面的第一部分base64解码后,又由以下四个部分组成

第一部分:第二部加第三部分的分长度 4个字节
第二部分:License前缀 当前为5个字节
第三部分:zip压缩后的License内容
第四部分:第二部加第三部分的签名

签名算法为 SHA1withDSA

代码 com.atlassian.extras.decoder.v2.Version2LicenseDecoder#checkAndGetLicenseText(String) 方法中

com.atlassian.extras.decoder.v2.Version2LicenseDecoder所在包

产品 所在包
Confluence $INSTALL_DIR/confluence/WEB-INF/lib/atlassian-extras-decoder-v2-3.2.jar
Jira Software $INSTALL_DIR/atlassian-jira/WEB-INF/lib/atlassian-extras-3.2.jar
Bitbucket $INSTALL_DIR/atlassian-bitbucket/WEB-INF/lib/atlassian-extras-decoder-v2-3.3.0.jar
Bamboo $INSTALL_DIR/atlassian-bamboo/WEB-INF/lib/atlassian-extras-decoder-v2-3.3.0.jar
Crucible & FishEye $INSTALL_DIR/lib/atlassian-extras-2.5.jar

破解思路

  1. com.atlassian.extras.decoder.v2.Version2LicenseDecoder中的
    公钥替换成自己的
  2. 根据License的结构,使用自己的私钥生成License

成果

破解成功!

替换公钥

反编 --> 替换公钥 --> 编译 --> 替换原jar包的class文件

生成 License 代码

import java.io.ByteArrayOutputStream;
import java.io.DataOutputStream;
import java.security.KeyFactory;
import java.security.PrivateKey;
import java.security.PublicKey;
import java.security.Signature;
import java.security.spec.PKCS8EncodedKeySpec;
import java.security.spec.X509EncodedKeySpec;
import java.util.Base64;
import java.util.Scanner;
import java.util.zip.Deflater;
import java.util.zip.DeflaterOutputStream;

/**
 * @author Shauway <shauway@qq.com>
 */
public class AtlassianLicenseGenerator {
    public static final String CONFLUENCE_SERVER_LICENSE_DESC = "Confluence (Server)";
    public static final String CONFLUENCE_DATA_CENTER_LICENSE_DESC = "Confluence (Data Center)";
    public static final String JIRA_SOFTWARE_SERVER_LICENSE_DESC = "Jira Software (Server)";
    public static final String JIRA_SOFTWARE_DATA_CENTER_LICENSE_DESC = "Jira Software (Data Center)";
    public static final String BITBUCKET_SERVER_LICENSE_DESC = "Bitbucket (Server)";
    public static final String BITBUCKET_DATA_CENTER_LICENSE_DESC = "Bitbucket (Data Center)";
    public static final String BAMBOO_SERVER_LICENSE_DESC = "Bamboo (Server) Unlimited Remote Agents";
    public static final String CRUCIBLE_SERVER_LICENSE_DESC = "Crucible (Server)";
    public static final String FISH_EYE_SERVER_LICENSE_DESC = "FishEye (Server)";
    static String COMMON_LICENSE_CONTENT = "Description=$$licenseDesc$$\n" +
            "ServerID=$$serverid$$\n" +
            "Organisation=$$org$$\n" +
            "ContactEMail=$$email$$\n" +
            "LicenseTypeName=COMMERCIAL\n" +
            "NumberOfUsers=-1\n" +
            "Subscription=true\n" +
            "Evaluation=false\n" +
            "PurchaseDate=2017-03-14\n" +
            "CreationDate=2017-03-14\n" +
            "LicenseExpiryDate=$$license_expiry_date$$\n" +
            "MaintenanceExpiryDate=$$maintenance_expiry_date$$\n" +
            "LicenseID=LIDSEN-L9418768\n" +
            "SEN=SEN-L9418768\n" +
            "licenseVersion=2\n";
    static String CONFLUENCE_SPECIAL_LICENSE_CONTENT = "conf.LicenseEdition=ENTERPRISE\n" +
            "conf.DataCenter=$$data_center$$\n" +
            "conf.active=true\n" +
            "conf.NumberOfUsers=-1\n" +
            "conf.Starter=false\n" +
            "conf.LicenseTypeName=COMMERCIAL";
    static String JIRA_SPECIAL_LICENSE_CONTENT = "greenhopper.LicenseEdition=ENTERPRISE\n" +
            "jira.NumberOfUsers=-1\n" +
            "jira.product.jira-software.active=true\n" +
            "jira.product.jira-software.DataCenter=false\n" +
            "jira.LicenseEdition=ENTERPRISE\n" +
            "greenhopper.LicenseTypeName=COMMERCIAL\n" +
            "jira.product.jira-software.NumberOfUsers=-1\n" +
            "jira.DataCenter=$$data_center$$\n" +
            "jira.product.jira-software.Starter=false\n" +
            "greenhopper.enterprise=true\n" +
            "jira.active=true\n" +
            "jira.LicenseTypeName=COMMERCIAL\n" +
            "greenhopper.active=true";
    static String BITBUCKET_SPECIAL_LICENSE_CONTENT = "stash.LicenseTypeName=COMMERCIAL\n" +
            "stash.DataCenter=$$data_center$$\n" +
            "stash.active=true\n" +
            "stash.NumberOfUsers=-1\n" +
            "stash.Starter=false";
    static String BAMBOO_SPECIAL_LICENSE_CONTENT = "bamboo.LicenseEdition=ENTERPRISE\n" +
            "bamboo.NumberOfBambooLocalAgents=-1\n" +
            "bamboo.active=true\n" +
            "bamboo.NumberOfBambooRemoteAgents=-1\n" +
            "bamboo.NumberOfBambooPlans=-1\n" +
            "bamboo.LicenseTypeName=COMMERCIAL";
    static String CRUCIBLE_SPECIAL_LICENSE_CONTENT = "crucible.LicenseTypeName=COMMERCIAL\n" +
            "crucible.Starter=false\n" +
            "crucible.NumberOfUsers=-1\n" +
            "crucible.active=true";
    static String FISH_SPECIAL_LICENSE_CONTENT = "fisheye.active=true\n" +
            "fisheye.Starter=false\n" +
            "fisheye.LicenseTypeName=COMMERCIAL\n" +
            "fisheye.NumberOfUsers=-1";

    static String LINE_SEPARATOR = System.getProperty("line.separator");
    static Scanner scanner = new Scanner(System.in);

    public static void main(String[] args) throws Exception {
        StringBuffer prompt = new StringBuffer();
        prompt.append("Select license type:").append(LINE_SEPARATOR);
        prompt.append("\t11: ").append(CONFLUENCE_SERVER_LICENSE_DESC).append(LINE_SEPARATOR);
        prompt.append("\t12: ").append(CONFLUENCE_DATA_CENTER_LICENSE_DESC).append(LINE_SEPARATOR);
        prompt.append("\t21: ").append(JIRA_SOFTWARE_SERVER_LICENSE_DESC).append(LINE_SEPARATOR);
        prompt.append("\t22: ").append(JIRA_SOFTWARE_DATA_CENTER_LICENSE_DESC).append(LINE_SEPARATOR);
        prompt.append("\t31: ").append(BITBUCKET_SERVER_LICENSE_DESC).append(LINE_SEPARATOR);
        prompt.append("\t32: ").append(BITBUCKET_DATA_CENTER_LICENSE_DESC).append(LINE_SEPARATOR);
        prompt.append("\t4: ").append(BAMBOO_SERVER_LICENSE_DESC).append(LINE_SEPARATOR);
        prompt.append("\t5: ").append(CRUCIBLE_SERVER_LICENSE_DESC).append(LINE_SEPARATOR);
        prompt.append("\t6: ").append(FISH_EYE_SERVER_LICENSE_DESC).append(LINE_SEPARATOR);
        System.out.print(prompt);
        System.out.print(">> Input selected license type code: ");
        int licenseTypeCode = scanner.nextInt();
        switch (licenseTypeCode) {
            case 11:
                COMMON_LICENSE_CONTENT = COMMON_LICENSE_CONTENT.replace("$$licenseDesc$$", CONFLUENCE_SERVER_LICENSE_DESC)
                        .replace("$$license_expiry_date$$", "unlimited")
                        .replace("$$maintenance_expiry_date$$", "253402257599855");
                CONFLUENCE_SPECIAL_LICENSE_CONTENT = CONFLUENCE_SPECIAL_LICENSE_CONTENT.replace("$$data_center$$", "false");
                break;
            case 12:
                COMMON_LICENSE_CONTENT = COMMON_LICENSE_CONTENT.replace("$$licenseDesc$$", CONFLUENCE_DATA_CENTER_LICENSE_DESC)
                        .replace("$$license_expiry_date$$", "253402257599855")
                        .replace("$$maintenance_expiry_date$$", "253402257599855");
                CONFLUENCE_SPECIAL_LICENSE_CONTENT = CONFLUENCE_SPECIAL_LICENSE_CONTENT.replace("$$data_center$$", "true");
                break;
            case 21:
                COMMON_LICENSE_CONTENT = COMMON_LICENSE_CONTENT.replace("$$licenseDesc$$", JIRA_SOFTWARE_SERVER_LICENSE_DESC)
                        .replace("$$license_expiry_date$$", "unlimited")
                        .replace("$$maintenance_expiry_date$$", "253402257599855");
                JIRA_SPECIAL_LICENSE_CONTENT = JIRA_SPECIAL_LICENSE_CONTENT.replace("$$data_center$$", "false");
                break;
            case 22:
                COMMON_LICENSE_CONTENT = COMMON_LICENSE_CONTENT.replace("$$licenseDesc$$", JIRA_SOFTWARE_DATA_CENTER_LICENSE_DESC)
                        .replace("$$license_expiry_date$$", "253402257599855")
                        .replace("$$maintenance_expiry_date$$", "253402257599855");
                JIRA_SPECIAL_LICENSE_CONTENT = JIRA_SPECIAL_LICENSE_CONTENT.replace("$$data_center$$", "true");
                break;
            case 31:
                COMMON_LICENSE_CONTENT = COMMON_LICENSE_CONTENT.replace("$$licenseDesc$$", BITBUCKET_SERVER_LICENSE_DESC)
                        .replace("$$license_expiry_date$$", "unlimited")
                        .replace("$$maintenance_expiry_date$$", "253402257599855");
                BITBUCKET_SPECIAL_LICENSE_CONTENT = BITBUCKET_SPECIAL_LICENSE_CONTENT.replace("$$data_center$$", "false");
                break;
            case 32:
                COMMON_LICENSE_CONTENT = COMMON_LICENSE_CONTENT.replace("$$licenseDesc$$", BITBUCKET_DATA_CENTER_LICENSE_DESC)
                        .replace("$$license_expiry_date$$", "253402257599855")
                        .replace("$$maintenance_expiry_date$$", "253402257599855");
                BITBUCKET_SPECIAL_LICENSE_CONTENT = BITBUCKET_SPECIAL_LICENSE_CONTENT.replace("$$data_center$$", "true");
                break;
            case 4:
                COMMON_LICENSE_CONTENT = COMMON_LICENSE_CONTENT.replace("$$licenseDesc$$", BAMBOO_SERVER_LICENSE_DESC)
                        .replace("$$license_expiry_date$$", "unlimited")
                        .replace("$$maintenance_expiry_date$$", "4102401599852");
                break;
            case 5:
                COMMON_LICENSE_CONTENT = COMMON_LICENSE_CONTENT.replace("$$licenseDesc$$", CRUCIBLE_SERVER_LICENSE_DESC)
                        .replace("$$license_expiry_date$$", "unlimited")
                        .replace("$$maintenance_expiry_date$$", "4102401599852");
                break;
            case 6:
                COMMON_LICENSE_CONTENT = COMMON_LICENSE_CONTENT.replace("$$licenseDesc$$", FISH_EYE_SERVER_LICENSE_DESC)
                        .replace("$$license_expiry_date$$", "unlimited")
                        .replace("$$maintenance_expiry_date$$", "4102401599852");
                break;
            default:
                System.out.println("Invalid license type code!");
                System.exit(1);
        }
        System.out.print(">> Input your server id: ");
        String serverId = scanner.next();
        System.out.print(">> Input your organisation name: ");
        String organisation = scanner.next();
        System.out.print(">> Input your email address: ");
        String email = scanner.next();
        COMMON_LICENSE_CONTENT = COMMON_LICENSE_CONTENT.replace("$$serverid$$", serverId)
                .replace("$$org$$", organisation).replace("$$email$$", email);
        String licenseContent = null;
        switch (licenseTypeCode) {
            case 11:
            case 12:
                licenseContent = COMMON_LICENSE_CONTENT + CONFLUENCE_SPECIAL_LICENSE_CONTENT;
                break;
            case 21:
            case 22:
                licenseContent = COMMON_LICENSE_CONTENT + JIRA_SPECIAL_LICENSE_CONTENT;
                break;
            case 31:
            case 32:
                licenseContent = COMMON_LICENSE_CONTENT + BITBUCKET_SPECIAL_LICENSE_CONTENT;
                break;
            case 4:
                licenseContent = COMMON_LICENSE_CONTENT + BAMBOO_SPECIAL_LICENSE_CONTENT;
                break;
            case 5:
                licenseContent = COMMON_LICENSE_CONTENT + CRUCIBLE_SPECIAL_LICENSE_CONTENT;
                break;
            case 6:
                licenseContent = COMMON_LICENSE_CONTENT + FISH_SPECIAL_LICENSE_CONTENT;
                break;
        }
        System.out.println();
        System.out.println("Your license content. Enjoy it!");
        System.out.println();
        System.out.println(getLicense(licenseContent));
        System.out.println();
        System.out.println(licenseContent);
    }

    static PublicKey publicKey;
    static PrivateKey privateKey;
    static byte[] licenseTextPrefix = {13, 14, 12, 10, 15};

    static {
        try {
            KeyFactory keyFactory = KeyFactory.getInstance("DSA");
            publicKey = keyFactory.generatePublic(new X509EncodedKeySpec(Base64.getUrlDecoder().decode("MIIBuDCCASwGByqGSM44BAEwggEfAoGBAP1_U4EddRIpUt9KnC7s5Of2EbdSPO9EAMMeP4C2USZpRV1AIlH7WT2NWPq_xfW6MPbLm1Vs14E7gB00b_JmYLdrmVClpJ-f6AR7ECLCT7up1_63xhv4O1fnxqimFQ8E-4P208UewwI1VBNaFpEy9nXzrith1yrv8iIDGZ3RSAHHAhUAl2BQjxUjC8yykrmCouuEC_BYHPUCgYEA9-GghdabPd7LvKtcNrhXuXmUr7v6OuqC-VdMCz0HgmdRWVeOutRZT-ZxBxCBgLRJFnEj6EwoFhO3zwkyjMim4TwWeotUfI0o4KOuHiuzpnWRbqN_C_ohNWLx-2J6ASQ7zKTxvqhRkImog9_hWuWfBpKLZl6Ae1UlZAFMO_7PSSoDgYUAAoGBAOshUqTDMJgJhrrooXl9ajUjDyunW8FSX1IjOOyNRwd0TEwtzfZzzAzUsGm4bPYjIHQpe1ovONVVUpEzYJGJMxVXbbBHQYMbevdvSUdq90LLWXhgwwlXRAwqPq9S0YZP7r9uisPruk59LVj-D-L_GVacH01LlWkm74ya1CusMxDc")));
            privateKey = keyFactory.generatePrivate(new PKCS8EncodedKeySpec(Base64.getUrlDecoder().decode("MIIBTAIBADCCASwGByqGSM44BAEwggEfAoGBAP1_U4EddRIpUt9KnC7s5Of2EbdSPO9EAMMeP4C2USZpRV1AIlH7WT2NWPq_xfW6MPbLm1Vs14E7gB00b_JmYLdrmVClpJ-f6AR7ECLCT7up1_63xhv4O1fnxqimFQ8E-4P208UewwI1VBNaFpEy9nXzrith1yrv8iIDGZ3RSAHHAhUAl2BQjxUjC8yykrmCouuEC_BYHPUCgYEA9-GghdabPd7LvKtcNrhXuXmUr7v6OuqC-VdMCz0HgmdRWVeOutRZT-ZxBxCBgLRJFnEj6EwoFhO3zwkyjMim4TwWeotUfI0o4KOuHiuzpnWRbqN_C_ohNWLx-2J6ASQ7zKTxvqhRkImog9_hWuWfBpKLZl6Ae1UlZAFMO_7PSSoEFwIVAIPdS-RMIsqurIg1ONM3UjobnZiz")));
        } catch (Exception e) {
        }
    }

    static String getLicense(String licenseContent) throws Exception {
        byte[] licenseTextBytes = licenseContent.getBytes("UTF-8"); // 未压缩

        Deflater deflater = new Deflater(Deflater.BEST_COMPRESSION);
        ByteArrayOutputStream baos2 = new ByteArrayOutputStream();
        DeflaterOutputStream deflaterOutputStream = new DeflaterOutputStream(baos2, deflater);
        deflaterOutputStream.write(licenseTextBytes);
        deflaterOutputStream.close();
        byte[] licenseTextBytesZiped = baos2.toByteArray(); // 已压缩

        Signature signature = Signature.getInstance("SHA1withDSA");
        signature.initSign(privateKey);
        signature.update(licenseTextPrefix);
        signature.update(licenseTextBytesZiped);
        byte[] sign = signature.sign();

        ByteArrayOutputStream baos = new ByteArrayOutputStream();
        DataOutputStream dos = new DataOutputStream(baos);
        dos.writeInt(licenseTextBytesZiped.length + licenseTextPrefix.length);
        dos.write(licenseTextPrefix);
        dos.write(licenseTextBytesZiped);
        dos.write(sign);

        dos.close();
        byte[] licenseContentBytes = baos.toByteArray();
        String licenseContentBase64 = Base64.getEncoder().encodeToString(licenseContentBytes);
        String licenseContentWithVersion = licenseContentBase64 + "X02";
        return licenseContentWithVersion + Integer.toString(licenseContentBase64.length(), 31);
    }
}


作者:Shauway
链接:http://www.jianshu.com/p/20dbcf85f962
來源:简书
著作权归作者所有。商业转载请联系作者获得授权,非商业转载请注明出处。
阅读更多
个人分类: Tool
想对作者说点什么? 我来说一句

没有更多推荐了,返回首页

关闭
关闭
关闭