對接第三方平臺JAVA接口問題推送和解決

前言

本節所講爲實際項目中與第三方對接出現的問題最後仍是靠老大解決了問題以此做爲備忘錄,本篇分爲三小節,一小節解析Java加密接口數據,二小節解析XML文件需注意問題,最後一節則是請求Java Soap協議接口。由於第三方平臺都是採用JAVA語言,因此這種狀況應該對你們有所幫助。html

DES加密/解密Java接口

關於Java中序列化爲XML文件就不搞了,咱們首先須要講解的是關於加密問題,因爲是第三方是採用的DES加密,因此咱們只講解DES,有不少人可能有疑問了,這不過期了麼且不安全,沒必要糾結,這個也不是你我能決定的問題,不在討論範疇內。剛開始覺得只是加密和解密而已,很簡單嘛,何況網上的例子多如牛毛,慢慢發現是我太自信了,事後開始研究Java中加密的實現,網上文檔如此說:Java中默認DES加密方式爲ECB(電子密碼本模式),而C#中默認DES加密方式爲CBC(加密分組連接模式)這兩者是最多見的DES加密方式,且加密key都爲8位,其餘的咱們就不看了。然後看過各類Java和C#中DES加密文檔,看到此連接文章時內心開始激動了:http://luanxiyuan.iteye.com/blog/1938348,結果一掃,文中所給的Java加密爲CBC模式,而恰好對應C#中默認加密模式,,沒毛病不是我想要的,再看看園中其餘文章,恩,也挺好,做者給出了Java中的實現,C#中的實現也給出了,可是下面評論一看,加密或者解密出錯,做者回復:在C#上未實踐。我是沒招了,搞了很久也沒弄出來,最後仍是沒弄出來,老大搞定,我就等着吃現成的吧,過後我看了看代碼,而後再次查了查資料發現其中區別,因此在這裏做此備忘錄。對此還特地下了個IDEA,玩玩Java,對比下java中C#中的實現,首先咱們來看看Java中實現。原諒我沒接觸過Java,搞了兩個小時才研究明白下載IDEA,破解IDEA,下載JDK,使用IDEA,導入包,調試java,本篇博客名稱能夠起名爲:從Java到.NET,仍是.NET好 ,結果很顯然會噴,由於做爲初學者沒深刻了解Java,因此仍是老老實實起個正經博文名稱。一邊打開IDEA,一邊打開VS,那叫一個卡啊。先看看安裝最新IDEA 2017.2.1版本界面。java

首先咱們來看看在.NET中DES加密中ECB模式的實現。程序員

        public static string Encrypt(string originalString, string key)
        {
            if (String.IsNullOrEmpty(originalString))
            {
                throw new ArgumentNullException
                       ("The string which needs to be encrypted can not be null.");
            }
            var bytes = Encoding.UTF8.GetBytes(key);
            DESCryptoServiceProvider cryptoProvider = new DESCryptoServiceProvider()
            {
                Mode = CipherMode.ECB,
                Padding = PaddingMode.None
            };

            MemoryStream memoryStream = new MemoryStream();
            CryptoStream cryptoStream = new CryptoStream(memoryStream,
                cryptoProvider.CreateEncryptor(bytes, bytes), CryptoStreamMode.Write);
            StreamWriter writer = new StreamWriter(cryptoStream);
            writer.Write(originalString);
            writer.Flush();
            cryptoStream.FlushFinalBlock();
            writer.Flush();
            return Convert.ToBase64String(memoryStream.GetBuffer(), 0, (int)memoryStream.Length);
        }
        public static string Decrypt(string cryptedString, string key)
        {
            if (String.IsNullOrEmpty(cryptedString))
            {
                throw new ArgumentNullException
                   ("The string which needs to be decrypted can not be null.");
            }
            var bytes = Encoding.UTF8.GetBytes(key);
            DESCryptoServiceProvider cryptoProvider = new DESCryptoServiceProvider()
            {
                Mode = CipherMode.ECB,
                Padding = PaddingMode.None
            };
            MemoryStream memoryStream = new MemoryStream
                    (Convert.FromBase64String(cryptedString));
            CryptoStream cryptoStream = new CryptoStream(memoryStream,
                cryptoProvider.CreateDecryptor(bytes, bytes), CryptoStreamMode.Read);
            StreamReader reader = new StreamReader(cryptoStream);
            return reader.ReadToEnd();
        }

 調用以下:算法

var encryptStr = Encrypt("Jeffcky from cnblogs", "88888888");

