Import RSA Key from XML

Hi,

I need to import both keys public and private, they were given to me as a XML file, generated by a .NET program.

The private xml has this structure:

<RSAKeyValue>

<Modulus></Modulus>

<Exponent></Exponent>

<P></P>

<Q></Q>

<DP></DP>

<DQ></DQ>

<InverseQ></InverseQ>

<D></D>

</RSAKeyValue>

Someone have any example?

Thanks,

Eddiedu

[536 byte] By [Eddiedua] at [2007-10-3 11:03:35]
# 1
1>use SAX module2>RTFM
rajesh_bhidea at 2007-7-15 13:25:56 > top of Java-index,Security,Cryptography...
# 2
> 1>use SAX moduleWhy? For a small file such as this, DOM would be better.> 2>RTFMUncalled for. I don't know of a manual which shows how to do this. If you know of one then point the OP to it. Then he can 'RTFM'.
sabre150a at 2007-7-15 13:25:56 > top of Java-index,Security,Cryptography...
# 3
I'd agree with using a DOM parser, and extract the modulus and exponent from the XML.If you have the modulus and exponent, you're all set. Look at RSAPublicKeySpec, and RSAPrivateKeySpec, and at KeyFactory.generatePrivate() and .generatePublic().Good luck,Grant
ggaineya at 2007-7-15 13:25:56 > top of Java-index,Security,Cryptography...
# 4

Hi,

Thanks for all tips, but the problem is that the .NET key is base64 encoded, and when i try to decode it I can磘 recover the BigInteger of modulus and exponent, and i didn磘 try with the private key.

I found a link which shows how to convert a key from Java to .NET but i couldn磘 do the opposite any idea? when I try to decode the modulus it brings only weird characters.

The link is:

http://www.codeproject.com/csharp/porting_java_public_key.asp

Any idea?

Thanks,

Eddiedu

Eddiedua at 2007-7-15 13:25:56 > top of Java-index,Security,Cryptography...
# 5

Hi. I'm having the same problem: I need to encode a string using JAVA, and the public key is provided by .NET as follows:

<RSAKeyValue>

<Modulus>pJbz24aBed556dtzQGn7Dn+NmaaEWONSg82HQLn3T2x0DkTQEuU40Shr5jcLTyvwxRxA0HG7NlYE7JEPLoUCKIpdBh5sRonn4ZGgF7jYIhWaHqT4sB7+oCVazdKe5c1EmBwS9c0JJPiU4mPSeVEY4NbRduxakMHp/jjMJe25OamWex8cn3ktEPbe15OWCN9hoTnK/anGQmXkzWL/YDNI7WwXIpCI2hxEt3l0AhkgZecOwTYn08wj7YTei8I15pXYiV1nNuxB5AuZrTVxFIwXYHOonblWoubJa68dAkCr5leNfuIa/JzyBAsHMnXU4aLOlrJgQx5yRdd7n0g1YMF9uw==</Modulus>

<Exponent>AQAB</Exponent>

</RSAKeyValue>

I managed to encode the string using the Cipher class, but tried the "RSA", "RSA/ECB/NoPadding" and "RSA/ECB/PKCS1Padding" and the resulted string wasn't what I expected

For example, the string 'hola' should convert to '2BC19BA2FDD7512F624B647CD153CE061BF5760A505B185DD6DD751B685BB9F5EA1BA4F8963D01FEDB72BE9C13F29F782E7AD87FEFB51B7CBFA6740C4814A8E84A80C2768C5248A6B11BE847388945C318B268C611563C1D7E3758ED5D0086DAC6425D9808F04BEAAC72AB54D623BE530815AC9F562E85F1EF61892B6B91E8A06BC89BA62AB7ED7864AC61C9AF048836DA7C5BB3F98A4FAD20FA46D4ABDA652E645DB3F98B0479BFCFED717F4FC891E768FDBA49BFB95774D84E80BCA235634044F5995674582AF8835B5C74E124704B1B629E9C115B369F61AF699EE128054F15A64743BA4F81024384BC299D83B5A491CA825F9CD3463437297B545502ECE6'

, could anybody help me to achieve this?

Thanks

Here's the class I'm using:

import java.security.interfaces.*;

import java.math.*;

import sun.misc.*;

import java.io.*;

import javax.crypto.Cipher;

import javax.xml.parsers.DocumentBuilder;

import javax.xml.parsers.DocumentBuilderFactory;

import org.w3c.dom.Document;

import org.w3c.dom.Element;

import org.w3c.dom.NodeList;

import org.w3c.dom.Node;

public class RSA implements RSAPublicKey

{

final String rsaPublicKeyAsXMLString = "<RSAKeyValue><Modulus>pJbz24aBed556dtzQGn7Dn+NmaaEWONSg82HQLn3T2x0DkTQEuU40Shr5jcLTyvwxRxA0HG7NlYE7JEPLoUCKIpdBh5sRonn4ZGgF7jYIhWaHqT4sB7+oCVazdKe5c1EmBwS9c0JJPiU4mPSeVEY4NbRduxakMHp/jjMJe25OamWex8cn3ktEPbe15OWCN9hoTnK/anGQmXkzWL/YDNI7WwXIpCI2hxEt3l0AhkgZecOwTYn08wj7YTei8I15pXYiV1nNuxB5AuZrTVxFIwXYHOonblWoubJa68dAkCr5leNfuIa/JzyBAsHMnXU4aLOlrJgQx5yRdd7n0g1YMF9uw==</Modulus><Exponent>AQAB</Exponent></RSAKeyValue>";

final BigInteger modulus;

final BigInteger exponent;

public RSA() throws Exception

{

String modulusAsBase64String = null;

String exponentAsBase64String = null;

DocumentBuilderFactory dbf = DocumentBuilderFactory.newInstance();

DocumentBuilder db = dbf.newDocumentBuilder();

Document document = db.parse(new ByteArrayInputStream(rsaPublicKeyAsXMLString.getBytes("UTF-8")));

Element root = document.getDocumentElement();

// Go down the children looking for our interest nodes

NodeList nodeList = root.getChildNodes();

for (int i = 0; i < nodeList.getLength(); i++)

{

Node node = nodeList.item(i);

if (node.getNodeType() == Node.ELEMENT_NODE)

{

Element element = (Element)node;

String nodeName = element.getNodeName();

if (nodeName.equals("Modulus"))

{

modulusAsBase64String = element.getTextContent();

}

else if (nodeName.equals("Exponent"))

{

exponentAsBase64String = element.getTextContent();;

}

}

}

assert modulusAsBase64String != null;

assert exponentAsBase64String != null;

final BASE64Decoder base64Decoder = new BASE64Decoder();

final byte[] modulusAsBytes = base64Decoder.decodeBuffer(modulusAsBase64String);

final byte[] exponentAsBytes = base64Decoder.decodeBuffer(exponentAsBase64String);

modulus = new BigInteger(1, modulusAsBytes);

exponent = new BigInteger(1, exponentAsBytes);

}

public String getAlgorithm()

{

return "RSA";

}

public String getFormat()

{

return "MS"; // I don't know what the correct format specification should be!

}

public byte[] getEncoded()

{

try

{

return rsaPublicKeyAsXMLString.getBytes("UTF-8");

}

catch (Exception e)

{

return null;

}

}

public BigInteger getPublicExponent()

{

return exponent;

}

public BigInteger getModulus()

{

return modulus;

}

public String codificar(String args)

{

byte[] encryptedData = null;

try

{

Cipher cipher = Cipher.getInstance("RSA");

cipher.init(Cipher.ENCRYPT_MODE, this);

byte[] dateToEncrypt = args.getBytes();

encryptedData = cipher.doFinal(dateToEncrypt);

System.out.println("RSA: " + toHexString(encryptedData).toUpperCase());

cipher = Cipher.getInstance("RSA/ECB/NoPadding");

cipher.init(Cipher.ENCRYPT_MODE, this);

dateToEncrypt = args.getBytes();

encryptedData = cipher.doFinal(dateToEncrypt);

System.out.println("RSA/ECB/NoPadding: " + toHexString(encryptedData).toUpperCase());

cipher = Cipher.getInstance("RSA/ECB/PKCS1Padding");

cipher.init(Cipher.ENCRYPT_MODE, this);

dateToEncrypt = args.getBytes();

encryptedData = cipher.doFinal(dateToEncrypt);

System.out.println("RSA/ECB/PKCS1Padding: " + toHexString(encryptedData).toUpperCase());

}

catch (Exception e)

{

e.printStackTrace();

}

//String a = new String(encryptedData.toString());

//return Arrays.toString(encryptedData);

//return toBase64String(encryptedData, Base64Chars);

return toHexString(encryptedData).toUpperCase();

}

private static final char[] HexChars = {

'0', '1', '2', '3', '4', '5', '6', '7',

'8', '9', 'A', 'B', 'C', 'D', 'E', 'F'

};

public static final String toHexString(byte[] bytes) {

StringBuffer sb = new StringBuffer();

int i;

for (i=0; i < bytes.length; i++) {

sb.append(HexChars[(bytes >> 4) & 0xf]);

sb.append(HexChars[bytes & 0xf]);

}

return new String(sb);

}

private static final char[] Base64Chars = {

'A', 'B', 'C', 'D', 'E', 'F', 'G', 'H',

'I', 'J', 'K', 'L', 'M', 'N', 'O', 'P',

'Q', 'R', 'S', 'T', 'U', 'V', 'W', 'X',

'Y', 'Z', 'a', 'b', 'c', 'd', 'e', 'f',

'g', 'h', 'i', 'j', 'k', 'l', 'm', 'n',

'o', 'p', 'q', 'r', 's', 't', 'u', 'v',

'w', 'x', 'y', 'z', '0', '1', '2', '3',

'4', '5', '6', '7', '8', '9', '+', '/',

'='

};

public static final String toBase64String(byte[] bytes, char[] chars) {

StringBuffer sb = new StringBuffer();

int len = bytes.length, i=0, ival;

while (len >= 3) {

ival = ((int)bytes[i++] + 256) & 0xff;

ival <<= 8;

ival += ((int)bytes[i++] + 256) & 0xff;

ival <<= 8;

ival += ((int)bytes[i++] + 256) & 0xff;

len -= 3;

sb.append(chars[(ival >> 18) & 63]);

sb.append(chars[(ival >> 12) & 63]);

sb.append(chars[(ival >> 6) & 63]);

sb.append(chars[ival & 63]);

}

switch (len) {

case 0:// No pads needed.

break;

case 1: // Two more output bytes and two pads.

ival = ((int)bytes[i++] + 256) & 0xff;

ival <<= 16;

sb.append(chars[(ival >> 18) & 63]);

sb.append(chars[(ival >> 12) & 63]);

sb.append(chars[64]);

sb.append(chars[64]);

break;

case 2:// Three more output bytes and one pad.

ival = ((int)bytes[i++] + 256) & 0xff;

ival <<= 8;

ival += ((int)bytes + 256) & 0xff;

ival <<= 8;

sb.append(chars[(ival >> 18) & 63]);

sb.append(chars[(ival >> 12) & 63]);

sb.append(chars[(ival >> 6) & 63]);

sb.append(chars[64]);

break;

}

return new String(sb);

}

}

Fr3dYa at 2007-7-15 13:25:56 > top of Java-index,Security,Cryptography...
# 6

Please post code using code tags.

Please post only code that compiles.

You write: "...the string 'hola' should convert to 2BC....", but how do you know this? RSA encryption with PKCS1 padding includes a random component which is different every time. PKCS1 padding is the default, so that is what you get when your algorithm parameter to Cipher.getInstance() is "RSA" or "RSA/ECB/PKCS1Padding"; only when "RSA/ECB/NoPadding" is used will the result be the same everytime.

Furthermore, it is rare to encrypt data directly with RSA. There is no way to know how the data was encrypted without looking at the .Net code, not that I'm asking to.

ghstarka at 2007-7-15 13:25:56 > top of Java-index,Security,Cryptography...
# 7

Hi. Sorry for the non-compiling code, I just removed some stuff before copying the text and didn't checked it out.

About the 'hola' string encoding to '2BC...' , it's just because I asked the .NET guy to encode it, so I could compare with the strings I was getting with the JAVA code. I don't know if they use padding or not, here's the .net function:

Private Shared Function EncryptRSA(ByVal sTexto As String) As String

Dim sTextoEnc As String

Dim CspParameters As CspParameters = New CspParameters

CspParameters.Flags = CspProviderFlags.UseMachineKeyStore

Dim rsa As New RSACryptoServiceProvider(CspParameters)

Dim i As Integer

Dim PlainTextBArray As Byte()

Dim CypherTextBArray As Byte()

Dim xmlPublicKey As String

Dim oAcceso As Espec.IAccesoDatos

Try

'Get the public key from the registry

oAcceso = New Espec.AccesoDatos

xmlPublicKey = oAcceso.ReadKeyFromRegistry(Espec.IAccDatNoTrans.TpKey.PublicKey)

If xmlPublicKey <> Constantes.ERROR_KEY_NOT_FOUND Then

rsa.FromXmlString(xmlPublicKey)

If sTexto.Length > 58 Then

Throw New Exception("Key too long")

End If

'Convert the key to Byte Array

PlainTextBArray = (New UnicodeEncoding).GetBytes(sTexto)

'Encrypt

CypherTextBArray = rsa.Encrypt(PlainTextBArray, False)

'Convert the Byte Array to a String of its elements in Hex format

For i = 0 To CypherTextBArray.Length - 1

sTextoEnc &= Right("0" & Hex(CypherTextBArray(i)), 2)

Next i

Else

Throw New Exception("There is no Public Key")

End If

Catch ex As Exception

Finally

EncryptRSA = sTextoEnc

Funciones.DisposeSeguro(oAcceso)

Funciones.DisposeSeguro(rsa)

End Try

End Function

Fr3dYa at 2007-7-15 13:25:56 > top of Java-index,Security,Cryptography...
# 8

So the lineCypherTextBArray = rsa.Encrypt(PlainTextBArray, False)

shows that PKCS1 padding is being used. Therefore the encrypted output will be different every time, and it is hopeless to try to test the correctness of your Java code by matching the encrypted output. Furthermore, the character set encoding corresponds to the java charset UTF-16. e.g. String.getBytes("UTF-16") and new String (byte [], "UTF-16") are the methods to use.

The only way to know for sure if your java code is interoperable with the .Net code is verify that:

1) data encrypted by .Net can be correctly decrypted by Java, and

2) data encrypted by Java can by decrypted by .Net.

ghstarka at 2007-7-15 13:25:56 > top of Java-index,Security,Cryptography...
# 9

Thanks for your help. I managed to encode the string and now the .NET webservice accepts it.

The problem now is that the decoded .NET string doesn't match the original one. How is this possible?

Here's the .NET function that does the decoding:

Private Shared Function DecryptRSA(ByVal sTexto As String, Optional ByVal bPrivado As Boolean = False) As String

Dim sTextoDesenc As String

Dim CspParameters As CspParameters = New CspParameters

CspParameters.Flags = CspProviderFlags.UseMachineKeyStore

Dim rsa As New RSACryptoServiceProvider(CspParameters)

Dim i As Integer

Dim CypherTextBArray(255) As Byte

Dim oAcceso As Centralizada.IAccesoDatosT

Dim xmlKeys As String

Try

For i = 1 To sTexto.Length Step 2 'Get the Byte Array from the Hex String

CypherTextBArray((i - 1) / 2) = CByte(CInt("&H" & Mid(sTexto, i, 2)))

Next

oAcceso = New Centralizada.AccesoDatos

If bPrivado Then 'If encrypted with private key, we decode using the public one

xmlKeys = oAcceso.LeeClaveDelRegistro(AccesoDatos.TpClave.ClavePublica)

Else

xmlKeys = oAcceso.LeeClaveDelRegistro(AccesoDatos.TpClave.ClavePrivada)

End If

If xmlKeys <> AccesoDatos.ERROR_KEY_NOT_FOUND Then

rsa.FromXmlString(xmlKeys)'Get the RSA object

Dim RestoredPlainText As Byte() = rsa.Decrypt(CypherTextBArray, False) 'Decode the key

For i = 0 To (RestoredPlainText.Length - 1) Step 2'Convert the Byte Array to String and get the decoded key

sTextoDesenc &= Chr(RestoredPlainText(i))

Next i

Else

Throw New Exception("There is no private key")

End If

Catch ex As Exception

If ex.GetType.FullName = "System.Security.Cryptography.CryptographicException" Then

Throw New Exception("Active Directory; Error decoding RSA: " & ex.Message)

Else

Throw ex

End If

Finally

DecryptRSA = sTextoDesenc

Funciones.DisposeSeguro(oAcceso)

End Try

End Function

Fr3dYa at 2007-7-15 13:25:56 > top of Java-index,Security,Cryptography...