在Java中将RSA密钥对象导出为XML

时间:2011-03-03 09:15:52

标签: java xml encryption rsa key

我在Java中成功运行RSA加密/解密。这就是我生成密钥的方式。

        ObjectOutputStream oos = new ObjectOutputStream(new FileOutputStream(path));
        KeyPairGenerator kpg = KeyPairGenerator.getInstance("RSA");
        kpg.initialize(1024);
        KeyPair keypair = kpg.generateKeyPair();
        oos.writeObject(keypair);

但现在我需要将我的系统与.Net代码集成。是否可以按以下格式将此KeyPair对象导出为XML(因为.Net代码只能接受XML格式的密钥):

<RSAKeyValue>
    <Modulus>.....</Modulus>
    <Exponent>......</Exponent>
    <P>.....</P>
    <Q>....</Q>
    <DP>.......</DP>
    <DQ>......</DQ>
    <InverseQ>.........</InverseQ>
    <D>........</D>
</RSAKeyValue>

3 个答案:

答案 0 :(得分:14)

试试这个:

// key pair is in 'kp'
KeyFactory kf = KeyFactory.getInstance("RSA");
RSAPrivateCrtKeySpec ks = kf.getKeySpec(
    kp.getPrivate(), RSAPrivateCrtKeySpec.class);
System.out.println("<RSAKeyValue>");
System.out.println("    <Modulus>" + ks.getModulus() + "</Modulus>");
System.out.println("    <Exponent>" + ks.getPublicExponent() + "</Exponent>");
System.out.println("    <P>" + ks.getPrimeP() + "</P>");
System.out.println("    <Q>" + ks.getPrimeQ() + "</Q>");
System.out.println("    <DP>" + ks.getPrimeExponentP() + "</DP>");
System.out.println("    <DQ>" + ks.getPrimeExponentQ() + "</DQ>");
System.out.println("    <InverseQ>" + ks.getCrtCoefficient() + "</InverseQ>");
System.out.println("    <D>" + ks.getPrivateExponent() + "</D>");
System.out.println("</RSAKeyValue>");

这适用于内部使用“CRT”表示的所有RSA密钥对,并允许导出;这是JDK默认使用您显示的代码生成的密钥对的情况。

(这里我打印出System.out上的密钥,而不是将其写入文件,但你明白了。)

答案 1 :(得分:4)

Thomas Pornin的解决方案基本上是正确的,但对我来说并不适用,因为这些方法,例如getModulus(),返回BigInteger,产生数字字符串,而标准的.Net XML格式使用Base64编码的字节。

我使用&#34; getModulus()。toByteArray()&#34;得到字节。然后我需要修剪数组的第一个元素(Exponent除外),因为它有一个不需要的零字节。 (我认为因为BigInteger已经签名,所以它会添加一个额外的字节,因此前导位可以指示符号)。

我已经在GitHub上发布了the code

主要是:

static String getPrivateKeyAsXml(PrivateKey privateKey) throws Exception{
    KeyFactory keyFactory = KeyFactory.getInstance(KEY_ALGORITHM);
    RSAPrivateCrtKeySpec spec = keyFactory.getKeySpec(privateKey, RSAPrivateCrtKeySpec.class);
    StringBuilder sb = new StringBuilder();

    sb.append("<RSAKeyValue>" + NL);
    sb.append(getElement("Modulus", spec.getModulus()));
    sb.append(getElement("Exponent", spec.getPublicExponent()));
    sb.append(getElement("P", spec.getPrimeP()));
    sb.append(getElement("Q", spec.getPrimeQ()));
    sb.append(getElement("DP", spec.getPrimeExponentP()));
    sb.append(getElement("DQ", spec.getPrimeExponentQ()));
    sb.append(getElement("InverseQ", spec.getCrtCoefficient()));
    sb.append(getElement("D", spec.getPrivateExponent()));
    sb.append("</RSAKeyValue>");

    return sb.toString();
}

static String getElement(String name, BigInteger bigInt) throws Exception {
    byte[] bytesFromBigInt = getBytesFromBigInt(bigInt);
    String elementContent = getBase64(bytesFromBigInt);
    return String.format("  <%s>%s</%s>%s", name, elementContent, name, NL);
}

static byte[] getBytesFromBigInt(BigInteger bigInt){
    byte[] bytes = bigInt.toByteArray();
    int length = bytes.length;

    // This is a bit ugly.  I'm not 100% sure of this but I presume
    // that as Java represents the values using BigIntegers, which are
    // signed, the byte representation contains an 'extra' byte that
    // contains the bit which indicates the sign.
    //
    // In any case, it creates arrays of 129 bytes rather than the
    // expected 128 bytes.  So if the array's length is odd and the
    // leading byte is zero then trim the leading byte.
    if(length % 2 != 0 && bytes[0] == 0) {
        bytes = Arrays.copyOfRange(bytes, 1, length);
    }

    return bytes;
}

static String getBase64(byte[] bytes){
    return Base64.getEncoder().encodeToString(bytes);
}

答案 2 :(得分:0)

您可以使用某种形式的XMLObjectOutputStream,使其输出为XML而不是here中的专有二进制格式。