神馬啊,加密的數據長度無效,對數據長度還有要求啊,啥破玩意啊。而後須要刪除填充模式:apache

            DESCryptoServiceProvider cryptoProvider = new DESCryptoServiceProvider()
            {
                Mode = CipherMode.ECB,
                Padding = PaddingMode.None
            };

 好了,沒毛病了,在.NET中DES的ECB模式加密和解密就實現了,到了剛纔才發現這麼篇文章:http://www.cnblogs.com/Lawson/archive/2012/05/20/2510781.html,文中關鍵點在這裏:ECB模式:電子密本方式,這是JAVA封裝的DES算法的默認模式,就是將數據按照8個字節一段進行DES加密或解密獲得一段8個字節的密文或者明文,最後一段不足8個字節,則補足8個字節(注意:這裏就涉及到數據補位了)進行計算,以後按照順序將計算所得的數據連在一塊兒便可,各段數據之間互不影響。而後咱們再來看看Java中的實現。安全

     import javax.crypto.Cipher;
     import javax.crypto.SecretKey;
     import javax.crypto.SecretKeyFactory;
     import javax.crypto.spec.DESKeySpec;
     import java.security.SecureRandom;
     import  org.apache.commons.codec.binary.Base64;

    public class DesAlgorithm {
    private static final String CIPHER_ALGORITHM = "DES/ECB/NoPadding";

    private static SecretKey keyGenerator(String keyStr) throws  Exception {
        DESKeySpec desKey = new DESKeySpec(keyStr.getBytes());
        SecretKeyFactory keyFactory = SecretKeyFactory.getInstance("DES");
        SecretKey secureKey = keyFactory.generateSecret(desKey);
        return secureKey;
    }

    private static String paddingChar(String date) {
        if (date.getBytes().length % 8 > 0) {
            for (int i = 0; i < date.getBytes().length % 8; i++) {
                date = " " + date;
            }
        }
        return date;
    }

    public static String encrypt(String data, String key) throws Exception {
        SecretKey desKey = keyGenerator(key);
        Cipher cipher = Cipher.getInstance(CIPHER_ALGORITHM);
        SecureRandom random = new SecureRandom();
        cipher.init(Cipher.ENCRYPT_MODE, desKey, random);
        byte[] results = cipher.doFinal(paddingChar(data).getBytes());
        return Base64.encodeBase64String(results);
    }

    public static String decrypt(String data, String key) throws Exception {
        SecretKey desKey = keyGenerator(key);
        Cipher cipher = Cipher.getInstance(CIPHER_ALGORITHM);
        cipher.init(Cipher.DECRYPT_MODE, desKey);
        return new String(cipher.doFinal(Base64.decodeBase64(data))).trim();
    }
}

調用以下:dom

public class Main {

    public static void main(String[] args) {
        try {
            String source =
                    "<?xml version=\"1.0\" encoding=\"GBK\"?>\n" +
                            "<REQUEST>\n" +
                            "  <workyear>4</workyear>\n" +
                            "  <sex>0</sex>\n" +
                            "  <name>汪鵬</name>\n" +
                            "  <idtype>1</idtype>\n" +
                            "  <idno>421081199109284899</idno>\n" +
                            "  <brithday>1991-09-28</brithday>\n" +
                            "  <school>北華大學</school>\n" +
                            "  <email>2752154844@qq.com</email>\n" +
                            "</REQUEST>";
            String key = "88888888";
            String encryptData = DesAlgorithm.encrypt(source,key);
            System.out.println("加密後: " + encryptData);
            String decryptData = DesAlgorithm.decrypt("hYbvHlD/ZpOBLyjofjVJmE4oAqitG9BAhhhuykmI0sc7C3TCLtuxCmjxp5WB+OXdEuwt1CqQxtLBCxpvsGbQUHw37J2LSABl+Zx4cM6Z8o5X4VdhTibUjryYkVPwYrzHgaiHA4VVDQ7P7RpMTsnFk372ZP1W+fr2UhpHC8hkohyBaOZ1NWOieQQvPvOLErhzcGWcmjUsnjp0vNEfM7y/FRsQhhvTKtRiPWPdRpWZGH+TofsSuhNtmcE61u0tgEhLcOpDvifLS9zGj2F7Jn8nR05Au7/uz5gl8jB6FCHc97YKAPR0jx69egA+MKfv6IYTmpSZSnWJGgFnnP4SpLGnH3+7Mm6uX8ni2sBaM0/9H9YpVgqpXJ2fCw==", key);
            System.out.println("解密後: " + decryptData);
        }
        catch (Exception ex)
        {

        }
    }
}

