我正在尝试使用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
和私钥:
308193020100301306072A8648CE3D020106082A8648CE3D030107047930770201010420219AB4B3701630973A4B2917D53F69A4BE6DAD61F48016BFEF147B2999575CB2A00A06082A8648CE3D030107A14403420004483ABA9F322240010ECF00E818C041A60FE71A2BD64C64CD5A60519985F110AEDE6308027D2730303F5E2478F083C7F5BB683DCAC22BFEB62F3A48BD01009F40
网站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;
}
答案 0 :(得分:4)
Java PublicKey
的默认编码是"X.509"
,它不仅仅是EC点;它是一个ASN.1结构,用于识别算法(EC)和参数(此处为prime256v1)以及包含该点的BIT STRING;见rfc5280 section 4.2.1.7和rfc3279 section 2.3.5。
类似地,PrivateKey
的默认编码是"PKCS#8"
(未加密),这是一个包含AlgorithmIdentifier加上包含数据的OCTET STRING的结构,在这种情况下包含私钥值和副本公钥,请参阅rfc5208 section 5文档SEC 1的http://www.secg.org和C.4,但标记为[0]
但标记[1]
已存在。
要将其中的一个或两个读回Java,请分别在KeyFactory.getInstance("EC")
或generate{Public,Private}
上获取X509EncodedKeySpec
并使用PKCS8EncodedKeySpec
。
ECDSA和ECDH(以及ECMQV等)使用相同的键结构,不同于使用相同数学结构($ Z_p ^ * $)但表示略有不同的经典整数DSA和DH。
PS:javadoc for java.security.Key
告诉你大部分内容。
答案 1 :(得分:1)
更实际的例子。将生成的公钥转换为已解码的字节数组或十六进制字符串:
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));