简体   繁体   中英

Blowfish Results different between C# and Classic ASP

I need to encrypt a value in C# and then decrypt it in classic ASP. The closest I've come to getting this to work at all is with Blowfish. But the problem is that the two implementations produce slightly different results and I'm not sure why.

Implementations in use:

C#: https://defuse.ca/blowfish.htm

vbscript: http://www.di-mgt.com.au/cryptoBlowfishASP.html

C# Code:

var input = "Hello World";
var key = "04B915BA43FEB5B6";
BlowFish b = new BlowFish(key);

string enc, dec;

enc = b.Encrypt_ECB(input);
dec = b.Decrypt_ECB(enc);

vbscript:

Dim aKey()
Dim nKeyLen, szTxtKey, szTxtPlain, szTxtKeyAsString, szTxtCipher, szTxtCipherHex, szTxtCipher64, szTxtDecrypt

szTxtKey = "04B915BA43FEB5B6"
szTxtPlain = "Hello World"

ReDim aKey((Len(szTxtKey) \ 2) - 1)
nKeyLen = bu_HexStr2Bytes(szTxtKey, aKey)
Call blf_Key(aKey, nKeyLen)
szTxtKeyAsString = bu_Bytes2HexStr(aKey, nKeyLen) 

szTxtCipher = blf_StringEnc(szTxtPlain)
szTxtCipherHex = bu_Str2Hex(szTxtCipher) 

C# Output:

819dd50a925a5eb83ed723bea6d84984

VBScript Output:

819DD50A925A5EB8CABE974A654A18A8

The first half of the output is the same: "819DD50A925A5EB8"

An the funny thing is, if I decrypt the vbscript output with the C# library I get this: Hello World♣♣♣♣♣

So...it almost works but there's some sort of padding or something going on. I don't know how to fix this though.

If you can shell out to the command line from VBScript, you could create a console app using the same C# library that encrypts the string. A bit of a workaround but you'd be using the same library.

As @artjom-b has already mentioned in the comments , the culprit is different padding.

There is a good explanation of different padding methods here .

Analysing the blowfish.cs file shows it's using NULL padding (note this snippet from the file) ;

/// <summary>
/// Decrypts a string (ECB)
/// </summary>
/// <param name="ct">hHex string of the ciphertext</param>
/// <returns>Plaintext ascii string</returns>
public string Decrypt_ECB(string ct)
{
    return Encoding.ASCII.GetString(Decrypt_ECB(HexToByte(ct))).Replace("\0", "");
}

In contrast the Classic ASP implementation uses PKCS5 padding (snippet from basBlowfishFns.asp shows PKCS5 method)

From Using Padding in Encryption
Pad with bytes all of the same value as the number of padding bytes (PKCS5 padding)

' Get # of padding bytes from last char
nPad = Asc(Right(strData, 1))
If nPad > 8 Then nPad = 0   ' In case invalid
strData = Left(strData, nLen - nPad)

The fix is to apply a workaround for the NULL padding used by the c# library.

Here is the modified basBlowfishFns.asp (just showing the modified functions) ;

Public Function blf_StringEnc(strData, padMethod)
' Encrypts plaintext strData after adding RFC 2630 padding
' Returns encrypted string.
' Requires key and boxes to be already set up.
' Version 5. Completely revised.
' The speed improvement here is due to Robert Garofalo.
    Dim strIn
    Dim strOut
    Dim nLen
    Dim sPad
    Dim nPad
    Dim nBlocks
    Dim i
    Dim j
    Dim aBytes(7)
    Dim sBlock
    Dim iIndex

    ' Pad data string to multiple of 8 bytes
    strIn = PadString(strData, padMethod)
    ' Calc number of 8-byte blocks
    nLen = Len(strIn)
    nBlocks = nLen \ 8
    ' Allocate output string here so we can use Mid($ below
    ' strOut = String(nLen, " ")
    strOut = ""     ' Fix for VBScript

    ' Work through string in blocks of 8 bytes
    iIndex = 0
    For i = 1 To nBlocks
        sBlock = Mid(strIn, iIndex + 1, 8)
        ' Convert to bytes
        ' aBytes() = StrConv(sBlock, vbFromUnicode)
        Call bu_String2Bytes(sBlock, aBytes)
        ' Encrypt the block
        Call blf_EncryptBytes(aBytes)
        ' Convert back to a string
        ' sBlock = StrConv(aBytes(), vbUnicode)
        sBlock = bu_Bytes2String(aBytes, 8)
        ' Copy to output string
        ' Mid(strOut, iIndex + 1, 8) = sBlock
        strOut = strOut & sBlock
        iIndex = iIndex + 8
    Next

    blf_StringEnc = strOut

