/*\r
- * This file is part of NixNote \r
+ * This file is part of NeverNote \r
* Copyright 2009 Randy Baumgarte\r
* \r
* This file may be licensed under the terms of of the\r
*/\r
package cx.fbn.nevernote.evernote;\r
\r
-//**********************************************\r
-//**********************************************\r
-//* Utility used to encript or decrypt the \r
-//* text in a note.\r
-//**********************************************\r
-//**********************************************\r
-\r
import java.io.IOException;\r
+import java.nio.ByteBuffer;\r
+import java.nio.CharBuffer;\r
+import java.nio.charset.Charset;\r
import java.security.InvalidAlgorithmParameterException;\r
import java.security.InvalidKeyException;\r
import java.security.MessageDigest;\r
import java.security.NoSuchAlgorithmException;\r
+import java.util.Arrays;\r
import java.util.zip.CRC32;\r
\r
import javax.crypto.BadPaddingException;\r
\r
return strbuf.toString();\r
}\r
+\r
+// public static class EnCryptException extends Exception {\r
+// public EnCryptException(String message, Throwable cause) {\r
+// super(message, cause);\r
+// }\r
+// }\r
+ \r
+ /**\r
+ * Choose the character set to use for encoding\r
+ */\r
+ private Charset getCharset() {\r
+ \r
+ // Just hard-coding choice here\r
+ boolean useUtf8 = true; \r
+ \r
+ final Charset charSet;\r
+ if(useUtf8) {\r
+ charSet = Charset.forName("UTF-8");\r
+ } else {\r
+ charSet = Charset.defaultCharset();\r
+ }\r
+ return charSet;\r
+ }\r
+ \r
+ \r
+ // Useful for debugging, but not normally used\r
+ @SuppressWarnings("unused")\r
+ private byte[] encodeStringOld(String text) {\r
+\r
+ int len = text.length()+4;\r
+ int mod = (len%8);\r
+ if (mod>0) {\r
+ for (; mod !=0; len++) {\r
+ mod = len%8;\r
+ }\r
+ len--;\r
+ }\r
+ len = len-4;\r
+ StringBuffer textBuffer = new StringBuffer(text);\r
+ textBuffer.setLength(len);\r
+\r
+ // Setup parms for the cipher\r
+ String encoded = crcHeader(textBuffer.toString()) +textBuffer;\r
+\r
+ return encoded.getBytes();\r
+ }\r
+\r
+ /**\r
+ * Main changes are\r
+ * \r
+ * 1. Do padding based on encoded bytes, not string length (some chars -> 2 bytes)\r
+ * 2. Use specific named charset\r
+ */\r
+ private byte[] encodeStringNew(String text) {\r
+ \r
+ final Charset charSet = getCharset();\r
+ \r
+ // Convert to bytes using given encoding, and align *bytes* to multiple of\r
+ // 8, with 4 bytes reserved for the crc\r
+ final byte[] bytes = text.getBytes(charSet);\r
+ int align8 = (bytes.length + 4) % 8;\r
+ int paddingNeeded = 8 - align8;\r
+ final byte[] paddedBytes = Arrays.copyOf(bytes, bytes.length + paddingNeeded);\r
+ \r
+ // Now calculate the crc, using the bytes\r
+ String crc = crcHeader(paddedBytes);\r
+ \r
+ byte[] crcBytes = crc.getBytes(charSet);\r
+ if(crcBytes.length != 4) {\r
+ System.err.println("CRC Bytes really should be 4 in length!");\r
+ return null;\r
+ }\r
+ \r
+ // Now combine crc bytes and string bytes into byte array\r
+ // for encryption\r
+ byte[] total = new byte[paddedBytes.length + crcBytes.length];\r
+ System.arraycopy(crcBytes, 0, total, 0, crcBytes.length);\r
+ System.arraycopy(paddedBytes, 0, total, crcBytes.length, paddedBytes.length);\r
+ \r
+ return total;\r
+ }\r
+\r
+ \r
+ /**\r
+ * Same as for encryption: use named charset, and\r
+ * @param bytes\r
+ * @return\r
+ */\r
+ private String decodeBytesNew(byte[] bytes) {\r
+ \r
+ Charset charSet = getCharset();\r
+ \r
+ byte[] crcBytes = Arrays.copyOfRange(bytes, 0, 4);\r
+ byte[] textBytes = Arrays.copyOfRange(bytes, 4, bytes.length);\r
+ \r
+ CharBuffer crcChar = charSet.decode(ByteBuffer.wrap(crcBytes));\r
+ CharBuffer textChar = charSet.decode(ByteBuffer.wrap(textBytes));\r
+ \r
+ // Get crc of text to see if same\r
+ String cryptCRC = crcChar.toString();\r
+ String realCRC = crcHeader(textBytes);\r
+ \r
+ if(realCRC.equals(cryptCRC)) {\r
+ // Trim nulls at end\r
+ while(textChar.get(textChar.limit() - 1) == 0 && textChar.limit() != 0) {\r
+ textChar.limit(textChar.limit() - 1);\r
+ }\r
+ String str = textChar.toString();\r
+ return str;\r
+ }\r
+ \r
+ return null;\r
+ }\r
+ \r
+ /** \r
+ * For reference: old version. Useful for debugging\r
+ * @param bytes\r
+ * @return\r
+ */ \r
+ @SuppressWarnings("unused")\r
+ private String decodeBytesOld(byte[] bytes) {\r
+\r
+ // We have a result. Separate it into the 4 byte header and the decrypted text\r
+ StringBuffer buffer = new StringBuffer(new String(bytes));\r
+ String cryptCRC = buffer.substring(0,4);\r
+ String clearText = buffer.substring(4);\r
+ String realCRC = crcHeader(clearText);\r
+ // We need to get the real CRC of the decrypted text\r
+ if (realCRC.equalsIgnoreCase(cryptCRC)) {\r
+ int endPos = clearText.length();\r
+ for (int i=buffer.length()-1; i>=0; i--) {\r
+ if (buffer.charAt(i) == 0) \r
+ endPos--;\r
+ else\r
+ i=-1;\r
+ }\r
+ clearText = clearText.substring(0,endPos);\r
+ return clearText;\r
+ }\r
+\r
+ return null;\r
+\r
+ }\r
+\r
+ \r
+ \r
// Encrypte the text and return the base64 string\r
public String encrypt(String text, String passphrase, int keylen) {\r
+ \r
+ \r
RC2ParameterSpec parm = new RC2ParameterSpec(keylen);\r
- MessageDigest md;\r
+ \r
try {\r
- int len = text.length()+4;\r
- int mod = (len%8);\r
- if (mod>0) {\r
- for (; mod !=0; len++) {\r
- mod = len%8;\r
- }\r
- len--;\r
- }\r
- len = len-4;\r
- StringBuffer textBuffer = new StringBuffer(text);\r
- textBuffer.setLength(len);\r
// Get a MD5 for the passphrase\r
- md = MessageDigest.getInstance("MD5");\r
- md.update(passphrase.getBytes());\r
+ MessageDigest md = MessageDigest.getInstance("MD5");\r
+ // NB Use specific Charset\r
+ md.update(passphrase.getBytes(getCharset()));\r
\r
// Setup parms for the cipher\r
SecretKeySpec skeySpec = new SecretKeySpec(md.digest(), "RC2");\r
Cipher cipher = Cipher.getInstance("RC2/ECB/NoPadding");\r
cipher.init(Cipher.ENCRYPT_MODE, skeySpec, parm);\r
- String encoded = crcHeader(textBuffer.toString()) +textBuffer;\r
- byte[] d = cipher.doFinal(encoded.getBytes());\r
+ \r
+ //byte[] oldBytes = encodeStringOld(text);\r
+ byte[] newBytes = encodeStringNew(text);\r
+ //boolean areSame = Arrays.equals(oldBytes, newBytes);\r
+ //System.out.println("Same? " + areSame);\r
+ byte[] d = cipher.doFinal(newBytes);\r
+ \r
return Base64.encodeBytes(d);\r
} catch (NoSuchAlgorithmException e) {\r
e.printStackTrace();\r
try {\r
// Get a MD5 for the passphrase\r
md = MessageDigest.getInstance("MD5");\r
- StringBuffer p = new StringBuffer(passphrase);\r
- md.update(p.toString().getBytes());\r
+ md.update(passphrase.getBytes(getCharset()));\r
\r
// Setup parms for the cipher\r
SecretKeySpec skeySpec = new SecretKeySpec(md.digest(), "RC2");\r
byte[] dString = Base64.decode(text);\r
byte[] d = cipher.doFinal(dString);\r
\r
- // We have a result. Separate it into the 4 byte header and the decrypted text\r
- StringBuffer buffer = new StringBuffer(new String(d));\r
- String cryptCRC = buffer.substring(0,4);\r
- String clearText = buffer.substring(4);\r
- String realCRC = crcHeader(clearText);\r
- // We need to get the real CRC of the decrypted text\r
- if (realCRC.equalsIgnoreCase(cryptCRC)) {\r
- int endPos = clearText.length();\r
- for (int i=buffer.length()-1; i>=0; i--) {\r
- if (buffer.charAt(i) == 0) \r
- endPos--;\r
- else\r
- i=-1;\r
- }\r
- clearText = clearText.substring(0,endPos);\r
- return clearText;\r
- }\r
+ //String clearTextOld = decodeBytesOld(d);\r
+ String clearTextNew = decodeBytesNew(d);\r
+ //if(clearTextNew != null) {\r
+ // System.out.println("Are same decrypted ? " + clearTextNew.equals(clearTextOld));\r
+ //}\r
+ return clearTextNew;\r
} catch (NoSuchAlgorithmException e) {\r
e.printStackTrace();\r
} catch (NoSuchPaddingException e) {\r
// Utility function to return the CRC header of an encoded string. This is\r
// used to verify good decryption and put in front of a new encrypted string\r
private String crcHeader(String text) {\r
+ return crcHeader(text.getBytes());\r
+ }\r
+ private String crcHeader(byte[] bytes) {\r
CRC32 crc = new CRC32();\r
- crc.update(text.getBytes());\r
+ crc.update(bytes);\r
int realCRC = (int)crc.getValue();\r
\r
// The first 4 chars of the hex string will equal the first\r
\r
}\r
\r
-}\r
+}
\ No newline at end of file