You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

Java RSA客户端签名报错:DerInputStream.getLength(): lengthTag=109

RSA签名时抛出InvalidKeySpecException:DerInputStream.getLength(): lengthTag=109, too big

执行命令java Client localhost 1234 server sample.txt时,程序抛出如下错误:

Connected to server
Exception in thread "main" java.security.spec.InvalidKeySpecException: java.security.InvalidKeyException: IOException : DerInputStream.getLength(): lengthTag=109, too big.
        at sun.security.rsa.RSAKeyFactory.engineGeneratePrivate(Unknown Source)
        at java.security.KeyFactory.generatePrivate(Unknown Source)
        at Client.sign(Client.java:123)
        at Client.main(Client.java:54)
Caused by: java.security.InvalidKeyException: IOException : DerInputStream.getLength(): lengthTag=109, too big.
        at sun.security.pkcs.PKCS8Key.decode(Unknown Source)
        at sun.security.pkcs.PKCS8Key.decode(Unknown Source)
        at sun.security.rsa.RSAPrivateCrtKeyImpl.<init>(Unknown Source)
        at sun.security.rsa.RSAPrivateCrtKeyImpl.newKey(Unknown Source)
        at sun.security.rsa.RSAKeyFactory.generatePrivate(Unknown Source)
        ... 4 more

引发错误的sign方法代码如下:

public static byte[] sign(byte[] data, String privateKeyFile) throws Exception {
        // Read the private key from file
        FileInputStream keyfis = new FileInputStream(privateKeyFile);
        byte[] keyBytes = new byte[keyfis.available()];
        keyfis.read(keyBytes);
        keyfis.close();

        // Create the private key object
        PKCS8EncodedKeySpec spec = new PKCS8EncodedKeySpec(keyBytes);
        KeyFactory kf = KeyFactory.getInstance("RSA");
        PrivateKey privateKey = kf.generatePrivate(spec);

        // Sign the data using SHA-256 with RSA
        Signature signature = Signature.getInstance("SHA256withRSA");
        signature.initSign(privateKey);
        signature.update(data);
        byte[] signed = signature.sign();

        return signed;
}

调用该方法的代码行:

byte[] signature = sign(encryptedBytes, userid + PRIVATE_KEY_FILENAME);

完整的Client.java代码:

import java.io.*;
import java.net.*;
import java.security.*;
import java.security.spec.*;
import javax.crypto.*;
import javax.crypto.spec.*;
import java.util.*;
import java.math.BigInteger;
import java.security.interfaces.*;
import java.security.Signature;
import java.security.SignatureException;

public class Client {

    private static final String PUBLIC_KEY_FILENAME = "server.pub";
    private static final String PRIVATE_KEY_FILENAME = ".prv";
    private static final int AES_KEY_SIZE = 256;
    private static final int IV_SIZE = 16;

    public static void main(String[] args) throws Exception {

        // Check command line arguments
        if (args.length != 4) {
            System.out.println("Usage: java Client host port userid filename");
            return;
        }

        // Get command line arguments
        String host = args[0];
        int port = Integer.parseInt(args[1]);
        String userid = args[2];
        String filename = args[3];

        // Connect to server
        Socket socket = new Socket(host, port);
        System.out.println("Connected to server");

        // Set up input and output streams
        DataInputStream input = new DataInputStream(socket.getInputStream());
        DataOutputStream output = new DataOutputStream(socket.getOutputStream());

        // Send userid to server
        output.writeUTF(userid);

        // Key agreement stage
        byte[] clientBytes = new byte[16];
        SecureRandom random = new SecureRandom();
        random.nextBytes(clientBytes);

        byte[] encryptedBytes = encryptRSA(clientBytes, userid + PRIVATE_KEY_FILENAME);
        output.writeInt(encryptedBytes.length);
        output.write(encryptedBytes);

        byte[] signature = sign(encryptedBytes, userid + PRIVATE_KEY_FILENAME);
        output.writeInt(signature.length);
        output.write(signature);

        int serverEncryptedBytesLength = input.readInt();
        byte[] serverEncryptedBytes = new byte[serverEncryptedBytesLength];
        input.readFully(serverEncryptedBytes);

        int serverSignatureLength = input.readInt();
        byte[] serverSignature = new byte[serverSignatureLength];
        input.readFully(serverSignature);

        if (!verify(serverEncryptedBytes, serverSignature, PUBLIC_KEY_FILENAME)) {
            System.out.println("Signature verification failed");
            return;
        }

        byte[] serverBytes = decryptRSA(serverEncryptedBytes, userid + PRIVATE_KEY_FILENAME);
        byte[] keyBytes = new byte[AES_KEY_SIZE / 8];
        System.arraycopy(clientBytes, 0, keyBytes, 0, IV_SIZE);
        System.arraycopy(serverBytes, 0, keyBytes, IV_SIZE, IV_SIZE);

        SecretKey key = new SecretKeySpec(keyBytes, "AES");

        // File transmission stage
        File file = new File(filename);
        byte[] fileBytes = new byte[(int) file.length()];
        FileInputStream fileInput = new FileInputStream(file);
        fileInput.read(fileBytes);
        fileInput.close();

        byte[] iv = new byte[IV_SIZE];
        random.nextBytes(iv);

        byte[] encryptedFileBytes = encryptAES(fileBytes, key, iv);

        output.writeInt(iv.length);
        output.write(iv);
        output.writeInt(encryptedFileBytes.length);
        output.write(encryptedFileBytes);

        socket.close();
    }

    private static byte[] encryptRSA(byte[] data, String privateKeyFilename) throws Exception {
        PrivateKey privateKey = readPrivateKey(privateKeyFilename);
        Cipher cipher = Cipher.getInstance("RSA");
        cipher.init(Cipher.ENCRYPT_MODE, privateKey);
        return cipher.doFinal(data);
    }

    private static byte[] decryptRSA(byte[] cipherText, String privateKeyFile) throws Exception {
        PrivateKey privateKey = readPrivateKey(privateKeyFile);
        Cipher cipher = Cipher.getInstance("RSA");
        cipher.init(Cipher.DECRYPT_MODE, privateKey);
        return cipher.doFinal(cipherText);
    }

    // Method to sign data using the specified private key file
    public static byte[] sign(byte[] data, String privateKeyFile) throws Exception {
        // Read the private key from file
        FileInputStream keyfis = new FileInputStream(privateKeyFile);
        byte[] keyBytes = new byte[keyfis.available()];
        keyfis.read(keyBytes);
        keyfis.close();

        // Create the private key object
        PKCS8EncodedKeySpec spec = new PKCS8EncodedKeySpec(keyBytes);
        KeyFactory kf = KeyFactory.getInstance("RSA");
        PrivateKey privateKey = kf.generatePrivate(spec);

        // Sign the data using SHA-256 with RSA
        Signature signature = Signature.getInstance("SHA256withRSA");
        signature.initSign(privateKey);
        signature.update(data);
        byte[] signed = signature.sign();

        return signed;
    }

    private static boolean verify(byte[] data, byte[] signature, String publicKeyFile) throws Exception {
        PublicKey publicKey = readPublicKey(publicKeyFile);
        Signature verifier = Signature.getInstance("SHA256withRSA");
        verifier.initVerify(publicKey);
        verifier.update(data);
        return verifier.verify(signature);
    }

    private static byte[] encryptAES(byte[] plainText, SecretKey key, byte[] iv) throws Exception {
        Cipher cipher = Cipher.getInstance("AES/CBC/PKCS5Padding");
        cipher.init(Cipher.ENCRYPT_MODE, key, new IvParameterSpec(iv));
        return cipher.doFinal(plainText);
    }

    private static PrivateKey readPrivateKey(String fileName) throws Exception {
        ObjectInputStream inputStream = new ObjectInputStream(new FileInputStream(fileName));
        PrivateKey key = (PrivateKey) inputStream.readObject();
        inputStream.close();
        return key;
    }

    private static PublicKey readPublicKey(String fileName) throws Exception {
        ObjectInputStream inputStream = new ObjectInputStream(new FileInputStream(fileName));
        PublicKey key = (PublicKey) inputStream.readObject();
        inputStream.close();
        return key;
    }
}

问题原因

代码中存在私钥读取方式不匹配的问题:

  • 已有的readPrivateKey方法是通过ObjectInputStream读取序列化后的PrivateKey对象;
  • 但sign方法却把私钥文件内容当作PKCS8编码的原始字节处理,用PKCS8EncodedKeySpec解析,格式完全不兼容,触发DER解析错误。

解决方案

修改sign方法,复用已有的readPrivateKey方法读取私钥,避免格式不匹配:

public static byte[] sign(byte[] data, String privateKeyFile) throws Exception {
    // 复用已有的readPrivateKey方法读取私钥
    PrivateKey privateKey = readPrivateKey(privateKeyFile);

    // 用SHA-256 with RSA签名数据
    Signature signature = Signature.getInstance("SHA256withRSA");
    signature.initSign(privateKey);
    signature.update(data);
    byte[] signed = signature.sign();

    return signed;
}

注意encryptRSA和decryptRSA方法已经正确使用readPrivateKey,仅需修正sign方法即可。

内容的提问来源于stack exchange,提问作者Anirudh Saladi

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.07.22 18:37:00