import java.io.*; import java.math.BigInteger; import java.security.*; import java.security.spec.*; import java.security.cert.*; import java.security.interfaces.*; public class PubConvert{ private static final byte PUBLICKEYBLOB = 0x06; private static final byte CUR_BLOB_VERSION = 0x02; private static final short RESERVED = 0x0000; private static final int CALG_RSA_KEYX = 0x0000a400; private static final int CALG_RSA_SIGN = 0x00002400; private static final int AT_KEYEXCHANGE = 1; private static final int AT_SIGNATURE = 2; private static final int[] KEYSPECS = {0, CALG_RSA_KEYX, CALG_RSA_SIGN } ; private static final String MAGIC = "RSA1" ; // 0x31415352 private static final String OUTFILE = "_publickeyblob" ; private static final String XMLRSAPUBKEY = "XMLPubKey.txt" ; private static int bitlen = 00; private static int bytelen = 00; private static byte[] modulus = null; private static int pubexp = 0; public static void main(String args[]){ DataOutputStream dos = null; if (args.length != 1 && args.length !=2) { System.out.println("Usage:\n java PubConvert "); System.exit(0) ; } FileOutputStream fos = null; try{ int keyspec = AT_KEYEXCHANGE ; byte[] encodedPubKey = getFileBytes(args[0]); dumpRSAPublickey(encodedPubKey) ; byte [] publickeyblob = subjectpublickeyinfoToPublickeyblob(encodedPubKey, keyspec) ; System.out.println("\nKeySize: " +bitlen + " bits"); System.out.println("keyspec " + keyspec) ; int blobbytes = 20 + bytelen; //for sanity check ofPUBLICKEYBLOB size. if(publickeyblob.length != blobbytes) { System.out.println("Publickeyblob length problem"); System.exit(0) ; } fos = new FileOutputStream(OUTFILE); fos.write(publickeyblob); fos.close(); System.out.println("Wrote PUBLICKEYBLOB file '" + OUTFILE + "' (" +publickeyblob.length + " bytes)") ; String xmlpubkey = subjectpublickeyinfoToXMLRSAPubKey(encodedPubKey).replaceAll("[ \t\n\r]" ,""); //remove an CrLf etc.. System.out.println("\n\nXML Publickey:"); System.out.println(xmlpubkey) ; FileWriter fwri = new FileWriter(XMLRSAPUBKEY); fwri.write(xmlpubkey); fwri.close(); System.out.println("\nWrote XML RSA public key file '" + XMLRSAPUBKEY + "'") ; } catch(Exception e) {System.err.println(e);} finally{ try{if (fos !=null) fos.close(); } catch (IOException e) {} } } private static byte[] subjectpublickeyinfoToPublickeyblob(byte[] encodedPubkey, int keyspec) { if(encodedPubkey == null || (keyspec != AT_KEYEXCHANGE && keyspec !=AT_SIGNATURE)) return null; try{ ByteArrayOutputStream bos = new ByteArrayOutputStream(); DataOutputStream dos = new DataOutputStream(bos); X509EncodedKeySpec pubKeySpec = new X509EncodedKeySpec(encodedPubkey); KeyFactory keyFactory = KeyFactory.getInstance("RSA"); RSAPublicKey pubKey = (RSAPublicKey)keyFactory.generatePublic(pubKeySpec); BigInteger mod = pubKey.getModulus(); modulus = mod.toByteArray() ; if(modulus[0] == 0) //if high-order byte is zero, it's for sign bit; don't count in bit-size calculation bytelen = modulus.length-1 ; else bytelen = modulus.length ; bitlen = 8*bytelen; dos.write(PUBLICKEYBLOB); dos.write(CUR_BLOB_VERSION); dos.writeShort(RESERVED); writeLEInt(KEYSPECS[keyspec], dos); //write Little Endian dos.writeBytes(MAGIC) ; writeLEInt(bitlen, dos); //write Little Endian pubexp = Integer.parseInt(pubKey.getPublicExponent().toString()) ; writeLEInt(pubexp, dos); //write Little Endian byte[] data = modulus; ReverseMemory(data); //reverse array to Little Endian order; since data is same ref. as modulus, modulus is also reversed. dos.write(data, 0, bytelen) ; // note that modulus may contain an extra zero byte (highest order byte after reversing) // specifying bytelen bytes to write will drop high-order zero byte dos.flush(); dos.close(); return bos.toByteArray(); } catch(Exception e) {System.err.println(e); return null ;} } // --- Returns XML encoded RSA public key string suitable for .NET CryptoServiceProvider.FromXmlString(true) ------ // --- Leading zero bytes (most significant) must be removed for XML encoding for .NET; otherwise format error --- private static String subjectpublickeyinfoToXMLRSAPubKey(byte[] encodedPubkey) { try{ StringBuffer buff = new StringBuffer(1024); X509EncodedKeySpec pubKeySpec = new X509EncodedKeySpec(encodedPubkey); KeyFactory keyFactory = KeyFactory.getInstance("RSA"); RSAPublicKey pubKey = (RSAPublicKey)keyFactory.generatePublic(pubKeySpec); buff.append("") ; buff.append("" + b64encode(removeMSZero(pubKey.getModulus().toByteArray())) + ""); buff.append("" + b64encode(removeMSZero(pubKey.getPublicExponent().toByteArray())) + ""); buff.append("") ; return buff.toString(); } catch(Exception e) {System.err.println(e); return null ; } } private static void dumpRSAPublickey(byte[] encodedPubkey) { try{ X509EncodedKeySpec pubKeySpec = new X509EncodedKeySpec(encodedPubkey); KeyFactory keyFactory = KeyFactory.getInstance("RSA"); //---- Display private key components in BigInteger decimal format -- RSAPublicKey pubKey = (RSAPublicKey)keyFactory.generatePublic(pubKeySpec); System.out.println("\nModulus:\n" + pubKey.getModulus().toString()); displayData(pubKey.getModulus().toByteArray()) ; System.out.println("\n\nPublic Exponent:\n" + pubKey.getPublicExponent().toString()); displayData(pubKey.getPublicExponent().toByteArray()) ; } catch(Exception e) {System.err.println(e);} } // --------- remove leading (Most Significant) zero byte if present ---------------- private static byte[] removeMSZero(byte[] data) { byte[] data1 ; int len = data.length; if (data[0] == 0) { data1 = new byte[data.length-1] ; System.arraycopy(data, 1, data1, 0, len-1); } else data1 = data; return data1; } private static final String b64encode(byte[] data) { //Use internal sun class for B64 encoding sun.misc.BASE64Encoder enc = new sun.misc.BASE64Encoder() ; String b64str = enc.encodeBuffer(data).trim(); //removing any leading/trailing whitespace return b64str ; } private static void writeLEInt(int i, OutputStream out) throws IOException { out.write (i & 0xFF); out.write((i >>>8) & 0xFF); out.write((i >>>16) & 0xFF); out.write((i >>>24) & 0xFF); } private static void displayData(byte[] data) { int bytecon = 0; //to get unsigned byte representation for(int i=1; i<=data.length ; i++){ bytecon = data[i-1] & 0xFF ; // byte-wise AND converts signed byte to unsigned. if(bytecon<16) System.out.print("0" + Integer.toHexString(bytecon).toUpperCase() + " "); // pad on left if single hex digit. else System.out.print(Integer.toHexString(bytecon).toUpperCase() + " "); // pad on left if single hex digit. if(i%16==0) System.out.println(); } } private static void ReverseMemory (byte[] pBuffer) { byte b ; int iLength = pBuffer.length; for (int i = 0 ; i < iLength/ 2 ; i++) { b = pBuffer [i] ; pBuffer [i] = pBuffer [iLength - i - 1] ; pBuffer [iLength - i - 1] = b ; } } private static byte[] getFileBytes(String infile){ File f = new File(infile) ; int sizecontent = ((int) f.length()); byte[] data = new byte[sizecontent]; try { FileInputStream freader = new FileInputStream(f); freader.read(data, 0, sizecontent) ; freader.close(); return data; } catch(IOException ioe) { System.out.println(ioe.toString()); return null; } } }