Java tutorial
/******************************************************************************* * Copyright (c) 2013 Ale46. * All rights reserved. This program and the accompanying materials * are made available under the terms of the GNU Public License v3.0 * which accompanies this distribution, and is available at * http://www.gnu.org/licenses/gpl.html * * Contributors: * @NT2005 - initial API and implementation ******************************************************************************/ import org.json.JSONArray; import org.json.JSONException; import org.json.JSONObject; import javax.crypto.BadPaddingException; import javax.crypto.Cipher; import javax.crypto.CipherOutputStream; import javax.crypto.IllegalBlockSizeException; import javax.crypto.NoSuchPaddingException; import javax.crypto.spec.IvParameterSpec; import javax.crypto.spec.SecretKeySpec; import java.io.BufferedReader; import java.io.File; import java.io.FileOutputStream; import java.io.IOException; import java.io.InputStream; import java.io.InputStreamReader; import java.io.OutputStream; import java.io.OutputStreamWriter; import java.io.UnsupportedEncodingException; import java.math.BigInteger; import java.net.HttpURLConnection; import java.net.URL; import java.net.URLConnection; import java.security.InvalidAlgorithmParameterException; import java.security.InvalidKeyException; import java.security.KeyFactory; import java.security.NoSuchAlgorithmException; import java.security.PrivateKey; import java.security.spec.RSAPrivateKeySpec; import java.util.ArrayList; import java.util.HashMap; import java.util.Random; public class MegaHandler { private String email, password, sid; private int sequence_number; private long[] master_key; private BigInteger[] rsa_private_key; private long[] password_aes; HashMap<String, long[]> user_keys = new HashMap<String, long[]>(); public MegaHandler(String email, String password) { this.email = email; this.password = password; Random rg = new Random(); sequence_number = rg.nextInt(Integer.MAX_VALUE); } public int login() throws IOException { password_aes = MegaCrypt.prepare_key_pw(password); String uh = MegaCrypt.stringhash(email, password_aes); JSONObject json = new JSONObject(); try { json.put("a", "us"); json.put("user", email); json.put("uh", uh); } catch (JSONException e) { e.printStackTrace(); } while (true) { String response = api_request(json.toString()); if (isInteger(response)) return Integer.parseInt(response); try { if (login_process(new JSONObject(response), password_aes) != -2) { break; } } catch (JSONException e) { e.printStackTrace(); } } return 0; } private int login_process(JSONObject json, long[] password_aes) throws IOException { String master_key_b64 = null; try { master_key_b64 = json.getString("k"); } catch (JSONException e) { e.printStackTrace(); } if (master_key_b64 == null || master_key_b64.isEmpty()) return -1; long[] encrypted_master_key = MegaCrypt.base64_to_a32(master_key_b64); master_key = MegaCrypt.decrypt_key(encrypted_master_key, password_aes); if (json.has("csid")) { String encrypted_rsa_private_key_b64 = null; try { encrypted_rsa_private_key_b64 = json.getString("privk"); } catch (JSONException e) { e.printStackTrace(); } long[] encrypted_rsa_private_key = MegaCrypt.base64_to_a32(encrypted_rsa_private_key_b64); long[] rsa_private_key = MegaCrypt.decrypt_key(encrypted_rsa_private_key, master_key); String private_key = MegaCrypt.a32_to_str(rsa_private_key); this.rsa_private_key = new BigInteger[4]; for (int i = 0; i < 4; i++) { int l = ((((int) private_key.charAt(0)) * 256 + ((int) private_key.charAt(1)) + 7) / 8) + 2; this.rsa_private_key[i] = MegaCrypt.mpi_to_int(private_key.substring(0, l)); private_key = private_key.substring(l); } BigInteger encrypted_sid = null; try { encrypted_sid = MegaCrypt.mpi_to_int(MegaCrypt.base64_url_decode(json.getString("csid"))); } catch (JSONException e) { e.printStackTrace(); } BigInteger modulus = this.rsa_private_key[0].multiply(this.rsa_private_key[1]); BigInteger privateExponent = this.rsa_private_key[2]; BigInteger sid = null; try { PrivateKey privateKey = KeyFactory.getInstance("RSA") .generatePrivate(new RSAPrivateKeySpec(modulus, privateExponent)); Cipher cipher = Cipher.getInstance("RSA/ECB/NoPadding"); cipher.init(Cipher.DECRYPT_MODE, privateKey); // PyCrypt can handle >256 bit length... what the fuck... sometimes i get 257 if (encrypted_sid.toByteArray().length > 256) { Random rg = new Random(); sequence_number = rg.nextInt(Integer.MAX_VALUE); return -2; // lets get a new seession } sid = new BigInteger(cipher.doFinal(encrypted_sid.toByteArray())); } catch (Exception e) { e.printStackTrace(); return -1; } String sidS = sid.toString(16); if (sidS.length() % 2 != 0) sidS = "0" + sidS; try { byte[] sidsnohex = MegaCrypt.decodeHexString(sidS); this.sid = MegaCrypt.base64_url_encode(new String(sidsnohex, "ISO-8859-1").substring(0, 43)); } catch (Exception e) { e.printStackTrace(); return -1; } } return 0; } public String add_user(String email) { JSONObject json = new JSONObject(); try { json.put("a", "ur"); json.put("u", email); json.put("l", 1); } catch (JSONException e) { e.printStackTrace(); } return api_request(json.toString()); } public long get_quota() { JSONObject json = new JSONObject(); try { json.put("a", "uq"); json.put("xfer", 1); } catch (JSONException e) { e.printStackTrace(); } return new JSONObject(api_request(json.toString())).getLong("mstrg"); } public String get_user() { JSONObject json = new JSONObject(); try { json.put("a", "ug"); } catch (JSONException e) { e.printStackTrace(); } return api_request(json.toString()); } public ArrayList<MegaFile> get_files() throws UnsupportedEncodingException { JSONObject json = new JSONObject(); try { json.put("a", "f"); json.put("c", "1"); } catch (JSONException e) { e.printStackTrace(); } String files = api_request(json.toString()); // TODO check for negativ error //print(json.toString()); ArrayList<MegaFile> megaFiles = new ArrayList<MegaFile>(); JSONArray array = null; try { json = new JSONObject(files); array = json.getJSONArray("f"); for (int i = 0; i < array.length(); i++) { //print(array.get(i).toString()); megaFiles.add(process_file(new JSONObject(array.get(i).toString()))); } } catch (JSONException e) { e.printStackTrace(); return null; } return megaFiles; } private MegaFile process_file(JSONObject jsonFile) throws UnsupportedEncodingException { MegaFile file = new MegaFile(); try { if (jsonFile.getInt("t") < 2) { String key = ""; String uid = jsonFile.getString("u"); String h = (jsonFile.getString("h")); file.setUID(uid); file.setHandle(h); //print (h); if (jsonFile.getString("k").contains("/")) { String[] keys = jsonFile.getString("k").split("/"); int start = keys[0].indexOf(":") + 1; key = keys[0].substring(start); } String attributes = MegaCrypt.base64_url_decode(jsonFile.getString("a")); long[] k = new long[4]; if (!key.isEmpty()) { long[] keys_a32 = MegaCrypt.decrypt_key(MegaCrypt.base64_to_a32(key), master_key); if (jsonFile.getInt("t") == 0) { k[0] = keys_a32[0] ^ keys_a32[4]; k[1] = keys_a32[1] ^ keys_a32[5]; k[2] = keys_a32[2] ^ keys_a32[6]; k[3] = keys_a32[3] ^ keys_a32[7]; } else { k[0] = keys_a32[0]; k[1] = keys_a32[1]; k[2] = keys_a32[2]; k[3] = keys_a32[3]; file.setDirectory(true); } file.setKey(k); file.setAttributes(MegaCrypt.decrypt_attr(attributes, k)); } else if (!jsonFile.isNull("su") && !jsonFile.isNull("sk") && jsonFile.getString("k").contains(":")) { long[] keyS; user_keys.put(jsonFile.getString("u"), MegaCrypt.decrypt_key(MegaCrypt.base64_to_a32(jsonFile.getString("sk")), master_key)); //print("ShareKey->"+jsonFile.getString("sk")); int dd1 = jsonFile.getString("k").indexOf(':'); String sk = jsonFile.getString("k").substring(dd1 + 1); keyS = MegaCrypt.decrypt_key(MegaCrypt.base64_to_a32(sk), user_keys.get(jsonFile.getString("u"))); if (jsonFile.getInt("t") == 0) { long[] keys_a32S = keyS; k[0] = keys_a32S[0] ^ keys_a32S[4]; k[1] = keys_a32S[1] ^ keys_a32S[5]; k[2] = keys_a32S[2] ^ keys_a32S[6]; k[3] = keys_a32S[3] ^ keys_a32S[7]; } else { k = keyS; file.setDirectory(true); } file.setKey(k); file.setAttributes(MegaCrypt.decrypt_attr(attributes, k)); } else if (!jsonFile.isNull("u") && jsonFile.getString("k").contains(":") && user_keys.containsKey(jsonFile.getString("u"))) { int dd1 = jsonFile.getString("k").indexOf(':'); String sk = jsonFile.getString("k").substring(dd1 + 1); //print(user_keys.get(jsonFile.getString("u"))); long[] keyS = MegaCrypt.decrypt_key(MegaCrypt.base64_to_a32(sk), user_keys.get(jsonFile.getString("u"))); if (jsonFile.getInt("t") == 0) { long[] keys_a32S = keyS; k[0] = keys_a32S[0] ^ keys_a32S[4]; k[1] = keys_a32S[1] ^ keys_a32S[5]; k[2] = keys_a32S[2] ^ keys_a32S[6]; k[3] = keys_a32S[3] ^ keys_a32S[7]; } else { k = keyS; file.setDirectory(true); } file.setKey(k); file.setAttributes(MegaCrypt.decrypt_attr(attributes, k)); } else if (!jsonFile.isNull("k")) { int dd1 = jsonFile.getString("k").indexOf(':'); key = jsonFile.getString("k").substring(dd1 + 1); long[] keys_a32S = MegaCrypt.decrypt_key(MegaCrypt.base64_to_a32(key), master_key); if (jsonFile.getInt("t") == 0) { k[0] = keys_a32S[0] ^ keys_a32S[4]; k[1] = keys_a32S[1] ^ keys_a32S[5]; k[2] = keys_a32S[2] ^ keys_a32S[6]; k[3] = keys_a32S[3] ^ keys_a32S[7]; file.setDirectory(true); } /*else{ k = keys_a32S; file.setDirectory(true); }*/ file.setKey(k); file.setAttributes(MegaCrypt.decrypt_attr(attributes, k)); } else { file.setAttributes(jsonFile.toString()); } } else if (jsonFile.getInt("t") == 2) { file.setName("Cloud Drive"); } else if (jsonFile.getInt("t") == 3) { file.setName("Cloud Inbox"); } else if (jsonFile.getInt("t") == 4) { file.setName("Rubbish Bin"); } else { file.setName(jsonFile.toString()); } return file; } catch (JSONException e) { e.printStackTrace(); } //file.setAttributes(jsonFile.toString()); return file; } public String get_url(MegaFile f) { if (f.getHandle() == null || f.getKey() == null) return "Error"; JSONObject json = new JSONObject(); try { json.put("a", "l"); json.put("n", f.getHandle()); } catch (JSONException e) { e.printStackTrace(); } String public_handle = api_request(json.toString()); if (public_handle.equals("-11")) return "Shared file, no public url"; return "https://mega.co.nz/#!" + public_handle.substring(1, public_handle.length() - 1) + "!" + MegaCrypt.a32_to_base64(f.getKey()); } private String api_request(String data) { HttpURLConnection connection = null; try { String urlString = "https://g.api.mega.co.nz/cs?id=" + sequence_number; if (sid != null) urlString += "&sid=" + sid; URL url = new URL(urlString); connection = (HttpURLConnection) url.openConnection(); connection.setRequestMethod("POST"); //use post method connection.setDoOutput(true); //we will send stuff connection.setDoInput(true); //we want feedback connection.setUseCaches(false); //no caches connection.setAllowUserInteraction(false); connection.setRequestProperty("Content-Type", "text/xml"); OutputStream out = connection.getOutputStream(); try { OutputStreamWriter wr = new OutputStreamWriter(out); wr.write("[" + data + "]"); //data is JSON object containing the api commands wr.flush(); wr.close(); } catch (IOException e) { e.printStackTrace(); } finally { //in this case, we are ensured to close the output stream if (out != null) out.close(); } InputStream in = connection.getInputStream(); StringBuffer response = new StringBuffer(); try { BufferedReader rd = new BufferedReader(new InputStreamReader(in)); String line = ""; while ((line = rd.readLine()) != null) { response.append(line); } rd.close(); //close the reader } catch (IOException e) { e.printStackTrace(); } finally { //in this case, we are ensured to close the input stream if (in != null) in.close(); } return response.toString().substring(1, response.toString().length() - 1); } catch (IOException e) { e.printStackTrace(); } return ""; } public static boolean isInteger(String string) { if (string == null || string.isEmpty()) { return false; } int length = string.length(); int i = 0; if (string.charAt(i) == '[') { if (length == 1) return false; i++; } if (string.charAt(i) == '-') { if (length == 1 + i) return false; i++; } for (; i < length; i++) { char c = string.charAt(i); if (c <= '/' || c >= ':') { return false; } } return true; } public void download(String url, String path) throws NoSuchAlgorithmException, NoSuchPaddingException, InvalidKeyException, InvalidAlgorithmParameterException, IOException, IllegalBlockSizeException, BadPaddingException { //TODO DOWNLOAD mismatch? print("Download started"); String[] s = url.split("!"); String file_id = s[1]; byte[] file_key = MegaCrypt.base64_url_decode_byte(s[2]); int[] intKey = MegaCrypt.aByte_to_aInt(file_key); JSONObject json = new JSONObject(); try { json.put("a", "g"); json.put("g", "1"); json.put("p", file_id); } catch (JSONException e) { e.printStackTrace(); } JSONObject file_data = new JSONObject(api_request(json.toString())); int[] keyNOnce = new int[] { intKey[0] ^ intKey[4], intKey[1] ^ intKey[5], intKey[2] ^ intKey[6], intKey[3] ^ intKey[7], intKey[4], intKey[5] }; byte[] key = MegaCrypt.aInt_to_aByte(keyNOnce[0], keyNOnce[1], keyNOnce[2], keyNOnce[3]); int[] iiv = new int[] { keyNOnce[4], keyNOnce[5], 0, 0 }; byte[] iv = MegaCrypt.aInt_to_aByte(iiv); @SuppressWarnings("unused") int file_size = file_data.getInt("s"); String attribs = (file_data.getString("at")); attribs = new String(MegaCrypt.aes_cbc_decrypt(MegaCrypt.base64_url_decode_byte(attribs), key)); String file_name = attribs.substring(10, attribs.lastIndexOf("\"")); print(file_name); final IvParameterSpec ivSpec = new IvParameterSpec(iv); final SecretKeySpec skeySpec = new SecretKeySpec(key, "AES"); Cipher cipher = Cipher.getInstance("AES/CTR/nopadding"); cipher.init(Cipher.ENCRYPT_MODE, skeySpec, ivSpec); InputStream is = null; String file_url = file_data.getString("g"); FileOutputStream fos = new FileOutputStream(path + File.separator + file_name); final OutputStream cos = new CipherOutputStream(fos, cipher); final Cipher decipher = Cipher.getInstance("AES/CTR/NoPadding"); decipher.init(Cipher.ENCRYPT_MODE, skeySpec, ivSpec); int read = 0; final byte[] buffer = new byte[32767]; try { URLConnection urlConn = new URL(file_url).openConnection(); print(file_url); is = urlConn.getInputStream(); while ((read = is.read(buffer)) > 0) { cos.write(buffer, 0, read); } } finally { try { cos.close(); if (is != null) { is.close(); } } finally { if (fos != null) { fos.close(); } } } print("Download finished"); } public static void print(Object o) { System.out.println(o); } }