问题描述
我想在这里从 Google-Sign-in 的 JWK 数据手动生成公钥; https://www.googleapis.com/oauth2/v3/certs
{
"n": "3aOynmXd2aSH0ZOd0TIYd5RRaNXhLW306dlYw26nMp6QPGaJuOeMhTO3BO8Zt_ncRs4gdry4mEaUOetCKTUOyCCpIM2JAn0laN_iHfGKTYsNkjr16FiHWYJmvNJ1Q1-XXjWqNNKMFIKHKtMrsP2XPVD6ufp-lNQmt4Dl0g0qXJ4_Y_CKuP-uSlFWZuJ_0_2ukUgevvKtOZNcbth0iOiFalBRDr-2i1eNSJWOknEphy7GRs-JGPboTdHC7A3b-0dVFGMEMJFhxcEJHJgLCsQGdYdkphLJ5f21gCNdhp3g16H3Cqts2KTXgO4Rr8uhwZx5yiUjTuizD9wc7uDso4UJ7Q","use": "sig","kty": "RSA","kid": "b6f8d55da534ea91cb2cb00e1af4e8e0cdeca93d","alg": "RS256","e": "AQAB"
},
我应该如何使用模数和指数创建密钥?
我在这个问题中尝试了以下步骤; Generate RSA Public Key from Modulus and Exponent 但是我的语言是说给定的模数不是十六进制字符串。还有一些缺失的信息,比如那里的 berData 函数是什么。
我在这里看到的类似问题的所有解决方案似乎都是特定于语言/框架的。 我更喜欢一些我可以遵循的伪代码。
解决方法
事实证明,这个问题在很大程度上已经在 SO 上有了答案 - https://stackoverflow.com/a/29707204/2862341。
它比我现在想象的要好得多。
为了向您提供我认为您需要完成任务所需的其他详细信息 - JWT 编码是 base64-url-encoded (https://stackoverflow.com/a/55389212/2862341)。
为了完整起见,这里还有一个示例 java 代码
package com.example.so.q68285091;
import java.math.BigInteger;
import java.security.KeyFactory;
import java.security.NoSuchAlgorithmException;
import java.security.PublicKey;
import java.security.spec.InvalidKeySpecException;
import java.security.spec.RSAPublicKeySpec;
import java.security.spec.X509EncodedKeySpec;
import java.util.Base64;
/*
------------------------------------------
https://www.googleapis.com/oauth2/v3/certs
------------------------------------------
{
"keys": [
{
"kid": "1bf8a84d3ecd77e9f2ad5f06ffd260701dd06d90","use": "sig","n": "zw_voGnxIrSEnta0BxsS4Gmr0t106iCjmIK6nciUuppcUrDTKT3t3sZLsZ5Hl-WFzkGpo9u6jWj3ul64hcpgFTRJk5xIUnbKZ20za_IRfWDTZqhZ3LqeHqBQaw0BekUzICsw6Ip1-3kg3QoQyoU973Os6MAfpOP7SafcHp_tTRoMFAc7R3AWZdCmlMmw-xYLTT3UdI2hycXbTYOitFkCTYO4pL62IBt5inlOup4dk0HwazBp0zMYxyb49y1gqh_rfpswTSA7lVzySyU9SoXAWFHZW4rQrhiXwLOPz8cynr67lE5_D5t0XhLTfn5R2eV4k8bIpfoSDtMPHm-azuQnuQ","e": "AQAB","kty": "RSA","alg": "RS256"
},{
"kty": "RSA","n": "nQPB_WqGG18pjGSFGQwRLcZkcRojHHweN27mV1oTNeeH2quq5NvWibLEheiukVP60nXcGNpkP_PaycYahEfvAnJGLX_IscGAOJ67WWFs4M8wXHH6g2mTnalcAYgmpN1QDMVgz4NcWISXNTR-8FZfWgFN4LDZgK4f0wXOaJlh_Bzh-plPLJQUXyY7mZTEVsH8X3wg2fvV0Hxj_HudjgFlYPdDri1Oi4vI0wiKV4nJCRZ-INH3OIvPl-05WVjZ-XTSXdNjLNx35NM2Npcrr9VpZ8Xeg7pr0wjamqd_07xfEAdtFxsN6Ay6Ecz3k0onQP-6SLRCGLrMAxifziivmmafCQ","kid": "7f548f6708690c21120b0ab668caa079acbc2b2f","alg": "RS256"
}
]
}
------------------------------------------
Output:
-------
X509 Public Key :
-----BEGIN PUBLIC KEY-----
MIIBejANBgkqhkiG9w0BAQEFAAOCAWcAMIIBYgKCAVh6dy92b0dueElyU0VudGEw
QnhzUzRHbXIwdDEwNmlDam1JSzZuY2lVdXBwY1VyRFRLVDN0M3NaTHNaNUhsK1dG
emtHcG85dTZqV2ozdWw2NGhjcGdGVFJKazV4SVVuYktaMjB6YS9JUmZXRFRacWha
M0xxZUhxQlFhdzBCZWtVeklDc3c2SXAxKzNrZzNRb1F5b1U5NzNPczZNQWZwT1A3
U2FmY0hwL3RUUm9NRkFjN1IzQVdaZENtbE1tdyt4WUxUVDNVZEkyaHljWGJUWU9p
dEZrQ1RZTzRwTDYySUJ0NWlubE91cDRkazBId2F6QnAwek1ZeHliNDl5MWdxaC9y
ZnBzd1RTQTdsVnp5U3lVOVNvWEFXRkhaVzRyUXJoaVh3TE9QejhjeW5yNjdsRTUv
RDV0MFhoTFRmbjVSMmVWNGs4YklwZm9TRHRNUEhtK2F6dVFudVE9PQIEQVFBQg==
-----END PUBLIC KEY-----
*/
public class RSAPublicKeyFromModulus {
public static void main(String[] args) {
String modulusStr = "zw_voGnxIrSEnta0BxsS4Gmr0t106iCjmIK6nciUuppcUrDTKT3t3sZLsZ5Hl-WFzkGpo9u6jWj3ul64hcpgFTRJk5xIUnbKZ20za_IRfWDTZqhZ3LqeHqBQaw0BekUzICsw6Ip1-3kg3QoQyoU973Os6MAfpOP7SafcHp_tTRoMFAc7R3AWZdCmlMmw-xYLTT3UdI2hycXbTYOitFkCTYO4pL62IBt5inlOup4dk0HwazBp0zMYxyb49y1gqh_rfpswTSA7lVzySyU9SoXAWFHZW4rQrhiXwLOPz8cynr67lE5_D5t0XhLTfn5R2eV4k8bIpfoSDtMPHm-azuQnuQ";
String exponentStr = "AQAB";
byte[] modulusBytes = base64URLDecode(modulusStr);
byte[] exponentBytes = base64URLDecode(exponentStr);
try {
KeyFactory keyFactory = KeyFactory.getInstance("RSA");
PublicKey publicKey = keyFactory.generatePublic(new RSAPublicKeySpec(new BigInteger(modulusBytes),new BigInteger(exponentBytes)));
X509EncodedKeySpec encodedKeySpec = new X509EncodedKeySpec(publicKey.getEncoded());
String x509EncodedKeyStr = new String(Base64.getEncoder().encode(encodedKeySpec.getEncoded()));
String prefix = "-----BEGIN PUBLIC KEY-----";
String newline = System.getProperty("line.separator") ;// "\r\n";
String suffix = "-----END PUBLIC KEY-----";
StringBuilder result = new StringBuilder();
// prefix + newline + x509EncodedKeyStr + newline + suffix
result.append(prefix);
result.append(newline);
for(int i=0;i<x509EncodedKeyStr.length();) {
String temp = x509EncodedKeyStr.substring(i,i+64);
result.append(temp);
result.append(newline);
i = i+64;
}
result.append(suffix);
System.out.println("X509 Public Key :");
System.out.println(result);
} catch (NoSuchAlgorithmException e) {
e.printStackTrace();
} catch (InvalidKeySpecException e) {
e.printStackTrace();
}
}
/**
* <p> https://datatracker.ietf.org/doc/html/rfc7515#appendix-C </p>
* @param base64URlEncodedString
* @return
*/
private static String base64URLEncode(byte[] base64URlEncodedString) {
String tempResult = new String(base64URlEncodedString);
tempResult = tempResult.split("=")[0];
tempResult = tempResult.replace("+","-");
tempResult = tempResult.replace("_","/");
return tempResult;
}
/**
* <p> https://datatracker.ietf.org/doc/html/rfc7515#appendix-C </p>
*
* @param base64URLEncodedString
* @return
*/
private static byte[] base64URLDecode(String base64URLEncodedString) {
int size = base64URLEncodedString.length();
String tempResult = base64URLEncodedString;
tempResult = tempResult.replace("-","+");
tempResult = tempResult.replace("_","/");
int padding = size % 4;
switch (padding) {
case 0:
break;
case 2:
tempResult = tempResult.concat("==");
break;
case 3:
tempResult = tempResult.concat("=");
break;
default:
throw new IllegalArgumentException("Invalid base64urlencoded string");
}
return tempResult.getBytes();
}
}
,
这是 Crystal-lang 中的一个非常简化的实现
require "base64"
pp! jwk = {
"n": "3aOynmXd2aSH0ZOd0TIYd5RRaNXhLW306dlYw26nMp6QPGaJuOeMhTO3BO8Zt_ncRs4gdry4mEaUOetCKTUOyCCpIM2JAn0laN_iHfGKTYsNkjr16FiHWYJmvNJ1Q1-XXjWqNNKMFIKHKtMrsP2XPVD6ufp-lNQmt4Dl0g0qXJ4_Y_CKuP-uSlFWZuJ_0_2ukUgevvKtOZNcbth0iOiFalBRDr-2i1eNSJWOknEphy7GRs-JGPboTdHC7A3b-0dVFGMEMJFhxcEJHJgLCsQGdYdkphLJ5f21gCNdhp3g16H3Cqts2KTXgO4Rr8uhwZx5yiUjTuizD9wc7uDso4UJ7Q","kid": "b6f8d55da534ea91cb2cb00e1af4e8e0cdeca93d","alg": "RS256","e": "AQAB"
}
mod_hex = Base64.decode(jwk["n"]).hexstring
exp_hex = Base64.decode(jwk["e"]).hexstring
pub_key_hex = "30820122300D06092A864886F70D01010105000382010F003082010A0282010100" + mod_hex + "0203" + exp_hex
pp! pub_key = Base64.encode(String.new(pub_key_hex.hexbytes))