AES-CBC 加密算法的原理与代码实现

Posted by Johan Niu on December 10, 2017

AES CBC 加密算法的原理与代码实现

算法原理

python 代码实现

account_encrypt.py

#coding:utf-8
from Crypto.Cipher import AES  # pip install pycrypto
import binascii
import base64

class Account_encrypt:

	def __init__(self):
		self.__key = '1234567890abcdef'
		self.__iv = '1234567890abcdef'

	def encrypt(self,user):
		Bloack_Size=AES.block_size
		pad = lambda s: s + (Bloack_Size - len(s) % Bloack_Size) * chr(Bloack_Size - len(s) % Bloack_Size) 
		aes = AES.new(self.__key, AES.MODE_CBC, self.__iv)
		return base64.b64encode(aes.encrypt(pad(user)))

	def decrypt(self,encrypt_user):
		unpad = lambda s : s[0:-ord(s[-1])]
		aes =AES.new(self.__key,AES.MODE_CBC,self.__iv)
		return unpad(aes.decrypt(base64.b64decode(encrypt_user)))

if __name__ == "__main__" :
	ae = Account_encrypt()
	username = "test"
	print ae.encrypt(username)
	print ae.decrypt(ae.encrypt(username))

模块使用方法

第一种 不推荐

import account_encrypt
ae = account_encrypt.Account_encrypt();
ae.encrypt(username)

虽然通过ae.__key无法访问key与iv,但是通过ae._Account_encrtpy__key可以访问。

__双下划线的主要作用是用来区别与子类的成员变量

第二种 推荐

from account_encrypt import Account_encrypt
ae = Account_encrypt()
ae.encrypt(username)

C# 代码实现

using System;
using System.Diagnostics;
using System.IO;
using System.Security.Cryptography;
using System.Text;

namespace AES_CBC
{
    class Program
    {
        static void Main(string[] args)
        {
            try
            {
                Trace.Listeners.Clear();  //清除系统监听器 (就是输出到Console的那个)
                Trace.Listeners.Add(new MyTraceListener()); //添加MyTraceListener实例
                string _key = "znxckjhqiweuiq19";
                var _iv = "o1i23hbxcb1piw32";
                string username = "niuyuehan";  //"kevinwangxiaoning" 测试当用户名长度超过十六位时。
                string encryptResult = AES_encrypt(username, _key, _iv);
                Console.WriteLine("AES_encrypt function exe result:\n {0}", encryptResult);
                string decryptResult = AES_decrypt(encryptResult, _key, _iv);
                Console.WriteLine("AES_decrypt function exe result:\n {0}",decryptResult);
            }
            catch (Exception e)
            {
                Trace.WriteLine("异常出现: " + e.Message);
            }
        }
        
        /*
        * Encrypt method
        * Both Keys and IVs need to be 16 characters. 
        */ 
        static String AES_encrypt(String Input, String AES_Key, String AES_IV)
        {
            // Create encryptor
            var aes = new RijndaelManaged();
            aes.KeySize = 128;
            aes.BlockSize = 128;
            aes.Padding = PaddingMode.PKCS7;
            aes.Key = Encoding.Default.GetBytes(AES_Key);
            aes.IV = Encoding.Default.GetBytes(AES_IV);
            //aes.IV = Convert.FromBase64String(AES_IV);
            var encrypt = aes.CreateEncryptor(aes.Key, aes.IV);
            // Encrypt Input
            byte[] xBuff = null;
            using (var ms = new MemoryStream())
            {
                // Convert from UTF-8 String to byte array, write to memory stream and encrypt, then convert to byte array
                using (var cs = new CryptoStream(ms, encrypt, CryptoStreamMode.Write))
                {
                    byte[] xXml = Encoding.UTF8.GetBytes(Input);
                    cs.Write(xXml, 0, xXml.Length);
                }
                xBuff = ms.ToArray();
            }
 
            // Convert from byte array to base64 string then return
            String Output = Convert.ToBase64String(xBuff);
            return Output;
        }
        /*
        * Decrypt method
        * Both Keys and IVs need to be 16 characters. 
        */ 
        static String AES_decrypt(String Input, String AES_Key, String AES_IV)
        {
            // Create decryptor
            RijndaelManaged aes = new RijndaelManaged();
            aes.KeySize = 128;
            aes.BlockSize = 128;
            aes.Mode = CipherMode.CBC;
            aes.Padding = PaddingMode.PKCS7;
            //aes.Key = Convert.FromBase64String(AES_Key);
            aes.Key = Encoding.Default.GetBytes(AES_Key);
            aes.IV = Encoding.Default.GetBytes(AES_IV);
            var decrypt = aes.CreateDecryptor();
 
            // Decrypt Input
            byte[] xBuff = null;
            using (var ms = new MemoryStream())
            {
                // Convert from base64 string to byte array, write to memory stream and decrypt, then convert to byte array.
                using (var cs = new CryptoStream(ms, decrypt, CryptoStreamMode.Write))
                {
                    byte[] xXml = Convert.FromBase64String(Input);
                    cs.Write(xXml, 0, xXml.Length);
                }
                xBuff = ms.ToArray();
            }
 
            // Convert from byte array to UTF-8 string then return
            String Output = Encoding.UTF8.GetString(xBuff);
            return Output;
        }
    }
}

