提问者:小点点

如何获得RSA公钥和私钥,并获得明文的模和指数?


我对密码学有点陌生,一个要求需要我

"创建或检索用户的RSA-OAEP密钥。对于设备,每个用户应该有一个公钥/私钥对"

并以这种形式将其发送到服务器:

{"modulus":"qMBRpdYrAy5aMmo31NErUizh5sbweguSmh4wlK6uJEIDl+kwTlROnE34KOFExeTbJSX0WygPi+vWl0yNq7buIMUKpytossAAWut5khO3CQJxTk7G2gnEPNUUXHiExGgNrLzcSLv8YIlfVALhoRWyC67KOL+a+3taNq3h+BHeWhM=","exponent":"AQAB"}

我尝试使用OpenSSL命令生成RSA公钥/私钥对,结果如下:

公钥(为了安全起见,将值更改了一点):

ssh-rsa AAAAB3NzaC1yc2EAAAADAQABAAABAQC4NMwqhswK6Py+N1OlPBn3JelqEdZ8YwSn4j1Kvp5HK+pS/5gcABkx/89buDhhtKvZ8mfSNkhKHU2WuBPIikGAvEKbbbQ8DHKubHa07X1xTgY+qMyxTLaHnaEC6oOi6BixPfo6QAm+SnqxBWuvDPBzGqSS+/p5r7aydLBAlkoZ+DzpBbAHIIK72c+xqCL8oOFz4QqsVMakdKQ+fVdM1e8U2ak4ygHOleqJgcl8+xH7TFJsvUOfsgsMlz2FmNXWhCYUdOKglP8ZUVMuiAW9KEjAFta4xEf3bBGRDYss7mLQF5/RHbpzYeNwQ1RVp8bhTumpQytqJZshUj8DA3oqMFUr xyz@xyz.xyz

私钥(为了安全起见,将值更改了一点):

    -----BEGIN RSA PRIVATE KEY-----
Proc-Type: 4,ENCRYPTED
DEK-Info: AES-128-CBC,67A126EE3266B641AF4AC698AA18FE01

3w4GI7HfymD0A8+LokUbsIiI50CJfszBjO5PR4tW532ccKVMQRKywBMANUWx5B66
Mkr/uY9lH8mJOhWrwwiXCUFKMtDGcC06NHbIWIAu/TP85TidwhtGABqSYkjh+8Hm
SlwTMe2xjPq+X8Xc8+vW6Ng8mvXST0Ezz4DiTKA9yeH0eVxIQ4WIpvO7X9793xSD
FoXQ5sCY7Mr6GBNxln4f9cy5BwEsEGa86dtzXVkK8Gu8xRy28GE4W7D6/eNkgl6B
ovEuuC7IDibP/qrGAq/Nxdrl6QrnFTd2FcQfbR4jeArC+IaeXAxXgqhhTJH9ySk8
ZnEaMIruEGvRbChwzlvqS/sCMcS8eoD22Di94Gmkv4thSIWuk1MTRMTKFTMAgVOb
8shCKgCR5FJXjPHV8sUhIwk4TrQWX70fWM3QmBq3ue2AnGonWXhzXQU7jB36zATm
dhpsHZ2/80BuB/hMnFJpsjcYU16pm9BunSMs7tyMW3X3F91x6liC3j2ItUjEkPME
P6eZE2KDM+QxlDLfebL+bGMN6rYvEmfvKo44nwNIMnJM9J3ZYNM9KGt87B4loVwn
TeWIGrCQ9SRCpiAVbZj+M9DDDuqxSoA0wxSDrcYjWt8trzS20AWj7lsxBQgUvpBX
nuFAQgMgT4DK9X2z9ESplXi/l2uZ0iDBTN4SEHI3oR3ar2rWSjoQrTTfOg7cYlF1
ewLR6toCEEvturC4vLyWyrDIu3P/jiSz6eiSTeI9W02rQ/qILUrouKx1LwviIKR2
OGQnkzm3iiNq0jykzObwCsDLuY6rA4nv/ZBsjLDWB34gveKSzOrtx4dzqmtcv0Kq
ndua6xdaPmpV3n4slRD1PxSwNgKb4qwlYuQMPKLhCXUq4yG59IOoH7yfxS5UZ7wa
yndGMLMPmylcHDLX02U90X3feUcC9IiE7z6pOILy4uC28Z2X5KYjoK07pwA+5lNt
9RvryaK4IXysJZ5zqsBUaeYlqqBATcEPYn3YXbT5cSaxkv4lI36g6iG7/QmA3PGt
1l57kBW2xnUSrqm5XtZZMrsSu2iZ9Hiuh73SRkODjg7ToEMtwLECkN1TRL9PVEQj
QHAxauWleC+2yB0+1XH7/CywkYk2HxeNQJJDsWU+XJM/RFGFNV481LwfU0Hw58sZ
ai4R2LmDDoy4wwtcQGkY13ZHT8h/jxP4/Sr36GJdVkhhUCDnpFfdNHebgflj0PQg
eTHVY/6GqfnKvneOGtDRR4EkBFopUV5OLzuRX3z/rlHRV1iPCaqhooL9XO320JYq
xY5YMWq7tvgzE5jtqo65AwO8WWs12NTzG1KRhcXCyYE4Da1T6k7l8++MOeVGZy1v
qDxEddTfiAzIvCme2lEiYOb2/UUNAhEM+Ave/lfWmirW5dSAOppZenHUnuZh9eVd
iFVswrAxcd4BqA5GGczzu9EIqdzpspTrnG3hxOf+tXEXf3TTAH/sTVfQGQHO1iRd
UTh9FGgHk3WMswBnYyfpbSOR8Mghab966RRYP2xBCVJEvCymYuUE11x6vsvQFpGS
X2SEhOpzgINuKZRTuVmhK1oXRt3BZO8yQ13t+wtQCP6a8azS+Sc436aDqBlWQfJ2
-----END RSA PRIVATE KEY-----

