C#Java間でRSA暗号解読インタラクションを行う

7119 ワード

ここで、RSAアルゴリズムの復号化がC#とJavaの間でやり取りされている問題についてお話ししますが、この2、3日悩んでいて、他の人が書いた文章もたくさん見て、とても役に立ちましたが、私の実際の問題を解決することができず、ついに、私に壊されました.
まず、このコードを書く目的を紹介します:webService検証の問題を完成して、サーバー側はC#開発を採用して、クライアントはJava開発を採用します.サーバ側はクライアントに公開鍵を提供し、すでにデータ暗号化を行い、クライアントが暗号化した後、データをサーバに提出し、サーバは秘密鍵でデータを復号し、検証を行う. 
ここで遭遇する主な問題はC#RSACryptoServiceProviderクラスで発生する公開鍵、秘密鍵はxml文字列データであり、java RSAアルゴリズムで要求されるModulus、ExponentはBigIntegerタイプであり、両者の間の変換が問題である. 
JavaとC#がそれぞれ独立してRSA暗号解読を行うことについて、java RSA暗号解読実装()とC#におけるRSA暗号解読と署名と検証の実装の2つの文章を見ることができます.
次に、実装手順について説明します.
まずC#RSACryptoServiceProviderクラスから公開鍵、秘密鍵を生成
 /// 
        ///     、  
        /// 
        ///   、  ,   "PUBLIC",   "PRIVATE"
        public Dictionary createKeyPair()
        {
            Dictionary keyPair = new Dictionary();
            RSACryptoServiceProvider provider = new RSACryptoServiceProvider(1024);
            keyPair.Add("PUBLIC", provider.ToXmlString(false));
            keyPair.Add("PRIVATE", provider.ToXmlString(true));
            return keyPair;
        }

ここで生成される公開鍵は

	t+56m5jXXonAJAKC7mgkhAZX5gWJTZojbSloLpLBGEWiebFaM+aUUKALfRx83/HaUV79ZiR3zuLJOLBdALx1cmcPk/b9fdNblLmzqi4cfSnfmMLWh05xf+ZS1pKHSKQtui3dfuu+3XH6Ak+S38dpIZUj/hihQQuKysN6GJ9h+c8=
	
	AQAB

クライアント(Java)でC#に提供される公開鍵抽出ModulusとExponent
/**
	 *       modulus   exponent haspMap
	 * @return
	 * @throws MalformedURLException
	 * @throws DocumentException
	 */
	public static HashMap rsaParameters(String xmlPublicKey) throws MalformedURLException, DocumentException{
    	HashMap map = new HashMap(); 
		Document doc = DocumentHelper.parseText(xmlPublicKey);
		String mudulus = (String) doc.getRootElement().element("Modulus").getData();
		String exponent = (String) doc.getRootElement().element("Exponent").getData();
		map.put("mudulus", mudulus);
		map.put("exponent", exponent);
		return map;
	}

ModulusとExponentで公開鍵RSAPublicKey(java)を生成
ここで重要なステップは、まずMudolusとExponentをBase 64復号することである.これは、C#が生成した鍵ペアのためであり、そのパラメータはBase 64を介してStringタイプに符号化され、java RSAパラメータはbase 64を介して符号化されていないbyte[]タイプである.
Base 64の符号化、復号方法については、この記事を参照してjavaの符号化と復号化について詳しく考えたい.
public static byte[] decodeBase64(String input) throws Exception{  
        Class clazz=Class.forName("com.sun.org.apache.xerces.internal.impl.dv.util.Base64");  
        Method mainMethod= clazz.getMethod("decode", String.class);  
        mainMethod.setAccessible(true);  
         Object retObj=mainMethod.invoke(null, input);  
         return (byte[])retObj;  
    }
	
	/**
	 *   RSA  
	 * @param modules
	 * @param exponent
	 * @return
	 */
	public static PublicKey getPublicKey(String modulus, String exponent){
		try { 
			byte[] m = decodeBase64(modulus);
			byte[] e = decodeBase64(exponent);
            BigInteger b1 = new BigInteger(1,m);  
            BigInteger b2 = new BigInteger(1,e);  
            KeyFactory keyFactory = KeyFactory.getInstance("RSA");  
            RSAPublicKeySpec keySpec = new RSAPublicKeySpec(b1, b2);  
            return (RSAPublicKey) keyFactory.generatePublic(keySpec);  
        } catch (Exception e) {  
            e.printStackTrace();  
            return null;  
        }	
	}