C 语言实现

/*
 ============================================================================
 Name        : aes_cbc.c
 Author      : Johan Niu
 Version     :
 Copyright   : Your copyright notice
 Description : Hello World in C, Ansi-style
 ============================================================================
 */

#include <openssl/conf.h>
#include <openssl/evp.h>
#include <openssl/err.h>
#include <openssl/pem.h>
#include <string.h>


char *base64encode (const void *b64_encode_this, int encode_this_many_bytes){
    BIO *b64_bio, *mem_bio;      //Declares two OpenSSL BIOs: a base64 filter and a memory BIO.
    BUF_MEM *mem_bio_mem_ptr;    //Pointer to a "memory BIO" structure holding our base64 data.
    b64_bio = BIO_new(BIO_f_base64());                      //Initialize our base64 filter BIO.
    mem_bio = BIO_new(BIO_s_mem());                           //Initialize our memory sink BIO.
    BIO_push(b64_bio, mem_bio);            //Link the BIOs by creating a filter-sink BIO chain.
    BIO_set_flags(b64_bio, BIO_FLAGS_BASE64_NO_NL);  //No newlines every 64 characters or less.
    BIO_write(b64_bio, b64_encode_this, encode_this_many_bytes); //Records base64 encoded data.
    BIO_flush(b64_bio);   //Flush data.  Necessary for b64 encoding, because of pad characters.
    BIO_get_mem_ptr(mem_bio, &mem_bio_mem_ptr);  //Store address of mem_bio's memory structure.
    BIO_set_close(mem_bio, BIO_NOCLOSE);   //Permit access to mem_ptr after BIOs are destroyed.
    BIO_free_all(b64_bio);  //Destroys all BIOs in chain, starting with b64 (i.e. the 1st one).
    BUF_MEM_grow(mem_bio_mem_ptr, (*mem_bio_mem_ptr).length + 1);   //Makes space for end null.
    (*mem_bio_mem_ptr).data[(*mem_bio_mem_ptr).length] = '\0';  //Adds null-terminator to tail.
    return (*mem_bio_mem_ptr).data; //Returns base-64 encoded data. (See: "buf_mem_st" struct).
}

char *base64decode (const void *b64_decode_this, int decode_this_many_bytes){
    BIO *b64_bio, *mem_bio;      //Declares two OpenSSL BIOs: a base64 filter and a memory BIO.
    char *base64_decoded = calloc( (decode_this_many_bytes*3)/4+1, sizeof(char) ); //+1 = null.
    b64_bio = BIO_new(BIO_f_base64());                      //Initialize our base64 filter BIO.
    mem_bio = BIO_new(BIO_s_mem());                         //Initialize our memory source BIO.
    BIO_write(mem_bio, b64_decode_this, decode_this_many_bytes); //Base64 data saved in source.
    BIO_push(b64_bio, mem_bio);          //Link the BIOs by creating a filter-source BIO chain.
    BIO_set_flags(b64_bio, BIO_FLAGS_BASE64_NO_NL);          //Don't require trailing newlines.
    int decoded_byte_index = 0;   //Index where the next base64_decoded byte should be written.
    while ( 0 < BIO_read(b64_bio, base64_decoded+decoded_byte_index, 1) ){ //Read byte-by-byte.
        decoded_byte_index++; //Increment the index until read of BIO decoded data is complete.
    } //Once we're done reading decoded data, BIO_read returns -1 even though there's no error.
    BIO_free_all(b64_bio);  //Destroys all BIOs in chain, starting with b64 (i.e. the 1st one).
    return base64_decoded;        //Returns base-64 decoded data with trailing null terminator.
}

int main (void)
{

  /* A 128 bit key */
  unsigned char *key = (unsigned char *)"1234567890123456";

  /* A 128 bit IV */
  unsigned char *iv = (unsigned char *)"1234567890123456";

  /* Message to be encrypted */
  unsigned char *plaintext =
                (unsigned char *)"niuyuehan";

  /* Buffer for ciphertext. Ensure the buffer is long enough for the
   * ciphertext which may be longer than the plaintext, dependant on the
   * algorithm and mode
   */
  unsigned char ciphertext[128];

  /* Buffer for the decrypted text */
  unsigned char decryptedtext[128];

  int decryptedtext_len, ciphertext_len;
  /* Encrypt the plaintext */
  ciphertext_len = aes_cbc_encrypt(plaintext, strlen ((char *)plaintext), key, iv,
                            ciphertext);

  /*
  //Do something useful with the ciphertext here
  printf("Ciphertext is:\n");
  BIO_dump_fp (stdout, (const char *)ciphertext, ciphertext_len);
  */

  /* Decrypt the ciphertext */
  decryptedtext_len = aes_cbc_decrypt(ciphertext, ciphertext_len, key, iv,
    decryptedtext);

  /* Add a NULL terminator. We are expecting printable text */
  decryptedtext[decryptedtext_len] = '\0';

  /* Show the decrypted text */
  char *base64_encryted_usename = base64encode(ciphertext, ciphertext_len);
  printf("base64_encryted_usename is: \n%s\n", base64_encryted_usename);


  printf("Decrypted text is:\n");
  printf("%s\n", decryptedtext);


  return 0;
}

void handleErrors(void)
{
  ERR_print_errors_fp(stderr);
  abort();
}

int aes_cbc_encrypt(unsigned char *plaintext, int plaintext_len, unsigned char *key,
  unsigned char *iv, unsigned char *ciphertext)
{
  EVP_CIPHER_CTX *ctx;

  int len;

  int ciphertext_len;

  /* Create and initialise the context */
  if(!(ctx = EVP_CIPHER_CTX_new())) handleErrors();

  /* Initialise the encryption operation. IMPORTANT - ensure you use a key
   * and IV size appropriate for your cipher
   * In this example we are using 256 bit AES (i.e. a 256 bit key). The
   * IV size for *most* modes is the same as the block size. For AES this
   * is 128 bits */
  if(1 != EVP_EncryptInit_ex(ctx, EVP_aes_128_cbc(), NULL, key, iv))
    handleErrors();

  /* Provide the message to be encrypted, and obtain the encrypted output.
   * EVP_EncryptUpdate can be called multiple times if necessary
   */
  if(1 != EVP_EncryptUpdate(ctx, ciphertext, &len, plaintext, plaintext_len))
    handleErrors();
  ciphertext_len = len;

  /* Finalise the encryption. Further ciphertext bytes may be written at
   * this stage.
   */
  if(1 != EVP_EncryptFinal_ex(ctx, ciphertext + len, &len)) handleErrors();
  ciphertext_len += len;

  /* Clean up */
  EVP_CIPHER_CTX_free(ctx);

  return ciphertext_len;
}

int aes_cbc_decrypt(unsigned char *ciphertext, int ciphertext_len, unsigned char *key,
  unsigned char *iv, unsigned char *plaintext)
{
  EVP_CIPHER_CTX *ctx;

  int len;

  int plaintext_len;

  /* Create and initialise the context */
  if(!(ctx = EVP_CIPHER_CTX_new())) handleErrors();

  /* Initialise the decryption operation. IMPORTANT - ensure you use a key
   * and IV size appropriate for your cipher
   * In this example we are using 256 bit AES (i.e. a 256 bit key). The
   * IV size for *most* modes is the same as the block size. For AES this
   * is 128 bits */
  if(1 != EVP_DecryptInit_ex(ctx, EVP_aes_128_cbc(), NULL, key, iv))
    handleErrors();

  /* Provide the message to be decrypted, and obtain the plaintext output.
   * EVP_DecryptUpdate can be called multiple times if necessary
   */
  if(1 != EVP_DecryptUpdate(ctx, plaintext, &len, ciphertext, ciphertext_len))
    handleErrors();
  plaintext_len = len;

  /* Finalise the decryption. Further plaintext bytes may be written at
   * this stage.
   */
  if(1 != EVP_DecryptFinal_ex(ctx, plaintext + len, &len)) handleErrors();
  plaintext_len += len;

  /* Clean up */
  EVP_CIPHER_CTX_free(ctx);

  return plaintext_len;
}