Created
April 12, 2013 18:44
-
-
Save Robertof/5374181 to your computer and use it in GitHub Desktop.
Decrypts JDownloader account data from 'org.jdownloader.settings.accountsettings.accounts.ejs' files. Supports dumping to file and direct printing. For educational purposes only. (...)
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
/** | |
* Created by Robertof <[email protected] / http://about.me/roberto.f>. | |
* File creation date: 12-apr-2013 16.24.15 | |
* Licensed under GNU/GPL v3. | |
* | |
* This program is free software: you can redistribute it and/or modify | |
* it under the terms of the GNU General Public License as published by | |
* the Free Software Foundation, either version 3 of the License, or | |
* (at your option) any later version. | |
* | |
* This program is distributed in the hope that it will be useful, | |
* but WITHOUT ANY WARRANTY; without even the implied warranty of | |
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the | |
* GNU General Public License for more details. | |
* | |
* You should have received a copy of the GNU General Public License | |
* along with this program. If not, see <http://www.gnu.org/licenses/>. | |
*/ | |
package it.robertof; | |
import com.google.gson.Gson; | |
import com.google.gson.reflect.TypeToken; | |
import java.io.BufferedWriter; | |
import java.io.ByteArrayOutputStream; | |
import java.io.File; | |
import java.io.FileInputStream; | |
import java.io.FileWriter; | |
import java.io.InputStream; | |
import java.util.ArrayList; | |
import java.util.Arrays; | |
import java.util.Date; | |
import java.util.HashMap; | |
import java.util.List; | |
import java.util.Map; | |
import java.util.Scanner; | |
import java.util.Set; | |
import java.util.logging.Level; | |
import javax.crypto.Cipher; | |
import javax.crypto.spec.IvParameterSpec; | |
import javax.crypto.spec.SecretKeySpec; | |
public class JDownloaderPwdDecrypter { | |
public static void main(String[] args) | |
{ | |
if (args.length < 1) | |
{ | |
System.out.println ("Please specify a file to decrypt"); | |
System.exit(1); | |
} | |
String datfile = args[0]; | |
File f = new File (datfile); | |
if (!f.exists()) | |
{ | |
System.out.println ("File does not exist"); | |
System.exit (1); | |
} | |
try { | |
System.out.println ("> Reading"); | |
InputStream is = new FileInputStream (f); | |
ByteArrayOutputStream buffer = new ByteArrayOutputStream(); | |
int nRead; | |
byte[] data = new byte[(int)(f.length () + 1)]; | |
while ((nRead = is.read (data, 0, data.length)) != -1) | |
buffer.write (data, 0, nRead); | |
buffer.flush(); | |
byte[] fData = buffer.toByteArray(); | |
// defined in org.jdownloader.settings.AccountSettings | |
byte[] key = new byte[] { 1, 6, 4, 5, 2, 7, 4, 3, 12, 61, 14, 75, -2, -7, -44, 33 }; | |
System.out.println ("> Decrypting"); | |
String plainJson = decrypt (fData, key, key); | |
// done | |
System.out.println ("> Parsing JSON code"); | |
Map<String, List<Account>> json = new Gson().fromJson (plainJson, | |
new TypeToken<Map<String, List<Account>>>(){}.getType()); | |
// TODO: infinite asking, aka "okay, you did X, select again to do X|Y|Z" | |
// .. I know this will be a TODO forever | |
System.out.println ("> Found " + json.size() + " accounts. Select what do you want to do:"); | |
System.out.println ("[1] Dump in pure JSON in decrypted.json"); | |
System.out.println ("[2] Print username/password for an hoster"); | |
System.out.println ("[3] Print everything with a format like host:user:pwd"); | |
System.out.println ("[4] Dump in accounts.txt with host:user:pwd formatting"); | |
System.out.println ("[5] Print every available information about an hoster account"); | |
System.out.println ("[q] Quit"); | |
System.out.print (" > "); | |
Scanner in = new Scanner (System.in); | |
String sel = in.next(); | |
switch (sel) | |
{ | |
case "q": | |
System.out.println ("Quitting."); | |
System.exit (0); | |
break; | |
case "1": | |
System.out.println ("Dumping JSON in decrypted.json..."); | |
File dump = new File ("decrypted.json"); | |
if (!dump.exists()) dump.createNewFile(); | |
BufferedWriter w = new BufferedWriter (new FileWriter (dump)); | |
w.write (plainJson); | |
w.close(); | |
System.out.println ("Done. Quitting."); | |
System.exit (0); | |
break; | |
case "2": | |
System.out.println ("> Insert the desidered hoster for which you want to view usr/pwd."); | |
System.out.println (" > List:"); | |
Set<String> accountList = json.keySet(); | |
for (String accName : accountList) | |
{ | |
System.out.println (" > " + accName); | |
} | |
System.out.print (" > Name: "); | |
String s2 = in.next(); | |
if (!accountList.contains (s2)) | |
{ | |
System.err.println ("Invalid account specified. Try again."); | |
System.exit (1); | |
} | |
// time for some fun parsing | |
ArrayList<Account> accounts = (ArrayList<Account>) json.get (s2); | |
System.out.println (" > Hoster: " + s2); | |
for (Account acc : accounts) | |
{ | |
if (acc.getUser().isEmpty() || acc.getPassword().isEmpty()) | |
continue; | |
System.out.println (" > Username: " + acc.getUser()); | |
System.out.println (" > Password: " + acc.getPassword()); | |
System.out.println (" > ============================"); | |
} | |
System.out.println ("> Done. Quitting."); | |
System.exit (0); | |
break; | |
case "3": | |
System.out.println ("Printing."); | |
System.out.print (get14m0rzFormatting (json)); | |
break; | |
case "4": | |
System.out.println ("> Dumping to accounts.txt.."); | |
File fAcc = new File ("accounts.txt"); | |
if (!fAcc.exists()) fAcc.createNewFile(); | |
BufferedWriter fWriter = new BufferedWriter (new FileWriter (fAcc)); | |
fWriter.write (get14m0rzFormatting (json)); | |
fWriter.close(); | |
System.out.println ("> Completed. Bye."); | |
System.exit (0); | |
break; | |
case "5": | |
System.out.println ("> Please select an hoster from the list."); | |
// inb4 copypaste | |
System.out.println (" > List:"); | |
Set<String> accList = json.keySet(); | |
for (String accName : accList) | |
{ | |
System.out.println (" > " + accName); | |
} | |
System.out.print (" > Hoster: "); | |
String selection = in.next(); | |
if (!accList.contains (selection)) | |
{ | |
System.err.println ("Invalid hoster specified. Try again."); | |
System.exit (1); | |
} | |
ArrayList<Account> datAccounts = (ArrayList<Account>) json.get (selection); | |
System.out.println ("> Printing stuff about " + selection + "..."); | |
int counter = 0; | |
for (Account mmhAccount : datAccounts) | |
{ | |
System.out.println (" > Printing stuff about account #" + ++counter); | |
System.out.println (" > Username: " + mmhAccount.getUser()); | |
System.out.println (" > Password: " + mmhAccount.getPassword()); | |
System.out.println (" > Created (might not be defined): " + parseDate (mmhAccount.getCreateTime())); | |
System.out.println (" > Valid until (might not be defined): " + parseDate (mmhAccount.getValidUntil())); | |
System.out.println (" > Traffic left (might not be defined): " + mmhAccount.getTrafficLeft()); | |
System.out.println (" > Max traffic available (might not be defined): " + mmhAccount.getTrafficMax()); | |
System.out.println (" > Enabled: " + mmhAccount.isEnabled()); | |
System.out.println (" > Valid: " + mmhAccount.isValid()); | |
System.out.println (" > Is traffic unlimited: " + mmhAccount.isTrafficUnlimited()); | |
System.out.println (" > Is concurrent use possible: " + mmhAccount.isConcurrentUsePossible()); | |
if (mmhAccount.getProperties() != null && !((Map) mmhAccount.getProperties()).isEmpty()) | |
{ | |
System.out.println (" > Properties:"); | |
System.out.print (iterateMapRecursive (mmhAccount.getProperties(), 3)); | |
} | |
if (mmhAccount.getInfoProperties() != null && !((Map) mmhAccount.getInfoProperties()).isEmpty()) | |
{ | |
System.out.println (" > Info properties:"); | |
System.out.print (iterateMapRecursive (mmhAccount.getInfoProperties(), 3)); | |
} | |
if (counter != datAccounts.size()) | |
System.out.println (" > =============================="); | |
} | |
break; | |
default: | |
System.err.println ("WTF did you write. Ragequitting"); | |
System.exit (1); | |
break; | |
} | |
} | |
catch (Exception e) | |
{ | |
Log.exception (Level.SEVERE, e); | |
System.exit (1); | |
} | |
} | |
// > test = miao | |
// > gtest = | |
// > abdul | |
// > aziz | |
// > ram = | |
// > wat = r | |
// > u = doin | |
// > ram = | |
// > STAHP | |
// original name: yoDawgIHeardYouLikeMaps | |
private static String iterateMapRecursive (Map<String, Object> db, int spaceIndex) | |
{ | |
String acaro = ""; // grillo, stay out! :( | |
if (db == null) return acaro; | |
for (Map.Entry<String, Object> iter : db.entrySet()) | |
{ | |
acaro += "" + repeat (' ', spaceIndex) + "> " + iter.getKey() + " = "; | |
if (iter.getValue() instanceof Map) | |
acaro += System.getProperty ("line.separator") + iterateMapRecursive ((Map) iter.getValue(), spaceIndex + 1); | |
else if (iter.getValue() instanceof List) | |
{ | |
acaro += System.getProperty ("line.separator"); | |
for (Object val : (List) iter.getValue()) | |
{ | |
if (val instanceof Map) | |
acaro += iterateMapRecursive ((Map) val, spaceIndex + 1); | |
else if (val instanceof List) | |
Log.exception (Level.WARNING, new Exception ("List in lists are not allowed. Sorry YO DAWG fans.")); | |
else | |
acaro += "" + repeat (' ', spaceIndex + 1) + "> " + val + System.getProperty ("line.separator"); | |
} | |
} | |
else | |
{ | |
acaro += iter.getValue() + System.getProperty ("line.separator"); | |
} | |
} | |
return acaro; | |
} | |
private static String repeat (char ch, int length) | |
{ | |
char[] array = new char[length]; | |
Arrays.fill (array, ch); | |
return new String (array); | |
} | |
private static String get14m0rzFormatting (Map<String, List<Account>> data) | |
{ | |
String output = ""; | |
for (Map.Entry <String, List<Account>> entry : data.entrySet()) | |
{ | |
for (Account account : entry.getValue()) | |
{ | |
if (account.getUser().isEmpty() || account.getPassword().isEmpty()) | |
continue; | |
output += entry.getKey() + ":" + account.getUser() + ":" + | |
account.getPassword() + System.getProperty ("line.separator"); | |
} | |
} | |
return output; | |
} | |
private static String parseDate (long timestamp) | |
{ | |
if (timestamp <= 0) return "N/A"; | |
return new Date (timestamp).toString(); | |
} | |
/* | |
* -- NOTE: Function retrieved from AppWork's Crypto class. | |
*/ | |
public static String decrypt (final byte[] b, final byte[] key, final byte[] iv) { | |
Cipher cipher; | |
try { | |
final IvParameterSpec ivSpec = new IvParameterSpec(iv); | |
final SecretKeySpec skeySpec = new SecretKeySpec(key, "AES"); | |
cipher = Cipher.getInstance("AES/CBC/PKCS5Padding"); | |
cipher.init(Cipher.DECRYPT_MODE, skeySpec, ivSpec); | |
return new String(cipher.doFinal(b), "UTF-8"); | |
} catch (final Exception e) { | |
Log.exception (Level.WARNING, e); | |
final IvParameterSpec ivSpec = new IvParameterSpec(iv); | |
final SecretKeySpec skeySpec = new SecretKeySpec(key, "AES"); | |
try { | |
cipher = Cipher.getInstance("AES/CBC/nopadding"); | |
cipher.init(Cipher.DECRYPT_MODE, skeySpec, ivSpec); | |
return new String(cipher.doFinal(b), "UTF-8"); | |
} catch (final Exception e1) { | |
Log.exception(Level.WARNING, e1); | |
} | |
} | |
return null; | |
} | |
// Implemented to make decrypt() function working. I could simply remove | |
// any calls to this from that function, but I'm lazy. | |
private static class Log | |
{ | |
public static void exception (Level level, Throwable ex) | |
{ | |
System.err.println ("[" + level.getLocalizedName() + "] Got exception: " + ex.getMessage()); | |
} | |
} | |
} | |
class Account | |
{ | |
private HashMap<String, Object> properties; | |
private String hoster; | |
private int maxSimultanDownloads; | |
private String password; | |
private HashMap<String, Object> infoProperties; | |
private long createTime; | |
private long trafficLeft; | |
private long trafficMax; | |
private long validUntil; | |
private boolean active; | |
private boolean enabled; | |
private boolean tempDisabled; | |
private boolean valid; | |
private boolean trafficUnlimited; | |
private boolean specialtraffic; | |
private String user; | |
private boolean concurrentUsePossible = true; | |
// getters | |
public HashMap<String, Object> getProperties() | |
{ | |
return properties; | |
} | |
public String getHoster() | |
{ | |
return hoster; | |
} | |
public int getMaxSimultanDownloads() | |
{ | |
return maxSimultanDownloads; | |
} | |
public String getPassword() | |
{ | |
return password; | |
} | |
public boolean isConcurrentUsePossible() | |
{ | |
return concurrentUsePossible; | |
} | |
public HashMap<String, Object> getInfoProperties() | |
{ | |
return infoProperties; | |
} | |
public long getCreateTime() | |
{ | |
return createTime; | |
} | |
public long getTrafficLeft() | |
{ | |
return trafficLeft; | |
} | |
public long getTrafficMax() | |
{ | |
return trafficMax; | |
} | |
public long getValidUntil() | |
{ | |
return validUntil; | |
} | |
public String getUser() | |
{ | |
return user; | |
} | |
public boolean isActive() | |
{ | |
return active; | |
} | |
public boolean isEnabled() | |
{ | |
return enabled; | |
} | |
public boolean isTempDisabled() | |
{ | |
return tempDisabled; | |
} | |
public boolean isValid() | |
{ | |
return valid; | |
} | |
public boolean isTrafficUnlimited() | |
{ | |
return trafficUnlimited; | |
} | |
public boolean isSpecialtraffic() | |
{ | |
return specialtraffic; | |
} | |
// setters | |
public void setProperties (HashMap<String, Object> properties) | |
{ | |
this.properties = properties; | |
} | |
public void setHoster (String hoster) | |
{ | |
this.hoster = hoster; | |
} | |
public void setMaxSimultanDownloads (int maxSimultanDownloads) | |
{ | |
this.maxSimultanDownloads = maxSimultanDownloads; | |
} | |
public void setPassword (String password) | |
{ | |
this.password = password; | |
} | |
public void setConcurrentUsePossible (boolean concurrentUsePossible) | |
{ | |
this.concurrentUsePossible = concurrentUsePossible; | |
} | |
public void setInfoProperties (HashMap<String, Object> infoProperties) | |
{ | |
this.infoProperties = infoProperties; | |
} | |
public void setCreateTime (long createTime) | |
{ | |
this.createTime = createTime; | |
} | |
public void setTrafficLeft (long trafficLeft) | |
{ | |
this.trafficLeft = trafficLeft; | |
} | |
public void setTrafficMax (long trafficMax) | |
{ | |
this.trafficMax = trafficMax; | |
} | |
public void setValidUntil (long validUntil) | |
{ | |
this.validUntil = validUntil; | |
} | |
public void setActive (boolean active) | |
{ | |
this.active = active; | |
} | |
public void setEnabled (boolean enabled) | |
{ | |
this.enabled = enabled; | |
} | |
public void setTempDisabled (boolean tempDisabled) | |
{ | |
this.tempDisabled = tempDisabled; | |
} | |
public void setValid (boolean valid) | |
{ | |
this.valid = valid; | |
} | |
public void setTrafficUnlimited (boolean trafficUnlimited) | |
{ | |
this.trafficUnlimited = trafficUnlimited; | |
} | |
public void setSpecialtraffic (boolean specialtraffic) | |
{ | |
this.specialtraffic = specialtraffic; | |
} | |
public void setUser (String user) | |
{ | |
this.user = user; | |
} | |
@Override | |
public String toString() | |
{ | |
return String.format ("user: %s, password: %s", this.user, this.password); | |
} | |
} |
The code throws an exception while trying to dump the account data in a text file (accounts.txt) with host:user:pwd formatting (option [4]).
This is because the isEmpty method cannot be used on non-existent variables; it throws a Null Pointer exception.
To fix, you will have to handle the possible scenario of user and password not existing in certain datasets:
public String getPassword()
{
if (password == null) {
return "";
} else {
return password;
}
}
and
public String getUser()
{
if (user != null)
return user;
else {
return "";
}
}
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Thx for the code but i get this error: