8

I am successfully running RSA encryption/decryption in Java. This is how I generated the key.

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

But now I need to integrate my system with .Net code. Is it possible to export this KeyPair object into XML in the following format(as that .Net code can only accept keys in XML format):

<RSAKeyValue>
    <Modulus>.....</Modulus>
    <Exponent>......</Exponent>
    <P>.....</P>
    <Q>....</Q>
    <DP>.......</DP>
    <DQ>......</DQ>
    <InverseQ>.........</InverseQ>
    <D>........</D>
</RSAKeyValue>
dvl
  • 741
  • 2
  • 8
  • 19
  • 1
    Storing a keypair this way with no encryption (thus leaving your private key exposed) is a really, really bad idea. – Hut8 Mar 03 '11 at 09:23
  • Create your own wrapper class for the keypair that implements the Serializable interface. Override the `readObject()`, `writeObject()`, and `readObjectNoData()` methods. – President James K. Polk Mar 03 '11 at 13:00
  • @bowenl2 Can you suggest any good way to integrate with other platforms? – dvl Mar 05 '11 at 07:18

3 Answers3

15

Try this:

// 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>");

This will work for all RSA key pairs which internally use the 'CRT' representation, and allow export; this is the case for the key pairs that the JDK will generate by default with the code you show.

(Here I print out the key on System.out instead of writing it to a file, but you get the idea.)

CodesInChaos
  • 106,488
  • 23
  • 218
  • 262
Thomas Pornin
  • 72,986
  • 14
  • 147
  • 189
4

Thomas Pornin's solution is essentially correct but didn't work for me because the methods, e.g. getModulus(), return BigInteger which results in a numeric string, whereas the standard .Net XML format uses Base64 encoded bytes.

I used "getModulus().toByteArray()" to get the bytes. Then I needed to trim the first element of the array (except for Exponent) because there's an unwanted zero byte. (I presume because BigInteger is signed it adds an extra byte so the leading bit can indicate sign).

I've posted the code on GitHub.

The main bit is:

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);
}
codybartfast
  • 7,323
  • 3
  • 21
  • 25
  • is there no way to do place them in between RSAKeyValue tags other than doing this manually, for example automatically convert to .NET format or something similar? – Gravity Mar 12 '16 at 15:37
  • @Gravity I couldn't find one. The whole exercise made me feel I was missing something obvious. – codybartfast Mar 13 '16 at 07:46
  • I couldn't find one either, I saw your code on github, I hope it works, I am trying to do the exact same thing as you are – Gravity Mar 14 '16 at 09:51
  • @codybarfast The RsaCryptoServiceProvider generates keys really slowly when ran on Android via Xamarin. I've been trying to figure out how to generate equivalent keys and get them into XML for our existing .NET system and this was the trick. Thank you! I'd like to port this in the .NET version I wrote as a NuGet package as I haven't found anything else that worked. – dkoch74 Jul 21 '17 at 22:57
  • 1
    @codybarfast Here it is ported for .NET https://github.com/dkoch74/.NetDroidUtils/blob/master/DotNetDroidUtils/Cryptography.cs – dkoch74 Jul 22 '17 at 22:13
  • 1
    @dkoch74 Thanks for sharing the code. I've not used Xamarin before so it's an unusual mix of .Net and Java to my eyes :-) – codybartfast Aug 04 '17 at 05:10
0

You can have some form of XMLObjectOutputStream such that it outputs to XML instead of a proprietary binary format as in here.

Suraj Chandran
  • 24,433
  • 12
  • 63
  • 94