End Function

Public Function blf_StringDec(strData, padMethod)
' Decrypts ciphertext strData and removes RFC 2630 padding
' Returns decrypted string.
' Requires key and boxes to be already set up.
' Version 5. Completely revised.
' The speed improvement here is due to Robert Garofalo.
    Dim strIn
    Dim strOut
    Dim nLen
    Dim sPad
    Dim nPad
    Dim nBlocks
    Dim i
    Dim j
    Dim aBytes(7)
    Dim sBlock
    Dim iIndex

    strIn = strData
    ' Calc number of 8-byte blocks
    nLen = Len(strIn)
    nBlocks = nLen \ 8
    ' Allocate output string here so we can use Mid($ below
    'strOut = String(nLen, " ")
    strOut = ""

    ' Work through string in blocks of 8 bytes
    iIndex = 0
    For i = 1 To nBlocks
        sBlock = Mid(strIn, iIndex + 1, 8)
        ' Convert to bytes
        ' aBytes() = StrConv(sBlock, vbFromUnicode)
        Call bu_String2Bytes(sBlock, aBytes)
        ' Encrypt the block
        Call blf_DecryptBytes(aBytes)
        ' Convert back to a string
        'sBlock = StrConv(aBytes(), vbUnicode)
        sBlock = bu_Bytes2String(aBytes, 8)
        ' Copy to output string
        ' Mid(strOut, iIndex + 1, 8) = sBlock
        strOut = strOut & sBlock
        iIndex = iIndex + 8
    Next

    ' Strip padding, if valid
    strOut = UnpadString(strOut, padMethod)

    blf_StringDec = strOut

End Function

Public Function PadString(strData, method)
' Pad data string to next multiple of 8 bytes as per RFC 2630
    Dim nLen
    Dim sPad
    Dim nPad
    nLen = Len(strData)
    nPad = ((nLen \ 8) + 1) * 8 - nLen
    Select Case method
    Case "PKCS5"
        sPad = String(nPad, Chr(nPad))  ' Pad with # of pads (1-8)
    Case "NULL"
        sPad = String(nPad, Chr(0))  ' Pad with # of NULL characters
    End Select
    PadString = strData & sPad

End Function

Public Function UnpadString(strData, method)
' Strip RFC 2630-style padding
    Dim nLen
    Dim nPad
    nLen = Len(strData)
    If nLen = 0 Then Exit Function
    Select Case method
    Case "PKCS5"
        ' Get # of padding bytes from last char
        nPad = Asc(Right(strData, 1))
        If nPad > 8 Then nPad = 0   ' In case invalid
        strData = Left(strData, nLen - nPad)
    Case "NULL"
        'Remove any NULL characters, obviously, this method isn't ideal if
        'the data contains valid NULLs. This shouldn't be an issue with
        'ASCII text.
        strData = Replace(strData, Chr(0), "")
    End Select
    UnpadString = strData
End Function

the key modifications are to the PadString() and UnpadString() functions. I've added a parameter method allowing you to pass an identifier NULL or PKCS5 to determine how we pad / unpad the data. These functions already existed but for some reason were not used by the blf_StringEnc() and blf_StringDec() functions so in the interests of the DRY principle I've modified them so they are used.

With those modifications (which is only a quick stab at making the code more flexible) using the following code;

Dim aKey()
Dim nKeyLen, szTxtKey, szTxtPlain, szTxtKeyAsString, szTxtCipher, szTxtCipherHex, szTxtCipher64, szTxtDecrypt

szTxtKey = "04B915BA43FEB5B6"
szTxtPlain = "Hello World"

ReDim aKey((Len(szTxtKey) \ 2) - 1)
nKeyLen = bu_HexStr2Bytes(szTxtKey, aKey)
Call blf_Key(aKey, nKeyLen)
szTxtKeyAsString = bu_Bytes2HexStr(aKey, nKeyLen) 

'Encrypt using NULL padding method.
szTxtCipher = blf_StringEnc(szTxtPlain, "NULL")
szTxtCipherHex = bu_Str2Hex(szTxtCipher)

Call Response.Write(szTxtCipherHex)

Will result in;

819DD50A925A5EB83ED723BEA6D84984

as originally expected.

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM