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
[email protected]
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
注意:
- 若
conf.DataCenter=true
则LicenseExpiryDate
不能设置为unlimited
参见以下LicenseValidator
confluence-${version}.jar com.atlassian.confluence.license.validator.DataCenterLicenseExpiryValidator -
conf.DataCenter=true
只能用于生产环境,使用这种License,Confluence不支持再使用内置的数据库,需要配置受支持的其他数据库。
Jira Software
Description=JIRA Software (Data Center)
ServerID=BSVW-4XDC-RWIW-M083
Organisation=Your Organisation
[email protected]
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
[email protected]
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
[email protected]
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
[email protected]
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
[email protected]
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 |
破解思路
- 将
com.atlassian.extras.decoder.v2.Version2LicenseDecoder
中的
公钥替换成自己的 - 根据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
*/
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);
}
}