openssl学习篇之base64编码、解码 【迁入】

    因为拓展开发平台的原因,想把某网站的功能移植到手机客户端。决定先从IOS下手,但是服务器上的应用是基于 .net开发,
加密和解密算法与 objective-c下的算法并不一致,由此产生使用openssl库的想法,以确保在 IOS 和Android上都能运行一致。
但是openssl并不是有现成的函数供调用,需要自己写接口。
就以最基本的base64 编码和解码为例,也花了笔者若干天时间,现将过程记录如下 ,以飨后者。

步骤 1.
    当然是从 www.openssl.org 处下载 openssl 库 ,当时版本是 1.0.1c,下载完了,要安装cygwin或是active perl
 将压缩包编译成能在windows平台下使用的 dll和lib (这儿就不再赘述了,网上N多资料)

步骤 2.
     由于base64编码的运算在 openssl中有很多的实现方式: BIO EVP,后者新点,但是也是自己写步骤,
     
         a.可以直接使用EVP_EncodeBlock(...) / EVP_DecodeBlock(...) 编码、解码

         b.使用EVP_EncodeInit + EVP_EncodeUpdate + EVP_EncodeFinal / EVP_DecodeInit + EVP_DecodeUpdate + EVP_DecodeFinal 组合进行
     
          经过比照后,采用了后一种方式
