简体   繁体   中英

How do I get this to decode (Perl cbc-crypt to C cbc_crypt conversion)

I'm trying to be able to decode encoded strings that were encoded using a perl script. The reason I can't just change the perl script is because a lot of important data already exists that was saved via the perl script and it would be too much to decode in perl and recode another way. It's easier just to keep things in place (for now). However, the compiled perl code is 2MB. I want to write it in C so the executable will be a smaller size.

What I have so far is below. It won't work though. It basically gives me junk output. The problem I think is that the perl script used a hex-based encryption. How do I go about decoding that? Can someone point me to where I went wrong?

/*
Test to decode perl-encrypted string.
NOTE: Not all code written by me. Function code is either written by or derived
      from code from other people in response to similar questions found on the
      internet.

Required Lib: crypt (-lcrypt)

Perl Code from existing script (that is being converted to C):

    use Crypt::CBC;
    use Crypt::DES;
    my $text = "thisisalongtest";
    my $salt_key = "fOagmJOKu2SF";
    my $cipher = Crypt::CBC->new( -key => $salt_key, -cipher => 'DES' -header => 'none');
    my $enc_text = $cipher->encrypt_hex($text);
Perl crypt functions require libcrypt-cbc-perl & libcrypt-des-perl

Data:

Original Text: thisisalongtest
Salt Key: fOagmJOKu2SF
Resulting Encrypted String: 53616c7465645f5f167ebac84042fe7ceac836e1d3e7d3aa1dfc27e0e8cad0f1

Resulting output:

Decrypted: (unprintable junk characters)
*/

#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <rpc/des_crypt.h>

#define BUFFSIZE 420

int encrypt(char key[9], char salt[9], char pass[BUFFSIZE], int mode);
void enc_from_hex(const char* st, char ret_result[BUFFSIZE]);
int hex_to_int(char c);
int hex_to_ascii(char c, char d);

int main (int argc, char *argv[]) {

    int err;
    char passwd[BUFFSIZE];
    char result[BUFFSIZE];
    char key[13];

    sprintf(key,"fOagmJOKu2SF");

    /* Change this from hex pairs to ASCII */
    sprintf(passwd, "53616c7465645f5f167ebac84042fe7ceac836e1d3e7d3aa1dfc27e0e8cad0f1");
    enc_from_hex(passwd, result);

    /* Decrypt */
    err = encrypt(key, "", result, 1); /* DO NOT use 'NULL' for 2nd parameter! */
    if(err != 0) {
        printf("Error.\n");
    } else {
        printf("Decrypted: %s\n", result);
    }

    return 0;
}

/* Encryption */
int encrypt(char key[13], char salt[13], char pass[BUFFSIZE], int mode){
    char temp[13];
    strcpy(temp, salt);
    int buffsize;
    int errcode;

    des_setparity(key);
    buffsize = strlen(pass);

    /* Pad pass to ensure size is divisable by 8.*/
    while (buffsize % 8 && buffsize<BUFFSIZE) {
        pass[buffsize++] = '\0';
    }

    /* Determine Function */
    if(mode == 1) {
        errcode = cbc_crypt(key, pass, buffsize, DES_DECRYPT | DES_SW, temp);
    } else {
        errcode = cbc_crypt(key, pass, buffsize, DES_ENCRYPT | DES_SW, temp);
    }

    if (DES_FAILED(errcode) || strcmp(pass, "") == 0) {
        return errcode;
    }

    return errcode;
}

/* Hex conversion functions */
void enc_from_hex(const char* st, char ret_result[BUFFSIZE]) {
    char temp[2];

    int length = strlen(st);
    int i;
    char buf = 0;
    for(i = 0; i < length; i++) {
        if(i % 2 != 0) {
            sprintf(temp, "%c", hex_to_ascii(buf, st[i]));
            strcat(ret_result, temp);
        } else {
            buf = st[i];
        }
    }

}

int hex_to_int(char c) {
    int first = c / 16 - 3;
    int second = c % 16;
    int result = first*10 + second;
    if(result > 9) result--;
    return result;
}

int hex_to_ascii(char c, char d) {
    int high = hex_to_int(c) * 16;
    int low = hex_to_int(d);
    return high+low;
}

As @ikegami points out in the comments the value passed to key Crypt::CBC is not really a key. This value is passed to another function which combines it with a random salt then hashes it to generate the real key and initialization vector. The random salt is saved with the cypher text the idea being if you encrypt the same data with the same key more than once the output will be different every time.

If you convert your encrypted string to ascii you will notice the first eight characters spell Salted__ , this corresponds to the format used by OpenSSL.

See below for a somewhat crude example using OpenSSL's EVP api:

//
// compile with: gcc -o crypt crypt.c -lssl -lcrypto
//
#include <stdio.h>
#include <string.h>
#include <openssl/evp.h>

int main(int argc, char *argv[]){
  char pass[]="fOagmJOKu2SF";
  char text[]="53616c7465645f5f167ebac84042fe7ceac836e1d3e7d3aa1dfc27e0e8cad0f1";

  int i = 0;
  char *pos=text;
  while(*pos){ // converts hex string to binary in place
    sscanf(pos, "%2hhx", &text[i++]);
    pos += 2;
  } text[i]=0;

  EVP_CIPHER_CTX ctx;
  unsigned char key[8] = {0};
  unsigned char  iv[8] = {0};

  int len;
  char *clear = malloc(strlen(text)); 

  //                                             v-> First Charicter after 'Salted__' 
  EVP_BytesToKey(EVP_des_cbc(), EVP_md5(), &text[8], pass, strlen(pass), 1, key, iv);

  EVP_DecryptInit(&ctx, EVP_des_cbc(), key, iv);
  //                                          v-> Cypertext starts after salt
  EVP_DecryptUpdate(&ctx, clear, &len, &text[16], strlen(text)-15);
  EVP_DecryptFinal(&ctx, clear, &len);

  printf("%s\n", clear);
  return 0;
}

http://www.ict.griffith.edu.au/anthony/info/crypto/openssl.hints https://www.openssl.org/docs/manmaster/crypto/EVP_BytesToKey.html https://www.openssl.org/docs/manmaster/crypto/EVP_EncryptInit.html

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