使用vb.net編寫一個郵件定時發送程式(二)

來源:互聯網
上載者:User

    上次我已經設計出了基本的介面,而且也確定了使用ini檔案做為參數的儲存方式,調用Windows API函數進行INI檔案的讀寫操作,但有一個重要的問題,這個小程式涉及到了二個方面的密碼:一是資料庫的串連密碼,二是郵件伺服器的發送密碼,如果我們直接把密碼以明文的方式儲存到INI檔案中,如果一不小心INI檔案泄露的話,其他人很容易就可以得到這些參數,可能會對資料庫或郵件伺服器進行攻擊或者偽造郵件,造成不必要的麻煩。因此,我下一步考慮的是如何對敏感資訊進行加密。

    現在通用的密碼編譯演算法有很多,但首先要確定的一點就是,我們選擇的密碼編譯演算法一定要是可逆的,也就是說,使用同一個密碼,可以加密,也可以解密出明文,否則就無法將儲存的密碼恢複,這樣的話,最常用的MD5加密方式就不能使用了,我選擇的是DES加密方式,雖然這種加密方式可能已經有點老了,但用在這個場合感覺已經夠用了,最重要的是,它本身就包括在了vs的一個類中,很方便就可以調用。

Imports System.Security.CryptographyImports System.TextPublic Function Encrypt(ByVal pToEncrypt As String, ByVal sKey As String) As String        Dim des As New DESCryptoServiceProvider()        Dim inputByteArray() As Byte        inputByteArray = Encoding.Default.GetBytes(pToEncrypt)        ''建立加密對象的密鑰和位移量        ''原文使用ASCIIEncoding.ASCII方法的GetBytes方法        ''使得輸入密碼必須輸入英文文本        des.Key = ASCIIEncoding.ASCII.GetBytes(sKey)        des.IV = ASCIIEncoding.ASCII.GetBytes(sKey)        ''寫位元組到加密流        ''(把記憶體流中的內容全部寫入)        Dim ms As New System.IO.MemoryStream()        Dim cs As New CryptoStream(ms, des.CreateEncryptor, CryptoStreamMode.Write)        ''寫位元組到加密流        ''(把記憶體流中的內容全部寫入)        cs.Write(inputByteArray, 0, inputByteArray.Length)        cs.FlushFinalBlock()        ''建立輸出字串        Dim ret As New StringBuilder()        Dim b As Byte        For Each b In ms.ToArray()            ret.AppendFormat("{0:X2}", b)        Next        Return ret.ToString()    End Function    Public Function Decrypt(ByVal pToDecrypt As String, ByVal sKey As String) As String        Try            Dim des As New DESCryptoServiceProvider()            ''把字串放入byte數組            Dim len As Integer            len = pToDecrypt.Length / 2 - 1            Dim inputByteArray(len) As Byte            Dim x, i As Integer            For x = 0 To len                i = Convert.ToInt32(pToDecrypt.Substring(x * 2, 2), 16)                inputByteArray(x) = CType(i, Byte)            Next            ''建立加密對象的密鑰和位移量,此值重要,不能修改            des.Key = ASCIIEncoding.ASCII.GetBytes(sKey)            des.IV = ASCIIEncoding.ASCII.GetBytes(sKey)            Dim ms As New System.IO.MemoryStream()            Dim cs As New CryptoStream(ms, des.CreateDecryptor, CryptoStreamMode.Write)            cs.Write(inputByteArray, 0, inputByteArray.Length)            cs.FlushFinalBlock()            Return Encoding.Default.GetString(ms.ToArray)        Catch ex As Exception            Return ""        End Try    End Function

    要注意的是,DES演算法的密鑰必須是八位英文字母或數字,不能多也不能少,不然就會報錯。

    現在,我將讀寫INI的語句稍做改變,寫入時加上密碼,讀取時自動解密,假設密鑰使用一個固定的八位字串,範例程式碼如下:

 

IniFile = Application.StartupPath + "\sendmail.ini"'將密碼加密後寫入INI檔案,密鑰固定為"vvian999"WriteINI("SMTP", "password", Encrypt(Txt_MailPwd.Text, "vvian999"), IniFile)'將讀出的密碼解密後放入文字框內Txt_MailPwd.Text = Decrypt(GetINI("SMTP", "password", "", IniFile), "vvian999")

    這樣一來,存入INI檔案的密碼參數就已經是加密後的字串,在不知道密鑰的情況下,一般人已經很難將其破解,得到敏感資訊了。但安全永遠是放在第一位的,我又想到,如果得到INI檔案的那個人在其他的機器上運行了這個程式,文字框中就會顯示出星號的密碼明文,只要使用一個密碼查看器之類的軟體,密碼也會泄露了,而且密鑰如果寫死在可執行檔中,加密的規律是相同的,也不好呀,最好的辦法是每台機器的密鑰是一個固定的值,但各台機器的密鑰又不相同,這樣的話,就算把程式和INI檔案一起拷到其他伺服器上也無法正常執行,安全也有了保障。

    每台機器一個固定密鑰最好的辦法就是擷取該機器的某個硬體值,如硬碟序號、CPU編號、網卡MAC地址等一般不會變化的資料,使用這些資料得到一個八位長度的字串,以此為密鑰,加密一個只有自己才知道的字串,每次啟動時解密一次,如果解密結果相同,就繼續進行,如果解密結果不同,就報錯,要求重新輸入資料庫和郵件伺服器的密碼,最大程度的保證了資料的安全,這實際上也是共用軟體常用的註冊碼方式吧。

    擷取硬體資訊我使用的是System.Management,使用wmi來擷取,比如下面的例子就是擷取網卡的MAC地址,然後取前八位做為密鑰進行加密,因為MAC地址都是12位的,不用擔心長度不夠的問題,也可以取後八位、中間八位等方式來處理。加一個加密好的字串同時放入INI檔案後,以後每次啟動程式時進行一次檢查。

