簡體   English   中英

BouncyCastle如何生成ECDH“密鑰”?

[英]How Does BouncyCastle Generate ECDH “Keys”?

我有一個使用BouncyCastle作為安全提供程序的應用程序,但我想切換到直接使用OpenSSL的其他應用程序(Conscrypt)。 我遇到的問題是,我正在使用BouncyCastle提供的KeyGenerator中的ECDH“密鑰”,但在我的其他庫中沒有類似的KeyGenerator。

為了比較兩者,我將使用下面的輸入使用兩種方法對點進行解碼:

添加了換行符以提高可讀性

BADX_GAXp03z_5p05O1-op61KJAl4j9U2sBnAnJ4p_6GSAIyFGU3lM
oC4aIXw_2qlTnplykArgjvwCWw-2g6L44

使用BouncyCastle方法-

public org.bouncycastle.jce.interfaces.ECPublicKey loadECPublicKeyBC(String encodedPublicKey) throws NoSuchProviderException, NoSuchAlgorithmException, InvalidKeySpecException, IOException {
    Base64.Decoder base64Decoder = Base64.getUrlDecoder();
    byte[] decodedPublicKey = base64Decoder.decode(encodedPublicKey);
    KeyFactory keyFactory = KeyFactory.getInstance("ECDH", "BC");

    ECParameterSpec ecParameterSpec = ECUtil.getECParameterSpec(openSSLProvider, "prime256v1");
    ECPoint ecPoint = ECUtil.decodePoint(decodedPublicKey, ecParameterSpec.getCurve());

    ECPublicKeySpec pubSpec = new ECPublicKeySpec(ecPoint, ecParameterSpec);
    org.bouncycastle.jce.interfaces.ECPublicKey ecPublicKey = (org.bouncycastle.jce.interfaces.ECPublicKey)keyFactory.generatePublic(pubSpec);

    return ecPublicKey;
}

返回的getAlgorithmEC 返回的getFormatX.509

此方法的getEncoded值是-

[48,-126,1,51,48,-127,-20,6,7,42,-122,72,-50,61,2,1,48,
-127,-32,2,1,1,48,44,6,7,42,-122,72,-50,61,1,1,2,33,0,
-1,-1,-1,-1,0,0,0,1,0,0,0,0,0,0,0,0,0,0,0,0,-1,-1,-1,-1,
-1,-1,-1,-1,-1,-1,-1,-1,48,68,4,32,-1,-1,-1,-1,0,0,0,1,
0,0,0,0,0,0,0,0,0,0,0,0,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,
-1,-4,4,32,90,-58,53,-40,-86,58,-109,-25,-77,-21,-67,85,
118,-104,-122,-68,101,29,6,-80,-52,83,-80,-10,59,-50,60,
62,39,-46,96,75,4,65,4,107,23,-47,-14,-31,44,66,71,-8,-68,
-26,-27,99,-92,64,-14,119,3,125,-127,45,-21,51,-96,-12,
-95,57,69,-40,-104,-62,-106,79,-29,66,-30,-2,26,127,-101,
-114,-25,-21,74,124,15,-98,22,43,-50,51,87,107,49,94,-50,
-53,-74,64,104,55,-65,81,-11,2,33,0,-1,-1,-1,-1,0,0,0,0,-1,
-1,-1,-1,-1,-1,-1,-1,-68,-26,-6,-83,-89,23,-98,-124,-13,
-71,-54,-62,-4,99,37,81,2,1,1,3,66,0,4,0,-41,-4,96,23,-89,
77,-13,-1,-102,116,-28,-19,126,-94,-98,-75,40,-112,37,-30,
63,84,-38,-64,103,2,114,120,-89,-2,-122,72,2,50,20,101,55,
-108,-54,2,-31,-94,23,-61,-3,-86,-107,57,-23,-105,41,0,-82,
8,-17,-64,37,-80,-5,104,58,47,-114]

只需使用BouncyCastle EC算法(而非ECDH),我就可以

