簡體   English   中英

在Java中實現Diffie-Hellman密鑰交換

[英]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”}將被簽名並發送,但我不明白我將如何使用apvepk,因為這些標題參數在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中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 ,如您的規范中所示。 apvepk被添加到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.

 
粵ICP備18138465號  © 2020-2024 STACKOOM.COM