/*
*
* Copyright (c) 2009, Mulder3
*
* This program is free software; you can redistribute it and/or modify it
* under the terms and conditions of the GNU General Public License,
* version 2, as published by the Free Software Foundation.
*
* This program is distributed in the hope 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, write to the Free Software Foundation, Inc., 59 Temple
* Place - Suite 330, Boston, MA 02111-1307 USA.
*
* Authors:
* Mulder3 <mulder3@mulder3.net>
*
*/
import java.io.*;
import java.security.spec.AlgorithmParameterSpec;
import javax.crypto.spec.IvParameterSpec;
import javax.crypto.spec.SecretKeySpec;
import javax.crypto.Cipher;
public class TV2xmlDecrypt {
public static void main
(String[] args
) {
System.
out.
println("Microsoft Mediaroom XML decrypt by Mulder3 <mulder3@mulder3.net>");
if (args.length!=3 || args[2].length()!=32){
System.
out.
println("Usage: TV2xmlDecrypt inFile outFile AESkey(16bytes hex string without spaces or colons)");
System.
out.
println("Example: TV2xmlDecrypt LoginEx_encrypted.bin LoginEx.xml 0123456789ABCDEF0123456789ABCDEF");
}
System.
out.
println("Trying to decrypt "+args
[0]+"...");
try{
decryptTV2xml(inFile, outFile, args[3]);
inFile.close();
outFile.close();
e.printStackTrace();
}
System.
out.
println("File successfully decrypted... saved to "+args
[1]+"...");
}
boolean isFirstBock = true;
int offset = 0;
int padding = 0;
byte[] buffer;
byte[] buffer2;
byte[] decryptedXML = null;
byte[] iv = new byte[16];
byte[] OMAC1signature = new byte[16];
SecretKeySpec key = new SecretKeySpec(hexToByteArray(aeskey), "AES");
while(true){
if (isFirstBock){
buffer = new byte[1+16+8+4096+16]; //padding/version byte + 16bytes iv + 8bytes ramdom data + 4096 bytes encrypted block + 16bytes OMAC1 signature
offset = in.read(buffer, 0, (1+16+8+4096+16));
if (offset==-1) break;
if (((buffer
[0
] >> 4
) & 15
) != 1
){ System.
out.
println("ERROR: Stream version unknown"); System.
exit(0
);}
padding = buffer[0] & 15;
if (padding
> 15
){ System.
out.
println("ERROR: Invalid padding specified"); System.
exit(0
);}
System.
arraycopy(buffer, 1, iv, 0, 16
);
System.
arraycopy(buffer, offset
-16, OMAC1signature, 0, 16
);
buffer2 = new byte[offset-(1+16+8+16)]; //padding/version byte + 16bytes iv + 8bytes ramdom data + 16bytes OMAC1 signature
System.
arraycopy(buffer,
(1
+16
+8
), buffer2, 0, offset
-(1
+16
+8
+16
));
//VerifyOMAC1signature(OMAC1signature,buffer2); //NOT IMPLEMENTED!!
decryptedXML=decryptAesBlock(buffer2,iv, key, padding);
out.write(decryptedXML, 0, decryptedXML.length);
isFirstBock=false;
} else {
buffer = new byte[1+4096+16]; //padding/version byte + 4096 bytes encrypted block + 16bytes OMAC1 signature
offset = in.read(buffer, 0, (1+4096+16));
if (offset==-1) break;
if (((buffer
[0
] >> 4
) & 15
) != 1
){ System.
out.
println("ERROR: Stream version unknown"); System.
exit(0
);}
padding = buffer[0] & 15;
if (padding
> 15
){ System.
out.
println("ERROR: Invalid padding specified"); System.
exit(0
);}
System.
arraycopy(buffer, offset
-16, OMAC1signature, 0, 16
);
buffer2 = new byte[offset-(1+16)]; //padding/version byte + 16bytes OMAC1 signature
System.
arraycopy(buffer, 1, buffer2, 0, offset
-(1
+16
));
//VerifyOMAC1signature(OMAC1signature,buffer2); //NOT IMPLEMENTED!!!
decryptedXML=decryptAesBlock(buffer2,iv, key,padding);
out.write(decryptedXML, 0, decryptedXML.length);
}
}
}
private static byte[] decryptAesBlock
(byte[] block,
byte[] iv, SecretKeySpec key,
int padding
) throws Exception { // Java crypto subsystem throws a bunch of checked exceptions, so, i didn't bother to declare them all... it's a bad practice, i know...
Cipher decipher;
byte[] decryptedData = new byte[block.length];
byte[] result = new byte[block.length-padding];
decipher = Cipher.getInstance("AES/CBC/NoPadding");
decipher.init(Cipher.DECRYPT_MODE, key, ivSpec);
decryptedData=decipher.doFinal(block, 0, block.length);
System.
arraycopy(decryptedData, 0, result, 0, decryptedData.
length-padding
);
return result;
}
private static byte[] hexToByteArray
(String hexString
) {
String s
= hexString.
toLowerCase();
byte[] data = new byte[s.length() / 2];
for (int i = 0; i < s.length(); i += 2) {
data
[i
/ 2
] = (byte) ((Character.
digit(s.
charAt(i
), 16
) << 4
) + Character.
digit(s.
charAt(i
+1
), 16
));
}
return data;
}
}