【问题标题】:How can I generate a valid ECDSA EC key pair?如何生成有效的 ECDSA EC 密钥对?
【发布时间】:2017-03-02 12:57:17
【问题描述】:

我正在尝试在 Android 中使用 SpongyCastle 生成 ECDSA 密钥对。 这是代码:

static {
    Security.insertProviderAt(new org.spongycastle.jce.provider.BouncyCastleProvider(), 1);
}

public static KeyPair generate() {
        ECParameterSpec ecSpec = ECNamedCurveTable.getParameterSpec("prime256v1");
        KeyPairGenerator generator = KeyPairGenerator.getInstance("ECDSA", "SC");
        generator.initialize(ecSpec, new SecureRandom());
        KeyPair keyPair = g.generateKeyPair();
        Log.i(TAG, "EC Pub Key generated: " + utils.bytesToHex(keyPair.getPublic().getEncoded()));
        Log.i(TAG, "EC Private Key generated: " + utils.bytesToHex(keyPair.getPrivate().getEncoded()));            
       return generator.generateKeyPair();
}

出了点问题,因为我总是得到类似的例子 公钥:

3059301306072A8648CE3D020106082A8648CE3D03010703420004483ABA9F322240010ECF00E818C041A60FE71A2BD64C64CD5A60519985F110AEDE6308027D2730303F5E2478F083C7F5BB683DCAC22BFEB62F3A48BD01009F40 P>

和私钥:

308193020100301306072A8648CE3D020106082A8648CE3D030107047930770201010420219AB4B3701630973A4B2917D53F69A4BE6DAD61F48016BFEF147B2999575CB2A00A06082A8648CE3D030107A14403420004483ABA9F322240010ECF00E818C041A60FE71A2BD64C64CD5A60519985F110AEDE6308027D2730303F5E2478F083C7F5BB683DCAC22BFEB62F3A48BD01009F40 P>

ECDSA sample 站点给我“无效的 ECDSA 签名消息”,它们似乎与较小的私钥非常不同,并且总是以同一站点中生成的“04”公钥开头。

另外,我的后端验证给了我错误“无效点编码 0x30”

后端Java方法检查是:

public ECPublicKey getPublicKeyFromHex(String publicKeyHex)
        throws NoSuchAlgorithmException, DecoderException, ApplicationGenericException {
    byte[] rawPublicKey = Hex.decodeHex(publicKeyHex.toCharArray());
    ECPublicKey ecPublicKey = null;
    KeyFactory kf = null;

    ECNamedCurveParameterSpec ecNamedCurveParameterSpec = ECNamedCurveTable.getParameterSpec("prime256v1");
    ECCurve curve = ecNamedCurveParameterSpec.getCurve();
    EllipticCurve ellipticCurve = EC5Util.convertCurve(curve, ecNamedCurveParameterSpec.getSeed());
    java.security.spec.ECPoint ecPoint = ECPointUtil.decodePoint(ellipticCurve, rawPublicKey);
    java.security.spec.ECParameterSpec ecParameterSpec = EC5Util.convertSpec(ellipticCurve,
            ecNamedCurveParameterSpec);
    java.security.spec.ECPublicKeySpec publicKeySpec = new java.security.spec.ECPublicKeySpec(ecPoint,
            ecParameterSpec);

    kf = KeyFactory.getInstance("ECDSA", new BouncyCastleProvider());

    try {
        ecPublicKey = (ECPublicKey) kf.generatePublic(publicKeySpec);
    } catch (Exception e) {
        throw new ApplicationGenericException(e.getMessage(), e.getCause());
    }

    return ecPublicKey;
}

【问题讨论】:

标签: bouncycastle elliptic-curve ecdsa dsa spongycastle


【解决方案1】:

Java 对PublicKey 的默认编码是"X.509",这不仅仅是EC 点;它是一个 ASN.1 结构,标识算法(EC)和参数(这里是 prime256v1)加上一个 BIT STRING 包裹点;见rfc5280 section 4.2.1.7rfc3279 section 2.3.5

同样,PrivateKey 的默认编码是"PKCS#8"(未加密),它是一个包含算法标识符和包含数据的 OCTET STRING 的结构,在这种情况下包含私钥值和公钥副本,请参阅http://www.secg.org 的文档 SEC 1 的 rfc5208 section 5 和 C.4,省略了标签 [0],但存在标签 [1]

要将它们(其中一个或两个)读回 Java,请获取 KeyFactory.getInstance("EC") 并分别在 X509EncodedKeySpecPKCS8EncodedKeySpec 上使用 generate{Public,Private}。 ECDSA 和 ECDH(以及 ECMQV 等)使用相同的密钥结构,这与使用相同 数学 结构 ($Z_p^*$) 但表示略有不同的经典整数 DSA 和 DH 不同。

PS:javadoc for java.security.Key 告诉你大部分内容。

【讨论】:

    【解决方案2】:

    更实际的例子。将生成的公钥转换为解码的字节数组或十六进制字符串:

    public String getPublicKeyAsHex(PublicKey publicKey){
    
        ECPublicKey ecPublicKey = (ECPublicKey)publicKey;
        ECPoint ecPoint = ecPublicKey.getW();
    
        byte[] publicKeyBytes = new byte[PUBLIC_KEY_LENGTH];
        writeToStream(publicKeyBytes, 0, ecPoint.getAffineX(), PRIVATE_KEY_LENGTH);
        writeToStream(publicKeyBytes, PRIVATE_KEY_LENGTH, ecPoint.getAffineY(), PRIVATE_KEY_LENGTH);
    
        String hex = Hex.toHexString(publicKeyBytes);
    
        logger.debug("Public key bytes: " + Arrays.toString(publicKeyBytes));
        logger.debug("Public key hex: " + hex);
    
        return hex;
    }
    
    private void writeToStream(byte[] stream, int start, BigInteger value, int size) {
        byte[] data = value.toByteArray();
        int length = Math.min(size, data.length);
        int writeStart = start + size - length;
        int readStart = data.length - length;
        System.arraycopy(data, readStart, stream, writeStart, length);
    }
    

    将解码后的字节数组转换回PublicKey

    KeyFactory factory = KeyFactory.getInstance(ALGORITHM, ALGORITHM_PROVIDER);
    
    ECNamedCurveParameterSpec spec = ECNamedCurveTable.getParameterSpec(CURVE);
    
    ECNamedCurveSpec params = new ECNamedCurveSpec(CURVE, spec.getCurve(), spec.getG(), spec.getN());
    
    BigInteger xCoordinate = new BigInteger(1, Arrays.copyOfRange(decodedPublicKey, 0, PRIVATE_KEY_LENGTH));
    BigInteger yCoordinate = new BigInteger(1, Arrays.copyOfRange(decodedPublicKey, PRIVATE_KEY_LENGTH, PUBLIC_KEY_LENGTH));
    java.security.spec.ECPoint w = new java.security.spec.ECPoint(xCoordinate, yCoordinate);
    
    PublicKey encodedPublicKey = factory.generatePublic(new java.security.spec.ECPublicKeySpec(w, params));
    

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 2018-02-20
      • 2022-01-18
      • 1970-01-01
      • 1970-01-01
      • 2015-12-18
      • 2015-09-17
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多