Atlassian 授权机制研究

背景

其实这篇文章一年半前就应该写了,那个时候就曾经研究过 Atlassian 的授权机制。主要是那个时候发现了 Stash 这个宝贝。上网遍寻了一下,也没有发现关于 Stash 的破解。但是,关于 Jira 的破解文章就有好多,仔细看了一下,无外乎也就是替换 Version2LicenseDecoder.class 这个文件,然后使用自己编码的,或者明文的 License 授权文件,来达到激活产品的目的。既然 Jira 已经有很多人写过了,那么我们就拿 Stash 来玩玩吧。

声明:撰写本文的目的,仅在于研究学习 Atlassian 授权的原理和机制,请勿将本文涉及到的任何内容用于商业用途。请大家尽可能地支持正版软件,因为这样我们才能有更多好用的软件。Stash 官方 10 用户的授权仅仅为 $10 美金,也就是 60 多块钱的事儿,对于小团队来说绝对是白菜价了。

License 分析

其实 License 分析的部分早就有人已经做过了,请参考《Atlassian JIRA 授权许可证机制分析》。

不多说,先上一段 Python 代码。注意 M2Crypto 需要自行安装,该代码在 Python 2.7 下运行通过。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
#!/usr/bin/env python
# -*- coding: utf-8 -*-

import M2Crypto
import base64
import hashlib
import struct
import zlib

# Some constants
ENCODED_LICENSE_LENGTH_BASE = 31
LICENSE_PREFIX = ''.join(map(chr, [13, 14, 12, 10, 15]))
SEPARATOR = 'X'
ATLASSIAN_PUBLIC_KEY = './atlassian_pub.pem'

license_key = '''
AAABDg0ODAoPeNptUNtKxDAQfc9XBHyOpKlttZCH2gas9LK0VRF8idlRI91akrS4f293q4iyMAPDn
Msc5uwBtjgDhVmI6VXsRbEf4rTtMKMeQxlYZfTo9MfAb/MmeYqxmGU/ycMGpQaOQyYd8AOfLMUi9
K6NPC+0gsGC2OqjWlSdaDZN3gr068CdmeAPvduPUMkd8LQuS9GkeVKsuFROz7AK+pV7D8YeTBgqp
R4cDHJQID5HbfY/iXxCPcJCVJtXOWi7Hk17PY7a4Q6sW82rafcMpn65s4slJx5qwcxg8oxf06AgN
7TzSZFEAWkuxCNqRcWXJgXzaHQZROg7+0Iv8uwUcjrUZjLqTVr4/74vnyt/nTAsAhR8v6Zm5YfvZ
WVBnwouY7xhT+jwUgIUbRhGVaC8P9JCvDPT1MXIwnCgGqA=X02dp2
'''

# Remove any white spaces and return in the key
license_content = license_key.replace("\r", "").replace("\n", "").replace("\t", "").replace(" ", "")

# Check Version
x_position = license_content.rfind(SEPARATOR)
if license_content[x_position + 1:x_position + 3] != "02":
print "Invalid license version, only license version 2 is supported!"
exit()

# Get the base64 encoded license
license_length = int(license_content[x_position + 3:], ENCODED_LICENSE_LENGTH_BASE)
encoded_bytes = license_content[:license_length]

# Base64 decode
decoded_bytes = base64.b64decode(encoded_bytes)
text_length, = struct.unpack(">I", decoded_bytes[:4])
license_bytes = decoded_bytes[4:4 + text_length]

# Magic 13, 14, 12, 10, 15 for license header
if license_bytes[:5] != LICENSE_PREFIX:
print "Invalid license version 2 file"
exit()

# Get license original text and signature
license_text = zlib.decompress(license_bytes[5:])
license_hash = decoded_bytes[4 + text_length:]

print license_text

# Verify whether it is official key
public_key = M2Crypto.DSA.load_pub_key(ATLASSIAN_PUBLIC_KEY)
official = public_key.verify_asn1(hashlib.sha1(license_bytes).digest(), license_hash)

if official:
print "This license is official signed."
else:
print "This license is not official signed."

其中 license_key 就是我们从 Atlassian 官方得到的试用 License。出于隐私保护的角度出发,我这里使用了大眼夹的 License。./atlassian_pub.pem 是从 Atlassian 官方的授权文件中提取出来的 Public Key,其内容如下:

