Java脚本 - Java ECDH
Javascript - Java ECDH
在我的场景中,Alice 和 Bob 已就使用哪条曲线达成一致。
- 爱丽丝生成 public 密钥和私钥
- Alice 将 public 密钥发送给 Bob
- Bob 生成他的密钥,并根据他收到的 Alice public 密钥生成会话密钥(或秘密密钥,或共享密钥)。
我的问题是 Alice 的 public 键实际上是一个点,所以它有 xy 格式。
我需要将 x,y 坐标字节转换为 ECPublicKey。
这是我正在使用的源代码
// outerPublicKey is the raw bytes from x,y coordinates in hex format
KeyFactory kf = KeyFactory.getInstance("EC");
PublicKey remoteAlicePub = kf.generatePublic(new X509EncodedKeySpec(outerPublicKey));
KeyPairGenerator bobKeyGen = KeyPairGenerator.getInstance("ECDH", "BC");
bobKeyGen.initialize(new ECGenParameterSpec(properties.getCurveName()), new SecureRandom());
KeyPair bobPair = bobKeyGen.generateKeyPair();
ECPublicKey bobPub = (ECPublicKey)bobPair.getPublic();
ECPrivateKey bobPvt = (ECPrivateKey)bobPair.getPrivate();
byte[] bobPubEncoded = bobPub.getEncoded();
byte[] bobPvtEncoded = bobPvt.getEncoded();
KeyAgreement bobKeyAgree = KeyAgreement.getInstance("ECDH");
bobKeyAgree.init(bobPvt);
bobKeyAgree.doPhase(remoteAlicePub, true);
return DatatypeConverter.printHexBinary(bobKeyAgree.generateSecret());
问题是:
new X509EncodedKeySpec(outerPublicKey);
如何根据点的 xy 坐标创建 public 键?
因为 outerPublicKey
是 x,y 坐标的原始字节数组,我应该使用哪种格式?
我是这样解决的(Java服务器端)
// arrive a string like this 04456cb4ba8ee9263311485baa8562c27991f7ff22d59f3d8245b9a05661d159911b632a6f8a7a080d82f4ca77e4d12bb201b89c8ec93f61d5b4dd22df42e1b482
Map<String, Object> 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;
}
Java脚本(客户端)
ecdhHandShake() {
let _this = this;
this.keyGeneration()
.then( k => {
ajax({
url: "http://localhost:5050/test/ecdh/handshake",
headers: {
"Content-Type": "application/json"
},
body: {
publickey: this.buf2Hex(this.publicKey)
},
method: 'POST',
crossDomain: true,
responseType: 'json'
})
.subscribe(
payload => {
const publicKey = _this.hex2Arr(payload.response.publicKey);
_this.serverPublicKey = _this.hex2Arr(payload.response.publicKey);
_this.importServerKey()
.then(sharedSecret => {
const sharedSecretHex = this.buf2Hex(sharedSecret);
console.log("shared key: "+ sharedSecretHex);
})
.catch( e => {
console.log(e);
})
},
error => {
console.log(error);
},
() => console.log('done')
)
;
})
.catch( e => {
console.log(e);
})
;
}
keyGeneration() {
let _this = this;
return window.crypto.subtle.generateKey(
{
name: "ECDH",
namedCurve: "P-256", // the curve name
},
true, // <== Here if you want it to be exportable !!
["deriveKey", "deriveBits"] // usage
)
.then(key => {
_this.keys = key;
return window.crypto.subtle.exportKey(
"raw", //can be "jwk" (public or private), "raw" (public only), "spki" (public only), or "pkcs8" (private only)
_this.keys.publicKey
)
.then(rawPublicKey => {
_this.publicKey = rawPublicKey;
return rawPublicKey;
})
.catch(function(err){
console.error(err);
})
;
})
.catch(function(err){
console.error(err);
})
;
}
importServerKey() {
return window.crypto.subtle.importKey(
'raw',
this.serverPublicKey,
{
name: 'ECDH',
namedCurve: 'P-256'
},
true,
[]
)
.then(aliceKeyImported => {
return window.crypto.subtle.deriveBits(
{
name: 'ECDH',
namedCurve: 'P-256',
public: aliceKeyImported
},
this.keys.privateKey,
256)
})
.catch( e => {
console.log(e);
})
}
hex2Arr( str ) {
if (!str) {
return new Uint8Array();
}
const arr = [];
for (let i = 0, len = str.length; i < len; i+=2) {
arr.push(parseInt(str.substr(i, 2), 16));
}
return new Uint8Array(arr);
}
buf2Hex( buf ) {
return Array.from(new Uint8Array(buf))
.map(x => ('00' + x.toString(16)).slice(-2))
.join('')
}
在我的场景中,Alice 和 Bob 已就使用哪条曲线达成一致。
- 爱丽丝生成 public 密钥和私钥
- Alice 将 public 密钥发送给 Bob
- Bob 生成他的密钥,并根据他收到的 Alice public 密钥生成会话密钥(或秘密密钥,或共享密钥)。
我的问题是 Alice 的 public 键实际上是一个点,所以它有 xy 格式。
我需要将 x,y 坐标字节转换为 ECPublicKey。
这是我正在使用的源代码
// outerPublicKey is the raw bytes from x,y coordinates in hex format
KeyFactory kf = KeyFactory.getInstance("EC");
PublicKey remoteAlicePub = kf.generatePublic(new X509EncodedKeySpec(outerPublicKey));
KeyPairGenerator bobKeyGen = KeyPairGenerator.getInstance("ECDH", "BC");
bobKeyGen.initialize(new ECGenParameterSpec(properties.getCurveName()), new SecureRandom());
KeyPair bobPair = bobKeyGen.generateKeyPair();
ECPublicKey bobPub = (ECPublicKey)bobPair.getPublic();
ECPrivateKey bobPvt = (ECPrivateKey)bobPair.getPrivate();
byte[] bobPubEncoded = bobPub.getEncoded();
byte[] bobPvtEncoded = bobPvt.getEncoded();
KeyAgreement bobKeyAgree = KeyAgreement.getInstance("ECDH");
bobKeyAgree.init(bobPvt);
bobKeyAgree.doPhase(remoteAlicePub, true);
return DatatypeConverter.printHexBinary(bobKeyAgree.generateSecret());
问题是:
new X509EncodedKeySpec(outerPublicKey);
如何根据点的 xy 坐标创建 public 键?
因为 outerPublicKey
是 x,y 坐标的原始字节数组,我应该使用哪种格式?
我是这样解决的(Java服务器端)
// arrive a string like this 04456cb4ba8ee9263311485baa8562c27991f7ff22d59f3d8245b9a05661d159911b632a6f8a7a080d82f4ca77e4d12bb201b89c8ec93f61d5b4dd22df42e1b482
Map<String, Object> 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;
}
Java脚本(客户端)
ecdhHandShake() {
let _this = this;
this.keyGeneration()
.then( k => {
ajax({
url: "http://localhost:5050/test/ecdh/handshake",
headers: {
"Content-Type": "application/json"
},
body: {
publickey: this.buf2Hex(this.publicKey)
},
method: 'POST',
crossDomain: true,
responseType: 'json'
})
.subscribe(
payload => {
const publicKey = _this.hex2Arr(payload.response.publicKey);
_this.serverPublicKey = _this.hex2Arr(payload.response.publicKey);
_this.importServerKey()
.then(sharedSecret => {
const sharedSecretHex = this.buf2Hex(sharedSecret);
console.log("shared key: "+ sharedSecretHex);
})
.catch( e => {
console.log(e);
})
},
error => {
console.log(error);
},
() => console.log('done')
)
;
})
.catch( e => {
console.log(e);
})
;
}
keyGeneration() {
let _this = this;
return window.crypto.subtle.generateKey(
{
name: "ECDH",
namedCurve: "P-256", // the curve name
},
true, // <== Here if you want it to be exportable !!
["deriveKey", "deriveBits"] // usage
)
.then(key => {
_this.keys = key;
return window.crypto.subtle.exportKey(
"raw", //can be "jwk" (public or private), "raw" (public only), "spki" (public only), or "pkcs8" (private only)
_this.keys.publicKey
)
.then(rawPublicKey => {
_this.publicKey = rawPublicKey;
return rawPublicKey;
})
.catch(function(err){
console.error(err);
})
;
})
.catch(function(err){
console.error(err);
})
;
}
importServerKey() {
return window.crypto.subtle.importKey(
'raw',
this.serverPublicKey,
{
name: 'ECDH',
namedCurve: 'P-256'
},
true,
[]
)
.then(aliceKeyImported => {
return window.crypto.subtle.deriveBits(
{
name: 'ECDH',
namedCurve: 'P-256',
public: aliceKeyImported
},
this.keys.privateKey,
256)
})
.catch( e => {
console.log(e);
})
}
hex2Arr( str ) {
if (!str) {
return new Uint8Array();
}
const arr = [];
for (let i = 0, len = str.length; i < len; i+=2) {
arr.push(parseInt(str.substr(i, 2), 16));
}
return new Uint8Array(arr);
}
buf2Hex( buf ) {
return Array.from(new Uint8Array(buf))
.map(x => ('00' + x.toString(16)).slice(-2))
.join('')
}