实现Instagram登录密码加密时遇InvalidKeySpecException求助
Instagram登录机器人密码加密异常排查
我正在开发一个需要登录Instagram的机器人,Instagram采用特定加密系统处理密码,我按照指定的加密流程实现后,始终抛出以下异常:
java.security.spec.InvalidKeySpecException: java.security.InvalidKeyException: IOException: null
我的实现代码
// Generate a random IV byte[] iv = new byte[12]; new SecureRandom().nextBytes(iv); // Get the system current time in milliseconds long timestamp = System.currentTimeMillis(); // Converting public key to byte array from hex string byte[] publicKeyBytes = hexStringToByteArray(publicKey); // Create a random key byte[] randomKey = new byte[32]; new SecureRandom().nextBytes(randomKey); // rebuild key using SecretKeySpec SecretKey publicKeyClass = new SecretKeySpec(randomKey, 0, randomKey.length, "AES"); //generate the cipher text Cipher cipher = Cipher.getInstance("AES/GCM/NoPadding"); cipher.init(Cipher.ENCRYPT_MODE, publicKeyClass, new GCMParameterSpec(128, iv)); // Add the current time in milliseconds as additional authenticated data cipher.updateAAD(Long.toString(timestamp).getBytes(StandardCharsets.UTF_8)); byte[] cipherText = cipher.doFinal(pwd.getBytes(StandardCharsets.UTF_8)); // Get the tag from the end of the cipher text byte[] tag = Arrays.copyOfRange(cipherText, cipherText.length - 16, cipherText.length); // Now we encrypt a random key using the same public key // use RSA encryption to encrypt the random key X509EncodedKeySpec keySpec = new X509EncodedKeySpec(publicKeyBytes, "RSA"); KeyFactory keyFactory = KeyFactory.getInstance("RSA"); PublicKey encryptedKeyClass = keyFactory.generatePublic(keySpec); // 异常抛出点 Cipher cipher2 = Cipher.getInstance("RSA/ECB/PKCS1Padding"); cipher2.init(Cipher.ENCRYPT_MODE, encryptedKeyClass); byte[] encryptedRandomKey = cipher2.doFinal(randomKey); // Generate the final encrypted text String encryptedText = publicKeyId + encryptedRandomKey.toString() + tag.toString() + cipherText.toString(); //Base64 encode the encrypted text String encryptedTextBase64 = Base64.getEncoder().encodeToString(encryptedText.getBytes(StandardCharsets.UTF_8));
辅助函数hexStringToByteArray
public static byte[] hexStringToByteArray(String s) { int len = s.length(); byte[] data = new byte[len / 2]; for (int i = 0; i < len; i += 2) { data[i / 2] = (byte) ((Character.digit(s.charAt(i), 16) << 4) + Character.digit(s.charAt(i+1), 16)); } return data; }
整体流程
- 向函数传入密码和公钥(字符串);
- 生成随机IV和随机密钥;
- 使用随机密钥通过AES_GCM_256加密密码;
- 将Instagram返回的64字节十六进制编码公钥解码后,用于加密上述随机密钥;
- 按要求组合所有数据并返回最终密文。
问题出在第4步,执行PublicKey encryptedKeyClass = keyFactory.generatePublic(keySpec);时抛出无效密钥规范异常。
Instagram返回的示例公钥:
c81814218a8fe89a6a5794ff5f2a192bf5ab9d3f7115bc8fbceb7b701079777c
我参考了多个在线资料,实现方式一致,请问哪里出错了?
问题分析与解决
- 核心错误:公钥格式处理错误
Instagram返回的64字节十六进制字符串是RSA公钥的原始模数(n),并非X.509标准的DER编码公钥。X509EncodedKeySpec要求输入的是包含算法标识、模数、指数等完整信息的DER编码字节数组,直接用模数字节数组初始化会导致密钥解析失败。
正确的做法是使用RSAPublicKeySpec手动构建公钥:Instagram的RSA公钥默认使用65537(0x10001)作为公钥指数(e),我们可以基于模数和指数构建合法的RSA公钥。
修正后的第4步代码:
// 将十六进制公钥转换为BigInteger类型的模数 BigInteger modulus = new BigInteger(1, publicKeyBytes); // 固定公钥指数65537 BigInteger publicExponent = new BigInteger("65537"); // 构建RSA公钥规范 RSAPublicKeySpec rsaKeySpec = new RSAPublicKeySpec(modulus, publicExponent); KeyFactory keyFactory = KeyFactory.getInstance("RSA"); PublicKey encryptedKeyClass = keyFactory.generatePublic(rsaKeySpec);
- 其他潜在问题
代码中encryptedRandomKey.toString()、tag.toString()、cipherText.toString()的调用完全错误:字节数组调用toString()返回的是对象的哈希标识(如[B@1234abcd),不是字节内容的字符串表示。需要用Base64或十六进制编码将字节数组转换为字符串,例如:
// 用Base64编码字节数组 String encryptedRandomKeyStr = Base64.getEncoder().encodeToString(encryptedRandomKey); String tagStr = Base64.getEncoder().encodeToString(tag); // 注意:cipherText包含密文+标签,之前已经提取了tag,这里应该取密文部分(去掉最后16字节) byte[] actualCipher = Arrays.copyOfRange(cipherText, 0, cipherText.length - 16); String cipherStr = Base64.getEncoder().encodeToString(actualCipher); // 组合最终文本 String encryptedText = publicKeyId + encryptedRandomKeyStr + tagStr + cipherStr;
内容的提问来源于stack exchange,提问作者Guhan SenSam
相关产品推荐
相关产品推荐