步骤 3.
     写自己的接口
 
     接口中调用的函数声明如下:
    
     #define IN
     #define OUT

     extern "C" __declspec(dllexport) int base64encode(
       IN const unsigned char *in,//输入字串
       IN int inl,//输入字串的长度
       IN OUT unsigned char *out
       );

     extern "C" __declspec(dllexport) int base64decode(
       IN const unsigned char *in,//输入字串
       IN int inl,
       IN OUT unsigned char *out
      );

   在需要调用功能的其他项目文件处声明入口
   [DllImport(@"Base64Test.dll", CharSet = CharSet.Auto, EntryPoint = "base64encode", CallingConvention = CallingConvention.Cdecl)]
   public static extern int base64encode(StringBuilder __in, int inl, StringBuilder __out);

   [DllImport(@"Base64Test.dll", CharSet = CharSet.Auto, EntryPoint = "base64decode", CallingConvention = CallingConvention.Cdecl)]
   public static extern int base64decode(StringBuilder __in, int inl, StringBuilder __out);

   在测试时编码一直不正确,出现了字符是非标准ascii码表中的字符,后经朋友 西门 提醒,unsigned char* 可以被当作是 byte[]处理
  
   这样,将入口函数声明修改为
   [DllImport(@"Base64Test.dll", CharSet = CharSet.Auto, EntryPoint = "base64encode", CallingConvention = CallingConvention.Cdecl)]
   public static extern int base64encode(byte[] __in, int inl, byte[] __out);

   [DllImport(@"Base64Test.dll", CharSet = CharSet.Auto, EntryPoint = "base64decode", CallingConvention = CallingConvention.Cdecl)]
   public static extern int base64decode(byte[] __in, int inl, byte[] __out);

   测试后,发现英文字符正常,但是如果有中文字符,就会出现错误,后来发现 是因为传入参数 inl 的问题,在计算时,没有考虑到中文字符 是占3个byte,完全修正后代码如下:

   接口项目名为:  Base64Test
  
  
   /************************************************Base64Test.h*********************************************************************/
 #define IN
 #define OUT
 
 #define SUCCESS 0
 #define INPUT_ERROR -1
 #define OUTPUT_BUFFER_NOT_READY -2
 
 extern "C" __declspec(dllexport) int base64encode(
  IN const unsigned char *in,//输入字串
  IN int inl,//输入字串的长度
  IN OUT unsigned char *out
  );
 
 extern "C" __declspec(dllexport) int base64decode(
  IN const unsigned char *in,//输入字串
  IN int inl,
  IN OUT unsigned char *out
  );
 
 int SizeOfUnsignedChar(unsigned char *p);
 
 
 /*************************************Base64Test.cpp***************************************************************************/
 #include "stdafx.h"
 #include <string.h>
 #include <openssl/sha.h>
 #include <openssl/hmac.h>
 #include <openssl/evp.h>
 #include <openssl/bio.h>
 #include <openssl/buffer.h>
 #include "Base64Test.h"
 
 /**
 *Use EVP to Base64 encode the input byte array to readable text
 */
 int base64encode(
      IN const unsigned char *in,
      IN int inl,
      IN OUT unsigned char *out
      )
 {
  if(in==NULL)
   return INPUT_ERROR;
  if(out==NULL)
   return OUTPUT_BUFFER_NOT_READY;
  if(inl<=0)
   return INPUT_ERROR;
  EVP_ENCODE_CTX ctx;
  int base64Len= (((inl+2)/3)*4)+1;//Base 64 text length
  int pemLen=base64Len+base64Len/64;// PEM adds a newline every 64 bytes
  unsigned char* base64=new unsigned char[pemLen];
  int ret;
  EVP_EncodeInit(&ctx);
  EVP_EncodeUpdate(&ctx,(unsigned char*)base64,&ret,(unsigned char*) in ,inl);
  EVP_EncodeFinal(&ctx,(unsigned char*)&base64[ret],&ret);
  memset(out, 0, ret);
  memcpy(out,base64,ret);
 
  if(base64 != NULL)
  {
   delete [] base64;
   base64 = NULL;
  }
  return ret;
 }
 
 /**
 *Use EVP to Base64 decode the input readable text to original bytes
 */
 int base64decode(IN const unsigned char *in,
      IN int inl,
      IN OUT unsigned char *out
      )
 {
  if(in==NULL)
   return INPUT_ERROR;
  if(out==NULL)
   return OUTPUT_BUFFER_NOT_READY;
  if(inl<=0)
   return INPUT_ERROR;
  EVP_ENCODE_CTX ctx;
  int orgLen = (((inl+2)/4)*3) + 1;
  unsigned char* orgBuf=new unsigned char[orgLen];
  int ret, tmpLen;
  EVP_DecodeInit(&ctx);
  EVP_DecodeUpdate(&ctx,(unsigned char*)orgBuf,&ret,(unsigned char*)in,inl);
  EVP_DecodeFinal(&ctx,(unsigned char*)orgBuf,&tmpLen);
  ret+=tmpLen;
  memset(out, 0, ret);
  memcpy(out,orgBuf,ret);
  if(orgBuf != NULL)
  {
   delete [] orgBuf;
   orgBuf = NULL;
  }
  return ret;//如果成功,返回编码后的字符长度
 }
 
 int SizeOfUnsignedChar(unsigned char *p)
 {
  int n = 0;
  while(*(p++)) 
  {
   n++;
   if(n==INT_MAX)
    return -1;
  }
  return n;
 }
 
 
 
 /*************************************myApp.cs****************************************/
 
 ...
 ...
 private string __Base64Encode(string data)
        {
            if (!string.IsNullOrEmpty(data))
            {
                byte[] encData_byte = System.Text.Encoding.UTF8.GetBytes(data);
                int __inl = System.Text.Encoding.UTF8.GetByteCount(data);//这种长度计算方式考虑了字符中如果有非标字符会占用多个字节的情况,原来就简单的使用了 data.Length
                int base64Len = (((__inl + 2) / 3) * 4) + 1;//Base 64 text length
                int pemLen = base64Len + base64Len / 64;// PEM adds a newline ever
                byte[] retData_byte = new byte[pemLen];
                int __retl = base64encode(encData_byte, __inl, retData_byte);
                int charCount = (new System.Text.UTF8Encoding().GetDecoder()).GetCharCount(retData_byte, 0, retData_byte.Length);
                char[] _ret = new char[charCount];
                (new System.Text.UTF8Encoding().GetDecoder()).GetChars(retData_byte, 0, retData_byte.Length, _ret, 0);
                return System.Text.RegularExpressions.Regex.Replace(new String(_ret), @"[\r\n\s\0]*$", "");
            }
            else
            {
                return "";
            }
        }

        private string __Base64Decode(string data)
        {
            if (!string.IsNullOrEmpty(data))
            {
                byte[] decData_byte = System.Text.Encoding.UTF8.GetBytes(data);
                int __inl = System.Text.Encoding.UTF8.GetByteCount(data);
                int orgLen = (((__inl + 2) / 4) * 3) + 1;
                byte[] retData_byte = new byte[orgLen];
                int __retl = base64decode(decData_byte, __inl, retData_byte);
                int charCount = (new System.Text.UTF8Encoding().GetDecoder()).GetCharCount(retData_byte, 0, retData_byte.Length);
                char[] _ret = new char[charCount];
                (new System.Text.UTF8Encoding().GetDecoder()).GetChars(retData_byte, 0, retData_byte.Length, _ret, 0);
                return System.Text.RegularExpressions.Regex.Replace(new String(_ret), @"[\r\n\s\0]*$", "");
            }
            else
            {
                return "";
            }
        }
  
  
  
/*********************************************************ACKNOWLEDGE*****************************************************************************/ 
感谢友人西门提醒了我 byte[]的关键作用、 Base64Test.cpp 中代码参考了CSDN 网友的博客

=====================================================

来源:CSDN 迁入

原文:https://blog.csdn.net/alpha_pluto/article/details/50930817

时间: 2016-03-19 14:06:38

 

 

 

 

 

 

 

 

 

 


  

posted @ 2020-05-28 13:51  哲明  阅读(631)  评论(0)    收藏  举报