zoukankan      html  css  js  c++  java
  • Java和C/C++进行DES/AES密文传输(借鉴)

    Java和C/C++进行DES/AES密文传输

    声明:对于新手来说很难解决的一个问题,终于在非常煎熬之后找到这篇文章,所以借鉴过来。原文地址http://blog.sina.com.cn/s/blog_48d4cf2d0101eqdf.html。
    本来觉得DES、AES这种流行加密算法,使用起来应该很简单。但研究后发现有两个变数:
    1)分块的方式。加密是逐块进行的。分块方法有:CBC、ECB、CFB……
    2)padding的方式。当数据的位数不及块的大小时,需要填充。填充方式有:NoPadding、PKCS5Padding……
    如果加解密端采用不同的分块方式或padding方式,即使都是采用DES/AES算法,同样无法解密成功。上次需要C端和Java端进行密文传输,就跪在这一点上(那时候没时间研究)。
    参考文章:Java AES算法和openssl配对 ,主要通过其了解openssl里比较高级的EVP系列API(其默认padding和java一样都是PKCS5Padding),节约了搜索时间。
    贴代码了,以下代码测试通过了。Java和C++均可以正确解密对方的密文。
    约定:分块和padding采用Java默认的 ECB + PKCS5Padding。
    加密程序,输入是"src.txt"文件(加密无需纯文本,只是为了可读性),输出保存在"enc.bin"文件。
    解码程序,输入是"enc.bin"文件,输出保存在"dec.txt"文件。
    ------------------------------------------
    DES:
    Java加密代码:
    import java.io.File;
    import java.io.FileInputStream;
    import java.io.FileOutputStream;
    import java.security.GeneralSecurityException;

    import javax.crypto.Cipher;
    import javax.crypto.spec.SecretKeySpec;

    public class DesEnc {
        public static byte[] desEncrypt(byte[] source, byte rawKeyData[])
                throws GeneralSecurityException {
            // 处理密钥
            SecretKeySpec key = new SecretKeySpec(rawKeyData, "DES");
            // 加密
            Cipher cipher = Cipher.getInstance("DES/ECB/PKCS5Padding");
            cipher.init(Cipher.ENCRYPT_MODE, key);
            return cipher.doFinal(source);
        }
        public static void main(String[] args) throws Exception {
            // DES算法要求密鈅64位(8字节)
            byte rawKeyData[] = "hellodes".getBytes("UTF-8");
            // 读取文件内容(为了简单忽略错误处理)
            File file = new File("src.txt");
            byte[] source = new byte[(int) file.length()];
            FileInputStream is = new FileInputStream(file);
            is.read(source, 0, (int) file.length());
            is.close();
            // 加密
            byte[] enc = desEncrypt(source, rawKeyData);
            System.out.println("desEncrypt:" + source.length + "->" + enc.length);
            // 输出到文件
            FileOutputStream os = new FileOutputStream(new File("enc.bin"));
            os.write(enc, 0, enc.length);
            os.close();
        }
    }
    Java解密代码:
    import java.io.File;
    import java.io.FileInputStream;
    import java.io.FileOutputStream;
    import java.security.GeneralSecurityException;

    import javax.crypto.Cipher;
    import javax.crypto.spec.SecretKeySpec;

    public class DesDec {
        public static byte[] desDecrypt(byte[] data, byte rawKeyData[])
                throws GeneralSecurityException {
            // 处理密钥
            SecretKeySpec key = new SecretKeySpec(rawKeyData, "DES");
            // 解密
            Cipher cipher = Cipher.getInstance("DES/ECB/PKCS5Padding");
            cipher.init(Cipher.DECRYPT_MODE, key);
            return cipher.doFinal(data);
        }
        public static void main(String[] args) throws Exception {
            // DES算法要求密鈅64位(8字节)
            byte rawKeyData[] = "hellodes".getBytes("UTF-8");
            // 读取文件内容(为了简单忽略错误处理)
            File file = new File("enc.bin");
            byte[] data = new byte[(int) file.length()];
            FileInputStream is = new FileInputStream(file);
            is.read(data, 0, (int) file.length());
            is.close();
            // 加密
            byte[] dec = desDecrypt(data, rawKeyData);
            System.out.println("desDecrypt:" + data.length + "->" + dec.length);
            // 输出到文件
            FileOutputStream os = new FileOutputStream(new File("dec.txt"));
            os.write(dec, 0, dec.length);
            os.close();
        }
    }
    C++加密代码:
    #include 见后文

    // 注意:参数和返回值全部是二进制数据
    std::string desEncrypt(const std::string& source, const std::string& key)
    {
        EVP_CIPHER_CTX ctx;
        EVP_CIPHER_CTX_init(&ctx);
        int ret = EVP_EncryptInit_ex(&ctx, EVP_des_ecb(), NULL, (const unsigned char*)key.data(), NULL);
        assert(ret == 1);
        unsigned char* result = new unsigned char[source.length() + 64]; // 弄个足够大的空间
        int len1 = 0;
        ret = EVP_EncryptUpdate(&ctx, result, &len1, (const unsigned char*)source.data(), source.length());
        assert(ret == 1);
        int len2 = 0;
        ret = EVP_EncryptFinal_ex(&ctx, result+len1, &len2); 
        assert(ret == 1);
        std::cout << len1 << "," << len2 << std::endl;
        ret = EVP_CIPHER_CTX_cleanup(&ctx);
        assert(ret == 1);
        std::string res((char*)result, len1+len2);
        delete[] result;
        return res;
    }
    int main()
    {
        std::string key("hellodes", 8);    // 二进制数据,而不是以0结尾的字符串
        // 读取文件内容(简单起见认为文件内容<100K)
        char buf[1024*100];
        FILE* fp = fopen("src.txt", "rb");
        int bytes = fread(buf, 1, 1024*100, fp);
        fclose(fp);
        std::string source(buf, bytes); // 二进制数据
        // 加密
        std::string enc = desEncrypt(source, key);
        std::cout << "desEncrypt:" << source.length() << "->" << enc.length() << std::endl;
        // 输出到文件
        fp =  fopen("enc.bin", "wb");
        fwrite(enc.data(), 1, enc.length(), fp);
        fclose(fp);
    }
    C++解密代码:
    #include 见后文

    // 注意:参数和返回值全部是二进制数据
    std::string desDecrypt(const std::string& ciphertext, const std::string& key)
    {
        EVP_CIPHER_CTX ctx;
        EVP_CIPHER_CTX_init(&ctx);
        int ret = EVP_DecryptInit_ex(&ctx, EVP_des_ecb(), NULL, (const unsigned char*)key.data(), NULL);
        assert(ret == 1);
        unsigned char* result = new unsigned char[ciphertext.length() + 64]; // 弄个足够大的空间
        int len1 = 0;
        ret = EVP_DecryptUpdate(&ctx, result, &len1, (const unsigned char*)ciphertext.data(), ciphertext.length());
        assert(ret == 1);
        int len2 = 0;
        ret = EVP_DecryptFinal_ex(&ctx, result+len1, &len2); 
        assert(ret == 1);
        std::cout << len1 << "," << len2 << std::endl;
        ret = EVP_CIPHER_CTX_cleanup(&ctx);
        assert(ret == 1);
        std::string res((char*)result, len1+len2);
        delete[] result;
        return res;
    }
    int main()
    {
        std::string key("hellodes", 8);    // 二进制数据,而不是以0结尾的字符串
        // 读取文件内容(简单起见认为文件内容<100K)
        char buf[1024*100];
        FILE* fp = fopen("enc.bin", "rb");
        int bytes = fread(buf, 1, 1024*100, fp);
        fclose(fp);
        std::string data(buf, bytes); // 二进制数据
        // 加密
        std::string dec = desDecrypt(data, key);
        std::cout << "desDecrypt:" << data.length() << "->" << dec.length() << std::endl;
        // 输出到文件
        fp =  fopen("dec.txt", "wb");
        fwrite(dec.data(), 1, dec.length(), fp);
        fclose(fp);
    }
    ------------------------------------------
    AES:
    Java加密代码:
    import java.io.File;
    import java.io.FileInputStream;
    import java.io.FileOutputStream;
    import java.security.GeneralSecurityException;

    import javax.crypto.Cipher;
    import javax.crypto.spec.SecretKeySpec;

    public class AesEnc {
        public static byte[] aesEncrypt(byte[] source, byte rawKeyData[])
                throws GeneralSecurityException {
            // 处理密钥
            SecretKeySpec key = new SecretKeySpec(rawKeyData, "AES");
            // 加密
            Cipher cipher = Cipher.getInstance("AES/ECB/PKCS5Padding");
            cipher.init(Cipher.ENCRYPT_MODE, key);
            return cipher.doFinal(source);
        }
        public static void main(String[] args) throws Exception {
            // AES算法要求密鈅128位(16字节)
            byte rawKeyData[] = "helloaeshelloaes".getBytes("UTF-8");
            // 读取文件内容(为了简单忽略错误处理)
            File file = new File("src.txt");
            byte[] source = new byte[(int) file.length()];
            FileInputStream is = new FileInputStream(file);
            is.read(source, 0, (int) file.length());
            is.close();
            // 加密
            byte[] enc = aesEncrypt(source, rawKeyData);
            System.out.println("aesEncrypt:" + source.length + "->" + enc.length);
            // 输出到文件
            FileOutputStream os = new FileOutputStream(new File("enc.bin"));
            os.write(enc, 0, enc.length);
            os.close();
        }
    }
    Java解密代码:
    import java.io.File;
    import java.io.FileInputStream;
    import java.io.FileOutputStream;
    import java.security.GeneralSecurityException;

    import javax.crypto.Cipher;
    import javax.crypto.spec.SecretKeySpec;

    public class AesDec {
        public static byte[] aesDecrypt(byte[] data, byte rawKeyData[])
                throws GeneralSecurityException {
            // 处理密钥
            SecretKeySpec key = new SecretKeySpec(rawKeyData, "AES");
            // 解密
            Cipher cipher = Cipher.getInstance("AES/ECB/PKCS5Padding");
            cipher.init(Cipher.DECRYPT_MODE, key);
            return cipher.doFinal(data);
        }
        public static void main(String[] args) throws Exception {
            // AES算法要求密鈅128位(16字节)
            byte rawKeyData[] = "helloaeshelloaes".getBytes("UTF-8");
            // 读取文件内容(为了简单忽略错误处理)
            File file = new File("enc.bin");
            byte[] data = new byte[(int) file.length()];
            FileInputStream is = new FileInputStream(file);
            is.read(data, 0, (int) file.length());
            is.close();
            // 加密
            byte[] dec = aesDecrypt(data, rawKeyData);
            System.out.println("desDecrypt:" + data.length + "->" + dec.length);
            // 输出到文件
            FileOutputStream os = new FileOutputStream(new File("dec.txt"));
            os.write(dec, 0, dec.length);
            os.close();
        }
    }
    C++加密代码:
    #include 见后文

    // 注意:参数和返回值全部是二进制数据
    std::string aesEncrypt(const std::string& source, const std::string& key)
    {
        EVP_CIPHER_CTX ctx;
        EVP_CIPHER_CTX_init(&ctx);
        int ret = EVP_EncryptInit_ex(&ctx, EVP_aes_128_ecb(), NULL, (const unsigned char*)key.data(), NULL);
        assert(ret == 1);
        unsigned char* result = new unsigned char[source.length() + 64]; // 弄个足够大的空间
        int len1 = 0;
        ret = EVP_EncryptUpdate(&ctx, result, &len1, (const unsigned char*)source.data(), source.length());
        assert(ret == 1);
        int len2 = 0;
        ret = EVP_EncryptFinal_ex(&ctx, result+len1, &len2); 
        assert(ret == 1);
        std::cout << len1 << "," << len2 << std::endl;
        ret = EVP_CIPHER_CTX_cleanup(&ctx);
        assert(ret == 1);
        std::string res((char*)result, len1+len2);
        delete[] result;
        return res;
    }
    int main()
    {
        std::string key("helloaeshelloaes", 16);    // 二进制数据,而不是以0结尾的字符串
        // 读取文件内容(简单起见认为文件内容<100K)
        char buf[1024*100];
        FILE* fp = fopen("src.txt", "rb");
        int bytes = fread(buf, 1, 1024*100, fp);
        fclose(fp);
        std::string source(buf, bytes); // 二进制数据
        // 加密
        std::string enc = aesEncrypt(source, key);
        std::cout << "aesEncrypt:" << source.length() << "->" << enc.length() << std::endl;
        // 输出到文件
        fp =  fopen("enc.bin", "wb");
        fwrite(enc.data(), 1, enc.length(), fp);
        fclose(fp);
    }
    C++解密代码:
    #include 见后文

    // 注意:参数和返回值全部是二进制数据
    std::string aesDecrypt(const std::string& ciphertext, const std::string& key)
    {
        EVP_CIPHER_CTX ctx;
        EVP_CIPHER_CTX_init(&ctx);
        int ret = EVP_DecryptInit_ex(&ctx, EVP_aes_128_ecb(), NULL, (const unsigned char*)key.data(), NULL);
        assert(ret == 1);
        unsigned char* result = new unsigned char[ciphertext.length() + 64]; // 弄个足够大的空间
        int len1 = 0;
        ret = EVP_DecryptUpdate(&ctx, result, &len1, (const unsigned char*)ciphertext.data(), ciphertext.length());
        assert(ret == 1);
        int len2 = 0;
        ret = EVP_DecryptFinal_ex(&ctx, result+len1, &len2); 
        assert(ret == 1);
        std::cout << len1 << "," << len2 << std::endl;
        ret = EVP_CIPHER_CTX_cleanup(&ctx);
        assert(ret == 1);
        std::string res((char*)result, len1+len2);
        delete[] result;
        return res;
    }
    int main()
    {
        std::string key("helloaeshelloaes", 16);    // 二进制数据,而不是以0结尾的字符串
        // 读取文件内容(简单起见认为文件内容<100K)
        char buf[1024*100];
        FILE* fp = fopen("enc.bin", "rb");
        int bytes = fread(buf, 1, 1024*100, fp);
        fclose(fp);
        std::string data(buf, bytes); // 二进制数据
        // 加密
        std::string dec = aesDecrypt(data, key);
        std::cout << "aesDecrypt:" << data.length() << "->" << dec.length() << std::endl;
        // 输出到文件
        fp =  fopen("dec.txt", "wb");
        fwrite(dec.data(), 1, dec.length(), fp);
        fclose(fp);
    }
    ===============================发表后的分割线===============================
    1、发表后发现C++代码中include类库都由于小括号的缘故,被卡擦掉了。这边补充一下,需要包含以下头文件:
    [string]
    [iostream]
    [stdio.h]
    [assert.h]
    [openssl/objects.h]
    [openssl/evp.h]
    2、DES、AES加密算法都是针对数据块,Java加解密函数参数使用byte数组。C++用std::string,那是因为这是C++中使用byte数组的最简单方式(std::string可以存储二进制数据,很多人没想到吧),缺点是拷贝内存的次数可能会略多些。如果想要优化拷贝效率,可以使用自己封装的Buffer类来代替std::string。
  • 相关阅读:
    仿美团pc,koa+ssr(四)
    基本的数据库操作脚本
    jQuery选择器总结
    经常学习参考网站
    WebAPI
    Sqlserver 基本面试题
    truncate和delete的区别
    Webservice,WCF,WebAPI 之间的区别
    WCF 学习
    学习angularJs(1)--引用文件
  • 原文地址:https://www.cnblogs.com/wanghaiyang1930/p/5474889.html
Copyright © 2011-2022 走看看