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

Android端将EC公钥十六进制字符串转换为PublicKey的问题咨询

Got it, let's tackle this issue step by step. The error you're seeing (InvalidKeySpecException with WRONG_TAG) has two main culprits: incorrect KeySpec usage for private keys, and potential edge cases in your hex-to-byte conversion logic. Here's how to fix it:

1. Fix Private Key Parsing (Critical Mistake)

Your getPrivateKey method is using X509EncodedKeySpec for private keys, which is wrong. X509 is designed for public keys—private keys use PKCS#8 encoding instead. That's the primary reason for the WRONG_TAG error, since the ASN.1 parser expects a completely different structure for private key data.

Update your ECDSA.getPrivateKey method to use the correct KeySpec:

public static PrivateKey getPrivateKey(byte[] privk) throws NoSuchAlgorithmException, InvalidKeySpecException {
    EncodedKeySpec privateKeySpec = new PKCS8EncodedKeySpec(privk); // Switch to PKCS8 here
    KeyFactory kf = KeyFactory.getInstance("EC");
    return kf.generatePrivate(privateKeySpec);
}

2. Robustify Hex String ↔ Byte Array Conversion

Your current hexStringToByteArray has a minor inefficiency (unnecessary boxing to Integer) and doesn't handle whitespace in hex strings. A more streamlined, safe implementation would look like this:

public static byte[] hexStringToByteArray(String hexString) {
    hexString = hexString.trim().replaceAll("\\s+", ""); // Clean up any whitespace
    int len = hexString.length();
    byte[] data = new byte[len / 2];
    for (int i = 0; i < len; i += 2) {
        data[i / 2] = (byte) ((Character.digit(hexString.charAt(i), 16) << 4)
                             + Character.digit(hexString.charAt(i+1), 16));
    }
    return data;
}

This directly computes the byte value without intermediate object creation, and handles signed byte values correctly (e.g., 0x80 converts to -128 as expected).

3. Verify Android EC Key Compatibility

On Android, the default Conscrypt security provider fully supports prime256v1 (also called secp256r1), so your key generation code is already correct. The only thing to confirm is that you're storing the full encoded key (from getEncoded()), which includes the ASN.1 metadata required for reconstruction.

Full Corrected ECDSA Class

Here's the updated class with all fixes, plus integrated robust hex conversion:

import java.security.*;
import java.security.spec.*;

public class ECDSA {
    public static KeyPair generateKeyPair() throws NoSuchAlgorithmException, InvalidAlgorithmParameterException {
        KeyPairGenerator keyGen = KeyPairGenerator.getInstance("EC");
        ECGenParameterSpec ecSpec = new ECGenParameterSpec("prime256v1");
        SecureRandom random = SecureRandom.getInstanceStrong(); // Prefer strong random over SHA1PRNG
        keyGen.initialize(ecSpec, random);
        return keyGen.generateKeyPair();
    }

    public static PublicKey getPublicKey(byte[] pk) throws NoSuchAlgorithmException, InvalidKeySpecException {
        EncodedKeySpec publicKeySpec = new X509EncodedKeySpec(pk);
        KeyFactory kf = KeyFactory.getInstance("EC");
        return kf.generatePublic(publicKeySpec);
    }

    public static PrivateKey getPrivateKey(byte[] privk) throws NoSuchAlgorithmException, InvalidKeySpecException {
        EncodedKeySpec privateKeySpec = new PKCS8EncodedKeySpec(privk);
        KeyFactory kf = KeyFactory.getInstance("EC");
        return kf.generatePrivate(privateKeySpec);
    }

    // Hex conversion utilities
    private static final char[] hexArray = "0123456789ABCDEF".toCharArray();
    public static String convertBytesToHex(byte[] bytes) {
        char[] hexChars = new char[bytes.length * 2];
        for (int j = 0; j < bytes.length; j++) {
            int v = bytes[j] & 0xFF;
            hexChars[j * 2] = hexArray[v >>> 4];
            hexChars[j * 2 + 1] = hexArray[v & 0x0F];
        }
        return new String(hexChars).toLowerCase();
    }

    public static byte[] hexStringToByteArray(String hexString) {
        hexString = hexString.trim().replaceAll("\\s", "");
        int len = hexString.length();
        byte[] data = new byte[len / 2];
        for (int i = 0; i < len; i += 2) {
            data[i / 2] = (byte) ((Character.digit(hexString.charAt(i), 16) << 4)
                                 + Character.digit(hexString.charAt(i+1), 16));
        }
        return data;
    }
}

Usage in MainActivity (With Proper Exception Handling)

Make sure to wrap key operations in try-catch blocks (Android doesn't allow uncaught exceptions on the main thread):

import android.os.Bundle;
import android.util.Log;
import androidx.appcompat.app.AppCompatActivity;
import java.security.*;
import java.security.spec.InvalidKeySpecException;

public class MainActivity extends AppCompatActivity {
    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);

        try {
            // Generate key pair
            KeyPair keyPair = ECDSA.generateKeyPair();
            PublicKey publicKey = keyPair.getPublic();
            PrivateKey privateKey = keyPair.getPrivate();

            // Convert to hex strings
            String publicKeyHex = ECDSA.convertBytesToHex(publicKey.getEncoded());
            String privateKeyHex = ECDSA.convertBytesToHex(privateKey.getEncoded());

            // Reconstruct keys from hex
            PublicKey pkReconstructed = ECDSA.getPublicKey(ECDSA.hexStringToByteArray(publicKeyHex));
            PrivateKey skReconstructed = ECDSA.getPrivateKey(ECDSA.hexStringToByteArray(privateKeyHex));

            // Verify reconstruction works (optional debug step)
            Log.d("ECDSA_TEST", "Public key matches: " + publicKey.equals(pkReconstructed));
            Log.d("ECDSA_TEST", "Private key matches: " + privateKey.equals(skReconstructed));

        } catch (NoSuchAlgorithmException | InvalidAlgorithmParameterException | InvalidKeySpecException e) {
            Log.e("ECDSA_ERROR", "Key operation failed", e);
        }
    }
}

Why This Works

  • Private Key Fix: Using PKCS8EncodedKeySpec tells the KeyFactory to parse the correct ASN.1 structure for private keys, eliminating the WRONG_TAG error entirely.
  • Hex Conversion: The revised method avoids unnecessary overhead and handles edge cases like whitespace in hex strings.
  • SecureRandom: getInstanceStrong() ensures you're using a cryptographically secure random source, which is better practice than the implementation-dependent SHA1PRNG.

This should resolve the exception you're seeing on Android 7.0 (API 24) and all newer Android versions.

内容的提问来源于stack exchange,提问作者Sudheesh Singanamalla

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.05.27 07:03:30