此時咱們將加密後的數據利用.NET來進行解密,這裏就有兩個問題須要解決,一個是Java中ECB模式爲不填充,第二個上述講到Java中ECB加密時會對數據進行補位,且上述演示例子也是對數據進行了補位且用空字符串,因此在.NET中咱們仍然須要加上ECB不填充且數據要補位和Java中一致,因此加密須要高修改成以下:ide

        public static string Encrypt(string originalString, string key)
        {
            if (String.IsNullOrEmpty(originalString))
            {
                throw new ArgumentNullException
                       ("The string which needs to be encrypted can not be null.");
            }
            byte[] textBytes = Encoding.UTF8.GetBytes(originalString);
            int mod = (8 - (textBytes.Length % 8));
            for (int i = 0; i < mod; i++)
            {
                originalString = " " + originalString;
            }
            var bytes = Encoding.UTF8.GetBytes(key);
            DESCryptoServiceProvider cryptoProvider = new DESCryptoServiceProvider()
            {
                Mode = CipherMode.ECB,
Padding = PaddingMode.None }; MemoryStream memoryStream
= new MemoryStream(); CryptoStream cryptoStream = new CryptoStream(memoryStream, cryptoProvider.CreateEncryptor(bytes, bytes), CryptoStreamMode.Write); StreamWriter writer = new StreamWriter(cryptoStream); writer.Write(originalString); writer.Flush(); cryptoStream.FlushFinalBlock(); writer.Flush(); return Convert.ToBase64String(memoryStream.GetBuffer(), 0, (int)memoryStream.Length); }

由於加密時對數據進行了補位,因此在加密時將補位的空字符串去除,因此上述解密出錯咱們只須要利用 Trim() 方法去除空字符串便可,咱們看看post

        public static string Decrypt(string cryptedString, string key)
        {
            if (String.IsNullOrEmpty(cryptedString))
            {
                throw new ArgumentNullException
                   ("The string which needs to be decrypted can not be null.");
            }

            var bytes = Encoding.UTF8.GetBytes(key);
            DESCryptoServiceProvider cryptoProvider = new DESCryptoServiceProvider()
            {
                Mode = CipherMode.ECB,
                Padding = PaddingMode.None
            };
            MemoryStream memoryStream = new MemoryStream
                    (Convert.FromBase64String(cryptedString));
            CryptoStream cryptoStream = new CryptoStream(memoryStream,
                cryptoProvider.CreateDecryptor(bytes, bytes), CryptoStreamMode.Read);
            StreamReader reader = new StreamReader(cryptoStream);
            return reader.ReadToEnd().Trim();
        }

 大功告成,Over,就是這麼簡單,解密時經過Tirm()方法去除空字符串便可。加密

解析XML文件

 解析XML文件本沒有任何問題,這是老生常談的問題了,可是仍是會遇到沒碰到的問題,做此記錄,請往下看,先聲明一個類:

    public class Blog
    {
        public string Name { get; set; }
    }

序列化和反序列化諾:

        public static string Serializer(Type type, object obj)
        {
            MemoryStream Stream = new MemoryStream();
            XmlSerializer xml = new XmlSerializer(type);
            xml.Serialize(Stream, obj);
            Stream.Position = 0;
            StreamReader sr = new StreamReader(Stream);
            string str = sr.ReadToEnd();

            sr.Dispose();
            Stream.Dispose();

            return str;
        }
        public static object Deserialize(Type type, string xml)
        {
            using (StringReader sr = new StringReader(xml))
            {
                XmlSerializer xmldes = new XmlSerializer(type);
                return xmldes.Deserialize(sr);
            }
        }

沒毛病,再看,上述Blog再添加一個屬性並實例化Url:

public string Url { get; set; }
            var blog = new Blog()
            {
                Name = "Jeffcky",
                Url = "https://i.cnblogs.com/EditPosts.aspx?postid=7295879&update=1"
            };

想說明的是Url序列化成了以下狀況:

<?xml version="1.0"?>
<Blog xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:xsd="http://www.w3.org/2001/XMLSchema">
  <Name>Jeffcky</Name>
  <Url>https://i.cnblogs.com/EditPosts.aspx?postid=7295879&amp;update=1</Url>
</Blog>

&變成了&amp,想必這是XML序列化的規則對有些特殊字符進行了處理。可是在調用Java接口反序列化時對於上述&卻沒進行翻譯,報錯以下,納悶:

The reference to entity "characterEncoding" must end with the ';' delimiter 

此時須要將&替換爲&amp;一樣對於>或者<亦是如此。

var data = data.Replace("&", "&amp;");

請求Java中SOAP接口

因爲未接觸過WebService和Soap,在請求Java上的Soap接口時須要在請求頭中添加 SOAPAction ,固然在C#中的Soap請求也可能要加上以下請求頭。以下:

               using (var httpClient = new HttpClient(handler))
                {
                    var httpContent = new StringContent(「xml」, Encoding.UTF8, "text/xml");
                    httpContent.Headers.Add("SOAPAction", 「action」);
                    response = await httpClient.PostAsync("url", httpContent);
                }

總結

對於上述遇到的問題想必有些讀者門已經碰見過了,不喜勿噴。對於解析Java中的加密數據過程可見,何須有語言之爭,多懂一門語言終歸是好的,都是爲了更好的發展不是,相煎何太急了,以上全部以此做爲備忘錄,如今想來對接真不是想象的那麼簡單啊,程序員都認爲本身寫的代碼沒有任何問題,有時候仍是好生交流纔是上策啊,首先懷疑是否是自身這邊是否是出了問題再言其餘,而非一棒子直接打死是對方的問題。see u。

相關文章
相關標籤/搜索