[英]Implement Diffie-Hellman key exchange in Java
我正在嘗試用Java實現Diffie-Hellman密鑰交換,但我很難理解規范:
根據JWA(RFC 7518)在直接密鑰協商模式下使用曲線P-256,dT和QC完成Diffie-Hellman密鑰交換過程作為本地機制,以生成一對CEK(每個方向一個),這些CEK由Transaction標識ID。 此版本規范支持的參數值為:
- “alg”:ECDH-ES
- “apv”:SDK參考編號
- “epk”:QC,采用JSON Web Key(JWK)格式
- {“kty”:“EC”“crv”:“P-256”}
- 所有其他參數:不存在
- CEK:“kty”:oct - 256位
創建以下數據的JSON對象作為要簽名的JWS有效內容:
{“MyPublicKey”:“QT”,“SDKPublicKey”:“QC”}
使用JWS Compact Serialization根據JWS(RFC 7515)生成完整JSON對象的數字簽名。 此版本規范支持的參數值為:
- “alg”:PS256或ES256
- “x5c”:X.5C v3:Cert(MyPb)以及可選的鏈接證書
根據我的理解,ECDH將生成一個密鑰。 在共享我的短暫公鑰(QT)之后,SDK生成相同的密鑰,因此我們以后可以交換使用相同密鑰加密的JWE消息。
JSON {“MyPublicKey”:“QT”,“SDKPublicKey”:“QC”}將被簽名並發送,但我不明白我將如何使用apv和epk,因為這些標題參數在JWE中使用而不是在第一個JWS將被共享。
在相同的規范中,他們談論這些JWE消息,但他們沒有這些apv和epk參數。
根據JWE(RFC 7516)使用SDK使用的相同“enc”算法加密JSON對象,獲得的CEK由“kid”和JWE Compact Serialization識別。 此版本規范支持的參數值為:
- “alg”:dir
- “enc”:A128CBC-HS256或A128GCM
- “kid”:交易ID
- 所有其他參數:不存在
我還閱讀了RFC 7518中的示例,其中我可以看到標題params apv和epk正在使用但是我不確定哪個標題參數,JWE或JWS?
關於如何使用nimbus-jose-jwt或任何其他java庫實現這一點的任何想法都會非常有用。 謝謝
apv
(Agreement PartyVInfo)和epk
(Ephemeral Public Key)都是可選的,因此它們可以以多種方式使用。 例如,您可以使用apv
來反映SDK版本。 它們被添加到JWE標頭中。
使用Nimbus JOSE的一個例子是:
import java.net.URI;
import java.net.URISyntaxException;
import java.security.KeyStore;
import java.util.ArrayList;
import java.util.HashSet;
import java.util.List;
import java.util.Set;
import com.nimbusds.jose.*;
import com.nimbusds.jose.jwk.Curve;
import com.nimbusds.jose.jwk.ECKey;
import com.nimbusds.jose.jwk.KeyOperation;
import com.nimbusds.jose.jwk.KeyUse;
import com.nimbusds.jose.util.Base64;
import com.nimbusds.jose.util.Base64URL;
public class Security {
public void generateJWE() throws JOSEException, URISyntaxException {
JWEHeader jweHeader = buildJWEHeader(new Base64URL("202333517"), buildECKey());
JWEObject jweObject = new JWEObject(jweHeader, new Payload("Hello World!"));
}
private JWEHeader buildJWEHeader(Base64URL apv, ECKey epk) {
JWEHeader.Builder jweBuilder = new JWEHeader.Builder(JWEAlgorithm.ECDH_ES, EncryptionMethod.A128GCM);
jweBuilder.agreementPartyVInfo(apv);
jweBuilder.ephemeralPublicKey(epk);
return jweBuilder.build();
}
private ECKey buildECKey() throws URISyntaxException {
Set<KeyOperation> keyOperations = new HashSet<>();
keyOperations.add(KeyOperation.ENCRYPT);
String transactionID = "73024831";
URI x5u = new URI("https//website.certificate");
KeyStore keystore = null; //initialize it
List<Base64> x5c = new ArrayList<>();
return new ECKey(Curve.P_256, new Base64URL("x"), new Base64URL("y"), KeyUse.ENCRYPTION, keyOperations, Algorithm.NONE, transactionID, x5u, new Base64URL("x5t"), new Base64URL("x5t256"), x5c, keystore);
}
}
您可以使用EncryptionMethod.A128CBC-HS256
代替EncryptionMethod.A128GCM
,如您的規范中所示。 apv
和epk
被添加到JWEHeader內部構建器中。 可以在JWEHeader.Builder和ECKey的構造函數中選擇其他參數。 我使用了ECDH-ES算法,A128GCM加密方法,P-256曲線(橢圓曲線默認為ECKey生成),事務ID是一個字符串。 我選擇了沒有任何清晰圖案的其他參數。 對於該示例,KeyStore的初始化將過於寬泛。 加密只是JWE可以做的一件事,在簽名和其他方面。
Nimbus(以及Jose4j)不是實現規范的最佳選擇(我猜它是3D安全2.xx)。
這些庫不返回內容加密密鑰,但根據JWE規范使用它來加密或解密消息。
我發現Apache CXF Jose庫在JWE / JWS / JWK實現方面做得很好。 除了生成短暫的密鑰對。 但是可以用Bouncy Castle輕松完成:
Security.addProvider(new BouncyCastleProvider());
ECGenParameterSpec ecGenSpec = new ECGenParameterSpec(JsonWebKey.EC_CURVE_P256);
KeyPairGenerator g = KeyPairGenerator.getInstance(ALGORITHM_SIGNATURE_EC, "BC");
g.initialize(ecGenSpec, new SecureRandom());
KeyPair keyPair = g.generateKeyPair();
可以使用以下代碼生成內容加密密鑰:
byte[] cek = JweUtils.getECDHKey(privateKey, publicKey, null, SDKReferenceNumber, "", 256);
並用於加密獲得JWE緊湊表示的消息:
JweEncryption jweEncryption = JweUtils.getDirectKeyJweEncryption(cek, contentAlgorithm);
JweHeaders head = new JweHeaders();
head.setHeader(JoseConstants.HEADER_KEY_ID, keyId);
String encrypted = jweEncryption.encrypt(contentToEncrypt.getBytes(StandardCharsets.UTF_8), head);
或者解密:
JweCompactConsumer compactConsumer = new JweCompactConsumer(encrypted);
JweHeaders head = compactConsumer.getJweHeaders();
JweDecryption jweDecryption = JweUtils.getDirectKeyJweDecryption(cek, head.getContentEncryptionAlgorithm());
JweDecryptionOutput out = jweDecryption.decrypt(encrypted);
String decrypted = out.getContentText();
我能夠為A128CBC-HS256生成密鑰。 但是A128GCM仍未成功。 使用nimbus-jose庫幫助添加A128CBC-HS256的工作樣品。 以下代碼僅用於密鑰生成。
還請注意我為我的用法覆蓋了nimbus-jose類。 希望能幫助到你。
private void generateSHA256SecretKey(AREQ areq, ECKey sdkPubKey, KeyPair acsKeyPair) throws Exception {
// Step 4 - Perform KeyAgreement and derive SecretKey
SecretKey Z = CustomECDH.deriveSharedSecret(sdkPubKey.toECPublicKey(), (ECPrivateKey)acsKeyPair.getPrivate(), null);
CustomConcatKDF concatKDF = new CustomConcatKDF("SHA-256");
String algIdString = "";
String partyVInfoString = areq.getSdkReferenceNumber();
int keylength = 256; //A128CBC-HS256
byte[] algID = CustomConcatKDF.encodeDataWithLength(algIdString.getBytes(StandardCharsets.UTF_8));
byte[] partyUInfo = CustomConcatKDF.encodeDataWithLength(new byte[0]);
byte[] partyVInfo = CustomConcatKDF.encodeDataWithLength(partyVInfoString.getBytes(StandardCharsets.UTF_8));
byte[] suppPubInfo = CustomConcatKDF.encodeIntData(keylength);
byte[] suppPrivInfo = CustomConcatKDF.encodeNoData();
SecretKey derivedKey = concatKDF.deriveKey(
Z,
keylength,
algID,
partyUInfo,
partyVInfo,
suppPubInfo,
suppPrivInfo);
System.out.println("Generated SHA256 DerivedKey : "+SecureUtils.bytesToHex(derivedKey.getEncoded()));
}
聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.