无法在JAVA中用PublicKey加密RSAOAEP384

问题描述 投票:0回答:1

美好的一天! 我有一个有效的 JavaScript 脚本,可以从服务器获取令牌。我需要在 Java 中实现类似的东西。本质是我们从服务器接收到一个“-----BEGIN PUBLIC KEY.....”形式的字符串,我们将其转换为RSAAublicKey并用它用RSAOAEP384加密一个密码并发送给服务器,但我的实现(使用 X509EncodedKeySpec)从服务器获取 system.error 响应。 我怀疑一切都是因为我尝试将字符串转换为 PublicKey 的方式,模数总是相同的数字,但它与脚本中获得的模数不同,指数始终是 65537,它是那里和这里都是一样的,但据我了解,这是标准的,在Java中将字符串转换为公钥可能有一些特殊之处?

工作中的JS:

const pubKey = KEYUTIL.getKey(pubkeyResp.pubKey)
    const encodedPassword = encodeURIComponent(password);
    let encryptedPassword = "";

    for (let i = 0; i < encodedPassword.length / 270; i++) {
        let currntValue = encodedPassword.substr(i * 270, 270);
        let encryptValueCurrent = KJUR.crypto.Cipher.encrypt(currntValue, pubKey, "RSAOAEP384");
        encryptedPassword = encryptedPassword == "" ? "" : encryptedPassword + "00000001";
        encryptedPassword = encryptedPassword + hextob64(encryptValueCurrent);
    }

我的JAVA代码:

public static RSAPublicKey createRsaPublicKey(String pubKey) {
        PemObject pemObject;
        try (PemReader pemReader = new PemReader(new StringReader(pubKey))) {
            pemObject = pemReader.readPemObject();
        }
        byte[] pemBytes = pemObject.getContent();
        KeyFactory keyFactory = KeyFactory.getInstance("RSA");
        X509EncodedKeySpec keySpec = new X509EncodedKeySpec(pemBytes);
        return (RSAPublicKey) keyFactory.generatePublic(keySpec);
    }

    public static String encryptPubKeyWithPassword(String pubKey, String password, String version) {
        try {
            String encodedPassword = URLEncoder.encode(password, StandardCharsets.UTF_8);
            RSAPublicKey publicKey = createRsaPublicKey(pubKey);
            StringBuilder encryptedPasswordBuilder = new StringBuilder();
            Cipher cipher = Cipher.getInstance("RSA/ECB/OAEPWithSHA-384AndMGF1Padding");
            cipher.init(Cipher.ENCRYPT_MODE, publicKey);
            for (int i = 0; i < encodedPassword.length(); i += 270) {
                String currentValue = encodedPassword.substring(i, Math.min(i + 270, encodedPassword.length()));
                if (encryptedPasswordBuilder.length() > 0) encryptedPasswordBuilder.append("00000001");
encryptedPasswordBuilder.append(Base64.getEncoder().encodeToString(cipher.doFinal(currentValue.getBytes(StandardCharsets.UTF_8))));
            }
            return encryptedPasswordBuilder + version;
}

公钥:

-----BEGIN PUBLIC KEY-----\nMIIBojANBgkqhkiG9w0BAQEFAAOCAY8AMIIBigKCAYEArjLfZpVakwA2ETwGjQLVA+5e5QYDNSu2V2fWPCveuxMF72EcKB/Mw+cx6Xe/AFwdysu3DBefH8FoRsL4HmCqilrdbTrGqiLvZ0ULd7vh8Y7bBT396hfro7CkS+AtauF9MxGLXdRN1ALpNoD2MDYKy/4WT7BKy0EAt2BxdtN841gtDpYTsC0Qv9+vEcI9PG2NI7nWZORAOsktZA1xpsMBTluDizsVcjAr0hKBlDpPhs+UWeugaiq7bQm5GblSvaYHsiRAJOse5bba5gRespgwFWGBiv/EdDSQ8WLB0RSfj7zWOQl6XlxPXkzKGtbXW7kP41Q5CtJmlRXaEaAzKRygUyxNYbdtAm/P0aY3wS1b/soNqTBnirFuUZlo8VVD2Cy/xPs/it+GVnhtztrt1VsgY0K/XI2YmtJyQc1VJsgIHtJkUExwp5s9bn96gD/UxvvDS5kW4IbKfTMsWdoBcDzkCYXgNvXN4DfuzKsu7Z4Q6V0iqXOSUnioBN2CsHPjgj3vAgMBAAE=\n-----END PUBLIC KEY-----

来自 JS 的 RSAKey:

RSAKey {
  n: BigInteger {
    '0': 58867183,
    '1': 137037630,
    '2': 145229021,
    '3': 121185575,
    '4': 157098665,
    '5': 249159950,
    '6': 248294190,
    '7': 215876478,
    '8': 98580213,
    '9': 63848600,
    '10': 165282160,
    '11': 131281605,
    '12': 115377866,
    '13': 204781969,
    '14': 265602811,
    '15': 133670915,
    '16': 127614318,
    '17': 84199178,
    '18': 136237668,
    '19': 89287808,
    '20': 41042381,
    '21': 148474285,
    '22': 54706012,
    '23': 223719942,
    '24': 231660269,
    '25': 140863366,
    '26': 188713695,
    '27': 46922831,
    '28': 22365144,
    '29': 85563023,
    '30': 126529902,
    '31': 14324486,
    '32': 224132810,
    '33': 174291986,
    '34': 40882129,
    '35': 102463184,
    '36': 5450829,
    '37': 53645770,
    '38': 98177440,
    '39': 220621137,
    '40': 55851274,
    '41': 96178430,
    '42': 169531095,
    '43': 83223756,
    '44': 159014492,
    '45': 198009744,
    '46': 18128783,
    '47': 253111325,
    '48': 74724496,
    '49': 135835644,
    '50': 137368929,
    '51': 4582185,
    '52': 95869670,
    '53': 38711790,
    '54': 129115200,
    '55': 86760032,
    '56': 163125689,
    '57': 44807888,
    '58': 166436970,
    '59': 141359429,
    '60': 26491471,
    '61': 45949224,
    '62': 185954864,
    '63': 95959219,
    '64': 113443150,
    '65': 104912666,
    '66': 3852589,
    '67': 224808516,
    '68': 227353529,
    '69': 203674566,
    '70': 266317585,
    '71': 184733963,
    '72': 219059731,
    '73': 130954626,
    '74': 7435987,
    '75': 68160374,
    '76': 263211723,
    '77': 213901668,
    '78': 103822858,
    '79': 244541455,
    '80': 72209410,
    '81': 18396637,
    '82': 182549811,
    '83': 79561430,
    '84': 195276964,
    '85': 266248574,
    '86': 249234749,
    '87': 197009176,
    '88': 121965431,
    '89': 178401014,
    '90': 225262278,
    '91': 178824621,
    '92': 49815136,
    '93': 202802284,
    '94': 202874655,
    '95': 212646768,
    '96': 251681821,
    '97': 52336507,
    '98': 265077735,
    '99': 101827201,
    '100': 185796079,
    '101': 63094251,
    '102': 106391510,
    '103': 3363515,
    '104': 241100038,
    '105': 2969662,
    '106': 20711053,
    '107': 154141537,
    '108': 258381146,
    '109': 713517,
    t: 110,
    s: 0
  },
  e: 65537,
  d: null,
  p: null,
  q: null,
  dmp1: null,
  dmq1: null,
  coeff: null,
  isPublic: true,
  isPrivate: false
}

我的RSA公钥:

2024-04-09T18:08:10.252+03:00  INFO 2876 --- [test-token] [nio-8080-exec-5] c.e.test-token.ClientUtil     : Sun RSA public key, 3072 bits
  params: null
  modulus: 3953226311687758595392111994184029919105572353883850175466519164541873393823923732214781564401042342584241029257336199908139796366497541069327180034755296728788397456817216176094110417813365270437034224139108683316529614756336144052543998303152918929941819677301941145250628636498495113956820927519270970709149908767271627169825801967465740997006710347979917608611090978116377701630380908449017037401297495565187968809576663765854765455615940054260715071874403034550696249783288415681844605115585869191283022766347390049238750984142454221808268833091744962848703466396221134103055688292413446595077216756761727193028024470215530244768050010826877173235699637691655955908473771170645074319262049197935623437229535078523321083906712227132222561047274756830518955068353360103889423586339385295309120615979992652763536784371821993620190106467929309258100886903422590542463117376022420894267089887465438053796937919013636309925359
  public exponent: 65537
javascript java rsa public-key oaep
1个回答
0
投票

问题是由 MGF1 摘要的不同值引起的:SunJCE 提供程序仅将 SHA-384 用于

RSA/ECB/OAEPWithSHA-384AndMGF1Padding
的 OAEP 摘要,而默认 SHA1 摘要应用于 MGF1 摘要。
然而,在 JavaScript 方面,对于
RSAOAEP384
,SHA-384 用于 both 摘要、OAEP 和 MGF1 摘要。这导致了两个代码的不兼容。

作为修复,必须使用

OAEPParameterSpec
:

将两个摘要显式设置为 SHA-384
...
Cipher cipher = Cipher.getInstance("RSA/ECB/OAEPPADDING");
OAEPParameterSpec oaepParameterSpec = new OAEPParameterSpec("SHA-384", "MGF1", MGF1ParameterSpec.SHA384, PSource.PSpecified.DEFAULT);
cipher.init(Cipher.ENCRYPT_MODE, publicKey, oaepParameterSpec);
...

或者,可以使用 BouncyCastle 提供程序(例如,如果 BouncyCastle 已被应用)。这会将 OAEP 和 MGF1 摘要设置为指定值:

...
Security.addProvider(new BouncyCastleProvider());           
Cipher cipher = Cipher.getInstance("RSA/NONE/OAEPWithSHA384AndMGF1Padding");
cipher.init(Cipher.ENCRYPT_MODE, publicKey);
...

encryptPubKeyWithPassword()
的完整代码:

public static String encryptPubKeyWithPassword(String pubKey, String password, String version) throws Exception {       
    String encodedPassword = URLEncoder.encode(password, StandardCharsets.UTF_8);
    RSAPublicKey publicKey = createRsaPublicKey(pubKey);
    Cipher cipher = Cipher.getInstance("RSA/ECB/OAEPPADDING");
    OAEPParameterSpec oaepParameterSpec = new OAEPParameterSpec("SHA-384", "MGF1", MGF1ParameterSpec.SHA384, PSource.PSpecified.DEFAULT);
    cipher.init(Cipher.ENCRYPT_MODE, publicKey, oaepParameterSpec);
    StringBuilder encryptedPasswordBuilder = new StringBuilder();
    for (int i = 0; i < encodedPassword.length(); i += 270) {
        String currentValue = encodedPassword.substring(i, Math.min(i + 270, encodedPassword.length()));
        if (encryptedPasswordBuilder.length() > 0) encryptedPasswordBuilder.append("00000001");
        encryptedPasswordBuilder.append(Base64.getEncoder().encodeToString(cipher.doFinal(currentValue.getBytes(StandardCharsets.UTF_8))));
    }
    return encryptedPasswordBuilder.toString();
} 

另一个困惑是

jsrsasign
库的 BigInteger 值是否包含正确的模数(如下所示的情况)。
要获取模数的参考值,可以将 PEM 密钥加载到 ASN.1 解析器中,例如这里。或者,可以在
X509EncodedKeySpec
实例中使用
RSAPublicKey
导入密钥后确定模数。已发布的公共 PEM 密钥的模数为(十进制):

3953226311687758595392111994184029919105572353883850175466519164541873393823923732214781564401042342584241029257336199908139796366497541069327180034755296728788397456817216176094110417813365270437034224139108683316529614756336144052543998303152918929941819677301941145250628636498495113956820927519270970709149908767271627169825801967465740997006710347979917608611090978116377701630380908449017037401297495565187968809576663765854765455615940054260715071874403034550696249783288415681844605115585869191283022766347390049238750984142454221808268833091744962848703466396221134103055688292413446595077216756761727193028024470215530244768050010826877173235699637691655955908473771170645074319262049197935623437229535078523321083906712227132222561047274756830518955068353360103889423586339385295309120615979992652763536784371821993620190106467929309258100886903422590542463117376022420894267089887465438053796937919013636309925359

jsrsasign 使用 jsbn 库来实现

BigInteger
。这会将
BigInteger
internally 的值存储在数组中。实际值不仅仅是数组中各个值的串联。相反,可以按如下方式重建原始值:数组的值将以大端顺序进行十六进制编码,并且十六进制值将以相反的顺序连接(109, 108,..., 1, 0):

'109': 713517      --> 0xae32d
'108': 258381146,  --> 0xf66955a
...
'1': 137037630,    --> 0x82b073e
'0': 58867183,     --> 0x3823def

这给出:

ae32df66955a930036113c068d02d503ee5ee50603352bb65767d63c2bdebb1305ef611c281fccc3e731e977bf005c1dcacbb70c179f1fc16846c2f81e60aa8a5add6d3ac6aa22ef67450b77bbe1f18edb053dfdea17eba3b0a44be02d6ae17d33118b5dd44dd402e93680f630360acbfe164fb04acb4100b7607176d37ce3582d0e9613b02d10bfdfaf11c23d3c6d8d23b9d664e4403ac92d640d71a6c3014e5b838b3b1572302bd21281943a4f86cf9459eba06a2abb6d09b919b952bda607b2244024eb1ee5b6dae6045eb298301561818affc4743490f162c1d1149f8fbcd639097a5e5c4f5e4cca1ad6d75bb90fe354390ad2669515da11a033291ca0532c4d61b76d026fcfd1a637c12d5bfeca0da930678ab16e519968f15543d82cbfc4fb3f8adf8656786dcedaedd55b206342bf5c8d989ad27241cd5526c8081ed264504c70a79b3d6e7f7a803fd4c6fbc34b9916e086ca7d332c59da01703ce40985e036f5cde037eeccab2eed9e10e95d22a973925278a804dd82b073e3823def

如果该值被解释为无符号大端十六进制值,则会产生正确的十进制模数。

但是,实际上不需要了解内部数据处理,因为导入的数据会自动转换为内部格式,并且

BigInteger
函数(
toString()
toRadix()
toByteArray()
,...)返回数据格式正确,如以下 JavaScript 所示:

var bi = new jsbn.BigInteger('3953226311687758595392111994184029919105572353883850175466519164541873393823923732214781564401042342584241029257336199908139796366497541069327180034755296728788397456817216176094110417813365270437034224139108683316529614756336144052543998303152918929941819677301941145250628636498495113956820927519270970709149908767271627169825801967465740997006710347979917608611090978116377701630380908449017037401297495565187968809576663765854765455615940054260715071874403034550696249783288415681844605115585869191283022766347390049238750984142454221808268833091744962848703466396221134103055688292413446595077216756761727193028024470215530244768050010826877173235699637691655955908473771170645074319262049197935623437229535078523321083906712227132222561047274756830518955068353360103889423586339385295309120615979992652763536784371821993620190106467929309258100886903422590542463117376022420894267089887465438053796937919013636309925359');
console.log(bi.toRadix(16))   // hex
console.log(bi.toRadix(10))   // dec
console.log(bi.toString())    // dec
console.log(bi.toByteArray()) // signed array
console.log(bi)               // internal data representation
<script src="https://cdn.jsdelivr.net/npm/[email protected]/index.min.js"></script>

© www.soinside.com 2019 - 2024. All rights reserved.