C#

2014-05-22 5 views
1

에 포트 된 암호화 알고리즘을 시도 나는 다음과 같은 링크가 나를 크게 도움이되었다고 말함으로써 시작합니다 :C#

Translating Win32 Crypto API calls to C# with System.Security.Cryptography

을 자신의 수정은하지 않았다 제외하고 나는, 그가 보내고 같은 문제가 있어요 내 코드를 고쳐라.

Private Const ALG_CLASS_DATA_ENCRYPT = 24576& 
Private Const ALG_CLASS_HASH = 32768 
Private Const ALG_SID_3DES = 3& 
Private Const ALG_SID_SHA1 = 4& 
Private Const ALG_TYPE_ANY = 0& 
Private Const ALG_TYPE_BLOCK = 1536& 
Private Const CALG_3DES = (ALG_CLASS_DATA_ENCRYPT Or ALG_TYPE_BLOCK Or ALG_SID_3DES) 
Private Const CALG_SHA1 = (ALG_CLASS_HASH Or ALG_TYPE_ANY Or ALG_SID_SHA1) 
Private Const CRYPT_SILENT = &H40& 
Private Const CRYPT_VERIFYCONTEXT = &HF0000000 
Private Const MS_ENHANCED_PROV = "Microsoft Enhanced Cryptographic Provider v1.0" 
Private Const PROV_RSA_FULL = 1& 

Private Declare Function CryptAcquireContextApi Lib "advapi32.dll" Alias "CryptAcquireContextA" (ByRef phProv As Long, ByVal pszContainer As String, ByVal pszProvider As String, ByVal dwProvType As Long, ByVal dwFlags As Long) As Long 
Private Declare Function CryptCreateHashApi Lib "advapi32.dll" Alias "CryptCreateHash" (ByVal hProv As Long, ByVal Algid As Long, ByVal hKey As Long, ByVal dwFlags As Long, ByRef phHash As Long) As Long 
Private Declare Function CryptDecryptApi Lib "advapi32.dll" Alias "CryptDecrypt" (ByVal hKey As Long, ByVal hHash As Long, ByVal Final As Long, ByVal dwFlags As Long, ByRef pbData As Byte, ByRef pdwDataLen As Long) As Long 
Private Declare Function CryptDeriveKeyApi Lib "advapi32.dll" Alias "CryptDeriveKey" (ByVal hProv As Long, ByVal Algid As Long, ByVal hBaseData As Long, ByVal dwFlags As Long, ByRef phKey As Long) As Long 
Private Declare Function CryptDestroyHashApi Lib "advapi32.dll" Alias "CryptDestroyHash" (ByVal hHash As Long) As Long 
Private Declare Function CryptDestroyKeyApi Lib "advapi32.dll" Alias "CryptDestroyKey" (ByVal hKey As Long) As Long 
Private Declare Function CryptEncryptApi Lib "advapi32.dll" Alias "CryptEncrypt" (ByVal hKey As Long, ByVal hHash As Long, ByVal Final As Long, ByVal dwFlags As Long, ByRef pbData As Byte, ByRef pdwDataLen As Long, ByVal dwBufLen As Long) As Long 
Private Declare Function CryptGetHashParamApi Lib "advapi32.dll" Alias "CryptGetHashParam" (ByVal hHash As Long, ByVal dwParam As Long, pbData As Any, pdwDataLen As Long, ByVal dwFlags As Long) As Long 
Private Declare Function CryptHashDataApi Lib "advapi32.dll" Alias "CryptHashData" (ByVal hHash As Long, ByRef pbData As Byte, ByVal dwDataLen As Long, ByVal dwFlags As Long) As Long 
Private Declare Function CryptReleaseContextApi Lib "advapi32.dll" Alias "CryptReleaseContext" (ByVal hProv As Long, ByVal dwFlags As Long) As Long 
Private Declare Function CryptGetKeyParamApi Lib "advapi32.dll" Alias "CryptGetKeyParam" (ByVal hKey As Long, ByVal dwParam As Long, ByRef pbData As Byte, ByRef pdwDataLen As Long, dwFlags As Long) As Long 

