本帖最后由 huojingang 于 2023-10-15 10:33 编辑
关于camunda ,它是一款工作流组件源自 activity5。
支持 BPMN(工作流和流程自动化)、CMMN(案例管理) 和 DMN(业务决策管理) java 框架。 德国的一个公司开发的。 题外话篇: 一,如何下载企业版 下载地址: https://camunda.com/download/ 下载之前需要注册去camunda官网注册账号,下载的账号和密码会发送到你的邮箱,如图: 点击即可下载。
题外话篇: 二,关于camunda的体验 注册后,他们的销售会联系你购买他们企业版,也就是推推业务员 基本都是给你发meeting会议链接 简单的通过文字会议形式了解了一下的结果就是:人家不服务中国,不做中国市场业务。感兴趣伙伴可以注册下沟通就知道。
===========================正文========================================
下载下来后内容如下: configuration 一些启动的配置 internal 主要的程序包 start.bat Windows下的启动脚本。
启动后访问地址:http://localhost:8080/camunda-welcome/index.html 界面 随便点击 tasklist,或其他的几个界面,就如登录界面。 默认账户密码都是 demo
登录后 这是没有注册的
可以看到这个是普通界面,并没有涵盖企业版的功能,需要输入注册的 License Key 才能看到企业级功能。 依次点击:Admin-》License Key 也可直接访问: http://localhost:8080/camunda/app/admin/default/#/system?section=system-settings-license 进入以下界面: 随便输入点什么,提交一下看看请求地址 请求地址:http://localhost:8080/camunda/api/admin/plugin/license/default/key 返回数据:{"invalidMessage":"License Key has wrong format.","customerId":null,"validUntil":null,"valid":false,"unlimited":false,"licenseProvided":true} 提示格式不对。 通过启动脚本的日志可以看出是使用的Spring-Boot,那么直接可以通过对应的请求地址,直接定位到他的服务类代码。
通过启动脚本分析: 结论:start.bat 调用了 internal\run.bat run.bat执行的程序包为 internal\camunda-bpm-run-core.jar 接下来就是分析这个程序包了,通过jd-gui查看,可以看到是没有加密或者混淆的,可以随便造。
https://github.com/camunda/camunda-bpm-platform 可以看看社区版的代码,这个是开源的,只是功能没有企业版强大。 最后的分析结论: internal\webapps\camunda-license-check-2.7.0.jar 分析完你会知道,最终验证的代码都在这个程序包里面。
题外话: 这里有很多种追踪方法, 这里说下我所用的方式,remotedebug
在run.bat里面的启动方法-jar前面加入以下代码: -agentlib:jdwp=transport=dt_socket,server=y,suspend=y,address=5005 通过通过调试模式启动这个服务 使用jd-gui打开camunda-license-check-2.7.0.jar,保存所有文件为源码,保存后直接导入IDE,有报错不管他 双击start.bat 等待debug连接后启动
IDE打断点位置: 去key那边提交一下key,断点会停下来 看看当前堆栈情况 可以看到索引1就是调用这个方法的地方,索引0是当前的类 索引1: org.camunda.bpm.admin.impl.plugin.resources.LicenseKeyResource
看它的包名可以知道,肯定是plugin模块,刚刚好,当前目录就有一个, 接口地址,请求方式,调用函数,直接拿下! LicenseKeyStore.INSTANCE.validateLicense((LicenseKey)licenseKeyImpl); LicenseKeyStore.INSTANCE.storeLicenseKey(getProcessEngine(), licenseKeyString); 接着正文部分:
本次的目的就是要分析LicenseKeyImpl的validate()函数。 这里直接贴代码,不贴图了
代码1: /* */ public void validate() throws InvalidLicenseException { /* 115 */ boolean signatureValid = SignatureValidator.validateSignature(this.validationData.getBytes(Charset.forName("UTF-8")), this.signature); /* */ /* */ /* 118 */ if (!signatureValid) /* 119 */ throw new InvalidLicenseException("Your license is invalid."); /* 120 */ if (isExpired()) { /* 121 */ throw new InvalidLicenseException("Your license has expired."); /* */ } /* */ } /* */
这里主要是验证一下SignatureValidator.validateSignature的返回值,直接往里面查找validateSignature
代码2: /* */ public static boolean validateSignature(byte[] data, String base64EncodedSignature) { /* 33 */ return validateSignature(data, BASE64.decode(base64EncodedSignature)); /* */ }
继续查找 代码3: /* */ public static boolean validateSignature(byte[] data, byte[] signatureBytes) { /* 44 */ PublicKey publicKey = determinePublicKey(signatureBytes); /* 45 */ String algorithmName = determineAlgorithmName(signatureBytes); /* */ /* */ try { /* 48 */ Signature sig = getSignature(algorithmName); /* 49 */ sig.initVerify(publicKey); /* 50 */ sig.update(data); /* 51 */ return sig.verify(signatureBytes); /* 52 */ } catch (GeneralSecurityException e) { /* 53 */ throw new RuntimeException("Cannot verify signature", e); /* */ } /* */ }
根据代码
PublicKey publicKey = determinePublicKey(signatureBytes); 可以分析这个得知,服务端通过私钥加密,这里再通过公钥去解密。
代码4: /* */ private static PublicKey determinePublicKey(byte[] signatureBytes) { /* */ String keyLocation; /* 82 */ LicenseType licenseType = determineLicenseType(signatureBytes); /* */ /* 84 */ if (licenseType == LicenseType.CAMUNDA_BPM) { /* 85 */ keyLocation = "camunda_public_key.der"; /* 86 */ } else if (licenseType == LicenseType.OPTIMIZE) { /* 87 */ keyLocation = "optimize_public_key.der"; /* */ } else { /* 89 */ keyLocation = "unified_public_key.der"; /* */ } /* */ /* 92 */ return (new PublicKeyFileReader(keyLocation)).getKey(); /* */ }
代码5 /* */ public static LicenseType determineLicenseType(byte[] signatureBytes) { /* */ LicenseType type; /* 70 */ if (signatureBytes.length == 128) { /* 71 */ type = LicenseType.CAMUNDA_BPM; /* 72 */ } else if (signatureBytes.length == 256) { /* 73 */ type = LicenseType.OPTIMIZE; /* */ } else { /* 75 */ type = LicenseType.UNIFIED; /* */ }
这里是判断应该使用哪一种公钥,由于注册的时候会给你一个临时的注册码:
可以确定是 unified_public_key.der 代码6: /* */ private static Signature getSignature(String algorithmName) { /* */ try { /* 113 */ return Signature.getInstance(algorithmName); /* */ } /* 115 */ catch (NoSuchAlgorithmException e) { /* 116 */ throw new RuntimeException("Cannot load signature algorithm " + algorithmName); /* */ } /* */ }
可以得出:
Signature.getInstance(“SHA256withRSA”); 通过这个方法去创建一个签名算法, 设置公钥publicKey,sig.initVerify(publicKey); 要验证的数据data,sig.update(data); 返回一个验证结果:return sig.verify(signatureBytes); ============================================= 分析data是怎么来的: 观察堆栈,不过四层而已,往上回溯。 再第三个地方直接明了,原来data是通过string类下面的getBytes(Charset.forName("UTF-8"))方法生成的
===============到此所有流程分析完毕============================
解决方式有二,以下将一 一详解。
==========================编码过程================================= 通过以上分析 ,得出需要对数据通过公钥验签 首先我们得需要自己生成的一个公钥对。
这里使用的是hutool-crypto工具类 KeyPair rsa1 = SecureUtil.generateKeyPair("RSA", 4096); PublicKey publicKey = rsa1.getPublic(); String publicKeyEncode = Base64.encode(publicKey.getEncoded()); PrivateKey aPrivate = rsa1.getPrivate(); String aPrivateEncode = Base64.encode(aPrivate.getEncoded()); System.out.println("publicKeyEncode:" + publicKeyEncode); System.out.println("aPrivateEncode:" + aPrivateEncode);
编写一段需要验签的数据字符串: 例如:customer = dslt-lvbuqing;expiryDate = 2098-04-07;cawemo = true;optimize = true;camundaBPM = true; 对字符串 转byte[]
byte[] validationDataBytes = validationData.getBytes(StandardCharsets.UTF_8);
用刚才生成的私钥进行验签: Signature signature = Signature.getInstance("SHA256withRSA"); signature.initSign(rsa.getPrivateKey()); signature.update(validationDataBytes); byte[] sign1 = signature.sign(); return Base64.encode(sign1);
记住这个加签的数据
将刚刚生成的私钥存为unified_public_key.der秘钥文件 FileUtil.writeBytes(rsa.getPublicKey().getEncoded(), "unified_public_key.der");
生成的方法使用方法就是替换原来jar包里面公钥文件。
对加签 数据处理成以下格式 KMl0OK6eUNCBdznq6Eu1dXUsTrdZU9Ps7mGjhB9GdlIG6xUEePTyPTbpz FEPAqrIlbGZFgaZubemqyGphyr935im51j5JWGCQzmf13/KMIL+60QoUJ Gad3FN0J+2+UlxwD3lxDuubjfiQdsEVlRZR7VJ00vFECOfeO/qD6kUwP8 s1hRnyg9Q0XcdCGBYqjkEVol0X7A6azX0JSmTO8CiGE8zCqla8sn0+uEg 0raU+ydGcUpF5lbhPYOkU0PFx39eZ1KfwTAnATlY3DXURB+5mEwBfnjgx Rima5OHUHzdi0U9wO6oduGjcgToP6+dS22desY8K7c1AEzTlYsXQ6+Lqf PRZNIqXSqGu7FTByzxycxtV2sWWfm1YqnUV3fFK94CLaNhX+I5RlSqcf5 1pBaY+tqvmYKiMPSBkHY52sLkKKpy768a3LvktTeZ5g3pHOL6YHFgtaAq 8sS7/BFyABBYxG4X3BKhJbkp5xd8FuJ/KhEltLAwnc7LMw9UiSnxGWgGj 9uEuEXzi0dqpT3MQQf5qPZtFNf1xWGGwBABNZxQf95u6DWN12ISlPWjnW CvYLye3rsCAxOQtOESFJm5dzlrhWOUFFz4SuIsRYPG3AsEaNmDalMeQco HeWc012bCEIWwWYEaJ/Ir0uX88DOGRWIRSCTACbgXCj6N9x33QhCeYWM=; customer = dslt-lvbuqing; expiryDate = 2098-04-07; cawemo = true; optimize = true; camundaBPM = true; 可以看到验证成功了,企业功能也出来了 ============================================= 上面是解决方式1,现在讲讲高阶的方法二,需要用到javassist 代码的大概意思是这样的,具体就是替换里面的公钥 static byte[] getbytes(string classname) { if (classname == null) { return null; } class_pool.appendclasspath(new loaderclasspath(thread.currentthread().getcontextclassloader())); string class_name = “org.camunda.bpm.licensecheck.publickeyfilereader”; string class_name_internal = class_name.replace('.', '/'); if (!class_name_internal.equals(classname)) { return null; } try { ctclass ctclass = class_pool.get(class_name); ctmethod cm = ctclass.getdeclaredmethod("readpublickeyfromclasspath"); string source = "这里填写公钥秘钥"; cm.insertbefore(source); return ctclass.tobytecode(); } catch (exception e) { return null; } }
主类代码: public static void premain(String agentArgs, Instrumentation inst) { inst.addtransformer((loader, classname, classbeingredefined, protectiondomain, classfilebuffer) -> getbytes(classname)); }
打包成jar包的调用 这样就可以啦!!!!
完结,撒花 ps:camunda公司真的是地域歧视
注:若转载请注明大神论坛来源(本贴地址)与作者信息。
|