在Android 原生api是不支持secp256k1算法的,所以要先集成以下库:
implementation 'com.madgag.spongycastle:core:1.58.0.0'
compile 'com.madgag.spongycastle:prov:1.54.0.0'
compile 'com.madgag.spongycastle:pkix:1.54.0.0'
compile 'com.madgag.spongycastle:pg:1.54.0.0'
然后在使用前需要添加一行代码
static {
Security.insertProviderAt(new org.spongycastle.jce.provider.BouncyCastleProvider(), 1);
}
1 获取公钥与私钥 :
private void nnnn() {
X9ECParameters ecp = SECNamedCurves.getByName("secp256k1");
ECDomainParameters domainParams = new ECDomainParameters(
ecp.getCurve(),
ecp.getG(),
ecp.getN(),
ecp.getH(),
ecp.getSeed());
// Generate a private key and a public key
AsymmetricCipherKeyPair keyPair;
ECKeyGenerationParameters keyGenParams = new ECKeyGenerationParameters(domainParams, new SecureRandom());
ECKeyPairGenerator generator = new ECKeyPairGenerator();
generator.init(keyGenParams);
keyPair = generator.generateKeyPair();
ECPrivateKeyParameters privateKey = (ECPrivateKeyParameters) keyPair.getPrivate();
ECPublicKeyParameters publicKey = (ECPublicKeyParameters) keyPair.getPublic();
byte[] privateKeyBytes = privateKey.getD().toByteArray();
// First print our generated private key and public key
Log.e("mlt",".......Private key:..........."+ECDH.toHex(privateKeyBytes));
Log.e("mlt","........Public key:..........."+ECDH.toHex(publicKey.getQ().getEncoded(true)));
// Then calculate the public key only using domainParams.getG() and private key
ECPoint Q = domainParams.getG().multiply(new BigInteger(privateKeyBytes));
Log.e("mlt",".......Calculated public key:...." +
"......."+ECDH.toHex(Q.getEncoded(true)));
// The calculated public key and generated public key should always match
if (!ECDH.toHex(publicKey.getQ().getEncoded(true)).equals(ECDH.toHex(Q.getEncoded(true)))) {
Log.e("mlt",".......ERROR: Public keys do not match!:...........");
} else {
Log.e("mlt",".......Congratulations, public keys match:...........");
}
}
字符转byte[]
public static String toHex(byte[] data) {
StringBuilder sb = new StringBuilder();
for (byte b: data) {
sb.append(String.format("%02x", b&0xff));
}
return sb.toString();
}
先看下密钥对:
//56477ec67d3e3426db2646a9f873cb6c90753bcfc51ea1fc8b0b982dffcd8791 Private key
//0384bb60ab084f42a6093839eec228d9b3f4641ff80b6fe96a1ad55ec12ade9a8f Public key
3 生成共享密钥
public static String generateAgreedKey(PrivateKey privateKey, PublicKey publicKey) throws NoSuchProviderException, NoSuchAlgorithmException, InvalidKeyException {
KeyAgreement keyAgreement = KeyAgreement.getInstance("ECDH", "SC");
keyAgreement.init(privateKey);
keyAgreement.doPhase(publicKey, true);
byte[] sharedKeyBytes = keyAgreement.generateSecret();
// return Base64.encodeToString(sharedKeyBytes, Base64.DEFAULT).replaceAll("\n", "");
return toHex(sharedKeyBytes);
}
4 因为生成对是16进制 key需要转publickey 和privatekey 还需另外方法
public static ECPublicKey keyToPublick(String key) throws NoSuchAlgorithmException, InvalidKeySpecException {
// transform from hex to ECPublicKey
byte[] ecRawExternalPublicKey = hexStringToByteArray(key);
ECPublicKey ecExternalPublicKey = null;
KeyFactory externalKeyFactor = null;
ECNamedCurveParameterSpec ecExternalNamedCurveParameterSpec = ECNamedCurveTable.getParameterSpec("secp256k1");
ECCurve curve = ecExternalNamedCurveParameterSpec.getCurve();
EllipticCurve ellipticCurve = EC5Util.convertCurve(curve, ecExternalNamedCurveParameterSpec.getSeed());
java.security.spec.ECPoint ecPoint = ECPointUtil.decodePoint(ellipticCurve, ecRawExternalPublicKey);
java.security.spec.ECParameterSpec ecParameterSpec = EC5Util.convertSpec(ellipticCurve, ecExternalNamedCurveParameterSpec);
java.security.spec.ECPublicKeySpec externalPublicKeySpec = new java.security.spec.ECPublicKeySpec(ecPoint, ecParameterSpec);
externalKeyFactor = java.security.KeyFactory.getInstance("EC");
// this is externalPubicKey
ecExternalPublicKey = (ECPublicKey) externalKeyFactor.generatePublic(externalPublicKeySpec);
return ecExternalPublicKey;
}
public static ECPrivateKey keyToPrivate(String key) throws NoSuchAlgorithmException, InvalidKeySpecException {
// transform from hex to ECPublicKey
byte[] ecRawExternalPublicKey = hexStringToByteArray(key);
ECPrivateKey ecPrivateKey = null;
KeyFactory externalKeyFactor = null;
ECNamedCurveParameterSpec ecExternalNamedCurveParameterSpec = ECNamedCurveTable.getParameterSpec("secp256k1");
ECCurve curve = ecExternalNamedCurveParameterSpec.getCurve();
EllipticCurve ellipticCurve = EC5Util.convertCurve(curve, ecExternalNamedCurveParameterSpec.getSeed());
java.security.spec.ECParameterSpec ecParameterSpec = EC5Util.convertSpec(ellipticCurve, ecExternalNamedCurveParameterSpec);
java.security.spec.ECPrivateKeySpec externalPublicKeySpec = new java.security.spec.ECPrivateKeySpec(new BigInteger(ecRawExternalPublicKey), ecParameterSpec);
externalKeyFactor = java.security.KeyFactory.getInstance("EC");
// this is externalPubicKey
ecPrivateKey = (ECPrivateKey) externalKeyFactor.generatePrivate(externalPublicKeySpec);
return ecPrivateKey;
}
string key 转 byte[]
public static byte[] hexStringToByteArray(String s) {
int len = s.length();
byte[] data = new byte[len / 2];
for (int i = 0; i < len; i += 2) {
data[i / 2] = (byte) ((Character.digit(s.charAt(i), 16) << 4)
+ Character.digit(s.charAt(i + 1), 16));
}
return data;
}
5 调用方法生成共享密钥
Log.e("mlt","...........aaa....."+ECDH.generateAgreedKey(globalData.getPrivateKey(), ECDH.keyToPublick(ss)));//随时生成私钥可以用这个
Log.e("mlt","...........aaa....."+ECDH.generateAgreedKey(ECDH.keyToPrivate(prsss), ECDH.keyToPublick(ss)));//把本地私钥保存本地
public static String generateAgreedKey(PrivateKey privateKey, PublicKey publicKey) throws NoSuchProviderException, NoSuchAlgorithmException, InvalidKeyException {
KeyAgreement keyAgreement = KeyAgreement.getInstance("ECDH", "SC");
keyAgreement.init(privateKey);
keyAgreement.doPhase(publicKey, true);
byte[] sharedKeyBytes = keyAgreement.generateSecret();
// return Base64.encodeToString(sharedKeyBytes, Base64.DEFAULT).replaceAll("\n", "");
return toHex(sharedKeyBytes);
}
基本就可以了,下面说几个问题,本来stringkey是用下面这两个方法,结果会出现问题
public static PublicKey stringToPublicKey(String key) throws UnsupportedEncodingException, NoSuchAlgorithmException, InvalidKeySpecException, NoSuchProviderException {
byte[] keyBytes = Base64.decode(key.getBytes("utf-8"), Base64.DEFAULT);
X509EncodedKeySpec spec = new X509EncodedKeySpec(keyBytes);
KeyFactory keyFactory = KeyFactory.getInstance("ECDH", "SC");
return keyFactory.generatePublic(spec);
}
public static PrivateKey stringToPrivateKey(String key) throws UnsupportedEncodingException, NoSuchProviderException, NoSuchAlgorithmException, InvalidKeySpecException {
byte[] keyBytes = Base64.decode(key.getBytes("utf-8"), Base64.DEFAULT);
PKCS8EncodedKeySpec spec = new PKCS8EncodedKeySpec(keyBytes);
KeyFactory keyFactory = KeyFactory.getInstance("ECDH", "SC");
return keyFactory.generatePrivate(spec);
}
这个两个不成功所以采用上面的方法实现
参考代码:
// arrive a string like this 04456cb4ba8ee9263311485baa8562c27991f7ff22d59f3d8245b9a05661d159911b632a6f8a7a080d82f4ca77e4d12bb201b89c8ec93f61d5b4dd22df42e1b482
Map result = new HashMap<>();
try {
// set provider
Security.addProvider(new BouncyCastleProvider());
// transform from hex to ECPublicKey
byte[] ecRawExternalPublicKey = this.toByte(externalRawPublicKey);
ECPublicKey ecExternalPublicKey = null;
KeyFactory externalKeyFactor = null;
ECNamedCurveParameterSpec ecExternalNamedCurveParameterSpec = ECNamedCurveTable.getParameterSpec("secp256r1");
ECCurve curve = ecExternalNamedCurveParameterSpec.getCurve();
EllipticCurve ellipticCurve = EC5Util.convertCurve(curve, ecExternalNamedCurveParameterSpec.getSeed());
java.security.spec.ECPoint ecPoint = ECPointUtil.decodePoint(ellipticCurve, ecRawExternalPublicKey);
java.security.spec.ECParameterSpec ecParameterSpec = EC5Util.convertSpec(ellipticCurve, ecExternalNamedCurveParameterSpec);
java.security.spec.ECPublicKeySpec externalPublicKeySpec = new java.security.spec.ECPublicKeySpec(ecPoint, ecParameterSpec);
externalKeyFactor = java.security.KeyFactory.getInstance("EC");
// this is externalPubicKey
ecExternalPublicKey = (ECPublicKey) externalKeyFactor.generatePublic(externalPublicKeySpec);
KeyPairGenerator keyGen = KeyPairGenerator.getInstance("ECDH","BC");
keyGen.initialize(new ECGenParameterSpec("secp256r1"), new SecureRandom());
KeyPair pair = keyGen.generateKeyPair();
ECPublicKey pub = (ECPublicKey)pair.getPublic();
ECPrivateKey pvt = (ECPrivateKey)pair.getPrivate();
byte[] pubEncoded = pub.getEncoded();
byte[] pvtEncoded = pvt.getEncoded();
KeyAgreement keyAgree = KeyAgreement.getInstance("ECDH");
keyAgree.init(pvt);
keyAgree.doPhase(ecExternalPublicKey, true);
System.out.println("sharedKey:"+ this.bytesToHex( keyAgree.generateSecret() ));
// internal public key
return"04"+ pub.getW().getAffineX().toString(16) + pub.getW().getAffineY().toString(16)
}
catch (Exception e ){
e.printStackTrace();
return null;
}
https://www.codenong.com/51861056/
https://www.lmlphp.com/user/151226/article/item/3360735/
https://cloud.tencent.com/developer/ask/sof/275206
http://www.17bigdata.com/study/programming/bcalg/bcalg-secp256k1.html
https://blog.csdn.net/weixin_29192211/article/details/114853972
// generate bogus keypair(!) with named-curve params
KeyPairGenerator kpg = KeyPairGenerator.getInstance("EC");
ECGenParameterSpec gps = new ECGenParameterSpec ("secp256r1"); // NIST P-256
kpg.initialize(gps);
KeyPair apair = kpg.generateKeyPair();
ECPublicKey apub = (ECPublicKey)apair.getPublic();
ECParameterSpec aspec = apub.getParams();
// could serialize aspec for later use (in compatible JRE)
//
// for test only reuse bogus pubkey, for real substitute values
ECPoint apoint = apub.getW();
BigInteger x = apoint.getAffineX(), y = apoint.getAffineY();
// construct point plus params to pubkey
ECPoint bpoint = new ECPoint (x,y);
ECPublicKeySpec bpubs = new ECPublicKeySpec (bpoint, aspec);
KeyFactory kfa = KeyFactory.getInstance ("EC");
ECPublicKey bpub = (ECPublicKey) kfa.generatePublic(bpubs);
//
// for test sign with original key, verify with reconstructed key
Signature sig = Signature.getInstance ("SHA256withECDSA");
byte [] data = "test".getBytes();
sig.initSign(apair.getPrivate());
sig.update (data);
byte[] dsig = sig.sign();
sig.initVerify(bpub);
sig.update(data);
System.out.println (sig.verify(dsig));
https://blog.csdn.net/weixin_39583751/article/details/116008623