Private Declare Sub CopyMemoryApi Lib "kernel32" Alias "RtlMoveMemory" (pDst As Any, pSrc As Any, ByVal ByteLen As Long) 

Function Encrypt(message As String, Password As String) As String 

    If CryptAcquireContextApi(provider, vbNullString, ProviderName, PROV_RSA_FULL, CRYPT_VERIFYCONTEXT) = 0 Then 
     Goto Encrypt_Failure 
    End-If 
    If CryptCreateHashApi(provider, CALG_SHA1, 0&, 0&, hash) = 0 Then 
     Goto Encrypt_Failure 
    End-If 

    buffer = StrConv(Password, vbFromUnicode) 
    If CryptHashDataApi(hash, buffer(0), CLng(UBound(buffer) + 1), 0&) = 0 Then 
     GoTo Encrypt_Failure 
    End If 

    If CryptDeriveKeyApi(provider, CALG_3DES, hash, 1&, key) = 0 Then 
     key = 0 
     GoTo Encrypt_Failure 
    End If 

    length = Len(message) 
    bfrlen = length 
    If CryptEncryptApi(key, 0&, 1&, 0&, ByVal 0&, bfrlen, bfrlen) = 0 Then 
     GoTo Encrypt_Failure 
    End If 

    ReDim buffer(bfrlen - 1) 

    For i = 0 To length - 1 
     buffer(i) = Asc(Mid(message, i + 1, 1)) 
    Next i 

    If CryptEncryptApi(key, 0&, 1&, 0&, buffer(0), length, bfrlen) = 0 Then 
     GoTo Encrypt_Failure 
    End If 

    Call CryptDestroyKeyApi(key) 
    Call CryptReleaseContextApi(provider, 0) 

    Encrypt = left(StrConv(buffer, vbUnicode), length) 

Encrypt_Failure: 
    If key Then 
     Call CryptDestroyKeyApi(key) 
    End If 
    If hash Then 
     Call CryptDestroyHashApi(hash) 
    End If 
    If provider Then 
     Call CryptReleaseContextApi(provider, 0) 
    End If 

Exit Function 

여기에 새로운 C# 버전입니다 :

public byte[] Encrypt(string source, string pass) 
    { 
     byte[] password = Encoding.UTF8.GetBytes(pass); 
     byte[] resultArray = null; 
     byte[] streamToEncrypt = Encoding.UTF8.GetBytes(source); 

     if(streamToEncrypt.Length % 8 != 0) 
     { 
      byte[] inputArray = new byte[streamToEncrypt.Length + (8 - (streamToEncrypt.Length % 8))]; //add padding to the end to make the message groups of 8 bytes 
      int i = 0; 
      foreach (byte element in streamToEncrypt) 
      { 
       inputArray[i] = element; 
       i++; 
      } 
      streamToEncrypt = inputArray; 
     } 


     using (TripleDESCryptoServiceProvider prov3des = new TripleDESCryptoServiceProvider()) 
     { 

      prov3des.Mode = CipherMode.CBC; 
      prov3des.Padding = PaddingMode.PKCS7; 
      prov3des.IV = new byte[] { 0, 0, 0, 0, 0, 0, 0, 0 }; //8 bytes, zero-ed 

      using (PasswordDeriveBytes pdb = new PasswordDeriveBytes(password, null)) //No salt needed here 
      { 
       prov3des.Key = pdb.CryptDeriveKey("TripleDES", "SHA1", 168, new byte[] { 0, 0, 0, 0, 0, 0, 0, 0 }); 
      } 

      ICryptoTransform cTransform = prov3des.CreateEncryptor(); 
      resultArray = cTransform.TransformFinalBlock(streamToEncrypt,0,streamToEncrypt.Length); 

     } 

     return resultArray; 

    } 

