假设我将以下JWK作为一些JWS(RFC7515)的反序列化体,其中出于显示目的,部分省略了模数n
{
"kty": "RSA",
"e": "AQAB",
"kid": "KAgNpWbRyy9Mf2rikl498LThMrvkbZWHVSQOBC4VHU4",
"n": "llWmHF8XA2KNLdmxOP3kxD9OY76p0Sr37j..."
}
JWS头指定签名验证所需的alg和kid字段。
如何从这个JWK
构造RSA公钥以便验证签名?在研究了一些相关问题之后,我有以下Java实现,它尝试从JWK
中的n
和e
字段构建RSA公钥
public void someMethod(){
String exjws ="eyJhbGciOiJSUzI1NiIsImtpZCI6IktBZ05wV2JSeXk5TWYycmlrbDQ5OExUaE1ydmtiWldIVlNRT0JDNFZIVTQiL"
+ "CJodG0iOiJwb3N0IiwiaHR1IjoiL3R4IiwidHMiOjE2MDM4MDA3ODN9.eyJjYXBhYmlsaXRpZXMiOltdLCJjbGllbnQiOnsia2V5Ijp7Imp3ayI6eyJrdHkiOiJSU0EiLCJ"
+ "hbGciOiJSUzI1NiIsImUiOiJBUUFCIiwia2lkIjoiS0FnTnBXYlJ5eTlNZjJyaW"
+ "tsNDk4TFRoTXJ2a2JaV0hWU1FPQkM0VkhVNCIsIm4iOiJsbFdtSEY4WEEyS05MZG14T1Aza3hEOU9ZNzZwMFNyMzdqZmh6OTRhOTN4bTJGTnFvU1BjUlpBUGQwbHFEUzhO"
+ "M1VpYTUzZEIyM1o1OU93WTRicE1fVmY4R0p2dnB0TFdueG8xUHlobVByIC0gZWNkU0NSUWRUY19aY01GNGhSVjQ4cXFsdnVEMG1xdGNEYklrU0JEdmNjSm1aSHdmVHBESG"
+ "luVDh0dHZjVlA4VmtBTUFxNGtWYXp4T3BNb0lSc295RXBfZUNlNXBTd3FIbzBkYUNXTktSI"
+ "C0gRXBLbTZOaU90ZWRGNE91bXQ4TkxLVFZqZllnRkhlQkRk"
+ "Q2JyckVUZDR2Qk13RHRBbmpQcjNDVkN3d3gyYkFRVDZTbHhGSjNmajJoaHlJcHE3cGM4clppYjVqTnlYS3dmQnVrVFZZWm96a3NodCAtIExvaHlBU2FLcFlUcDhMdE5aIC0gdyAifSw"
+ "icHJvb2YiOiJqd3MifSwibmFtZSI6Ik15IEZpcnN0IENsaWVu"
+ "dCIsInVyaSI6Imh0dHA6XC9cL2xvY2FsaG9zdFwvY2xpZW50XC9jbGllbnRJRCJ9LCJpbnRlcmFjdCI6eyJzdGFydCI6WyJyZWRpcmVjdCJ"
+ "dLCJmaW5pc2giOnsibWV0aG9kIjoicmVkaXJlY3QiLCJub25jZSI6ImQ5MDIxMzg4NGI4NDA5MjA1MzhiNWM1MSIsInVyaSI6Imh0dHA6XC9cL2xvY2FsaG9zdFwvY2xpZW50"
+ "XC9yZXF1ZXN0LWRvbmUifX0sImFjY2Vzc190b2tlbiI6eyJhY2Nlc3MiOlt7ImFjdGlvbnMiOlsicmVhZCIsInByaW50Il0sImxvY2F0aW9ucyI6WyJodHRwOlwvXC9sb2Nhb"
+ "Ghvc3RcL3Bob3RvcyJdLCJkYXRhdHlwZXMiOlsibWV0YWRhdGEiLCJpbWFnZXMiXSwidHlwZSI6InBob3RvLWFwaSJ9XX0sInN1YmplY3QiOnsic3ViX2lkcyI6WyJpc3Nfc3"
+ "ViIiwiZW1haWwiXX19.LUyZ8_fERmxbYARq8kBYMwzcd8GnCAKAlo2ZSYLRRNAYWPrp2XGLJOvg97WK1idf_LB08OJmLVsCXxCvn9mgaAkYNL_ZjHcusBvY1mNo0E1sdTEr31"
+ "CVKfC-6WrZCscb8YqE4Ayhh0Te8kzSng3OkLdy7xN4xeKuHzpF7yGsM52JZ0cBcTo6WrYEfGdr08AWQJ59ht72n3jTsmYNy9A6I4Wrvfgj3TNxmwYojpBAi"
+ "cfjnzA1UVcNm9F_xiSz1_y2tdH7j5rVqBMQife-k9Ewk95vr3lurthenliYSNiUinVfoW1ybnaIBcTtP1_YCxg_h1y-B5uZEvYNGCuoCqa6IQ";
String[] parts = exjws.split("\\.");
String payload = new Base64URL(parts[1]).decodeToString();
JsonObject jwk = JsonParser.parseString(payload).getAsJsonObject().get("client")
.getAsJsonObject().get("key").getAsJsonObject().get("jwk").getAsJsonObject();
BigInteger modulus = new BigInteger(1, new Base64URL(jwk.get("n").getAsString()).decode());
BigInteger exponent = new BigInteger(1, new Base64URL(jwk.get("e").getAsString()).decode());
byte[] signingInfo = String.join(".",parts[0],parts[1]).getBytes(StandardCharsets.UTF_8);
byte[] b64DecodedSig = new Base64(parts[2]).decode();
PublicKey pub = KeyFactory.getInstance("RSA").generatePublic(new RSAPublicKeySpec(modulus, exponent));
Signature verifier = Signature.getInstance("SHA256withRSA");
verifier.initVerify(pub);
verifier.update(signingInfo);
boolean okay = verifier.verify(b64DecodedSig);
System.out.println(okay);
}
验证()的结果当前正在返回false。
我尝试过生成RSA密钥对,使用生成密钥进行签名和验证,这很有效。我怀疑我的问题是,上面代码中构造的RSA密钥不知何故是错误的。感谢您的帮助。
编辑
JSON库是Gson
JWK库是nismus jose jwt,它提供Base64和Base64URl
我正在使用JWT库来完成此任务(我知道这有点过头了,但它正在工作……):https://bitbucket.org/connect2id/nimbus-jose-jwt/src/master/
它们提供了一个易于使用的界面,下面是生成RSA密钥对并打印出公钥的示例代码,然后将此公钥转换为JWK格式(也打印出来),然后将JWK格式“最终”转换为Java的RSAPublicKey格式-原始公钥与“双重转换”的新公钥相同:
RSA converting between Java keys and JWK keys
rsaPublicKey:
Sun RSA public key, 2048 bits
params: null
modulus: 26357316308141920246706187189832816184464669247574329274151600014987833498613081305553907167489639882497901020042668019684731733203493602029515963993706600847721534104752032126130361576446376774646308346723928903197131878300000630951097323650413651136361398382777541880437222482333326912353931641531474275115618239345544686220478026629436520040030688170796270228708165193211856330191604982765859609032534442818720461696078063893165568447273933782242398761845509532495844704423556107073518195030616464416564865911759432179943444938978123330642161124144169230685337930276039065398676755273689018037129036026967769360801
public exponent: 65537
jwkRsaPublicKey (JWK-Format)
{"kty":"RSA","e":"AQAB","n":"0MpIJE0koFXx5sZOOI-XsEMMQfvwHkizj1jaYGATZEz0YTdf-WUDrO2JeELP1UvwHRbD5Mt0y0IvSYEjG4btVoZWjoJwEIz-bT7rtJNnZ9bjY8vMYloCUM81nTLve0sVRqkjw3S7IFXsTXx05vkY7oV25Z9YeZH2f5b1ph3JGcTrQF8d3XZy6XAM_KaWWOPTwzoNtr3JQQzUJ2vS_BGCJyiVU1cEB0RlRu1Gd9EPqDcMGAN2nMoHUuQw0qNTd-ms0Du0RGnktRDpcm3SXLsUt2J4adbPp02eXjn-TDTISzR6FywC0sAL6ED0EqWhOgqEf7EftctSJGGdgLOkmL4poQ"}
rsaPublicKeyFromJwk:
Sun RSA public key, 2048 bits
params: null
modulus: 26357316308141920246706187189832816184464669247574329274151600014987833498613081305553907167489639882497901020042668019684731733203493602029515963993706600847721534104752032126130361576446376774646308346723928903197131878300000630951097323650413651136361398382777541880437222482333326912353931641531474275115618239345544686220478026629436520040030688170796270228708165193211856330191604982765859609032534442818720461696078063893165568447273933782242398761845509532495844704423556107073518195030616464416564865911759432179943444938978123330642161124144169230685337930276039065398676755273689018037129036026967769360801
public exponent: 65537
此代码没有异常处理,仅用于教育目的:
import com.nimbusds.jose.JOSEException;
import com.nimbusds.jose.jwk.RSAKey;
import java.security.KeyPair;
import java.security.KeyPairGenerator;
import java.security.NoSuchAlgorithmException;
import java.security.SecureRandom;
import java.security.interfaces.RSAPublicKey;
public class ConvertRsaKeysJavaJwk {
public static void main(String[] args) throws NoSuchAlgorithmException, JOSEException {
System.out.println("RSA converting between Java keys and JWK keys");
// generate a RSA key pair
KeyPair rsaKeyPair = generateRsaKeyPair(2048);
RSAPublicKey rsaPublicKey = (RSAPublicKey) rsaKeyPair.getPublic();
System.out.println("rsaPublicKey:\n" + rsaPublicKey);
// import the ecdsaPublicKey to JWK
// usage of nimbus-jose-jwt
// https://bitbucket.org/connect2id/nimbus-jose-jwt/src/master/
RSAKey jwkRsaPublicKey = new RSAKey.Builder(rsaPublicKey).build();
System.out.println("\njwkRsaPublicKey (JWK-Format)\n" + jwkRsaPublicKey);
// convert jwk to java
RSAPublicKey rsaPublicKeyFromJwk = jwkRsaPublicKey.toRSAPublicKey();
System.out.println("\nrsaPublicKeyFromJwk:\n" + rsaPublicKeyFromJwk);
}
public static KeyPair generateRsaKeyPair(int keylengthInt) throws NoSuchAlgorithmException {
KeyPairGenerator keypairGenerator = KeyPairGenerator.getInstance("RSA");
keypairGenerator.initialize(keylengthInt, new SecureRandom());
return keypairGenerator.generateKeyPair();
}
}
来自评论中与@Topaco的讨论:
问题中的代码确实成功地从模n和指数e构造了RSA公钥。
但是,签名。verify()调用返回false,因为请求中JWK中指定的公钥与用于签署请求的密钥不匹配。
我正在使用azure sdk从azure密钥库获取密钥。这将返回一个KeyBundle,它的key属性类型为 对于我的代码的不同部分,我需要从jwCrypto JWK制作一个签名令牌。make_signed_token方法使用JsonWebKey。从Azure返回的JsonWebKey(我认为)实际上是同一个类,在不同的包中定义。 有没有办法将Azure JWK变成jwcrypto JWK?我认为
我使用的是nimbus jose jwt 5.14,我用以下代码生成了RSA密钥对 现在我需要解释一些关于公钥的“元数据”: e 孩子 kty n 使用 x5c 如何获得x5c?是否可以使用此库生成X509证书?此字段为空:
如前所述,许多 Git 服务器都使用 SSH 公钥进行认证。 为了向 Git 服务器提供 SSH 公钥,如果某系统用户尚未拥有密钥,必须事先为其生成一份。 这个过程在所有操作系统上都是相似的。 首先,你需要确认自己是否已经拥有密钥。 默认情况下,用户的 SSH 密钥存储在其 ~/.ssh 目录下。 进入该目录并列出其中内容,你便可以快速确认自己是否已拥有密钥: $ cd ~/.ssh $ ls a
我以这种格式将私钥保存在DB中。 我需要从中提取公钥。openssl\u pkey\u get\u详细信息需要资源,但如何从字符串创建资源? 我试过这个 错误是openssl\u pkey\u get\u details()期望参数1是资源,给定布尔值 编辑1: 我试图从@kbarborak bellow获得解决方案,但$pk\u get是错误的: var_转储的输出为:
以下网站经常被引用,而且我认为是准确的: https://gobittest.appspot.com/Address 我试图在Golang中重现这些步骤,但第一步就失败了:-( 有人能给我提供一个Golang片段,给定ECDSA私钥,返回公钥吗?我想我可能具体指的是上述站点示例中的私钥指数和公钥指数。 i、 例如,给定随机生成的(十六进制编码的)私钥(指数?)<代码>E83385AF76B2B19
我正在Erlang中创建一个应用程序,给定一个RSA私钥,它可以返回RSA公钥和与该priv密钥相关联的x509公钥。 ----只是一个更新---- 在花了一些时间了解x509并阅读了Erlang public_key文档后,我找到了一个解决方案: 如果我可以通过简单地获取模数和publicExponent,从“RSaprivateKey”生成“RSapublicKey”,那么生成这两个键的代码如