[48,-126,1,51,48,-127,-20,6,7,42,-122,72,
-50,61,2,1,48,-127,-32,2,1,1,48,44,6,7,42,
-122,72,-50,61,1,1,2,33,0,-1,-1,-1,-1,0,0,
0,1,0,0,0,0,0,0,0,0,0,0,0,0,-1,-1,-1,-1,-1,
-1,-1,-1,-1,-1,-1,-1,48,68,4,32,-1,-1,-1,-1,
0,0,0,1,0,0,0,0,0,0,0,0,0,0,0,0,-1,-1,-1,-1,
-1,-1,-1,-1,-1,-1,-1,-4,4,32,90,-58,53,-40,
-86,58,-109,-25,-77,-21,-67,85,118,-104,-122,
-68,101,29,6,-80,-52,83,-80,-10,59,-50,60,62,
39,-46,96,75,4,65,4,107,23,-47,-14,-31,44,66,
71,-8,-68,-26,-27,99,-92,64,-14,119,3,125,
-127,45,-21,51,-96,-12,-95,57,69,-40,-104,-62,
-106,79,-29,66,-30,-2,26,127,-101,-114,-25,
-21,74,124,15,-98,22,43,-50,51,87,107,49,94,
-50,-53,-74,64,104,55,-65,81,-11,2,33,0,-1,-1,
-1,-1,0,0,0,0,-1,-1,-1,-1,-1,-1,-1,-1,-68,-26,
-6,-83,-89,23,-98,-124,-13,-71,-54,-62,-4,99,
37,81,2,1,1,3,66,0,4,0,-41,-4,96,23,-89,77,
-13,-1,-102,116,-28,-19,126,-94,-98,-75,40,
-112,37,-30,63,84,-38,-64,103,2,114,120,-89,
-2,-122,72,2,50,20,101,55,-108,-54,2,-31,-94,
23,-61,-3,-86,-107,57,-23,-105,41,0,-82,8,-17,
-64,37,-80,-5,104,58,47,-114]

現在使用Conscrypt方法-

public ECPublicKey loadECPublicKey(String encodedPublicKey) throws NoSuchProviderException, NoSuchAlgorithmException, InvalidKeySpecException, IOException {
    Base64.Decoder base64Decoder = Base64.getUrlDecoder();
    byte[] decodedPublicKey = base64Decoder.decode(encodedPublicKey);
    KeyFactory keyFactory = KeyFactory.getInstance("EC", "Conscrypt");

    ECParameterSpec ecParameterSpec = ECUtil.getECParameterSpec(openSSLProvider, "prime256v1");
    ECPoint ecPoint = ECUtil.decodePoint(decodedPublicKey, ecParameterSpec.getCurve());

    ECPublicKeySpec pubSpec = new ECPublicKeySpec(ecPoint, ecParameterSpec);
    ECPublicKey ecPublicKey = (ECPublicKey)keyFactory.generatePublic(pubSpec);

    return ecPublicKey;
}

返回的getAlgorithmEC 返回的getFormatX.509 此方法的getEncoded值是-

[48,89,48,19,6,7,42,-122,72,-50,61,2,1,6,8,42,
-122,72,-50,61,3,1,7,3,66,0,4,0,-41,-4,96,23,
-89,77,-13,-1,-102,116,-28,-19,126,-94,-98,-75,
40,-112,37,-30,63,84,-38,-64,103,2,114,120,-89,
-2,-122,72,2,50,20,101,55,-108,-54,2,-31,-94,23,
-61,-3,-86,-107,57,-23,-105,41,0,-82,8,-17,-64,
37,-80,-5,104,58,47,-114]

忽略兩個EC生成的密鑰之間的差異。 BouncyCastle在ECDH KeyGenerator中做什么?

DH是KeyAgreement,我假設它正在生成EC密鑰並通過DH KeyAgreement運行它,但是在KeyGenerator規范中未指定任何內容時,它將初始化為私鑰嗎?

也。 為什么當我兩個供應商都使用EC算法時,當兩個都使用prime256v1規范時,同一算法卻得到不同的結果嗎? 我認為這些至少是相等的。

編輯:

ECUtil來自sun.security.util.ECUtil

在我的示例中,對於BC和Java安全性庫都共享通用名稱的任何類(例如ECPoint),它始終是Java安全性庫。 僅當類以bouncycastle路徑(例如org.bouncycastle.jce.interfaces.ECPublicKey)為前綴時,它才是BC類。 openSSLProvider是Conscrypt庫中OpenSSLProvider的實例。

該項目可以在這里找到。

https://github.com/google/conscrypt

安裝的pom在這里-

<dependency>
    <groupId>org.conscrypt</groupId>
    <artifactId>conscrypt-openjdk-uber</artifactId>
    <version>2.1.0</version>
</dependency>

///

import org.conscrypt.OpenSSLProvider;
import org.bouncycastle.jce.provider.BouncyCastleProvider;
import sun.security.util.ECUtil;

Security.addProvider(new BouncyCastleProvider());
Security.addProvider(new OpenSSLProvider());

ECUtil.getECParameterSpec(new OpenSSLProvider, "prime256v1");

編輯編輯:

完整的最小可復制示例-

編輯編輯編輯:

現在的示例包括手動加載公鑰,而不是通過KeyFactory加載。

手動加載BC公鑰時,通過密鑰工廠加載時,其編碼值與Conscrypt公鑰的編碼值匹配...