如何从这些数据中提取服务器期望的模数和指数形式?


共1个答案

匿名用户

您可以简单地从RFC4253中获取格式,但您需要从RFC4251中找出mpint(多预置整数)格式。

这是Java的事情(Java),仅仅是因为JSch库太可怕了。

package nl.owlstead.crypto;

import java.math.BigInteger;
import java.nio.ByteBuffer;
import java.nio.charset.StandardCharsets;
import java.security.KeyFactory;
import java.security.NoSuchAlgorithmException;
import java.security.interfaces.RSAPublicKey;
import java.security.spec.InvalidKeySpecException;
import java.security.spec.RSAPublicKeySpec;
import java.util.Arrays;
import java.util.Base64;
import java.util.Base64.Encoder;

public class SSHPublicKeyDecoder {

    private static byte[] SSH_RSA_HEADER = "ssh-rsa".getBytes(StandardCharsets.US_ASCII);

    private static RSAPublicKey decodeRSAPublicKey(byte[] encodedRSAPublicKey)
            throws NoSuchAlgorithmException, InvalidKeySpecException {
        ByteBuffer buf = ByteBuffer.wrap(encodedRSAPublicKey);
        
        byte[] header = getSSHEncodedValueFromBuffer(buf);
        if (!Arrays.equals(header, SSH_RSA_HEADER)) {
            throw new IllegalArgumentException("Not an RSA public key");
        }
        
        byte[] eDataSigned = getSSHEncodedValueFromBuffer(buf);
        BigInteger e = new BigInteger(eDataSigned);
        
        byte[] nDataSigned = getSSHEncodedValueFromBuffer(buf);
        BigInteger n = new BigInteger(nDataSigned);
        
        KeyFactory rsaKeyFactory;
        try {
            rsaKeyFactory = KeyFactory.getInstance("RSA");
        } catch (NoSuchAlgorithmException ex) {
            throw new IllegalStateException(
                    "KeyFactory should exist for RSA", ex);
        }
        
        RSAPublicKeySpec spec = new RSAPublicKeySpec(n, e);
        return (RSAPublicKey) rsaKeyFactory.generatePublic(spec);
    }

    private static byte[] getSSHEncodedValueFromBuffer(ByteBuffer buf) {
        int size = buf.getInt();
        if (size < 0) {
            throw new IllegalArgumentException("Bad SSH encoded value in format");
        }
        byte[] data = new byte[size];
        buf.get(data);
        return data;
    }

    private static byte[] toUnsigned(BigInteger value) {
        if (value.compareTo(BigInteger.ZERO) <= 0) {
            throw new IllegalArgumentException("Negative numbers cannot be encoded as unsigned integers");
        }
        
        if (value.equals(BigInteger.ZERO)) {
            return value.toByteArray();
        }
        
        final byte[] signedBigEndian = value.toByteArray();
        
        if (signedBigEndian[0] == 0x00) {
            return Arrays.copyOfRange(signedBigEndian, 1, signedBigEndian.length);
        }
        
        return signedBigEndian;
    }

    private SSHPublicKeyDecoder() {
        
    }
    
    public static void main(String[] args) throws Exception {
        String[] parts = args[0].split("\\s+");
        String part2 = parts[1];
        byte[] encodedRSAPublicKey = Base64.getDecoder().decode(part2);
        
        RSAPublicKey pubKey = decodeRSAPublicKey(encodedRSAPublicKey);
        String format = encodeServerPublicKey(pubKey);
        
        System.out.println(format);
    }

    private static String encodeServerPublicKey(RSAPublicKey pubKey) {
        byte[] nData = toUnsigned(pubKey.getModulus());
        byte[] eData = toUnsigned(pubKey.getPublicExponent());
        
        Encoder base64Encoder = Base64.getEncoder();
        String format = String.format(
                "{\"modulus\":\"%s\",\"exponent\":\"%s\"}%n",
                base64Encoder.encodeToString(nData),
                base64Encoder.encodeToString(eData));
        return format;
    }
}