/* 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; import java.io.File; import java.io.FileNotFoundException; import java.io.IOException; import java.io.UnsupportedEncodingException; import org.json.JSONArray; import org.json.JSONException; import org.json.JSONObject; import com.polyvi.xface.exception.XCryptionException; import com.polyvi.xface.plugin.api.XIWebContext; 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; public class XSecurityExt extends XExtension { 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 String KEY_EMPTY_ERROR = "Error:key null or empty"; private static final String FILE_NOT_FOUND_ERROR = "Error: file not found"; 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"; /**加解密工具类*/ XCryptor mCryptor = new XCryptor(); @Override public void sendAsyncResult(String result) { } @Override public boolean isAsync(String action) { return true; } @Override public XExtensionResult exec(String action, JSONArray args, XCallbackContext callbackCtx) throws JSONException { XExtensionResult.Status status = XExtensionResult.Status.OK; String result = "Unsupported Operation: " + action; try { if (action.equals(COMMAND_ENCRYPT)) { result = encrypt(args.getString(0), args.getString(1),args.optJSONObject(2)); } else if (action.equals(COMMAND_DECRYPT)) { result = decrypt(args.getString(0), args.getString(1), args.optJSONObject(2)); } else if (action.equals(COMMAND_ENCRYPT_FILE)) { result = encryptFile(mWebContext, args.getString(0), args.getString(1), args.getString(2)); } else if (action.equals(COMMAND_DECRYPT_FILE)) { result = decryptFile(mWebContext, args.getString(0), args.getString(1), args.getString(2)); } else if (action.equals(COMMAND_DIGEST)) { result = digest(args.getString(0)); } else { status = XExtensionResult.Status.INVALID_ACTION; } return new XExtensionResult(status, result); } catch (IllegalArgumentException e) { e.printStackTrace(); return new XExtensionResult(XExtensionResult.Status.ERROR, PATH_ERR); } catch (FileNotFoundException e) { e.printStackTrace(); XLog.e(CLASS_NAME, FILE_NOT_FOUND_ERROR, e); return new XExtensionResult(XExtensionResult.Status.ERROR, FILE_NOT_FOUND_ERR); } catch (XCryptionException e) { e.printStackTrace(); XLog.e(CLASS_NAME, CRYPTION_ERROR, e); return new XExtensionResult(XExtensionResult.Status.ERROR, OPERATION_ERR); } } /** * 对称加密字节数组并返回 * * @param sKey * 密钥 * @param sourceData * 需要加密的数据 * @param options * 加解密配置选项 * @return 经过加密的数据 */ private String encrypt(String sKey, String sourceData, JSONObject options) throws XCryptionException { 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 webContext * 具体应用 * @param sKey * 密钥 * @param sourceFilePath * 需要加密的文件的路径(只支持相对路径) * @param targetFilePath * 经过加密得到的文件的路径 * @return 加密后文件的相对路径 * @throws XCryptionException * @throws FileNotFoundException */ private String encryptFile(XIWebContext webContext, String sKey, String sourceFilePath, String targetFilePath) throws FileNotFoundException, XCryptionException { return doFileCrypt(webContext, sKey, sourceFilePath, targetFilePath, true); } /** * 对称解密字节数组并返回 * * @param sKey * 密钥 * @param sourceData * 需要解密的数据 * @param options * 加解密配置选项 * @return 经过解密的数据 */ private String decrypt(String sKey, String sourceData, JSONObject options) throws XCryptionException { 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 webContext * 具体应用 * @param sKey * 密钥 * @param sourceFilePath * 需要解密的文件的路径(只支持相对路径) * @param targetFilePath * 经过解密得到的文件的路径 * @return 解密后文件的相对路径 * @throws FileNotFoundException */ private String decryptFile(XIWebContext webContext, String sKey, String sourceFilePath, String targetFilePath) throws XCryptionException, FileNotFoundException { return doFileCrypt(webContext, sKey, sourceFilePath, targetFilePath, false); } private String doFileCrypt(XIWebContext webContext, String sKey, String sourceFilePath, String targetFilePath, boolean isEncrypt) throws XCryptionException, FileNotFoundException { // 检查要加解密的key为空 if (XStringUtils.isEmptyString(sKey)) { XLog.e(CLASS_NAME, KEY_EMPTY_ERROR); throw new XCryptionException(KEY_EMPTY_ERROR); } // 检查传入文件路径是否为空 if (XStringUtils.isEmptyString(targetFilePath) || XStringUtils.isEmptyString(sourceFilePath)) { throw new IllegalArgumentException(); } // 对要解密的文件作路径解析和检测 String appWorkSpace = webContext.getWorkSpace(); File sourceFile = new File(appWorkSpace,sourceFilePath); String absSourceFilePath = getAbsFilePath(sourceFile); if((null == absSourceFilePath) || !XFileUtils.isFileAncestorOf(appWorkSpace, absSourceFilePath)) { throw new IllegalArgumentException(); } if (!sourceFile.exists()) { throw new FileNotFoundException(); } // 对解密后要生成的文件作路径解析和检测 File targetFile = new File(appWorkSpace, targetFilePath); String absTargetFilePath = getAbsFilePath(targetFile); if(null == absTargetFilePath || !XFileUtils.isFileAncestorOf(appWorkSpace, absTargetFilePath) || (null == new XPathResolver(targetFilePath, appWorkSpace).resolve())) { throw new IllegalArgumentException(); } if (targetFile.exists()) { targetFile.delete(); } if (!XFileUtils.createFile(absTargetFilePath)) { throw new FileNotFoundException(); } byte[] keyBytes = getBytesEncode(ENCODE_TYPE_STRING, sKey); if( isEncrypt ? mCryptor.encryptFileForDES(keyBytes, absSourceFilePath, absTargetFilePath) : mCryptor.decryptFileForDES(keyBytes, absSourceFilePath, absTargetFilePath)){ return targetFilePath; } throw new XCryptionException(CRYPTION_ERROR); } private String digest(String data) { XCryptor cryptor = new XCryptor(); try { return cryptor.calMD5Value(data.getBytes("UTF-8")); } catch (UnsupportedEncodingException e) { XLog.e(CLASS_NAME, "digest failed: UnsupportedEncodingException"); e.printStackTrace(); } return null; } /** * 获取文件的绝对路径 * @param targetFile * @return */ private String getAbsFilePath (File targetFile) throws IllegalArgumentException{ try { return targetFile.getCanonicalPath(); } catch (IOException e) { throw new IllegalArgumentException(); } } /** * 获取按直接编码解码后的keyBytes * @param encodeKeyType:编码类型 * @param keyString :解码前的keyString * @return 解码后的二进制串 */ private byte[] getBytesEncode(int encodeKeyType, String keyString) { if(XStringUtils.isEmptyString(keyString)) { return null; } 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(); } } }