import org.bouncycastle.jce.ECNamedCurveTable;
import org.bouncycastle.jce.provider.BouncyCastleProvider;
import org.bouncycastle.jce.provider.JCEECPublicKey;
import org.bouncycastle.jce.spec.ECNamedCurveParameterSpec;
import org.conscrypt.OpenSSLProvider;
import sun.security.util.ECUtil;

import java.io.IOException;
import java.security.*;
import java.security.interfaces.ECPublicKey;
import java.security.spec.ECParameterSpec;
import java.security.spec.ECPoint;
import java.security.spec.ECPublicKeySpec;
import java.security.spec.InvalidKeySpecException;
import java.util.*;

public class Main {

    public static void main(String[] args) throws NoSuchAlgorithmException, NoSuchProviderException, InvalidKeySpecException, IOException {
        Security.addProvider(new BouncyCastleProvider());
        Security.addProvider(new OpenSSLProvider());

        String pubKey = "BADX_GAXp03z_5p05O1-op61KJAl4j9U2sBnAnJ4p_6GSAIyFGU3lMoC4aIXw_2qlTnplykArgjvwCWw-2g6L44";

        ECPublicKey publicKey = (ECPublicKey)loadPublicKey(pubKey, "Conscrypt");
        org.bouncycastle.jce.interfaces.ECPublicKey publicKeyBC = (org.bouncycastle.jce.interfaces.ECPublicKey)loadPublicKey(pubKey, "BC");
        org.bouncycastle.jce.interfaces.ECPublicKey publicKeyBC2 = (org.bouncycastle.jce.interfaces.ECPublicKey) loadPublicKeyManually(pubKey);

        System.out.println(Arrays.toString(publicKey.getEncoded()));
        System.out.println(Arrays.toString(publicKeyBC.getEncoded()));
        System.out.println(Arrays.toString(publicKeyBC2.getEncoded()));
    }

    public static PublicKey loadPublicKey(String encodedPublicKey, String provider) throws NoSuchProviderException, NoSuchAlgorithmException, InvalidKeySpecException, IOException {
        Base64.Decoder base64Decoder = Base64.getUrlDecoder();
        byte[] decodedPublicKey = base64Decoder.decode(encodedPublicKey);
        KeyFactory keyFactory = KeyFactory.getInstance("EC", provider);

        ECParameterSpec ecParameterSpec = ECUtil.getECParameterSpec(new OpenSSLProvider(), "prime256v1");
        ECPoint ecPoint = ECUtil.decodePoint(decodedPublicKey, ecParameterSpec.getCurve());

        ECPublicKeySpec pubSpec = new ECPublicKeySpec(ecPoint, ecParameterSpec);
        ECPublicKey ecPublicKey = (ECPublicKey)keyFactory.generatePublic(pubSpec);

        return ecPublicKey;
    }

    public static PublicKey loadPublicKeyManually(String encodedPublicKey) {
        Base64.Decoder base64Decoder = Base64.getUrlDecoder();
        byte[] decodedPublicKey = base64Decoder.decode(encodedPublicKey);

        ECNamedCurveParameterSpec parameterSpec = ECNamedCurveTable.getParameterSpec("prime256v1");

        org.bouncycastle.jce.spec.ECPublicKeySpec ecPublicKeySpec = new org.bouncycastle.jce.spec.ECPublicKeySpec(
                parameterSpec.getCurve().decodePoint(decodedPublicKey),
                parameterSpec
        );
        org.bouncycastle.jce.interfaces.ECPublicKey ecPublicKey = new JCEECPublicKey(
                "EC",
                ecPublicKeySpec
        );

        return ecPublicKey;
    }
}

這兩個公鑰表示本質上是等效的。 兩者都是RFC 5280中描述的DER編碼的SubjectPublicKeyInfo結構的實例。 該結構不僅包含公用密鑰,還包含一些描述公用密鑰的算法上下文的元數據。 對於其中一種形式,元數據僅聲明上下文是“ prime256v1”曲線。 另一方面,提供了該曲線的所有參數。 公鑰本身作為兩種形式的最后一部分出現,並且您可以看到它們是相同的。

您擁有的是相同公共密鑰的3種不同表示形式。 base-64編碼的字符串僅包含根據SEC 1第2.3.3節編碼的4類(未壓縮)橢圓曲線點。 您在sun.security.util.ECUtil類中找到了一個未記錄且不受支持的API,可以將其轉換為PublicKey。

我不確定您對私鑰有什么疑問。 對於包括ECDH的DH方案,私鑰只是從基礎組順序范圍中“安全地”選擇的整數。 然后,通過將該整數乘以曲線的基點來計算公鑰(在橢圓曲線意義上)。 結果公鑰也是曲線上的一個點。

暫無
暫無

聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.

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