为什么使用 ECDH 加密算法的 JsonWebSignature 每次都给出不同的签名?
Why JsonWebSignature with ECDH encryption algorithm give a different signature everytime?
我想向 google FCM 发送请求以向浏览器发送推送请求以显示通知。
主要目标是使用带有 SHA256 的 ECDH 算法使用私钥对 JWT 负载进行签名以获得 JWT 令牌。
我尝试使用 ECDH 算法对令牌进行签名,但每次我都为相同的有效负载获得不同的签名。
这是我的代码截图
提前致谢
JwtClaims claims = new JwtClaims();
claims.setAudience("https://fcm.googleapis.com");
claims.setExpirationTime(NumericDate.fromSeconds(1560388318));
claims.setSubject("mailto:admin@example.com");
JsonWebSignature jws = new JsonWebSignature();
jws.setHeader("typ", "JWT");
jws.setHeader("alg", "ES256");
jws.setPayload(claims.toJson());
try {
Key key = loadPrivateKey("-kmhPYsH6JKiFjG8C1cS9vx4bCz594yofAwTLa_SOEE");
jws.setKey(key);
} catch (NoSuchProviderException e) {
e.printStackTrace();
} catch (NoSuchAlgorithmException e) {
e.printStackTrace();
} catch (InvalidKeySpecException e) {
e.printStackTrace();
}
jws.setAlgorithmHeaderValue(AlgorithmIdentifiers.ECDSA_USING_P256_CURVE_AND_SHA256);
try {
System.out.println(jws.getCompactSerialization());
} catch (JoseException e) {
e.printStackTrace();
}
.
public static PrivateKey loadPrivateKey(String encodedPrivateKey) throws NoSuchProviderException, NoSuchAlgorithmException, InvalidKeySpecException {
byte[] decodedPrivateKey = base64Decode(encodedPrivateKey);
// prime256v1 is NIST P-256
ECParameterSpec params = ECNamedCurveTable.getParameterSpec("prime256v1");
ECPrivateKeySpec prvkey = new ECPrivateKeySpec(new BigInteger(decodedPrivateKey), params);
KeyFactory kf = KeyFactory.getInstance("ECDH");
return kf.generatePrivate(prvkey);
}
.
public static byte[] base64Decode(String base64Encoded) {
if (base64Encoded.contains("+") || base64Encoded.contains("/")) {
return BaseEncoding.base64().decode(base64Encoded);
} else {
return BaseEncoding.base64Url().decode(base64Encoded);
}
}
第一次尝试我得到了:
eyj0exaioijkv1qilcjhbgcioijfuzi1nij9.eyjhdwqioijodhrwczovl2zjbs5nb29nbgvhcvhcgggggggggggggbbbbsisimvbbbssimimimimimimimimimimimimimimimimimibnciimtunciim an
我得到的第二个时间:
eyJ0eXAiOiJKV1QiLCJhbGciOiJFUzI1NiJ9.eyJhdWQiOiJodHRwczovL2ZjbS5nb29nbGVhcGlzLmNvbSIsImV4cCI6MTU2MDM4ODMxOCwic3ViIjoibWFpbHRvOmFkbWluQGV4YW1wbGUuY29tIn0.qEW0ci1BnXXUUjkfsQkdReznAyIDEPtygxV3B58Sl8v_gTlh8O4HHGzRtxsqdvL5UIJV06e_UJHYPOUDK_MF9A
根据设计,数字加密签名应仅满足以下条件
It should be verifiable by the corresponding public key.
如果您查看 Signature Generation algorithm 步骤 3,它会显示 Select a cryptographically secure random integer k from [1,n-1].
,其中 n
是曲线的阶数(您现在可以忽略此事实)。
然后计算(x1, y1) = k * G
,其中G
是椭圆曲线的生成点。然后,r = x1 mod n
。这个 r 是签名的一部分。因此,通过更改 k
,r
也会更改,因此签名会更改。
因此,对于每个签名生成,算法都会选择一个不同的参数,然后使用 is 来计算签名。
例如:
>>> from ecc import curves
>>> curve = curves.P256()
>>> pkey = 0x00c3f7c39a9be2418cd89a732e40d648b09fa0af9e909a4fb6864910144b5cbcdf
>>> s1 = c.sign(b'Hello', pkey)
(37527198291707833181859423619289327687028014812888685671525882103189540525356,7717531609084222009133798505588038563850333231389727023073200992747312618427)
>>> s2 = c.sign(b'Hello', pkey)
(55880701658034823360120047989457771316451459626784083177171213563603884569397,88917360761747520665103257272757357544674490240888454865713640275762122369837)
>>> s1 == s2
False
每次签名都不一样
参考:
ECDH 加密算法使用不同的安全随机数来计算每个签名生成的签名。
您可以指定此 Secure Random 每次都获得相同的签名。
对于你的情况:
ProviderContext context = new ProviderContext();
context.setSecureRandom(new SecureRandom(SecureRandom.getSeed(0)));
jws.setProviderContext(context);
我想向 google FCM 发送请求以向浏览器发送推送请求以显示通知。
主要目标是使用带有 SHA256 的 ECDH 算法使用私钥对 JWT 负载进行签名以获得 JWT 令牌。
我尝试使用 ECDH 算法对令牌进行签名,但每次我都为相同的有效负载获得不同的签名。
这是我的代码截图
提前致谢
JwtClaims claims = new JwtClaims();
claims.setAudience("https://fcm.googleapis.com");
claims.setExpirationTime(NumericDate.fromSeconds(1560388318));
claims.setSubject("mailto:admin@example.com");
JsonWebSignature jws = new JsonWebSignature();
jws.setHeader("typ", "JWT");
jws.setHeader("alg", "ES256");
jws.setPayload(claims.toJson());
try {
Key key = loadPrivateKey("-kmhPYsH6JKiFjG8C1cS9vx4bCz594yofAwTLa_SOEE");
jws.setKey(key);
} catch (NoSuchProviderException e) {
e.printStackTrace();
} catch (NoSuchAlgorithmException e) {
e.printStackTrace();
} catch (InvalidKeySpecException e) {
e.printStackTrace();
}
jws.setAlgorithmHeaderValue(AlgorithmIdentifiers.ECDSA_USING_P256_CURVE_AND_SHA256);
try {
System.out.println(jws.getCompactSerialization());
} catch (JoseException e) {
e.printStackTrace();
}
.
public static PrivateKey loadPrivateKey(String encodedPrivateKey) throws NoSuchProviderException, NoSuchAlgorithmException, InvalidKeySpecException {
byte[] decodedPrivateKey = base64Decode(encodedPrivateKey);
// prime256v1 is NIST P-256
ECParameterSpec params = ECNamedCurveTable.getParameterSpec("prime256v1");
ECPrivateKeySpec prvkey = new ECPrivateKeySpec(new BigInteger(decodedPrivateKey), params);
KeyFactory kf = KeyFactory.getInstance("ECDH");
return kf.generatePrivate(prvkey);
}
.
public static byte[] base64Decode(String base64Encoded) {
if (base64Encoded.contains("+") || base64Encoded.contains("/")) {
return BaseEncoding.base64().decode(base64Encoded);
} else {
return BaseEncoding.base64Url().decode(base64Encoded);
}
}
第一次尝试我得到了:
eyj0exaioijkv1qilcjhbgcioijfuzi1nij9.eyjhdwqioijodhrwczovl2zjbs5nb29nbgvhcvhcgggggggggggggbbbbsisimvbbbssimimimimimimimimimimimimimimimimimibnciimtunciim an
我得到的第二个时间:
eyJ0eXAiOiJKV1QiLCJhbGciOiJFUzI1NiJ9.eyJhdWQiOiJodHRwczovL2ZjbS5nb29nbGVhcGlzLmNvbSIsImV4cCI6MTU2MDM4ODMxOCwic3ViIjoibWFpbHRvOmFkbWluQGV4YW1wbGUuY29tIn0.qEW0ci1BnXXUUjkfsQkdReznAyIDEPtygxV3B58Sl8v_gTlh8O4HHGzRtxsqdvL5UIJV06e_UJHYPOUDK_MF9A
根据设计,数字加密签名应仅满足以下条件
It should be verifiable by the corresponding public key.
如果您查看 Signature Generation algorithm 步骤 3,它会显示 Select a cryptographically secure random integer k from [1,n-1].
,其中 n
是曲线的阶数(您现在可以忽略此事实)。
然后计算(x1, y1) = k * G
,其中G
是椭圆曲线的生成点。然后,r = x1 mod n
。这个 r 是签名的一部分。因此,通过更改 k
,r
也会更改,因此签名会更改。
因此,对于每个签名生成,算法都会选择一个不同的参数,然后使用 is 来计算签名。 例如:
>>> from ecc import curves
>>> curve = curves.P256()
>>> pkey = 0x00c3f7c39a9be2418cd89a732e40d648b09fa0af9e909a4fb6864910144b5cbcdf
>>> s1 = c.sign(b'Hello', pkey)
(37527198291707833181859423619289327687028014812888685671525882103189540525356,7717531609084222009133798505588038563850333231389727023073200992747312618427)
>>> s2 = c.sign(b'Hello', pkey)
(55880701658034823360120047989457771316451459626784083177171213563603884569397,88917360761747520665103257272757357544674490240888454865713640275762122369837)
>>> s1 == s2
False
每次签名都不一样
参考:
ECDH 加密算法使用不同的安全随机数来计算每个签名生成的签名。 您可以指定此 Secure Random 每次都获得相同的签名。 对于你的情况:
ProviderContext context = new ProviderContext();
context.setSecureRandom(new SecureRandom(SecureRandom.getSeed(0)));
jws.setProviderContext(context);