가 그리고, 위의 링크 된 포스터처럼, 암호의 해시가 동일하고,

여기에 내 옛날 (VB6) 코드입니다 암호화 된 바이트 배열의 처음 8 바이트는 동일합니다. 초기화 벡터를 8 바이트로 설정했지만 암호화 된 데이터의 처음 8 바이트가 동일하면 초기화 벡터가 일치해야합니다. 나는 왜 다음 8 바이트가 다른지 알 수 없다. 내가 여기서 잘못하고있는 아이디어는?

감사합니다.

+0

코드에 붙여 넣기와 관련하여 두 가지 버그가 있습니다. 함수 내에서'password'를 재정의하고 있습니다.'PaddingMode'는'PaddingMode.PKS7'가 아니라'PaddingMode.PKCS7'입니다. 또한 각 버전에 대한 샘플 입력 및 출력을 제공하여 비교할 수 있다면 도움이 될 것입니다. – Bobson

+1

버그 수정을 위해 코드를 수정했습니다. 그들은 실제 코드에 존재하지 않습니다. 암호화 ("시험" "이는 테스트입니다") : VB6에서 결과 : 여기서 출력의' {88 241 158 115 160 765 122 202 119 153 101 247 249 57 140}' 결과 from C# : '{88 241 158 115 160 7 65 122 45 163 192 121 255 202 43 69 175 83 151 24 25 121 240 70}' vb6의 결과가 16이라고 입력하면 알 수 있습니다. 바이트는 길지만 C#의 결과는 24 바이트입니다. 나는 또한 내가 어딘가에 선을 넘어 섰다는 것을 나타내고 있다고 생각하고있다. – zeusalmighty

답변

1

사실 아주 단순한 해결책이 있지만 분명하지는 않습니다. "이 데이터가 정확히 8의 배수가 아닌 경우 패드에 붙여 넣으십시오."라는 코드 블록이 있습니다. 그러나이 값은 이고 암호화하는 값은이므로 다른 결과가 나옵니다.

블록 if(streamToEncrypt.Length.Dump("Length") % 8 != 0) {} 전체를 제거하기 만하면 예상되는 결과가 나옵니다.

+0

우리는 원숭이의 삼촌이 될 것입니다. 초기 비교를 할 때, 이전 VB6 코드로 전달 된 바이트 배열이 길이를 검색하기 위해 첫 번째'CryptEncrypt()'호출로 채워진 다음 ReDim 버퍼 (bfrlen - 1)가 뒤 따르는 것을 보았습니다 for 루프에 의해 나는 단지 그것을 모방했다. 완벽하게 작동합니다! 감사! – zeusalmighty

+0

@zeusalmighty - 필자의 생각에 C# 라이브러리는 필요에 따라 자동 패드가 가능합니다. 때로는 눈의 또 다른 쌍이 필요합니다. 다행스럽게 도울 수있어! – Bobson

+0

그냥 그것에 대해 생각; C# 코드가 아닌 vb6 코드에서 패딩이 어떻게 작동합니까? 아마도 블록 암호에서 블록을 한 번에 (64 비트 오른쪽?) 인코딩하므로 패딩 된 16 바이트 배열은 패딩되지 않은 14 바이트 배열과 동일한 결과를 반환해야합니다. 아니요? – zeusalmighty

1

처음 8 바이트가 같고 나머지가 동일하지 않으면 원래 알고리즘과 다른 피드백 양식을 사용하는 것처럼 들립니다. 다른 값의 CipherMode를 사용해보십시오. 그 중 하나가 당신에게 일치하는지 알려줍니다.

+0

그건 내 첫 번째 생각 이었어. 열거 된 모든 값을 사용하지 않으려 고 시도했습니다. – zeusalmighty