/** * */ package com.mcxiaoke.minicat.util; import com.mcxiaoke.minicat.AppContext; import javax.crypto.Cipher; import javax.crypto.Mac; import javax.crypto.spec.IvParameterSpec; import javax.crypto.spec.SecretKeySpec; import java.io.PrintWriter; import java.io.StringWriter; import java.io.UnsupportedEncodingException; import java.math.BigInteger; import java.net.URLDecoder; import java.net.URLEncoder; import java.security.KeyFactory; import java.security.KeyPair; import java.security.KeyPairGenerator; import java.security.MessageDigest; import java.security.NoSuchAlgorithmException; import java.security.PrivateKey; import java.security.PublicKey; import java.security.spec.RSAKeyGenParameterSpec; import java.security.spec.RSAPrivateKeySpec; import java.security.spec.RSAPublicKeySpec; import java.util.Collection; import java.util.Iterator; import java.util.List; import java.util.regex.Matcher; import java.util.regex.Pattern; /** * @author mcxiaoke * @version 1.2 2011.12.02 */ public class StringHelper { // Regex that matches characters that have special meaning in HTML. '<', // '>', '&' and // multiple continuous spaces. private static final Pattern PLAIN_TEXT_TO_ESCAPE = Pattern .compile("[<>&]| {2,}|\r?\n"); /** * Escape some special character as HTML escape sequence. * * @param text Text to be displayed using WebView. * @return Text correctly escaped. */ public static String escapeCharacterToDisplay(String text) { Pattern pattern = PLAIN_TEXT_TO_ESCAPE; Matcher match = pattern.matcher(text); if (match.find()) { StringBuilder out = new StringBuilder(); int end = 0; do { int start = match.start(); out.append(text.substring(end, start)); end = match.end(); int c = text.codePointAt(start); if (c == ' ') { // Escape successive spaces into series of " ". for (int i = 1, n = end - start; i < n; ++i) { out.append(" "); } out.append(' '); } else if (c == '\r' || c == '\n') { out.append("<br>"); } else if (c == '<') { out.append("<"); } else if (c == '>') { out.append(">"); } else if (c == '&') { out.append("&"); } } while (match.find()); out.append(text.substring(end)); text = out.toString(); } return text; } public static String bytesToHexString(byte[] bytes) { // http://stackoverflow.com/questions/332079 StringBuffer sb = new StringBuffer(); for (int i = 0; i < bytes.length; i++) { String hex = Integer.toHexString(0xFF & bytes[i]); if (hex.length() == 1) { sb.append('0'); } sb.append(hex); } return sb.toString(); } public static String getStackMessageString(Throwable e) { StringBuffer message = new StringBuffer(); StackTraceElement[] stack = e.getStackTrace(); StackTraceElement stackLine = stack[stack.length - 1]; message.append(stackLine.getFileName()); message.append(":"); message.append(stackLine.getLineNumber()); message.append(":"); message.append(stackLine.getMethodName()); message.append(" "); message.append(e.getMessage()); return message.toString(); } public static String getStackTraceString(Throwable tr) { if (tr == null) { return ""; } StringWriter sw = new StringWriter(); PrintWriter pw = new PrintWriter(sw); tr.printStackTrace(pw); return sw.toString(); } public static String toString(List<String> array) { if (array == null || array.size() == 0) { return ""; } StringBuilder sb = new StringBuilder(); sb.append("("); for (int i = 0; i < array.size(); i++) { if (i > 0) { sb.append(", "); } sb.append(array.get(i)); } sb.append(")"); return sb.toString(); } public static String toString(String[] array) { if (array == null || array.length == 0) { return ""; } StringBuilder sb = new StringBuilder(); sb.append("("); for (int i = 0; i < array.length; i++) { if (i > 0) { sb.append(", "); } sb.append(array[i]); } sb.append(")"); return sb.toString(); } public static String join(Collection<?> items, String delimiter) { if (items == null || items.isEmpty()) { return ""; } final Iterator<?> iter = items.iterator(); final StringBuilder buffer = new StringBuilder(iter.next().toString()); while (iter.hasNext()) { buffer.append(delimiter).append(iter.next()); } return buffer.toString(); } /** * @param s 原始消息字符串 * @return 自动截断超过140个字符的消息,取前面133个字符,并添加...,预留转发的字符位置 */ public static String cut(String s) { String str = s.trim(); StringBuilder sb = new StringBuilder(); if (str.length() > 140) { return sb.append(str.substring(0, 135)).append("...").toString(); } else { return str; } } public static String join(String separator, String[] strings) { if (strings == null) { return null; } if (strings.length == 0) { return ""; } StringBuilder builder = new StringBuilder(strings[0]); for (int i = 1, length = strings.length; i < length; i++) { builder.append(separator); builder.append(strings[i]); } return builder.toString(); } public static String join(String separator, Integer[] integers) { if (integers == null) { return null; } if (integers.length == 0) { return ""; } StringBuilder builder = new StringBuilder(integers[0].toString()); for (int i = 1, length = integers.length; i < length; i++) { builder.append(separator); builder.append(integers[i]); } return builder.toString(); } /** * @param s 原始字符串 * @return 判断字符串是否为空 */ public static boolean isEmpty(String s) { return s == null || s.trim().equals(""); } public static String md5(String s) { StringBuffer result = new StringBuffer(); try { MessageDigest md = MessageDigest.getInstance("MD5"); md.update(s.getBytes()); byte digest[] = md.digest(); for (int i = 0; i < digest.length; i++) { result.append(Integer.toHexString(0xFF & digest[i])); } } catch (NoSuchAlgorithmException e) { e.printStackTrace(); } return (result.toString()); } /** * MD5加密函数 * * @param str 要加密的字符串 * @return 加密后的字符串 */ public static String md5old(String str) { if ((str == null) || ("".equals(str.trim()))) { return str; } else { MessageDigest messageDigest = null; try { messageDigest = MessageDigest.getInstance("MD5"); messageDigest.reset(); messageDigest.update(str.getBytes("UTF-8")); } catch (NoSuchAlgorithmException e) { e.printStackTrace(); } catch (UnsupportedEncodingException e) { e.printStackTrace(); } byte[] byteArray = messageDigest.digest(); StringBuffer md5StrBuff = new StringBuffer(); for (int i = 0; i < byteArray.length; i++) { if (Integer.toHexString(0xFF & byteArray[i]).length() == 1) md5StrBuff.append("0").append( Integer.toHexString(0xFF & byteArray[i])); else md5StrBuff.append(Integer.toHexString(0xFF & byteArray[i])); } return md5StrBuff.toString(); } } public static String getMD5Str(String str) { MessageDigest messageDigest = null; try { messageDigest = MessageDigest.getInstance("MD5"); messageDigest.reset(); messageDigest.update(str.getBytes("UTF-8")); } catch (NoSuchAlgorithmException e) { System.out.println("NoSuchAlgorithmException caught!"); System.exit(-1); } catch (UnsupportedEncodingException e) { e.printStackTrace(); } byte[] byteArray = messageDigest.digest(); StringBuffer md5StrBuff = new StringBuffer(); for (int i = 0; i < byteArray.length; i++) { if (Integer.toHexString(0xFF & byteArray[i]).length() == 1) md5StrBuff.append("0").append( Integer.toHexString(0xFF & byteArray[i])); else md5StrBuff.append(Integer.toHexString(0xFF & byteArray[i])); } // 16位加密,从第9位到25位 return md5StrBuff.substring(8, 24).toString().toUpperCase(); } public static String encode(String value) { String encoded = null; try { encoded = URLEncoder.encode(value, "UTF-8"); } catch (UnsupportedEncodingException ignore) { } StringBuffer buf = new StringBuffer(encoded.length()); char focus; for (int i = 0; i < encoded.length(); i++) { focus = encoded.charAt(i); if (focus == '*') { buf.append("%2A"); } else if (focus == '+') { buf.append("%20"); } else if (focus == '%' && (i + 1) < encoded.length() && encoded.charAt(i + 1) == '7' && encoded.charAt(i + 2) == 'E') { buf.append('~'); i += 2; } else { buf.append(focus); } } return buf.toString(); } /** * 字符串转化为数字 * * @param s 字符串参数 * @return 字符串代表的数字,如果无法转换,返回0 */ public static int toInt(String s) { try { return Integer.parseInt(s); } catch (NumberFormatException e) { return -1; } } /** * @param original * @return null if fails */ public static String urlencode(String original) { try { // return URLEncoder.encode(original, "utf-8"); // fixed: to comply with RFC-3986 return URLEncoder.encode(original, "utf-8").replace("+", "%20") .replace("*", "%2A").replace("%7E", "~"); } catch (UnsupportedEncodingException e) { if (AppContext.DEBUG) e.printStackTrace(); } return null; } /** * @param encoded * @return null if fails */ public static String urldecode(String encoded) { try { return URLDecoder.decode(encoded, "utf-8"); } catch (UnsupportedEncodingException e) { if (AppContext.DEBUG) e.printStackTrace(); } return null; } /** * @param original * @param key * @return null if fails */ public static String hmacSha1Digest(String original, String key) { return hmacSha1Digest(original.getBytes(), key.getBytes()); } /** * @param original * @param key * @return null if fails */ public static String hmacSha1Digest(byte[] original, byte[] key) { try { Mac mac = Mac.getInstance("HmacSHA1"); mac.init(new SecretKeySpec(key, "HmacSHA1")); byte[] rawHmac = mac.doFinal(original); return new String(Base64.encodeBytes(rawHmac)); } catch (Exception e) { if (AppContext.DEBUG) e.printStackTrace(); } return null; } /** * @param original * @return null if fails */ public static String md5sum(byte[] original) { try { MessageDigest md = MessageDigest.getInstance("MD5"); md.update(original, 0, original.length); StringBuffer md5sum = new StringBuffer(new BigInteger(1, md.digest()).toString(16)); while (md5sum.length() < 32) md5sum.insert(0, "0"); return md5sum.toString(); } catch (NoSuchAlgorithmException e) { if (AppContext.DEBUG) e.printStackTrace(); } return null; } /** * @param original * @return null if fails */ public static String md5sum(String original) { return md5sum(original.getBytes()); } /** * AES encrypt function * * @param original * @param key 16, 24, 32 bytes available * @param iv initial vector (16 bytes) - if null: ECB mode, otherwise: CBC * mode * @return */ public static byte[] aesEncrypt(byte[] original, byte[] key, byte[] iv) { if (key == null || (key.length != 16 && key.length != 24 && key.length != 32)) { return null; } if (iv != null && iv.length != 16) { return null; } try { SecretKeySpec keySpec = null; Cipher cipher = null; if (iv != null) { keySpec = new SecretKeySpec(key, "AES/CBC/PKCS7Padding"); cipher = Cipher.getInstance("AES/CBC/PKCS7Padding"); cipher.init(Cipher.ENCRYPT_MODE, keySpec, new IvParameterSpec( iv)); } else // if(iv == null) { keySpec = new SecretKeySpec(key, "AES/ECB/PKCS7Padding"); cipher = Cipher.getInstance("AES/ECB/PKCS7Padding"); cipher.init(Cipher.ENCRYPT_MODE, keySpec); } return cipher.doFinal(original); } catch (Exception e) { if (AppContext.DEBUG) e.printStackTrace(); } return null; } /** * AES decrypt function * * @param encrypted * @param key 16, 24, 32 bytes available * @param iv initial vector (16 bytes) - if null: ECB mode, otherwise: CBC * mode * @return */ public static byte[] aesDecrypt(byte[] encrypted, byte[] key, byte[] iv) { if (key == null || (key.length != 16 && key.length != 24 && key.length != 32)) { return null; } if (iv != null && iv.length != 16) { return null; } try { SecretKeySpec keySpec = null; Cipher cipher = null; if (iv != null) { keySpec = new SecretKeySpec(key, "AES/CBC/PKCS7Padding"); cipher = Cipher.getInstance("AES/CBC/PKCS7Padding"); cipher.init(Cipher.DECRYPT_MODE, keySpec, new IvParameterSpec( iv)); } else // if(iv == null) { keySpec = new SecretKeySpec(key, "AES/ECB/PKCS7Padding"); cipher = Cipher.getInstance("AES/ECB/PKCS7Padding"); cipher.init(Cipher.DECRYPT_MODE, keySpec); } return cipher.doFinal(encrypted); } catch (Exception e) { if (AppContext.DEBUG) e.printStackTrace(); } return null; } /** * generates RSA key pair * * @param keySize * @param publicExponent public exponent value (can be RSAKeyGenParameterSpec.F0 or F4) * @return */ public static KeyPair generateRsaKeyPair(int keySize, BigInteger publicExponent) { KeyPair keys = null; try { KeyPairGenerator keyGen = KeyPairGenerator.getInstance("RSA"); RSAKeyGenParameterSpec spec = new RSAKeyGenParameterSpec(keySize, publicExponent); keyGen.initialize(spec); keys = keyGen.generateKeyPair(); } catch (Exception e) { if (AppContext.DEBUG) e.printStackTrace(); } return keys; } /** * generates a RSA public key with given modulus and public exponent * * @param modulus (must be positive? don't know exactly) * @param publicExponent * @return */ public static PublicKey generateRsaPublicKey(BigInteger modulus, BigInteger publicExponent) { try { return KeyFactory.getInstance("RSA").generatePublic( new RSAPublicKeySpec(modulus, publicExponent)); } catch (Exception e) { if (AppContext.DEBUG) e.printStackTrace(); } return null; } /** * generates a RSA private key with given modulus and private exponent * * @param modulus (must be positive? don't know exactly) * @param privateExponent * @return */ public static PrivateKey generateRsaPrivateKey(BigInteger modulus, BigInteger privateExponent) { try { return KeyFactory.getInstance("RSA").generatePrivate( new RSAPrivateKeySpec(modulus, privateExponent)); } catch (Exception e) { if (AppContext.DEBUG) e.printStackTrace(); } return null; } /** * RSA encrypt function (RSA / ECB / PKCS1-Padding) * * @param original * @param key * @return */ public static byte[] rsaEncrypt(byte[] original, PublicKey key) { try { Cipher cipher = Cipher.getInstance("RSA/ECB/PKCS1Padding"); cipher.init(Cipher.ENCRYPT_MODE, key); return cipher.doFinal(original); } catch (Exception e) { if (AppContext.DEBUG) e.printStackTrace(); } return null; } /** * RSA decrypt function (RSA / ECB / PKCS1-Padding) * * @param encrypted * @param key * @return */ public static byte[] rsaDecrypt(byte[] encrypted, PrivateKey key) { try { Cipher cipher = Cipher.getInstance("RSA/ECB/PKCS1Padding"); cipher.init(Cipher.DECRYPT_MODE, key); return cipher.doFinal(encrypted); } catch (Exception e) { if (AppContext.DEBUG) e.printStackTrace(); } return null; } /** * converts given byte array to a hex string * * @param bytes * @return */ public static String byteArrayToHexString(byte[] bytes) { StringBuffer buffer = new StringBuffer(); for (int i = 0; i < bytes.length; i++) { if ((bytes[i] & 0xff) < 0x10) buffer.append("0"); buffer.append(Long.toString(bytes[i] & 0xff, 16)); } return buffer.toString(); } /** * converts given hex string to a byte array (ex: "0D0A" => {0x0D, 0x0A,}) * * @param str * @return */ public static final byte[] hexStringToByteArray(String str) { int i = 0; byte[] results = new byte[str.length() / 2]; for (int k = 0; k < str.length(); ) { results[i] = (byte) (Character.digit(str.charAt(k++), 16) << 4); results[i] += (byte) (Character.digit(str.charAt(k++), 16)); i++; } return results; } public static String urlencode_rfc3986(String text) { final String encoded = StringUtils.replace(URLEncoder.encode(text).replace("+", "%20"), "%7E", "~"); return encoded; } /** * Tweet Regex Pattern * * public static final Pattern a = Pattern.compile( * "\\b(([\\w-]+://?|www[.])[^\\s()<>]+(?:\\([\\w\\d]+\\)|([^[:punct:]\\s]|/)))" * ); public static final Pattern b = Pattern.compile( * "\\b(([hH][tT][tT][pP][sS]?://?)[^\\s()<>]+(?:\\([\\w\\d]+\\)|([^[:punct:]\\s]|/)))" * ); public static final Pattern c = Pattern.compile("[@#]{1}\\w+"); public * static final Pattern d = Pattern.compile("((\\s\\s+)|\\n|\\r)"); public * static final Pattern e = * Pattern.compile("^https?://twitter\\.com(/#!)?/\\w+/status/\\d+$"); * public static final Pattern f = Pattern.compile("\\A@?\\w+\\z"); * */ }