1
2
3
4
5
6
7
8
9
10
11
12
-----BEGIN PUBLIC KEY-----
MIIBuDCCASwGByqGSM44BAEwggEfAoGBAP1/U4EddRIpUt9KnC7s5Of2EbdSPO9E
AMMeP4C2USZpRV1AIlH7WT2NWPq/xfW6MPbLm1Vs14E7gB00b/JmYLdrmVClpJ+f
6AR7ECLCT7up1/63xhv4O1fnxqimFQ8E+4P208UewwI1VBNaFpEy9nXzrith1yrv
8iIDGZ3RSAHHAhUAl2BQjxUjC8yykrmCouuEC/BYHPUCgYEA9+GghdabPd7LvKtc
NrhXuXmUr7v6OuqC+VdMCz0HgmdRWVeOutRZT+ZxBxCBgLRJFnEj6EwoFhO3zwky
jMim4TwWeotUfI0o4KOuHiuzpnWRbqN/C/ohNWLx+2J6ASQ7zKTxvqhRkImog9/h
WuWfBpKLZl6Ae1UlZAFMO/7PSSoDgYUAAoGBAIvfweZvmGo5otwawI3no7Udanxa
l3hX2haw962KL/nHQrnC4FG2PvUFf34OecSK1KtHDPQoSQ+DHrfdf6vKUJphw0Kn
3gXm4LS8VK/LrY7on/wh2iUobS2XlhuIqEc5mLAUu9Hd+1qxsQkQ50d0lzKrnDqP
sM0WA9htkdJJw2nS
-----END PUBLIC KEY-----

如果你需要验证是否官方签名的话,可以自行创建这个文件,如果不需要的话,把 print license_text 之后的代码段全部删掉便可。运行上面的代码,我们得到如下的信息:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
#Wed Dec 26 09:17:36 CST 2012
Description=JIRA\: Evaluation
CreationDate=2012-12-27
jira.LicenseEdition=ENTERPRISE
Evaluation=true
jira.LicenseTypeName=COMMERCIAL
jira.active=true
licenseVersion=2
MaintenanceExpiryDate=2013-01-26
Organisation=Clippit Test
jira.NumberOfUsers=-1
ServerID=B05L-H0T3-LA75-R4EY
SEN=SEN-L2107857
LicenseID=LIDSEN-L2107857
LicenseExpiryDate=2013-01-26
PurchaseDate=2012-12-27

This license is official signed.

可以看到,这是一个经过官方签名和认证的有效 License,授权信息都已经比较清晰明了了,就不多做解释了。那么 Stash 的授权信息长什么样呢?Stash 的试用 License 大概长成这个样子(部分内容出于隐私保护已经马赛克化):

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
#Wed Feb 25 22:09:41 CST 2015
Description=Stash Unlimited Users [Legacy]\: Evaluation License
CreationDate=2015-02-26
Evaluation=true
stash.active=true
licenseVersion=2
MaintenanceExpiryDate=2015-03-28
Organisation=Tommy Lau
stash.LicenseTypeName=COMMERCIAL
ServerID=ABCD-1234-EFGH-5678
SEN=SEN-L1234567
LicenseID=LIDSEN-L1234567
LicenseExpiryDate=2015-03-28
stash.NumberOfUsers=-1
PurchaseDate=2015-02-26

This license is official signed.

可以看到大大的一个 Evaluation=true,其实只要把这里改成 false,再修改一下对应的信息,比如 Description, MaintenanceExpiryDate, LicenseExpiryDate, Organisation, ServerID 为自己的信息,重新编码一下,就可以生成一个新的证书了。

伪造数字签名

与前文提到的替换公钥的方式不同,我选择了修改源代码然后重新编译的方式。这样的好处是就算是修改了原始文件,官方的授权证书依然可以正常使用。而不会出现输入正版授权反而提示授权无效的情况。

要修改的主要是名为 Version2LicenseDecoder.classcom.atlassian.extras.decoder.v2.Version2LicenseDecoder 类。对于我现在使用的 Stash v3.7.0 来说,主要是以下两个文件:

  • atlassian-extras-decoder-v2-3.2.jar
  • atlassian-universal-plugin-manager-plugin-2.18.2.jar

第一个是用于 Stash 授权的,第二个是用于 Plugin 授权的。同时第二个文件是打包在 stash-bundled-plugins.zip 文件中的,需要提取、修改后重新替换包内的同名文件。而对于 Version2LicenseDecoder.java 的修改,主要是把 127 行开始的数字签名代码屏蔽掉,如下:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
/*
try
{
Signature signature = Signature.getInstance("SHA1withDSA");
signature.initVerify(PUBLIC_KEY);
signature.update(licenseText);
if (!signature.verify(hash))
{
throw new LicenseException("Failed to verify the license.");
}

}
catch (InvalidKeyException e)
{
throw new LicenseException(e);
}
catch (SignatureException e)
{
throw new LicenseException(e);
}
catch (NoSuchAlgorithmException e)
{
throw new LicenseException(e);
}
*/

然后重新编译成 Version2LicenseDecoder.class,并替换上述提到的两个文件,atlassian-extras-decoder-v2-3.2.jaratlassian-universal-plugin-manager-plugin-2.18.2.jar。这样,任意数字签名的符合 Atlassian 编码规范的 License 都可以正常的进行授权了。

最后

写本文的目的和初衷,都是为了研究和学习。请勿向本人索取修改好的文件,帮忙生成证书等。如果想要生成证书,请参考下面引用的博文。

参考: