📜 ⬆️ ⬇️

Teach Liferay Portal 6.0 EE SP2 to understand license files for version 6.1

Many developers for Liferay Portal 6.0 EE were not satisfied with the policies of Liferay inc. when version 6.1 was released, they lost the opportunity to use trial licenses while working on projects, and the keys to the new version are not backward-compatible. What to do? If a company producing this product does not meet developers and forcibly transfers everyone to a new version, it’s not a good idea to give all developers a real license from a production server for use during development. There is a way out, even if not the most elegant, but if they don’t leave a choice, you can still solve this problem. Welcome under cat.

What will be discussed in this article


In the Liferay Portal 6.0 EE version, there is a way to check licenses of some complexity (this section is not discussed in the article), but it has a very weak point (but we'll just talk about it), which allows, replacing the body of one of the methods, just in one line, forcing Liferay to accept license files from Liferay Portal 6.1 EE. But this is not the main advantage of this approach. As a result of the manipulations described below, it is possible to get the following result: the validation range of the license validity dates will be performed, as well as the value of the number of simultaneous sessions and other fields contained in the license file, but the contents of the <key /> field will not be checked to obtain a license with an unlimited number of simultaneous sessions, for use during development.

Some practice


Consider a class that performs a license check, which is located in the portal-impl.jar library. I think people who understand what they are talking about know where to look for it. Of course, in “APP_SERVER / webapps / ROOT / WEB-INF / lib” if you use Tomcat, by analogy you can find to determine the location of this file when using another server application.
So, we proceed to the practical part.
As a first step, using a Java-familiar tool for developers, such as “JD” or “jad”, “set on” portal-impl.jar, we get the code of the class “com.liferay.portal.ee.license.bd” which, actually, interests us.
Below I give the code that was obtained as a result of the operation described in the previous paragraph, so as not to distract the reader from the article.
package com.liferay.portal.ee.license.b; import com.liferay.portal.ee.license.a; import com.liferay.portal.kernel.log.Log; import com.liferay.portal.kernel.log.LogFactoryUtil; import com.liferay.portal.kernel.util.GetterUtil; import com.liferay.portal.kernel.util.StringUtil; import java.security.MessageDigest; import java.security.NoSuchAlgorithmException; import java.util.ArrayList; import java.util.Collections; import java.util.Date; import java.util.HashMap; import java.util.Iterator; import java.util.List; import java.util.Map; public class d { private static final String[] a = { "MD5", "SHA-1", "SHA-256", "SHA-512" }; private static final String b = "4a4beb2b97c151cff83cbca7096325086817360a7b8c912b66e1d1dea172033a8c5934cbbacbf7b443496cc119a6a482fc6225d28bcbcb2384f52862e6fd35e49a2625f1458d24a1f62e71235dc16b9de5a971e638af32a9784e566f33dd90234d89e1dde83e8a4a100a70d999b2bb7fa77eeb34fd1be9cdf3645f9478b14c2cd6b8f955"; private static final char[] c = { '0', '1', '2', '3', '4', '5', '6', '7', '8', '9', 'a', 'b', 'c', 'd', 'e', 'f' }; private static Log d = LogFactoryUtil.getLog(d.class); private static de = new d(); public static Map a(a parama) { Date localDate1 = parama.p(); Date localDate2 = parama.c(); return a(parama.i(), parama.p(), parama.a(), parama.f(), parama.g(), parama.m(), parama.n(), parama.l(), parama.k(), parama.j(), parama.b(), localDate2.getTime() - localDate1.getTime(), parama.o()); } public static ab(a parama) { String str = parama.g(); if (!str.equals("trial")) return parama; if (!c(parama)) return parama; parama.e("developer"); Map localMap = a(parama); parama.c(ea(localMap)); return parama; } public static boolean c(a a1) { String s = a1.i(); if (s.equals("1")) return true; Map map = a(a1); String s1 = ea(map); if (s1.equals("4a4beb2b97c151cff83cbca7096325086817360a7b8c912b66e1d1dea172033a8c5934cbbacbf7b443496cc119a6a482fc6225d28bcbcb2384f52862e6fd35e49a2625f1458d24a1f62e71235dc16b9de5a971e638af32a9784e566f33dd90234d89e1dde83e8a4a100a70d999b2bb7fa77eeb34fd1be9cdf3645f9478b14c2cd6b8f955")) return false; return s1.equals(a1.d()); } private String a(Map paramMap) { int i = GetterUtil.getInteger((String) paramMap.get("version")); try { if (i == 1) throw new IllegalArgumentException("Invalid version " + i); if (i == 2) return b(paramMap); } catch (Exception localException) { d.error(localException, localException); } return ""; } private String b(Map paramMap) { ArrayList localArrayList = new ArrayList(paramMap.keySet()); Collections.sort(localArrayList); Object localObject = new ArrayList(paramMap.size()); String str1; String str2; for (int i = 0; i < localArrayList.size(); i++) { str1 = (String) paramMap.get(localArrayList.get(i)); str2 = a[(i % a.length)]; String str3 = a(str1, str2); ((List) localObject).add(str3); } localObject = b((List) localObject); for (int i = 0; i < ((List) localObject).size(); i++) { str1 = (String) ((List) localObject).get(i); str2 = a[(i % a.length)]; str1 = a(str1, str2); ((List) localObject).set(i, str1); } return (String) a((List) localObject); } private String a(String paramString1, String paramString2) { MessageDigest localMessageDigest = null; byte[] arrayOfByte = null; try { localMessageDigest = MessageDigest.getInstance(paramString2); localMessageDigest.update(paramString1.getBytes()); arrayOfByte = localMessageDigest.digest(); } catch (NoSuchAlgorithmException e) { } StringBuilder localStringBuilder = new StringBuilder( arrayOfByte.length << 1); for (int i = 0; i < arrayOfByte.length; i++) { int j = arrayOfByte[i] & 0xFF; localStringBuilder.append(c[(j >> 4)]); localStringBuilder.append(c[(j & 0xF)]); } return localStringBuilder.toString(); } private static Map a(String paramString1, Date paramDate, String paramString2, String paramString3, String paramString4, String paramString5, String paramString6, String paramString7, int paramInt1, int paramInt2, String paramString8, long paramLong, String[] paramArrayOfString) { HashMap localHashMap = new HashMap(); localHashMap.put("version", paramString1); if (!paramString4.equals("trial")) localHashMap.put("startDate", String.valueOf(paramDate.getTime())); localHashMap.put("type", paramString4); localHashMap.put("productVersion", paramString6); localHashMap.put("owner", paramString7); localHashMap.put("description", paramString8); if (paramString4.equals("trial")) localHashMap.put("lifetime", String.valueOf(paramLong)); else localHashMap.put("expirationDate", String.valueOf(paramDate.getTime() + paramLong)); String str1; if (paramString1.equals("1")) { if ((paramString4.equals("cluster")) || (paramString4.equals("developer-cluster"))) { for (int i = 0; i < paramArrayOfString.length; i++) { String str2 = StringUtil.replace(paramArrayOfString[i], "-", ":"); str2 = str2.trim().toLowerCase(); localHashMap.put("macAddress." + i, str2); } } else if (paramString4.equals("production")) { str1 = paramArrayOfString[0].trim(); localHashMap.put("serverId", str1); } } else { localHashMap.put("accountEntryName", paramString2); localHashMap.put("licenseEntryName", paramString3); localHashMap.put("productEntryName", paramString5); if ((paramString4.equals("cluster")) || (paramString4.equals("developer-cluster"))) localHashMap.put("maxServers", String.valueOf(paramInt1)); if ((paramString4.equals("developer")) || (paramString4.equals("developer-cluster")) || (paramString4.equals("trial"))) localHashMap.put("maxHttpSessions", String.valueOf(paramInt2)); if (paramString4.equals("production")) { str1 = StringUtil.merge(paramArrayOfString); str1 = str1.toLowerCase(); str1 = StringUtil.replace(str1, "-", ":"); localHashMap.put("serverIds", str1); } } return localHashMap; } private String a(List paramList) { int i = paramList.size(); int j = 0; int k = 2147483647; Iterator localIterator1 = paramList.iterator(); int n; while (localIterator1.hasNext()) { Object localObject = (String) localIterator1.next(); n = ((String) localObject).length(); j += n; if (n >= k) continue; k = n; } Object localObject = new StringBuilder(j); for (int m = 0; m < k; m++) for (n = 0; n < i; n++) { String str2 = (String) paramList.get(n); ((StringBuilder) localObject).append(str2.charAt(m)); } Iterator localIterator2 = paramList.iterator(); while (localIterator2.hasNext()) { String str1 = (String) localIterator2.next(); if (str1.length() <= k) continue; ((StringBuilder) localObject).append(str1.substring(k)); } return (String) ((StringBuilder) localObject).toString(); } private List b(List paramList) { int i = paramList.size(); int j = i / 4; if (j * 4 < i) j++; ArrayList localArrayList = new ArrayList(4); StringBuilder localStringBuilder = new StringBuilder(); for (int k = 0; k < i; k++) { String str = (String) paramList.get(k); if ((k != 0) && (k % j == 0)) { localArrayList.add(localStringBuilder.toString()); localStringBuilder.setLength(0); } localStringBuilder.append(str); } if (localArrayList.size() < 4) localArrayList.add(localStringBuilder.toString()); return localArrayList; } } 

We are interested in the only method that returns the result of running a license key:
 public static boolean c(a a1) { ... } 

In order for any license file to pass the check, it is necessary to replace the body of this method with the simple construction of returning the value “true”.
 public static boolean c(a a1) { return true; } 

')
Compilation

Now you need to compile the modified class and return it to the proper place. Getting to the compilation, first create a basic directory structure:

 liferay_license_fix/ src/ classes/ lib/ 
liferay_license_fix/ src/ classes/ lib/
Let's save the source code of the class we received by creating in the “src /” the directories corresponding to the package. To compile the target class, we need the additional class “com.liferay.portal.ee.license.a”, which is contained in the same “portal-impl.jar”. When this library is included in the “classpath”, the target class will not compile because it is already contained in portal-impl.jar. Therefore, you need to unpack the class file “com.liferay.portal.ee.license.a” into the “classes” folder created earlier, and also copy “portal-service.jar” from “APP_SERVER / lib / ext” in the case of Tomcat or from another location, depending on your configuration, to the “lib” directory (you can avoid copying “portal-service.jar” by specifying its location in the compiler's “classpath” option, but the article will look at the copying case). You should have the following picture:

 liferay_license_fix/ src/ com/ liferay/ portal/ ee/ license/ b/ d.java classes/ com/ liferay/ portal/ ee/ license/ a.class lib/ portal-service.jar 
liferay_license_fix/ src/ com/ liferay/ portal/ ee/ license/ b/ d.java classes/ com/ liferay/ portal/ ee/ license/ a.class lib/ portal-service.jar
We move to the "liferay_license_fix" directory and compile the target class with the following command:

 javac -cp "lib\portal-service.jar;classes" -d classes src\com\liferay\portal\ee\license\b\d.java 
javac -cp "lib\portal-service.jar;classes" -d classes src\com\liferay\portal\ee\license\b\d.java
The class compiled by us can be found here:

 liferay_license_fix/classes/com/liferay/portal/ee/license/b/d.class 
liferay_license_fix/classes/com/liferay/portal/ee/license/b/d.class

Patch preparation

Now it is necessary to replace the original class contained in portal-impl.jar with the one obtained in the course of the above steps. This can be done either manually, simply by replacing the file inside portal-impl.jar or with the patch and patching tool included in Liferay Portal, but we will need a tool from a newer version than the LR EE 6.0 version those. contained in the Liferay Poral 6.1 EE version, which can be downloaded from the official website, and simply replace the current version of the “patching-tool”. After the above steps are completed, we will prepare a patch. The advantage of this approach is that the installation and removal of a patch is performed using simple commands and makes these operations extremely simple, and also does not have to take care of making backup copies of files affected by the imposition of a patch, and if necessary, you can easily roll back the changes.
Prepare the patch directory structure:

 liferay-license-fix-6012/ backup/ WAR_PATH/ WEB-INF/ lib/ portal-impl.jar/ com/ liferay/ portal/ ee/ license/ b/ jdk5/ WAR_PATH/ WEB-INF/ lib/ portal-impl.jar/ com/ liferay/ portal/ ee/ license/ b/ jdk6/ WAR_PATH/ WEB-INF/ lib/ portal-impl.jar/ com/ liferay/ portal/ ee/ license/ b/ 
liferay-license-fix-6012/ backup/ WAR_PATH/ WEB-INF/ lib/ portal-impl.jar/ com/ liferay/ portal/ ee/ license/ b/ jdk5/ WAR_PATH/ WEB-INF/ lib/ portal-impl.jar/ com/ liferay/ portal/ ee/ license/ b/ jdk6/ WAR_PATH/ WEB-INF/ lib/ portal-impl.jar/ com/ liferay/ portal/ ee/ license/ b/
Consider a possible option to create a directory structure, first create the following directories “WAR_PATH \ WEB-INF / lib / portal-impl.jar /” in “liferay-license-fix-6012 / backup” and copy them into “liferay-license-fix- 6012 / jdk5 ", as well as in" liferay-license-fix-6012 / jdk6 ". Next, we will copy the class compiled above, together with the directory structure of the “com / liferay / portal / ee / license / b / d.class” package into “liferay-license-fix-6012 / jdk5” and “liferay-license-fix-6012 / jdk6. Now you need to unpack the original "com.liferay.portal.ee.license.bd" class from the "portal-impl.jar" in the directory "liferay-license-fix-6012 / backup / WAR_PATH / WEB-INF / lib / portal-impl .jar / com / liferay / portal / ee / license / b ".
Next, create a file "liferay-license-fix-6012 / fixpack_documentation.xml" with the following contents:

 <?xml version="1.0"?> <patch> <id>liferay-license-fix-6012</id> <name>liferay-license-fix</name> <patching-tool-version>2</patching-tool-version> <incremental>false</incremental> <version>1</version> <rank>1</rank> <requirements></requirements> <component>security-hotfix</component> <product>6012</product> <fixed-issues></fixed-issues> <module-name></module-name> <module-id></module-id> </patch> 