公開鍵を取得するとRSA暗号化処理を行うことができるが、ここでさらに、RSA暗号化復号には最大長制限があり、暗号化最大長は117バイト、復号最大長は128バイトである.また、ここで暗号化したデータはBase 64符号化処理によって処理される
public static String encrypt(byte[] source, PublicKey publicKey) throws Exception	{
		String encryptData ="";
		try {
			Cipher cipher = Cipher.getInstance("RSA");
			cipher.init(Cipher.ENCRYPT_MODE, publicKey);
			int length = source.length;
			int offset = 0;
			byte[] cache;
			ByteArrayOutputStream outStream = new ByteArrayOutputStream();
			int i = 0;
			while(length - offset > 0){
				if(length - offset > MAXENCRYPTSIZE){
					cache = cipher.doFinal(source, offset, MAXENCRYPTSIZE);
				}else{
					cache = cipher.doFinal(source, offset, length - offset);
				}
				outStream.write(cache, 0, cache.length);
				i++;
				offset = i * MAXENCRYPTSIZE;
			}
			return encodeBase64(outStream.toByteArray());
		} catch (NoSuchAlgorithmException e) {
			e.printStackTrace();
		} catch (NoSuchPaddingException e) {
			e.printStackTrace();
		} catch (InvalidKeyException e) {
			e.printStackTrace();
		} catch (IllegalBlockSizeException e) {
			e.printStackTrace();
		} catch (BadPaddingException e) {
			e.printStackTrace();
		}
		return encryptData;		
	}

暗号化されたデータはC#サーバ側に提出して復号します.もちろん、ここでも最大長制限の問題に注意してください.
/// 
        /// RSA  
        /// 
        ///   Base64     
        ///   
        /// RSA      
        public static string decrypt(string encryptData, string privateKey)
        {
            string decryptData = "";
            try
            {
                RSACryptoServiceProvider provider = new RSACryptoServiceProvider();
                provider.FromXmlString(privateKey);
                byte[] bEncrypt = Convert.FromBase64String(encryptData);                
                int length = bEncrypt.Length;
                int offset = 0;
                string cache ;
                int i = 0;
                while (length - offset > 0)
                {
                    if (length - offset > MAXDECRYPTSIZE)
                    {
                        cache = Encoding.UTF8.GetString(provider.Decrypt(getSplit(bEncrypt, offset, MAXDECRYPTSIZE), false));
                    }
                    else
                    {
                        cache = Encoding.UTF8.GetString(provider.Decrypt(getSplit(bEncrypt, offset, length - offset), false));
                    }
                    decryptData += cache;
                    i++;
                    offset = i*MAXDECRYPTSIZE;
                }
            }
            catch(Exception e)
            {
                throw e;
            }
            return decryptData;
        }

        /// 
        ///           
        /// 
        /// 
        ///      
        ///     
        /// 
        private static byte[] getSplit(byte[] input, int offset, int length)
        { 
            byte[] output = new byte[length];
            for (int i = offset; i < offset + length; i++)
            {
                output[i - offset] = input[i];
            }
            return output;
        }

これで、無事完成しました.
テストを経て、このようにして確かに正しい結果を得た.
何か問題があれば,皆さんのご指摘をお待ちしております.
----------------------------------------------------------------------------------------
C#Java間でRSA暗号解読インタラクションを行う(二)
C#Java間でRSA暗号解読インタラクションを行う(三)