'先擷取硬體代碼,這裡使用的是網卡MAC地址的前八位Dim HwCode as StringDim macaddr As StringDim Wmimac As New System.Management.ManagementObjectSearcher("SELECT  * FROM Win32_NetworkAdapterConfiguration")For Each WmiObj As ManagementObject In Wmimac.Get    If CBool(WmiObj("IPEnabled")) Then        macaddr = WmiObj("MACAddress")    End IfNextHwCode = Microsoft.VisualBasic.Left(Replace(macaddr, ":", ""),8))'把驗證字串寫入INI檔案WriteINI("SendMail", "CRC", Encrypt("clicvvian2010", HwCode), IniFile)

    還有一點,如果是伺服器上一般不止一塊網址,我使用的是最後一塊網卡的地址,你也可以使用其他方式來指定某一塊網卡,使用wmi來擷取其他硬體資訊的方法,大家可以通過google搜尋,這裡就不多說了。

   

    Private Sub sendmail_Load(ByVal sender As System.Object, ByVal e As System.EventArgs) Handles MyBase.Load'前面的一些代碼已經刪除,只留下判斷過程的                If Decrypt(GetINI("SendMail", "CRC", "", IniFile), HwCode) <> "clicvvian2010" Then                    MessageBox.Show("注意:檢測到伺服器有變化,請重新輸入資料庫、郵件伺服器密碼後儲存,現密碼已解碼出錯!", "注意", MessageBoxButtons.OK, MessageBoxIcon.Information)                End If'    End Sub

    處理了一個問題後,對於第二個問題也可以開始考慮,就是將解密後的密碼放迴文本框後,如果有心人可以接觸到伺服器,一樣可以通過密碼查看軟體得到密碼,因此我的處理方式是將讀入的密碼密文直接放到文字框中,將解密後的密碼明文放到一個Public變數中,在要使用密碼的地方,先進行一次判斷,將變數中的密碼加密後與文字框中的對比,如果一致,就使用變數中的密碼,如果不一致,說明使用者修改過密碼,就將文字框中的密碼符值給變數然後使用,當使用者每點擊一次“儲存參數”時,再將加密後的密碼重新放到密碼框中。當然這也有問題,就是使用者修改了密碼,又沒有點擊儲存參數時,密碼還是以明文方式存放在文字框中,我嘗試使用了Txt_MailPwd_LostFocus和Txt_MailPwd_Validated二種方式都沒能做過修改後即時變換,不知道各位有沒有什麼好的辦法。

    與上次相比,介面做了一點點改動,增加了一個測試郵件的輸入框和發送測試郵件的按鈕,同時去掉了“資料庫優先”選項,就直接預設為資料庫的資訊優先吧(指的是如果資料庫中的某條記錄指定了寄件者姓名,就不再使用本處輸入的內容),比上次的排列更好看了一些,呵呵

    原打算在這次就進入資料庫設計的,看來又要推到下一次了,呵呵,我力爭加大進度,在國慶長假期間完成這個小程式吧。

   

聯繫我們

該頁面正文內容均來源於網絡整理,並不代表阿里雲官方的觀點,該頁面所提到的產品和服務也與阿里云無關,如果該頁面內容對您造成了困擾,歡迎寫郵件給我們,收到郵件我們將在5個工作日內處理。

如果您發現本社區中有涉嫌抄襲的內容,歡迎發送郵件至: info-contact@alibabacloud.com 進行舉報並提供相關證據,工作人員會在 5 個工作天內聯絡您,一經查實,本站將立刻刪除涉嫌侵權內容。

A Free Trial That Lets You Build Big!

Start building with 50+ products and up to 12 months usage for Elastic Compute Service

  • Sales Support

    1 on 1 presale consultation

  • After-Sales Support

    24/7 Technical Support 6 Free Tickets per Quarter Faster Response

  • Alibaba Cloud offers highly flexible support services tailored to meet your exact needs.