最近在浏览网站时发现了一个有趣的网站:https://www.umpox.com/zero-width-detection
这个网站可以将自己不想给别人看到的数据转换成零宽字符隐藏到一段话里,感觉挺有意思的我就去网上找了下相关的内容,发现几乎都是js实现的(可能浏览器上效果更好)。
本人我是学习java的就想用java也实现一个这样的效果,然后翻阅读大量的相关信息明白了实现的原理,就着手写出。
原理是利用零宽字符这种在文本中不显示的特殊字符,对加密文本进行转码,嵌入到普通文本当中,从而隐藏加密内容;表面看起来是一段普通文本,复制粘贴不会丢失。
实现代码:
//中文转unicode编码 public static String gbEncoding(final String gbString) { char[] utfBytes = gbString.toCharArray(); String unicodeBytes = ""; for (int i = 0; i < utfBytes.length; i++) { String hexB = Integer.toHexString(utfBytes[i]); if (hexB.length() <= 2) { hexB = "00" + hexB; } unicodeBytes = unicodeBytes + "\\u" + hexB; } return unicodeBytes; } //unicode编码转中文 public static String decodeUnicode(final String dataStr) { int start = 0; int end = 0; final StringBuffer buffer = new StringBuffer(); while (start > -1) { end = dataStr.indexOf("\\u", start + 2); String charStr = ""; if (end == -1) { charStr = dataStr.substring(start + 2, dataStr.length()); } else { charStr = dataStr.substring(start + 2, end); } char letter = (char) Integer.parseInt(charStr, 16); // 16进制parse整形字符串。 buffer.append(new Character(letter).toString()); start = end; } return buffer.toString(); } //汉字转换成二进制字符串 public static String strToBinStr(String str) { char[] chars=str.toCharArray(); StringBuffer result = new StringBuffer(); for(int i=0; i<chars.length; i++) { result.append(Integer.toBinaryString(chars[i])); result.append(" "); } return result.toString(); } //二进制字符串转换成汉字 public static String BinStrTostr(String binary) { String[] tempStr=binary.split(" "); char[] tempChar=new char[tempStr.length]; for(int i=0;i<tempStr.length;i++) { tempChar[i]=BinstrToChar(tempStr[i]); } return String.valueOf(tempChar); } //将二进制字符串转换成int数组 public static int[] BinstrToIntArray(String binStr) { char[] temp=binStr.toCharArray(); int[] result=new int[temp.length]; for(int i=0;i<temp.length;i++) { result[i]=temp[i]-48; } return result; } //将二进制转换成字符 public static char BinstrToChar(String binStr){ int[] temp=BinstrToIntArray(binStr); int sum=0; for(int i=0; i<temp.length;i++){ sum +=temp[temp.length-1-i]<<i; } return (char)sum; } //零宽字加密 public static String ZeroWidthWrdEncryption(String str){ char[] chars=gbEncoding(str).toCharArray(); StringBuffer result = new StringBuffer(); StringBuffer jtext = new StringBuffer("\\uFEFF"); for(int i=0; i<chars.length; i++) { if(Integer.toBinaryString(chars[i]).length()==7){ result.append("0"); result.append(Integer.toBinaryString(chars[i])); }else { result.append("00"); result.append(Integer.toBinaryString(chars[i])); } } for(int i=0; i<result.toString().length(); i++) { if(i%2==0){ switch(result.toString().substring(i,i+2)){ case "00" : jtext.append("\\u200a"); break; case "01" : jtext.append("\\u200b"); break; case "10" : jtext.append("\\u200c"); break; case "11" : jtext.append("\\u200d"); break; } } } jtext.append("\\uFEFF"); return decodeUnicode(jtext.toString()); } //零宽字解密 public static String ZeroWidthWordDecryption(String txt){ String text = gbEncoding(txt); String str = text.substring(text.indexOf("\\ufeff")+6, text.lastIndexOf("\\ufeff")); str = str.replace("\\u200a","00"); str = str.replace("\\u200b","01"); str = str.replace("\\u200c","10"); str = str.replace("\\u200d","11"); char[] tempChar=new char[str.length()/8]; for(int i=0;i<str.length();i++) { if(i%8==0){ tempChar[i/8]=BinstrToChar(str.substring(i,i+8)); } } return decodeUnicode(String.valueOf(tempChar)); } public static void main(String[] args) { String cat = ZeroWidthWrdEncryption("猫"); String zoo = "狗"+cat+"狗"; System.out.println("加密后:"+zoo); System.out.println("解密出:"+ZeroWidthWordDecryption(zoo)); }
控制台上看到加密后的信息中有空格,但复制出来发到QQ上就看不到空格了。
PC版QQ上看到的可能有乱码,安卓版上没什么问题,所以看来零宽字符在浏览器上比较友好。
发表评论