The final step in preparing the patch is to create a zip archive "liferay-license-fix-6012.zip" with all the contents of the "liferay-license-fix-6012" directory, not including the directory itself.

Patch management

To install the patch, you need to copy it into "/ patching-tool / patches" and perform the initialization of the "patching-tool" if it has not been done previously with the following command:

 patching-tool auto-discovery 
patching-tool auto-discovery
After the initialization has been completed, you can see the “default.properties” file, in which, if necessary, you can adjust the location paths of the server instance with Liferay Portal 6.0 EE SP2, to which the patches will be applied.
Then using the command:

 patching-tool info 
patching-tool info
You can get information about the current status (list of patches, as well as their status). If the output contains the following lines:

 Available patches: [ I] liferay-license-fix-6012 :: Currently not installed; Will be installed. 
Available patches: [ I] liferay-license-fix-6012 :: Currently not installed; Will be installed.
This means that the patch can be installed.
The command to install the patch:

 patching-tool install 
patching-tool install
If the result of the command is the following:

 The installation was successful. One patch is installed on the system. 
The installation was successful. One patch is installed on the system.
this indicates that the patch has been successfully installed.
If it is necessary to cancel the changes resulting from the installation of the patch, the following command should be executed:

 patching-tool revert 
patching-tool revert
If successful, you will see the following message:

 Revert has been executed successfully. There is no patch installed on the system. 
Revert has been executed successfully. There is no patch installed on the system.
After doing the manipulations, you can safely use the license files from Liferay Portal 6.1 EE or create a license file with the following content:

 <?xml version="1.0"?> <license> <account-name>Liferay Developer</account-name> <owner>Developer</owner> <description>Trial license</description> <product-name>Portal Enterprise</product-name> <product-version>6.0 SP 2</product-version> <license-name>Portal Enterprise</license-name> <license-type>enterprise</license-type> <license-version>2</license-version> <start-date>Sunday, January 1, 2012 00:00:00 AM GMT</start-date> <expiration-date>Thursday, January 1, 2099 00:00:00 AM GMT</expiration-date> <max-http-sessions>1000</max-http-sessions> <key></key> </license> 

Download the archive containing all the files described in the article, as well as the prepared patch and the “patching-tool” of the required version.

UPD. The comments offer a very simple way to renew licenses for LR EE 6.0 SP1 / SP2. Thanks to the user Sauron .

Source: https://habr.com/ru/post/143722/


All Articles