RSA加密数据生成QR码扫码后无法解密问题解决指导
问题描述
已完成数据的RSA加密处理,加密后密文生成QR码存储。使用QR扫码器扫描对应QR码时,仅能读取到加密后的密文,无法将密文解密还原为原始明文。
原有加解密功能代码
public class SecureQRcodeWithDigitalSignature { private PrivateKey privateKey; private PublicKey publicKey; public SecureQRcodeWithDigitalSignature () { try { KeyPairGenerator generator = KeyPairGenerator.getInstance("RSA"); generator.initialize(2048); KeyPair pair = generator.generateKeyPair(); privateKey = pair.getPrivate(); publicKey = pair.getPublic(); } catch (Exception ignored) { } } public String encrypt(String message) throws Exception{ byte[] messageToBytes = message.getBytes(); Cipher cipher = Cipher.getInstance("RSA/ECB/PKCS1Padding"); cipher.init(Cipher.ENCRYPT_MODE,publicKey); byte[] encryptedBytes = cipher.doFinal(messageToBytes); return encode(encryptedBytes); } private String encode(byte[] data){ return Base64.getEncoder().encodeToString(data); } public String decrypt(String encryptedMessage) throws Exception{ byte[] encryptedBytes = decode(encryptedMessage); Cipher cipher = Cipher.getInstance("RSA/ECB/PKCS1Padding"); cipher.init(Cipher.DECRYPT_MODE,privateKey); byte[] decryptedMessage = cipher.doFinal(encryptedBytes); return new String(decryptedMessage,"UTF8"); } private byte[] decode(String data){ return Base64.getDecoder().decode(data); } public static void main(String[] args) { Scanner message = new Scanner(System.in); String data= message.nextLine(); SecureQRcodeWithDigitalSignature rsa = new SecureQRcodeWithDigitalSignature (); try{ String encryptedMessage = rsa.encrypt(data); String decryptedMessage = rsa.decrypt(encryptedMessage); System.err.println("Encrypted:\n"+encryptedMessage); System.err.println("Decrypted:\n"+decryptedMessage); }catch (Exception ignored){} } }
原有扫码功能代码
public void run (){ do { try { Thread.sleep(100); } catch (InterruptedException ex) { Logger.getLogger(ScannerQR.class.getName()).log(Level.SEVERE, null, ex); } Result result = null; BufferedImage image = null; String data = result_field.getText(); if(webcam.isOpen()){ if((image = webcam.getImage()) ==null){ continue; } } LuminanceSource source = new BufferedImageLuminanceSource(image); BinaryBitmap bitmap = new BinaryBitmap (new HybridBinarizer(source)); try { result= new MultiFormatReader().decode(bitmap); } catch (NotFoundException ex) { Logger.getLogger(ScannerQR.class.getName()).log(Level.SEVERE, null, ex); } if (result != null){ SecureQRcodeWithDigitalSignature b = new SecureQRcodeWithDigitalSignature (); result_field.setText(result.getText()); result_field.setEditable(false); plaintext_result.setText(data); } }while(true); }
核心问题排查
- 密钥对不匹配:
SecureQRcodeWithDigitalSignature的构造函数每次执行都会生成全新的2048位RSA密钥对。生成QR码时加密用的是当时实例的密钥对,扫码时新建实例生成的是完全无关的新密钥,用新密钥的私钥根本解不了旧公钥加密的内容。 - 未执行解密逻辑:扫码识别到QR码内容后,代码直接把密文设置到了结果框,全程没有调用
decrypt()方法做解密处理。虽然创建了加解密类的实例b,但没有调用该实例的任何方法,属于无效代码。 - 变量取值顺序错误:在还没拿到扫码结果的时候,就提前把
result_field的旧值取出来赋值给data变量,后续展示明文用的是这个旧值,根本不是扫到的密文解密后的内容。 - 异常被空捕获:加解密环节的异常直接被吞掉,没有任何日志输出,出问题时完全无法定位错误点。
修复步骤
- 持久化RSA密钥对:第一次生成密钥对后,将公钥、私钥通过Base64编码后存储到本地文件或配置项中,加密和解密环节加载同一套密钥,禁止每次实例化都生成新密钥。
- 补全解密调用逻辑:扫码拿到密文后,加载存储的私钥初始化RSA解密实例,调用
decrypt()方法处理密文得到明文。 - 修正变量取值顺序:拿到
result.getText()的密文内容后,再做解密处理,最后把明文赋值给plaintext_result展示。 - 移除空异常捕获,加解密、扫码环节的异常要打印错误栈,方便排查问题。
修正后的扫码逻辑参考
public void run (){ // 提前初始化加解密实例,加载持久化存储的同一套密钥对,不要每次扫码都新建实例 SecureQRcodeWithDigitalSignature rsaProcessor = new SecureQRcodeWithDigitalSignature(); do { try { Thread.sleep(100); } catch (InterruptedException ex) { Logger.getLogger(ScannerQR.class.getName()).log(Level.SEVERE, null, ex); } Result result = null; BufferedImage image = null; if(webcam.isOpen()){ if((image = webcam.getImage()) ==null){ continue; } } LuminanceSource source = new BufferedImageLuminanceSource(image); BinaryBitmap bitmap = new BinaryBitmap (new HybridBinarizer(source)); try { result= new MultiFormatReader().decode(bitmap); } catch (NotFoundException ex) { // 未识别到QR码时直接跳过,避免冗余日志 continue; } if (result != null){ String encryptedContent = result.getText(); result_field.setText(encryptedContent); result_field.setEditable(false); try { // 调用解密方法拿到明文 String plainContent = rsaProcessor.decrypt(encryptedContent); plaintext_result.setText(plainContent); } catch (Exception e) { Logger.getLogger(ScannerQR.class.getName()).log(Level.SEVERE, "解密失败", e); plaintext_result.setText("密文解密失败,请检查密钥是否匹配"); } } }while(true); }
注意:RSA 2048位单次最多加密245字节内容,如果要加密更长的明文,建议用RSA加密对称密钥、对称密钥加密明文的混合加密方案,避免密文过长导致QR码容量不足、解密失败。
内容的提问来源于stack exchange,提问作者Hanis Rashid
相关产品推荐
相关产品推荐

