Java tutorial
/* Copyright 2012-2013, Polyvi Inc. (http://polyvi.github.io/openxface) This program is distributed under the terms of the GNU General Public License. This file is part of xFace. xFace 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. xFace 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 xFace. If not, see <http://www.gnu.org/licenses/>. */ package com.polyvi.xface.extension.security; import java.io.File; import java.io.FileNotFoundException; import java.io.IOException; import java.io.InputStream; import java.io.UnsupportedEncodingException; import org.apache.cordova.CallbackContext; import org.apache.cordova.CordovaInterface; import org.apache.cordova.CordovaPlugin; import org.apache.cordova.CordovaResourceApi; import org.apache.cordova.CordovaWebView; import org.json.JSONArray; import org.json.JSONException; import org.json.JSONObject; import android.net.Uri; import com.polyvi.xface.exception.XCryptionException; import com.polyvi.xface.util.XBase64; import com.polyvi.xface.util.XCryptor; import com.polyvi.xface.util.XFileUtils; import com.polyvi.xface.util.XLog; import com.polyvi.xface.util.XPathResolver; import com.polyvi.xface.util.XStringUtils; import com.polyvi.xface.view.XAppWebView; public class XSecurityExt extends CordovaPlugin { private static final String CLASS_NAME = XSecurityExt.class.getSimpleName(); /** Security ??js??? */ private static final String COMMAND_ENCRYPT = "encrypt"; private static final String COMMAND_DECRYPT = "decrypt"; private static final String COMMAND_ENCRYPT_FILE = "encryptFile"; private static final String COMMAND_DECRYPT_FILE = "decryptFile"; private static final String COMMAND_DIGEST = "digest"; /** */ private static final int FILE_NOT_FOUND_ERR = 1; private static final int PATH_ERR = 2; private static final int OPERATION_ERR = 3; private static final int UNKNOWN_ERR = 4; /** */ private static final String KEY_EMPTY_ERROR = "Error:key null or empty"; private static final String CRYPTION_ERROR = "Error:cryption error"; /** */ private static final int DES_ALOGRITHEM = 1; // DES? private static final int TRIPLE_DES_ALOGRITHEM = 2; // 3DES? private static final int RSA_ALOGRITHEM = 3; // RSA? /** ? */ private static final int ENCODE_TYPE_STRING = 0; // ?String private static final int ENCODE_TYPE_BASE64 = 1; // ?Base64?? private static final int ENCODE_TYPE_HEX = 2; // ?16?? /** ??? */ private static final String KEY_CRYPT_ALGORITHM = "CryptAlgorithm"; private static final String KEY_ENCODE_DATA_TYPE = "EncodeDataType"; private static final String KEY_ENCODE_KEY_TYPE = "EncodeKeyType"; /** */ private XCryptor mCryptor; private CordovaResourceApi mResourceApi; private interface SecurityOp { void run() throws Exception; } @Override public void initialize(CordovaInterface cordova, CordovaWebView webView) { super.initialize(cordova, webView); mCryptor = new XCryptor(); mResourceApi = this.webView.getResourceApi(); } public boolean execute(String action, final JSONArray args, final CallbackContext callbackContext) throws JSONException { if (action.equals(COMMAND_ENCRYPT)) { threadhelper(new SecurityOp() { @Override public void run() throws Exception { String encryptResult = encrypt(args.getString(0), args.getString(1), args.optJSONObject(2)); callbackContext.success(encryptResult); } }, callbackContext); } else if (action.equals(COMMAND_DECRYPT)) { threadhelper(new SecurityOp() { @Override public void run() throws Exception { String decryptResult = decrypt(args.getString(0), args.getString(1), args.optJSONObject(2)); callbackContext.success(decryptResult); } }, callbackContext); } else if (action.equals(COMMAND_ENCRYPT_FILE)) { threadhelper(new SecurityOp() { @Override public void run() throws Exception { String result = encryptFile(args.getString(0), args.getString(1), args.getString(2)); callbackContext.success(result); } }, callbackContext); } else if (action.equals(COMMAND_DECRYPT_FILE)) { threadhelper(new SecurityOp() { @Override public void run() throws Exception { String result = decryptFile(args.getString(0), args.getString(1), args.getString(2)); callbackContext.success(result); } }, callbackContext); } else if (action.equals(COMMAND_DIGEST)) { threadhelper(new SecurityOp() { @Override public void run() throws Exception { String result = digest(args.getString(0)); callbackContext.success(result); } }, callbackContext); } else { return false; // Invalid action, return false } return true; } /** * ? * * @param securityOp * @param callbackContext * @param action */ private void threadhelper(final SecurityOp securityOp, final CallbackContext callbackContext) { cordova.getThreadPool().execute(new Runnable() { public void run() { try { securityOp.run(); } catch (Exception e) { XLog.e(CLASS_NAME, e.getMessage()); e.printStackTrace(); if (e instanceof IllegalArgumentException) { callbackContext.error(PATH_ERR); } else if (e instanceof FileNotFoundException) { callbackContext.error(FILE_NOT_FOUND_ERR); } else if (e instanceof XCryptionException) { callbackContext.error(OPERATION_ERR); } else if (e instanceof IOException) { callbackContext.error(OPERATION_ERR); } else { callbackContext.error(UNKNOWN_ERR); } } } }); } /** * ?workspace * * @return */ private String getWorkspacePath() { XAppWebView xAppWebView = (XAppWebView) this.webView; String appWorkspace = xAppWebView.getOwnerApp().getWorkSpace(); return appWorkspace; } /** * ? * * @param filePath * @return * @throws IllegalArgumentException */ private Uri resolveUri(String filePath) throws IllegalArgumentException { // ? if (XStringUtils.isEmptyString(filePath)) { throw new IllegalArgumentException(); } XPathResolver pathResolver = new XPathResolver(filePath, getWorkspacePath()); return pathResolver.getUri(mResourceApi); } /** * ??? * * @param filePath * ??? * @return ? * @throws IOException */ private InputStream readFile(String filePath) throws IllegalArgumentException, IOException { Uri fileUri = resolveUri(filePath); if (!XFileUtils.isFilePathValid(fileUri.getPath())) { throw new IllegalArgumentException(); } InputStream inputStream = mResourceApi.openForRead(fileUri).inputStream; if (null == inputStream) { throw new FileNotFoundException(); } return inputStream; } /** * * * @param absFilePath * @return * @throws FileNotFoundException */ private String createTargetFile(String absFilePath) throws FileNotFoundException, IllegalArgumentException { // ? absFilePath = resolveUri(absFilePath).getPath(); File requestFile = new File(absFilePath); if (requestFile.exists()) { requestFile.delete(); } if (!XFileUtils.createFile(absFilePath)) { throw new FileNotFoundException(); } return absFilePath; } /** * * * @param sKey * * @param sourceData * ?? * @param options * ? * @return ?? */ private String encrypt(String sKey, String sourceData, JSONObject options) throws XCryptionException, XCryptionException { if (XStringUtils.isEmptyString(sKey)) { XLog.e(CLASS_NAME, KEY_EMPTY_ERROR); throw new XCryptionException(KEY_EMPTY_ERROR); } int cryptAlgorithm = DES_ALOGRITHEM; int encodeDataType = ENCODE_TYPE_STRING; int encodeKeyType = ENCODE_TYPE_STRING; if (options != null) { cryptAlgorithm = options.optInt(KEY_CRYPT_ALGORITHM, DES_ALOGRITHEM); encodeDataType = options.optInt(KEY_ENCODE_DATA_TYPE, ENCODE_TYPE_BASE64); encodeKeyType = options.optInt(KEY_ENCODE_KEY_TYPE, ENCODE_TYPE_STRING); } byte[] keyBytes = null; keyBytes = getBytesEncode(encodeKeyType, sKey); switch (cryptAlgorithm) { case TRIPLE_DES_ALOGRITHEM: switch (encodeDataType) { case ENCODE_TYPE_HEX: return XStringUtils.hexEncode(mCryptor.encryptBytesFor3DES(sourceData.getBytes(), keyBytes)); default: return XBase64.encodeToString((mCryptor.encryptBytesFor3DES(sourceData.getBytes(), keyBytes)), XBase64.NO_WRAP); } case RSA_ALOGRITHEM: switch (encodeDataType) { case ENCODE_TYPE_HEX: return XStringUtils.hexEncode(mCryptor.encryptRSA(sourceData.getBytes(), keyBytes)); default: return XBase64.encodeToString((mCryptor.encryptRSA(sourceData.getBytes(), keyBytes)), XBase64.NO_WRAP); } default: switch (encodeDataType) { case ENCODE_TYPE_HEX: return XStringUtils.hexEncode(mCryptor.encryptBytesForDES(sourceData.getBytes(), keyBytes)); default: return XBase64.encodeToString((mCryptor.encryptBytesForDES(sourceData.getBytes(), keyBytes)), XBase64.NO_WRAP); } } } /** * * * @param sKey * * @param sourceFilePath * ? * @param targetFilePath * ? * @return ? * @throws XCryptionException * @throws IOException * @throws IllegalArgumentException * @throws FileNotFoundException */ private String encryptFile(String sKey, String sourceFilePath, String targetFilePath) throws XCryptionException, IllegalArgumentException, IOException { InputStream sourceIs = readFile(sourceFilePath); if (!XFileUtils.isFilePathValid(Uri.parse(targetFilePath).getPath())) { XLog.e(CLASS_NAME, "Method encryptFile: targetFilePath is illegal!"); throw new IllegalArgumentException(); } targetFilePath = createTargetFile(targetFilePath); return cryptFile(sKey, sourceIs, targetFilePath, true); } /** * * * @param sKey * * @param sourceData * ?? * @param options * ? * @return ?? */ private String decrypt(String sKey, String sourceData, JSONObject options) throws XCryptionException { if (XStringUtils.isEmptyString(sKey)) { XLog.e(CLASS_NAME, KEY_EMPTY_ERROR); throw new XCryptionException(KEY_EMPTY_ERROR); } int cryptAlgorithm = DES_ALOGRITHEM; int encodeDataType = ENCODE_TYPE_STRING; int encodeKeyType = ENCODE_TYPE_STRING; if (options != null) { cryptAlgorithm = options.optInt(KEY_CRYPT_ALGORITHM, DES_ALOGRITHEM); encodeDataType = options.optInt(KEY_ENCODE_DATA_TYPE, ENCODE_TYPE_STRING); encodeKeyType = options.optInt(KEY_ENCODE_KEY_TYPE, ENCODE_TYPE_STRING); } byte[] keyBytes = null; keyBytes = getBytesEncode(encodeKeyType, sKey); switch (cryptAlgorithm) { case TRIPLE_DES_ALOGRITHEM: switch (encodeDataType) { case ENCODE_TYPE_HEX: return new String(mCryptor.decryptBytesFor3DES(XStringUtils.hexDecode(sourceData), keyBytes)); default: return new String( mCryptor.decryptBytesFor3DES(XBase64.decode(sourceData, XBase64.NO_WRAP), keyBytes)); } case RSA_ALOGRITHEM: switch (encodeDataType) { case ENCODE_TYPE_HEX: return new String(mCryptor.decryptRSA(XStringUtils.hexDecode(sourceData), keyBytes)); default: return new String(mCryptor.decryptRSA(XBase64.decode(sourceData, XBase64.NO_WRAP), keyBytes)); } default: switch (encodeDataType) { case ENCODE_TYPE_HEX: return new String(mCryptor.decryptBytesForDES(XStringUtils.hexDecode(sourceData), keyBytes)); default: return new String( mCryptor.decryptBytesForDES(XBase64.decode(sourceData, XBase64.NO_WRAP), keyBytes)); } } } /** * * * @param sKey * * @param sourceFilePath * ? * @param targetFilePath * ? * @return ? * @throws IOException * @throws IllegalArgumentException */ private String decryptFile(String sKey, String sourceFilePath, String targetFilePath) throws XCryptionException, IllegalArgumentException, IOException { InputStream sourceIs = readFile(sourceFilePath); if (!XFileUtils.isFilePathValid(Uri.parse(targetFilePath).getPath())) { XLog.e(CLASS_NAME, "Method decryptFile: targetFilePath is illegal!"); throw new IllegalArgumentException(); } targetFilePath = createTargetFile(targetFilePath); return cryptFile(sKey, sourceIs, targetFilePath, false); } /** * ?, * * @param sKey * @param fileIs * @param absTargetFilePath * @param isEncrypt * @return * @throws XCryptionException */ private String cryptFile(String sKey, InputStream fileIs, String absTargetFilePath, boolean isEncrypt) throws XCryptionException { byte[] keyBytes = getBytesEncode(ENCODE_TYPE_STRING, sKey); byte[] cryptedBytes = null; if (isEncrypt) { cryptedBytes = mCryptor.encryptStreamForDES(keyBytes, fileIs); } else { cryptedBytes = mCryptor.decryptStreamForDES(keyBytes, fileIs); } if (XFileUtils.writeFileByByte(absTargetFilePath, cryptedBytes)) { return absTargetFilePath; } throw new XCryptionException(CRYPTION_ERROR); } /** * md5 * * @param data * @return * @throws UnsupportedEncodingException * @throws XCryptionException */ private String digest(String data) throws XCryptionException { if (XStringUtils.isEmptyString(data)) { XLog.e(CLASS_NAME, KEY_EMPTY_ERROR); throw new XCryptionException(KEY_EMPTY_ERROR); } XCryptor cryptor = new XCryptor(); try { return cryptor.calMD5Value(data.getBytes("UTF-8")); } catch (UnsupportedEncodingException e) { XLog.e(CLASS_NAME, "digest failed: UnsupportedEncodingException"); e.printStackTrace(); throw new XCryptionException(CRYPTION_ERROR); } } /** * ????keyBytes * * @param encodeKeyType * ? * @param keyString * ??keyString * @return ?? */ private byte[] getBytesEncode(int encodeKeyType, String keyString) throws IllegalArgumentException { if (XStringUtils.isEmptyString(keyString)) { throw new IllegalArgumentException(); } switch (encodeKeyType) { case ENCODE_TYPE_BASE64: return XBase64.decode(keyString, XBase64.NO_WRAP); case ENCODE_TYPE_HEX: return XStringUtils.hexDecode(keyString); default: return keyString.getBytes(); } } }