java – 问题将Base64字符串转换为十六进制字符串
TLDR:
我遗漏的边缘情况是什么,或者我的算法中将Base64字符串转换为十六进制字符串是否有错误? 我最近决定尝试Matasano Crypto Challenges,但无论出于何种原因,我决定尝试编写第一个挑战,而不使用库来转换Hex和Base64字符串. 我已经设法让Hex到Base64转换工作,但是从输出中可以看出,当我尝试将Base64转换回Hex时会有轻微的异常(例如,将Base64的最后四个值与Hex输出进行比较) ).
我使用https://conv.darkbyte.ru/来检查我的一些值,并假设该站点上的代码是正确的,似乎我的问题是从Base64获取Base10表示,而不是从Base10到Hex:
似乎所有带错误的值都聚集在40-60和100-120之间,但我不确定从那里到底要去哪里.我猜是有一些边缘情况我会注意处理,但我不确定那会是什么. 相关代码: private static final Character[] base64Order = new Character[] { 'A','B','C','D','E','F','G','H','I','J','K','L','M','N','O','P','Q','R','S','T','U','V','W','X','Y','Z','a','b','c','d','e','f','g','h','i','j','k','l','m','n','o','p','q','r','s','t','u','v','w','x','y','z','0','1','2','3','4','5','6','7','8','9','+','/',}; private static final Character[] hexOrder = new Character[] { '0','f' }; public static String base64ToHex(String base64) throws Exception { if (base64.length() % 4 != 0 || base64.contains("[^a-zA-Z0-9+/]")) throw new Exception("InputNotBase64"); else { int charValue = 0; int index = 0; String hex = ""; BitSet bits = new BitSet(); for (int i = 0; i < base64.length(); i++) { charValue = base64.charAt(i); // get actual value from ASCII table if (charValue > 64 && charValue < 91) charValue -= 65; if (charValue > 96 && charValue < 123) charValue -= 71; /// loop that adds to the BitSet reads right-to-left,so reverse // the bits and then shift charValue = Integer.reverse(charValue << 24) & 0xff; charValue >>= 2; // append binary values to the BitSet while (charValue != 0L) { if (charValue % 2 != 0) { bits.set(index); } index++; charValue >>= 1; } // account for trailing 0s while (index % 6 != 0) { index++; } } // read 8-bit integer value for hex-value lookup String temp; int remainder; for (int i = 0; i < index; i++) { charValue = (charValue | (bits.get(i) ? 1 : 0)); if ((i + 1) % 8 == 0) { temp = ""; while (charValue != 0L) { remainder = charValue % 16; temp = hexOrder[remainder] + temp; charValue /= 16; } hex += temp; } charValue <<= 1; } return hex; } } 解决方法
您忘记在代码中处理以下字符:’0′,’1′,’2′,’3′,’4′,’5′,’6′,’7′,’8′,’9′,’ ‘,’/’
如果您替换以下代码 if (charValue > 64 && charValue < 91) charValue -= 65; if (charValue > 96 && charValue < 123) charValue -= 71; 通过 charValue = getPositionInBase64(charValue); 哪里 public static int getPositionInBase64(int n) { for (int p = 0; p < base64Order.length; p++) { if (n == base64Order[p]) { return p; } } return -1; } 一切正常 此外,当您使用字符而不是幻数时,代码更易读 if (charValue >= 'A' && charValue <= 'Z') charValue -= 'A'; ... 在这种情况下,发现问题更容易 因为你问我正在提出可能的改进来加速计算. 准备下表并初始化一次 // index = character,value = index of character from base64Order private static final int[] base64ToInt = new int[128]; public static void initBase64ToIntTable() { for (int i = 0; i < base64Order.length; i++) { base64ToInt[base64Order[i]] = i; } } 现在您可以通过简单的操作替换您的if / else链 charValue = base64ToInt[base64.charAt(i)]; 使用这个我写的方法比你的方法快几倍 private static String intToHex(int n) { return String.valueOf(new char[] { hexOrder[n/16],hexOrder[n%16] }); } public static String base64ToHexVer2(String base64) throws Exception { StringBuilder hex = new StringBuilder(base64.length()*3/4); //capacity could be 3/4 of base64 string length if (base64.length() % 4 != 0 || base64.contains("[^a-zA-Z0-9+/]")) { throw new Exception("InputNotBase64"); } else { for (int i = 0; i < base64.length(); i += 4) { int n0 = base64ToInt[base64.charAt(i)]; int n1 = base64ToInt[base64.charAt(i+1)]; int n2 = base64ToInt[base64.charAt(i+2)]; int n3 = base64ToInt[base64.charAt(i+3)]; // in descriptions I treat all 64 base chars as 6 bit // all 6 bites from 0 and 1st 2 from 1st (00000011 ........ ........) hex.append(intToHex(n0*4 + n1/16)); // last 4 bites from 1st and first 4 from 2nd (........ 11112222 ........) hex.append(intToHex((n1%16)*16 + n2/4)); // last 2 bites from 2nd and all from 3rd (........ ........ 22333333) hex.append(intToHex((n2%4)*64 + n3)); } } return hex.toString(); } 我认为这段代码更快,主要是因为简单的转换为十六进制.如果您想要并且需要它,您可以测试它. 要测试速度,您可以使用以下构造 String b64 = "SSdtIGtpbGxpbmcgeW91ciBicmFpbiBsaWtlIGEgcG9pc29ub3VzIG11c2hyb29t"; try { Base64ToHex.initBase64ToIntTable(); System.out.println(Base64ToHex.base64ToHex(b64)); System.out.println(Base64ToHex.base64ToHexVer2(b64)); int howManyIterations = 100000; Date start,stop; long period; start = new Date(); for (int i = 0; i < howManyIterations; i++) { Base64ToHex.base64ToHexVer2(b64); } stop = new Date(); period = stop.getTime() - start.getTime(); System.out.println("Ver2 taken " + period + " ms"); start = new Date(); for (int i = 0; i < howManyIterations; i++) { Base64ToHex.base64ToHex(b64); } stop = new Date(); period = stop.getTime() - start.getTime(); System.out.println("Ver1 taken " + period + " ms"); } catch (Exception ex) { } 示例结果是 49276d206b696c6c696e6720796f757220627261696e206c696b65206120706f69736f6e6f7573206d757368726f6f6d 49276d206b696c6c696e6720796f757220627261696e206c696b65206120706f69736f6e6f7573206d757368726f6f6d Ver2 taken 300 ms Ver1 taken 2080 ms 但它只是近似值.当您首先检查Ver1而将Ver2检查为第二个时,结果可能会略有不同.另外,对于不同的javas(第6,7,8)以及用于启动java的不同设置,结果可能不同 (编辑:李大同) 【声明】本站内容均来自网络,其相关言论仅代表作者个人观点,不代表本站立场。若无意侵犯到您的权利,请及时与联系站长删除相关内容! |