GmSSL的编译和使用

个人博客地址:https://blog.yookingh.cn
该文章地址:https://blog.yookingh.cn/dev/201216-gmssl.html

简述

GmSSL是一个开源的密码工具箱,支持SM2/SM3/SM4/SM9/ZUC等国密(国家商用密码)算法、SM2国密数字证书及基于SM2证书的SSL/TLS安全通信协议,支持国密硬件密码设备,提供符合国密规范的编程接口与命令行工具,可以用于构建PKI/CA、安全通信、数据加密等符合国密标准的安全应用。
GmSSL项目是OpenSSL项目的分支,并与OpenSSL保持接口兼容。因此GmSSL可以替代应用中的OpenSSL组件,并使应用自动具备基于国密的安全能力。GmSSL项目采用对商业应用友好的类BSD开源许可证,开源且可以用于闭源的商业应用。
GmSSL项目由北京大学关志副研究员的密码学研究组开发维护,项目源码托管于GitHub。自2014年发布以来,GmSSL已经在多个项目和产品中获得部署与应用,并获得2015年度“一铭杯”中国Linux软件大赛二等奖(年度最高奖项)与开源中国密码类推荐项目。GmSSL项目的核心目标是通过开源的密码技术推动国内网络空间安全建设。

安装

编译环境参考OpenSSL的编译和使用

# 安装 目录 - yooking@ubuntu:~/Downloads/gmssl$
wget https://github.com/guanzhi/GmSSL/archive/master.zip
unzip master.zip

编译

  1. 生成Makefile文件

    ./config生成Makefile文件

    # 目录 - yooking@ubuntu:~/Downloads/gmssl/GmSSL-master$ 
    # 使用./执行config生成Makefile文件
    ./config
    # 报错,信息如下
    Operating system: x86_64-whatever-linux2
    "glob" is not exported by the File::Glob module
    Can't continue after import errors at ./Configure line 18.
    BEGIN failed--compilation aborted at ./Configure line 18.
    "glob" is not exported by the File::Glob module
    Can't continue after import errors at ./Configure line 18.
    BEGIN failed--compilation aborted at ./Configure line 18.
    This system (linux-x86_64) is not supported. See file INSTALL for details.
    

    打开Configure文件,修改line 18

    gedit Configure
    #line 18原始内容如下
    use if $^O ne "VMS", 'File::Glob' => qw/glob/;
    #修改为
    use if $^O ne "VMS", 'File::Glob' => qw/:glob/;
    

    重新执行./config

    ./config
    # 再次报错,信息如下
    Something wrong with this line:
    Program fragment delivered error ``"glob" is not exported by the File::Glob module
    at /home/yooking/Downloads/gmssl/GmSSL-master/test/build.info at ./Configure line 1644.
    

    修改对应文件test/build.info

    gedit test/build.info
    # build.info文件中发现
    use if $^O ne "VMS", 'File::Glob' => qw/glob/;
    # 同样进行修改
    use if $^O ne "VMS", 'File::Glob' => qw/:glob/;
    

    再次执行./config即可生成Makefile文件

  2. 编写编译脚本(参考官方文档)(修改自Github开源项目

    下载开源项目,并根据提示进行了对应的修改,修改请看注释#No

    运行前先执行

    # 请使用你的NDK路径
    export ANDROID_NDK=/home/yooking/Public/android/android-sdk/ndk/android-ndk-r21d
    

    _shared.sh文件

    #!/bin/bash
    
    TOOLS_ROOT=`pwd`
    
    #1 这里使用的 ndk是r21d r21d 不具备 mips 和mips64
    ARCHS=("android" "android-armeabi" "android64-aarch64" "android-x86" "android64")
    # "android-mips" "android-mips64"
    ABIS=("armeabi" "armeabi-v7a" "arm64-v8a" "x86" "x86_64" )
    # "mips" "mips64"
    # Default to API 21 for it is the minimum requirement for 64 bit archs.
    #2 编译64位需要使用21或以上Anddroid-Api
    ANDROID_API=${ANDROID_API:-21}
    NDK=${ANDROID_NDK}
    
    configure() {
      ARCH=$1; OUT=$2; CLANG=${3:-""};
    
      #3 记住这里,很占空间,github项目上并没有对这个进行删除,这里我们会在后续进行删除
      TOOLCHAIN_ROOT=${TOOLS_ROOT}/${OUT}-android-toolchain
    
      if [ "$ARCH" == "android" ]; then
        export ARCH_FLAGS="-mthumb"
        export ARCH_LINK=""
        export TOOL="arm-linux-androideabi"
        NDK_FLAGS="--arch=arm"
      elif [ "$ARCH" == "android-armeabi" ]; then
        export ARCH_FLAGS="-march=armv7-a -mfloat-abi=softfp -mfpu=vfpv3-d16 -mthumb -mfpu=neon"
        export ARCH_LINK="-march=armv7-a -Wl,--fix-cortex-a8"
        export TOOL="arm-linux-androideabi"
        NDK_FLAGS="--arch=arm"
      elif [ "$ARCH" == "android64-aarch64" ]; then
        export ARCH_FLAGS=""
        export ARCH_LINK=""
        export TOOL="aarch64-linux-android"
        NDK_FLAGS="--arch=arm64"
      elif [ "$ARCH" == "android-x86" ]; then
        export ARCH_FLAGS="-march=i686 -mtune=intel -msse3 -mfpmath=sse -m32"
        export ARCH_LINK=""
        export TOOL="i686-linux-android"
        NDK_FLAGS="--arch=x86"
      elif [ "$ARCH" == "android64" ]; then
        export ARCH_FLAGS="-march=x86-64 -msse4.2 -mpopcnt -m64 -mtune=intel"
        export ARCH_LINK=""
        export TOOL="x86_64-linux-android"
        NDK_FLAGS="--arch=x86_64"
      elif [ "$ARCH" == "android-mips" ]; then
        export ARCH_FLAGS=""
        export ARCH_LINK=""
        export TOOL="mipsel-linux-android"
        NDK_FLAGS="--arch=mips"
      elif [ "$ARCH" == "android-mips64" ]; then
        export ARCH="linux64-mips64"
        export ARCH_FLAGS=""
        export ARCH_LINK=""
        export TOOL="mips64el-linux-android"
        NDK_FLAGS="--arch=mips64"
      fi;
    
      [ -d ${TOOLCHAIN_ROOT} ] || python $NDK/build/tools/make_standalone_toolchain.py \
                                         --api ${ANDROID_API} \
                                         --stl libc++ \
                                         --install-dir=${TOOLCHAIN_ROOT} \
                                         $NDK_FLAGS
    
      export TOOLCHAIN_PATH=${TOOLCHAIN_ROOT}/bin
      export NDK_TOOLCHAIN_BASENAME=${TOOLCHAIN_PATH}/${TOOL}
      export SYSROOT=${TOOLCHAIN_ROOT}/sysroot
      export CROSS_SYSROOT=$SYSROOT
      if [ -z "${CLANG}" ]; then
        export CC=${NDK_TOOLCHAIN_BASENAME}-gcc
        export CXX=${NDK_TOOLCHAIN_BASENAME}-g++
      else
        export CC=${TOOLCHAIN_PATH}/clang
        export CXX=${TOOLCHAIN_PATH}/clang++
      fi;
      export LINK=${CXX}
      export LD=${NDK_TOOLCHAIN_BASENAME}-ld
      export AR=${NDK_TOOLCHAIN_BASENAME}-ar
      export RANLIB=${NDK_TOOLCHAIN_BASENAME}-ranlib
      export STRIP=${NDK_TOOLCHAIN_BASENAME}-strip
      export CPPFLAGS=${CPPFLAGS:-""}
      export LIBS=${LIBS:-""}
      export CFLAGS="${ARCH_FLAGS} -fpic -ffunction-sections -funwind-tables -fstack-protector -fno-strict-aliasing -finline-limit=64"
      export CXXFLAGS="${CFLAGS} -std=c++11 -frtti -fexceptions"
      export LDFLAGS="${ARCH_LINK}"
      echo "**********************************************"
      echo "use ANDROID_API=${ANDROID_API}"
      echo "use NDK=${NDK}"
      echo "export ARCH=${ARCH}"
      echo "export NDK_TOOLCHAIN_BASENAME=${NDK_TOOLCHAIN_BASENAME}"
      echo "export SYSROOT=${SYSROOT}"
      echo "export CC=${CC}"
      echo "export CXX=${CXX}"
      echo "export LINK=${LINK}"
      echo "export LD=${LD}"
      echo "export AR=${AR}"
      echo "export RANLIB=${RANLIB}"
      echo "export STRIP=${STRIP}"
      echo "export CPPFLAGS=${CPPFLAGS}"
      echo "export CFLAGS=${CFLAGS}"
      echo "export CXXFLAGS=${CXXFLAGS}"
      echo "export LDFLAGS=${LDFLAGS}"
      echo "export LIBS=${LIBS}"
      echo "**********************************************"
    }
    

    build-gmssl4android.sh文件

    #!/bin/bash
    #
    # Copyright 2016 leenjewel
    # 
    # Licensed under the Apache License, Version 2.0 (the "License");
    # you may not use this file except in compliance with the License.
    # You may obtain a copy of the License at
    # 
    #     http://www.apache.org/licenses/LICENSE-2.0
    # 
    # Unless required by applicable law or agreed to in writing, software
    # distributed under the License is distributed on an "AS IS" BASIS,
    # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
    # See the License for the specific language governing permissions and
    # limitations under the License.
    
    set -u
    
    source ./_shared.sh
    
    # Setup architectures, library name and other vars + cleanup from previous runs
    LIB_NAME="GmSSL-master"
    LIB_DEST_DIR=${TOOLS_ROOT}/libs
    [ -d ${LIB_DEST_DIR} ] && rm -rf ${LIB_DEST_DIR}
    
    #4 这里判断了master.zip压缩包是否存在,如果不存在则下载最新的包,这里建议修改为判断解压后的文件夹是否存在,如果不存在才去重新下载,原因后续解释,例如[-f "GmSSL-master"] || wget https://github.com/guanzhi/GmSSL/archive/master.zip;
    [ -f "master.zip" ] || wget https://github.com/guanzhi/GmSSL/archive/master.zip;
    # Unarchive library, then configure and make for specified architectures
    configure_make() {
      ARCH=$1; ABI=$2;
      rm -rf "${LIB_NAME}"
      
      #5 这里建议修改为 
      #5 if[![-f "GmSSL-master"]];then 
      #5 unzip -o "master.zip"
      #5 fi
      unzip -o "master.zip"
      pushd "${LIB_NAME}"
    
      configure $*
    
      #support openssl-1.0.x
      if [[ $LIB_NAME != "GmSSL-master" ]]; then
        if [[ $ARCH == "android-armeabi" ]]; then
            ARCH="android-armv7"
        elif [[ $ARCH == "android64" ]]; then 
            ARCH="linux-x86_64 shared no-ssl2 no-ssl3 no-hw "
        elif [[ "$ARCH" == "android64-aarch64" ]]; then
            ARCH="android shared no-ssl2 no-ssl3 no-hw "
        fi
      fi
    
    echo "use android api:${ANDROID_API}"
    
      ./Configure $ARCH \
                  --prefix=${LIB_DEST_DIR}/${ABI} \
                  --with-zlib-include=$SYSROOT/usr/include \
                  --with-zlib-lib=$SYSROOT/usr/lib \
                  zlib \
                  no-asm \
                  no-shared \
                  no-unit-test\
                  no-serpent
    
      PATH=$TOOLCHAIN_PATH:$PATH
    
      if make -j4; then
        make install
    
        OUTPUT_ROOT=${TOOLS_ROOT}/../output/android/gmssl-${ABI}
        [ -d ${OUTPUT_ROOT}/include ] || mkdir -p ${OUTPUT_ROOT}/include
        cp -r ${LIB_DEST_DIR}/${ABI}/include/openssl ${OUTPUT_ROOT}/include
    
        [ -d ${OUTPUT_ROOT}/lib ] || mkdir -p ${OUTPUT_ROOT}/lib
        cp ${LIB_DEST_DIR}/${ABI}/lib/libcrypto.a ${OUTPUT_ROOT}/lib
        cp ${LIB_DEST_DIR}/${ABI}/lib/libssl.a ${OUTPUT_ROOT}/lib
      fi;
      popd
    
    }
    
    
    
    for ((i=0; i < ${#ARCHS[@]}; i++))
    do
      if [[ $# -eq 0 ]] || [[ "$1" == "${ARCHS[i]}" ]]; then
        # Do not build 64 bit arch if ANDROID_API is less than 21 which is
        # the minimum supported API level for 64 bit.
        [[ ${ANDROID_API} < 21 ]] && ( echo "${ABIS[i]}" | grep 64 > /dev/null ) && continue;
        configure_make "${ARCHS[i]}" "${ABIS[i]}"
      fi
      
      #6 每次循环体结束,删除后缀为 -android-toolchain的文件,防止占用存储空间过大
      rm -rf ${TOOLS_ROOT}/*-android-toolchain
    done
    

    这里解释下注释#4的理由:
    该Github开源项目已三年没有更新了,或许旧版本可以正常使用,但新版本中确实要修改Configuretest/build.info两个文件后才可以正常使用,因此在下载完毕master包后,需要对zip压缩包内的两个文件进行修改,后续解压后该代码才可正常执行。修改参考1.生成Makefile文件
    如果参照#4#5的修改建议,则可以修改解压后的GmSSL-master内的对应文件。

引用

  1. 配置
    Android Studio - gradle:3.5.2
    ndk - android-ndk-r21d

  2. 新建一个Native C++项目

  3. 将脚本生成的.a文件放在app/libs/下(必须,否则会报错)

  4. 将CMakeLists.txt文件移动到app/

  5. 修改app/buidl.gradle

    android {
        defaultConfig {
            externalNativeBuild {
                cmake {
                    cppFlags "-frtti -fexceptions -lz"
                }
                ndk {
                    abiFilters 'armeabi', 'armeabi-v7a', 'arm64-v8a', 'x86', 'x86_64'
                }
            }
        }
        externalNativeBuild {
            cmake {
                path "CMakeLists.txt"
            }
        }
    }
    
  1. 修改CMakeLists.txt

    # For more information about using CMake with Android Studio, read the
    # documentation: https://d.android.com/studio/projects/add-native-code.html
    
    # Sets the minimum version of CMake required to build the native library.
    
    cmake_minimum_required(VERSION 3.4.1)
    
    # Creates and names a library, sets it as either STATIC
    # or SHARED, and provides the relative paths to its source code.
    # You can define multiple libraries, and CMake builds them for you.
    # Gradle automatically packages shared libraries with your APK.
    
    add_library( # Sets the name of the library.
            native-lib
    
            # Sets the library as a shared library.
            SHARED
    
            # Provides a relative path to your source file(s).
            src/main/cpp/native-lib.cpp
            src/main/cpp/utils.cpp
            )
    
    #引用头文件目录
    include_directories( src/main/cpp/include/)
    include_directories(src/main/cpp)
    add_library(crypto STATIC IMPORTED)
    set_target_properties(crypto
            PROPERTIES IMPORTED_LOCATION ${CMAKE_SOURCE_DIR}/libs/${ANDROID_ABI}/libcrypto.a)
    
    # Searches for a specified prebuilt library and stores the path as a
    # variable. Because CMake includes system libraries in the search path by
    # default, you only need to specify the name of the public NDK library
    # you want to add. CMake verifies that the library exists before
    # completing its build.
    
    find_library( # Sets the name of the path variable.
            log-lib
    
            # Specifies the name of the NDK library that
            # you want CMake to locate.
            log )
    
    # Specifies libraries CMake should link to your target library. You
    # can link multiple libraries, such as libraries you define in this
    # build script, prebuilt third-party libraries, or system libraries.
    
    target_link_libraries( # Specifies the target library.
            native-lib
            crypto
            # Links the target library to the log library
            # included in the NDK.
            ${log-lib} )
    
  1. e_os.h文件和openssl文件夹移动到app/src/main/cpp/include

  2. crypto文件夹移动到app/src/main/cpp

使用

  1. utils.h

    #ifndef MYAPPLICATION2_UTILS_H
    #define MYAPPLICATION2_UTILS_H
    
    unsigned char *hex2bin(const char *data, int size, int *outlen);
    
    char *bin2hex(unsigned char *data, int size);
    
    int b64_op(const unsigned char *in, int in_len, char *out, int out_len, int op);
    
    /*存储文件*/
    int writeBufToFile(char *file, char *buf);
    /*读取文件*/
    int readBufFromFile(char *file, char *buf);
    
    int initEcKey(EC_KEY *ec_key,char * path);
    
    
    #endif //MYAPPLICATION2_UTILS_H
    
  2. utils.cpp

    #include <openssl/bio.h>
    #include <openssl/evp.h>
    #include <fcntl.h>
    #include <e_os.h>
    #include <string>
    #include <openssl/ec.h>
    #include <crypto/ec/ec_lcl.h>
    #include "utils.h"
    
    
    unsigned char *hex2bin(const char *data, int size, int *outlen) {
        int i = 0;
        int len = 0;
        char char1 = '\0';
        char char2 = '\0';
        unsigned char value = 0;
        unsigned char *out = NULL;
    
        if (size % 2 != 0) {
            return NULL;
        }
    
        len = size / 2;
        out = (unsigned char *) malloc(len * sizeof(char) + 1);
        if (out == NULL) {
            return NULL;
        }
    
        while (i < len) {
            char1 = *data;
            if (char1 >= '0' && char1 <= '9') {
                value = (char1 - '0') << 4;
            } else if (char1 >= 'a' && char1 <= 'f') {
                value = (char1 - 'a' + 10) << 4;
            } else if (char1 >= 'A' && char1 <= 'F') {
                value = (char1 - 'A' + 10) << 4;
            } else {
                free(out);
                return NULL;
            }
            data++;
    
            char2 = *data;
            if (char2 >= '0' && char2 <= '9') {
                value |= char2 - '0';
            } else if (char2 >= 'a' && char2 <= 'f') {
                value |= char2 - 'a' + 10;
            } else if (char2 >= 'A' && char2 <= 'F') {
                value |= char2 - 'A' + 10;
            } else {
                free(out);
                return NULL;
            }
    
            data++;
            *(out + i++) = value;
        }
        *(out + i) = '\0';
    
        if (outlen != NULL) {
            *outlen = i;
        }
    
        return out;
    }
    
    char *bin2hex(unsigned char *data, int size) {
        int i = 0;
        int v = 0;
        char *p = NULL;
        char *buf = NULL;
        char base_char = 'A';
    
        buf = p = (char *) malloc(size * 2 + 1);
        for (i = 0; i < size; i++) {
            v = data[i] >> 4;
            *p++ = v < 10 ? v + '0' : v - 10 + base_char;
    
            v = data[i] & 0x0f;
            *p++ = v < 10 ? v + '0' : v - 10 + base_char;
        }
    
        *p = '\0';
        return buf;
    }
    
    int b64_op(const unsigned char *in, int in_len,
               char *out, int out_len, int op) {
        int ret = 0;
        BIO *b64 = BIO_new(BIO_f_base64());
        BIO *bio = BIO_new(BIO_s_mem());
        BIO_set_flags(b64, BIO_FLAGS_BASE64_NO_NL);
        BIO_push(b64, bio);
        if (op == 0) {
            ret = BIO_write(b64, in, in_len);
            BIO_flush(b64);
            if (ret > 0) {
                ret = BIO_read(bio, out, out_len);
            }
    
        } else {
            ret = BIO_write(bio, in, in_len);
            BIO_flush(bio);
            if (ret) {
                ret = BIO_read(b64, out, out_len);
            }
        }
        BIO_free(b64);
        return ret;
    }
    
    int writeBufToFile(char *file, char *buf) {
        int fd = open(file, O_CREAT | O_RDWR, 0666);
        if (fd == -1)
            return -1;
        int offset = write(fd, buf, 1024);
        if (offset == 0)
            return -2;
        close(fd);
        return 0;
    }
    
    int readBufFromFile(char *file, char *buf) {
        int fd = open(file, O_CREAT | O_RDWR, 0666);
        if (fd == -1)
            return -1;
        int offset = read(fd, buf, 1024);
        if (offset == 0)
            return -2;
        close(fd);
        return 0;
    }
    
  3. native-lib.cpp

    #include <jni.h>
    #include <string>
    #include <malloc.h>
    #include <openssl/ec.h>
    #include <openssl/evp.h>
    #include <openssl/sm2.h>
    #include <openssl/sms4.h>
    #include <crypto/ec/ec_lcl.h>
    #include <openssl/aes.h>
    #include <crypto/sm2/sm2_lcl.h>
    #include "utils.h"
    #include <syslog.h>
    #include <openssl/pem.h>
    
    using std::string;
    
    char *path;
    
    //获取sm2的密钥
    EC_KEY *getEcKey() {
        std::string p1 = path;
        p1.append("/private");
    
        std::string p2 = path;
        p2.append("/public");
    
        char *privateChar = (char *) malloc(1024);
        memset(privateChar, 0, 1024);
        readBufFromFile((char *) p1.c_str(), privateChar);
    
        char *publicChar = (char *) malloc(1024);
        memset(publicChar, 0, 1024);
        readBufFromFile((char *) p2.c_str(), publicChar);
    
        EC_KEY *ec_key;
        ec_key = EC_KEY_new_by_curve_name(NID_sm2p256v1);
        BN_CTX *ctx1 = BN_CTX_new();
        EC_POINT *pubkey_point = EC_POINT_hex2point(ec_key->group, publicChar, NULL, ctx1);
        int iret = EC_KEY_set_public_key(ec_key, pubkey_point);
        BIGNUM *bn_prikey = BN_new();
        iret = BN_hex2bn(&bn_prikey, privateChar);
        iret = EC_KEY_set_private_key(ec_key, bn_prikey);
        p1.clear();
        p2.clear();
        free(publicChar);
        free(privateChar);
        BN_free(bn_prikey);
        EC_POINT_free(pubkey_point);
        return ec_key;
    }
    
    extern "C"
    JNIEXPORT jbyteArray JNICALL
    Java_com_yooking_gmssldemo_MainActivity_aesEnc(JNIEnv *env,
                                                                jobject instance,
                                                                jbyteArray in_,
                                                                jint length,
                                                                jbyteArray key_) {
        jbyte *in = env->GetByteArrayElements(in_, NULL);
        jbyte *key = env->GetByteArrayElements(key_, NULL);
    
        int pading = AES_BLOCK_SIZE - length % AES_BLOCK_SIZE;
        int block = length / AES_BLOCK_SIZE;
        int endLen = AES_BLOCK_SIZE - pading;
    
        unsigned char *p = (unsigned char *) malloc(AES_BLOCK_SIZE + 1);
        memset(p, 0, AES_BLOCK_SIZE + 1);
        memset(p + endLen, pading, (size_t) pading);
        memcpy(p, in + block * AES_BLOCK_SIZE, (size_t) endLen);
    
        AES_KEY aes_key;
        AES_set_encrypt_key((const unsigned char *) key, 16 * 8, &aes_key);
    
        unsigned char *out = (unsigned char *) malloc((size_t) (length + pading + 1));
        memset(out, 0, (size_t) (length + pading + 1));
    
        for (int i = 0; i < block; i++) {
            AES_encrypt((const unsigned char *) (in + (i * AES_BLOCK_SIZE)),
                        out + i * AES_BLOCK_SIZE,
                        &aes_key);
        }
        AES_encrypt(p, out + block * AES_BLOCK_SIZE, &aes_key);
    
        jbyteArray array = env->NewByteArray(length + pading);
        env->SetByteArrayRegion(array, 0, length + pading, (const jbyte *) out);
    
        free(p);
        free(out);
    
        env->ReleaseByteArrayElements(in_, in, 0);
        env->ReleaseByteArrayElements(key_, key, 0);
    
        return array;
    }
    
    extern "C"
    JNIEXPORT jbyteArray JNICALL
    Java_com_yooking_gmssldemo_MainActivity_aesDec(JNIEnv *env,
                                                                jobject instance,
                                                                jbyteArray in_,
                                                                jint length,
                                                                jbyteArray key_) {
        jbyte *in = env->GetByteArrayElements(in_, NULL);
        jbyte *key = env->GetByteArrayElements(key_, NULL);
    
        AES_KEY aes_key;
        AES_set_decrypt_key((const unsigned char *) key, 16 * 8, &aes_key);
    
        unsigned char *out = (unsigned char *) malloc(length);
        memset(out, 0, length);
    
        for (int i = 0; i < length / 16; i++) {
            AES_decrypt((const unsigned char *) (in + (i * AES_BLOCK_SIZE)),
                        out + i * AES_BLOCK_SIZE,
                        &aes_key);
        }
        //去补位
        int padinglen = out[length - 1];
        memset(out + length - padinglen, 0, padinglen);
    
        jbyteArray array = env->NewByteArray(length - padinglen);
        env->SetByteArrayRegion(array, 0, length - padinglen, (const jbyte *) out);
    
        free(out);
        env->ReleaseByteArrayElements(in_, in, 0);
        env->ReleaseByteArrayElements(key_, key, 0);
    
        return array;
    }
    
    extern "C"
    JNIEXPORT jbyteArray JNICALL
    Java_com_yooking_gmssldemo_MainActivity_sha1(JNIEnv *env,
                                                              jobject instance,
                                                              jbyteArray in_,
                                                              jint length) {
        jbyte *in = env->GetByteArrayElements(in_, NULL);
    
        unsigned char *sha1Msg = (unsigned char *) malloc(SHA_DIGEST_LENGTH + 1);
        memset(sha1Msg, 0, SHA_DIGEST_LENGTH + 1);
    
        SHA1((const unsigned char *) in, length, sha1Msg);
    
        jbyteArray array = env->NewByteArray(SHA_DIGEST_LENGTH);
        env->SetByteArrayRegion(array, 0, SHA_DIGEST_LENGTH, (const jbyte *) sha1Msg);
    
        free(sha1Msg);
        env->ReleaseByteArrayElements(in_, in, 0);
    
        return array;
    }
    
    extern "C"
    JNIEXPORT jint JNICALL
    Java_com_yooking_gmssldemo_MainActivity_genSM2KeyPairs(JNIEnv *env,
                                                                        jobject instance,
                                                                        jstring path_) {
    
        const char *p = env->GetStringUTFChars(path_, JNI_FALSE);
        int pLen = env->GetStringUTFLength(path_);
    
        path = (char *) malloc(pLen + 1);
        memset(path, 0, pLen + 1);
        memcpy(path, p, pLen);
    
        std::string p1 = path;
        p1.append("/private");
    
        std::string p2 = path;
        p2.append("/public");
    
        EC_KEY *ec_key = EC_KEY_new();
        ec_key = EC_KEY_new_by_curve_name(NID_sm2p256v1);
        EC_KEY_generate_key(ec_key);
        const EC_POINT *point = EC_KEY_get0_public_key(ec_key);
        char *publicChar = EC_POINT_point2hex(EC_KEY_get0_group(ec_key),
                                              point,
                                              POINT_CONVERSION_UNCOMPRESSED,
                                              BN_CTX_new());
        const BIGNUM *privateKey = EC_KEY_get0_private_key(ec_key);
        char *privateChar = BN_bn2hex(privateKey);
    
        int iRet = writeBufToFile((char *) p1.c_str(), privateChar);
        iRet = writeBufToFile((char *) p2.c_str(), publicChar);
    
        EC_KEY_free(ec_key);
        return 0;
    }
    
    extern "C"
    JNIEXPORT jbyteArray JNICALL
    Java_com_yooking_gmssldemo_MainActivity_sm3(JNIEnv *env,
                                                             jobject instance,
                                                             jbyteArray in_,
                                                             jint length) {
        jbyte *in = env->GetByteArrayElements(in_, NULL);
    
        unsigned char *sm3Msg = (unsigned char *) malloc(SM3_DIGEST_LENGTH + 1);
        memset(sm3Msg, 0, SM3_DIGEST_LENGTH + 1);
    
        sm3((const unsigned char *) in, length, sm3Msg);
    
        jbyteArray array = env->NewByteArray(SM3_DIGEST_LENGTH);
        env->SetByteArrayRegion(array, 0, SM3_DIGEST_LENGTH, (const jbyte *) sm3Msg);
    
        free(sm3Msg);
        env->ReleaseByteArrayElements(in_, in, 0);
    
        return array;
    }
    
    
    
    extern "C"
    JNIEXPORT jbyteArray JNICALL
    Java_com_yooking_gmssldemo_MainActivity_sm4Enc(JNIEnv *env,
                                                                jobject instance,
                                                                jbyteArray in_,
                                                                jint length,
                                                                jbyteArray key_) {
        jbyte *in = env->GetByteArrayElements(in_, NULL);
        jbyte *key = env->GetByteArrayElements(key_, NULL);
    
        int pading = SMS4_KEY_LENGTH - length % SMS4_KEY_LENGTH;
        int block = length / SMS4_KEY_LENGTH;
        int endLen = SMS4_KEY_LENGTH - pading;
    
        unsigned char *p = (unsigned char *) malloc(SMS4_KEY_LENGTH + 1);
        memset(p, 0, SMS4_KEY_LENGTH + 1);
        memset(p + endLen, pading, (size_t) pading);
        memcpy(p, in + block * SMS4_KEY_LENGTH, (size_t) endLen);
    
        sms4_key_t sms4EncKey;
        sms4_set_encrypt_key(&sms4EncKey, (const unsigned char *) key);
    
        unsigned char *out = (unsigned char *) malloc((size_t) (length + pading + 1));
        memset(out, 0, (size_t) (length + pading + 1));
    
        for (int i = 0; i < block; i++) {
            sms4_encrypt((const unsigned char *) (in + (i * 16)), out + i * 16, &sms4EncKey);
        }
        sms4_encrypt(p, out + block * 16, &sms4EncKey);
    
        jbyteArray array = env->NewByteArray(length + pading);
        env->SetByteArrayRegion(array, 0, length + pading, (const jbyte *) out);
    
        free(p);
        free(out);
        env->ReleaseByteArrayElements(in_, in, 0);
        env->ReleaseByteArrayElements(key_, key, 0);
    
        return array;
    }
    
    extern "C"
    JNIEXPORT jbyteArray JNICALL
    Java_com_yooking_gmssldemo_MainActivity_sm4Dec(JNIEnv *env,
                                                                jobject instance,
                                                                jbyteArray in_,
                                                                jint length,
                                                                jbyteArray key_) {
        jbyte *in = env->GetByteArrayElements(in_, NULL);
        jbyte *key = env->GetByteArrayElements(key_, NULL);
    
        sms4_key_t sms4DecKey;
        sms4_set_decrypt_key(&sms4DecKey, (const unsigned char *) key);
    
        unsigned char *out = (unsigned char *) malloc(length);
        memset(out, 0, length);
    
        for (int i = 0; i < length / 16; i++) {
            sms4_decrypt((const unsigned char *) (in + (i * 16)), out + i * 16, &sms4DecKey);
        }
        //去补位
        int padinglen = out[length - 1];
        memset(out + length - padinglen, 0, padinglen);
    
        jbyteArray array = env->NewByteArray(length - padinglen);
        env->SetByteArrayRegion(array, 0, length - padinglen, (const jbyte *) out);
    
        free(out);
        env->ReleaseByteArrayElements(in_, in, 0);
        env->ReleaseByteArrayElements(key_, key, 0);
    
        return array;
    }
    
    extern "C"
    JNIEXPORT jbyteArray JNICALL
    Java_com_yooking_gmssldemo_MainActivity_sm2Enc(JNIEnv *env,
                                                                jobject instance,
                                                                jbyteArray in_,
                                                                jint length) {
        jbyte *in = env->GetByteArrayElements(in_, NULL);
    
        int iRet = 0;
        EC_KEY *ec_key = getEcKey();
        size_t sm2EncLen = SM2_MAX_PLAINTEXT_LENGTH;
    
        unsigned char *sm2EncMsg = (unsigned char *) malloc(SM2_MAX_PLAINTEXT_LENGTH);
        memset(sm2EncMsg, 0, SM2_MAX_PLAINTEXT_LENGTH);
    
        iRet = SM2_encrypt(NID_sm3,
                           (const unsigned char *) in,
                           (size_t) length,
                           sm2EncMsg,
                           &sm2EncLen,
                           ec_key);
    
        if (!iRet) {
            ERR_load_ERR_strings();
            ERR_load_crypto_strings();
    
            unsigned long ulErr = ERR_get_error(); // 获取错误号
    
            const char *pTmp = ERR_reason_error_string(ulErr);
            puts(pTmp);
        }
    
        jbyteArray array = env->NewByteArray(sm2EncLen);
        env->SetByteArrayRegion(array, 0, sm2EncLen, (const jbyte *) sm2EncMsg);
    
        free(sm2EncMsg);
        EC_KEY_free(ec_key);
        env->ReleaseByteArrayElements(in_, in, 0);
    
        return array;
    }
    
    extern "C"
    JNIEXPORT jbyteArray JNICALL
    Java_com_yooking_gmssldemo_MainActivity_sm2Dec(JNIEnv *env,
                                                                jobject instance,
                                                                jbyteArray in_,
                                                                jint length) {
        jbyte *in = env->GetByteArrayElements(in_, NULL);
    
        int iRet = 0;
        EC_KEY *ec_key = getEcKey();
        size_t sm2DecLen = 0;
    
        iRet = SM2_decrypt(NID_sm3,
                           (const unsigned char *) in,
                           (size_t) length,
                           NULL,
                           &sm2DecLen,
                           ec_key);
    
        unsigned char *sm2DecMsg = (unsigned char *) malloc(sm2DecLen + 1);
        memset(sm2DecMsg, 0, sm2DecLen);
    
        iRet = SM2_decrypt(NID_sm3,
                           (const unsigned char *) in,
                           (size_t) length,
                           sm2DecMsg,
                           &sm2DecLen,
                           ec_key);
    
        jbyteArray array = env->NewByteArray(sm2DecLen);
        env->SetByteArrayRegion(array, 0, sm2DecLen, (const jbyte *) sm2DecMsg);
    
        free(sm2DecMsg);
        EC_KEY_free(ec_key);
        env->ReleaseByteArrayElements(in_, in, 0);
    
        return array;
    }
    
    
    extern "C"
    JNIEXPORT jbyteArray JNICALL
    Java_com_yooking_gmssldemo_MainActivity_sm2Sign(JNIEnv *env,
                                                                 jobject instance,
                                                                 jbyteArray in_,
                                                                 jint length) {
        jbyte *in = env->GetByteArrayElements(in_, NULL);
        int iret = -1;
    
        EC_KEY *ec_key = getEcKey();
    
    
        size_t zlen = 0;
        iret = SM2_compute_message_digest(EVP_sm3(),
                                          EVP_sm3(),
                                          (const unsigned char *) in,
                                          length,
                                          SM2_DEFAULT_ID_GMT09,
                                          SM2_DEFAULT_ID_LENGTH,
                                          NULL,
                                          &zlen,
                                          ec_key);
        if (!iret) {
            return NULL;
        }
        unsigned char *z = (unsigned char *) malloc(zlen + 1);
        memset(z, 0, zlen + 1);
        iret = SM2_compute_message_digest(EVP_sm3(),
                                          EVP_sm3(),
                                          (const unsigned char *) in,
                                          length,
                                          SM2_DEFAULT_ID_GMT09,
                                          SM2_DEFAULT_ID_LENGTH,
                                          z,
                                          &zlen,
                                          ec_key);
        if (!iret) {
            return NULL;
        }
    
        unsigned int signLen = 0;
        iret = SM2_sign(NID_sm3, z, zlen, NULL, &signLen, ec_key);
        if (!iret) {
            return NULL;
        }
        unsigned char *signMsg = (unsigned char *) malloc(signLen + 1);
        memset(signMsg, 0, signLen + 1);
        iret = SM2_sign(NID_sm3, z, zlen, signMsg, &signLen, ec_key);
        if (!iret) {
            return NULL;
        }
    
        jbyteArray array = env->NewByteArray(signLen);
        env->SetByteArrayRegion(array, 0, signLen, (const jbyte *) signMsg);
    
        free(signMsg);
        free(z);
        EC_KEY_free(ec_key);
        env->ReleaseByteArrayElements(in_, in, 0);
        return array;
    }
    
    extern "C"
    JNIEXPORT jint JNICALL
    Java_com_yooking_gmssldemo_MainActivity_sm2Verify(JNIEnv *env,
                                                                   jobject instance,
                                                                   jbyteArray in_,
                                                                   jint length,
                                                                   jbyteArray sign_,
                                                                   jint signLen) {
        jbyte *in = env->GetByteArrayElements(in_, NULL);
        jbyte *sign = env->GetByteArrayElements(sign_, NULL);
        int iret = -1;
        EC_KEY *ec_key = getEcKey();
    
        size_t zlen = 0;
        iret = SM2_compute_message_digest(EVP_sm3(),
                                          EVP_sm3(),
                                          (const unsigned char *) in,
                                          length,
                                          SM2_DEFAULT_ID_GMT09,
                                          SM2_DEFAULT_ID_LENGTH,
                                          NULL,
                                          &zlen,
                                          ec_key);
    
        unsigned char *z = (unsigned char *) malloc(zlen + 1);
        memset(z, 0, zlen + 1);
        iret = SM2_compute_message_digest(EVP_sm3(),
                                          EVP_sm3(),
                                          (const unsigned char *) in,
                                          length,
                                          SM2_DEFAULT_ID_GMT09,
                                          SM2_DEFAULT_ID_LENGTH,
                                          z,
                                          &zlen,
                                          ec_key);
        if (!iret) {
            return -2;
        }
    
        iret = SM2_verify(NID_sm3, z, zlen, (const unsigned char *) sign, signLen, ec_key);
    
        free(z);
        EC_KEY_free(ec_key);
        env->ReleaseByteArrayElements(in_, in, 0);
        env->ReleaseByteArrayElements(sign_, sign, 0);
        return iret;
    }
    
    extern "C" JNIEXPORT jbyteArray JNICALL
    Java_com_yooking_gmssldemo_MainActivity_rsaEnc(JNIEnv *env, jobject instance, jbyteArray keys_, jbyteArray src_) {
    //    LOGI("RSA->非对称密码算法,也就是说该算法需要一对密钥,使用其中一个加密,则需要用另一个才能解密");
        jbyte *keys = env->GetByteArrayElements(keys_, NULL);
        jbyte *src = env->GetByteArrayElements(src_, NULL);
        jsize src_Len = env->GetArrayLength(src_);
    
        int ret = 0, src_flen = 0, cipherText_offset = 0, desText_len = 0, src_offset = 0;
    
        RSA *rsa = NULL;
        BIO *keybio = NULL;
    
    //    LOGI("RSA->从字符串读取RSA公钥");
        keybio = BIO_new_mem_buf(keys, -1);
    //    LOGI("RSA->从bio结构中得到RSA结构");
        rsa = PEM_read_bio_RSA_PUBKEY(keybio, NULL, NULL, NULL);
    //    LOGI("RSA->释放BIO");
        BIO_free_all(keybio);
    
        int flen = RSA_size(rsa);
        desText_len = flen * (src_Len / (flen - 11) + 1);
    
        unsigned char *srcOrigin = (unsigned char *) malloc(src_Len);
        unsigned char *cipherText = (unsigned char *) malloc(flen);
        unsigned char *desText = (unsigned char *) malloc(desText_len);
        memset(desText, 0, desText_len);
    
        memset(srcOrigin, 0, src_Len);
        memcpy(srcOrigin, src, src_Len);
    
    //    LOGI("RSA->进行公钥加密操作");
        //RSA_PKCS1_PADDING最大加密长度:128-11;RSA_NO_PADDING最大加密长度:128
        for (int i = 0; i <= src_Len / (flen - 11); i++) {
            src_flen = (i == src_Len / (flen - 11)) ? src_Len % (flen - 11) : flen - 11;
            if (src_flen == 0) {
                break;
            }
    
            memset(cipherText, 0, flen);
            ret = RSA_public_encrypt(src_flen, srcOrigin + src_offset, cipherText, rsa, RSA_PKCS1_PADDING);
    
            memcpy(desText + cipherText_offset, cipherText, ret);
            cipherText_offset += ret;
            src_offset += src_flen;
        }
    
        RSA_free(rsa);
    //    LOGI("RSA->CRYPTO_cleanup_all_ex_data");
        CRYPTO_cleanup_all_ex_data();
    
    //    LOGI("RSA->从jni释放数据指针");
        env->ReleaseByteArrayElements(keys_, keys, 0);
        env->ReleaseByteArrayElements(src_, src, 0);
    
        jbyteArray cipher = env->NewByteArray(cipherText_offset);
    //    LOGI("RSA->在堆中分配ByteArray数组对象成功,将拷贝数据到数组中");
        env->SetByteArrayRegion(cipher, 0, cipherText_offset, (jbyte *) desText);
    //    LOGI("RSA->释放内存");
        free(srcOrigin);
        free(cipherText);
        free(desText);
    
        return cipher;
    }
    
    extern "C" JNIEXPORT jbyteArray JNICALL
    Java_com_yooking_gmssldemo_MainActivity_rsaDes(JNIEnv *env, jobject instance, jbyteArray keys_, jbyteArray src_) {
    //    LOGI("RSA->非对称密码算法,也就是说该算法需要一对密钥,使用其中一个加密,则需要用另一个才能解密");
        jbyte *keys = env->GetByteArrayElements(keys_, NULL);
        jbyte *src = env->GetByteArrayElements(src_, NULL);
        jsize src_Len = env->GetArrayLength(src_);
    
        int ret = 0, src_flen = 0, plaintext_offset = 0, descText_len = 0, src_offset = 0;
    
        RSA *rsa = NULL;
        BIO *keybio = NULL;
    
    //    LOGI("RSA->从字符串读取RSA私钥");
        keybio = BIO_new_mem_buf(keys, -1);
    //    LOGI("RSA->从bio结构中得到RSA结构");
        rsa = PEM_read_bio_RSAPrivateKey(keybio, NULL, NULL, NULL);
    //    LOGI("RSA->释放BIO");
        BIO_free_all(keybio);
    
        int flen = RSA_size(rsa);
        descText_len = (flen - 11) * (src_Len / flen + 1);
    
        unsigned char *srcOrigin = (unsigned char *) malloc(src_Len);
        unsigned char *plaintext = (unsigned char *) malloc(flen - 11);
        unsigned char *desText = (unsigned char *) malloc(descText_len);
        memset(desText, 0, descText_len);
    
        memset(srcOrigin, 0, src_Len);
        memcpy(srcOrigin, src, src_Len);
    
    //    LOGI("RSA->进行私钥解密操作");
        //一次性解密数据最大字节数RSA_size
        for (int i = 0; i <= src_Len / flen; i++) {
            src_flen = (i == src_Len / flen) ? src_Len % flen : flen;
            if (src_flen == 0) {
                break;
            }
    
            memset(plaintext, 0, flen - 11);
            ret = RSA_private_decrypt(src_flen, srcOrigin + src_offset, plaintext, rsa, RSA_PKCS1_PADDING);
    
            memcpy(desText + plaintext_offset, plaintext, ret);
            plaintext_offset += ret;
            src_offset += src_flen;
        }
    
        RSA_free(rsa);
    //    LOGI("RSA->CRYPTO_cleanup_all_ex_data");
        CRYPTO_cleanup_all_ex_data();
    
    //    LOGI("RSA->从jni释放数据指针");
        env->ReleaseByteArrayElements(keys_, keys, 0);
        env->ReleaseByteArrayElements(src_, src, 0);
    
        jbyteArray cipher = env->NewByteArray(plaintext_offset);
    //    LOGI("RSA->在堆中分配ByteArray数组对象成功,将拷贝数据到数组中");
        env->SetByteArrayRegion(cipher, 0, plaintext_offset, (jbyte *) desText);
    //    LOGI("RSA->释放内存");
        free(srcOrigin);
        free(plaintext);
        free(desText);
    
        return cipher;
    }
    
    extern "C" JNIEXPORT jbyteArray JNICALL
    Java_com_yooking_gmssldemo_MainActivity_rsaSign(JNIEnv *env, jobject instance, jbyteArray keys_, jbyteArray src_) {
    //    LOGI("RSA->非对称密码算法,也就是说该算法需要一对密钥,使用其中一个加密,则需要用另一个才能解密");
        jbyte *keys = env->GetByteArrayElements(keys_, NULL);
        jbyte *src = env->GetByteArrayElements(src_, NULL);
        jsize src_Len = env->GetArrayLength(src_);
    
        unsigned int siglen = 0;
        unsigned char digest[SHA_DIGEST_LENGTH];
    
        RSA *rsa = NULL;
        BIO *keybio = NULL;
    
    //    LOGI("RSA->从字符串读取RSA公钥");
        keybio = BIO_new_mem_buf(keys, -1);
    //    LOGI("RSA->从bio结构中得到RSA结构");
        rsa = PEM_read_bio_RSAPrivateKey(keybio, NULL, NULL, NULL);
    //    LOGI("RSA->释放BIO");
        BIO_free_all(keybio);
    
        unsigned char *sign = (unsigned char *) malloc(129);
        memset(sign, 0, 129);
    
        SHA1((const unsigned char *) src, src_Len, digest);
        RSA_sign(NID_sha1, digest, SHA_DIGEST_LENGTH, sign, &siglen, rsa);
    
        RSA_free(rsa);
    //    LOGI("RSA->CRYPTO_cleanup_all_ex_data");
        CRYPTO_cleanup_all_ex_data();
    
    //    LOGI("RSA->从jni释放数据指针");
        env->ReleaseByteArrayElements(keys_, keys, 0);
        env->ReleaseByteArrayElements(src_, src, 0);
    
        jbyteArray cipher = env->NewByteArray(siglen);
    //    LOGI("RSA->在堆中分配ByteArray数组对象成功,将拷贝数据到数组中");
        env->SetByteArrayRegion(cipher, 0, siglen, (jbyte *) sign);
    //    LOGI("RSA->释放内存");
        free(sign);
    
        return cipher;
    }
    
    
    extern "C" JNIEXPORT jint JNICALL
    Java_com_yooking_gmssldemo_MainActivity_rsaVerify(JNIEnv *env, jobject instance, jbyteArray keys_, jbyteArray src_, jbyteArray sign_) {
    //    LOGI("RSA->非对称密码算法,也就是说该算法需要一对密钥,使用其中一个加密,则需要用另一个才能解密");
        jbyte *keys = env->GetByteArrayElements(keys_, NULL);
        jbyte *src = env->GetByteArrayElements(src_, NULL);
        jbyte *sign = env->GetByteArrayElements(sign_, NULL);
    
        jsize src_Len = env->GetArrayLength(src_);
        jsize siglen = env->GetArrayLength(sign_);
    
        int ret;
        unsigned char digest[SHA_DIGEST_LENGTH];
    
        RSA *rsa = NULL;
        BIO *keybio = NULL;
    
    //    LOGI("RSA->从字符串读取RSA公钥");
        keybio = BIO_new_mem_buf(keys, -1);
    //    LOGI("RSA->从bio结构中得到RSA结构");
        rsa = PEM_read_bio_RSA_PUBKEY(keybio, NULL, NULL, NULL);
    //    LOGI("RSA->释放BIO");
        BIO_free_all(keybio);
    
        SHA1((const unsigned char *) src, src_Len, digest);
        ret = RSA_verify(NID_sha1, digest, SHA_DIGEST_LENGTH, (const unsigned char *) sign, siglen, rsa);
    
        RSA_free(rsa);
    //    LOGI("RSA->CRYPTO_cleanup_all_ex_data");
        CRYPTO_cleanup_all_ex_data();
    
    //    LOGI("RSA->从jni释放数据指针");
        env->ReleaseByteArrayElements(keys_, keys, 0);
        env->ReleaseByteArrayElements(src_, src, 0);
        env->ReleaseByteArrayElements(sign_, sign, 0);
    
        return ret;
    }
    
最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 212,542评论 6 493
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 90,596评论 3 385
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 158,021评论 0 348
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 56,682评论 1 284
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 65,792评论 6 386
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 49,985评论 1 291
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 39,107评论 3 410
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 37,845评论 0 268
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 44,299评论 1 303
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 36,612评论 2 327
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 38,747评论 1 341
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 34,441评论 4 333
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 40,072评论 3 317
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 30,828评论 0 21
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 32,069评论 1 267
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 46,545评论 2 362
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 43,658评论 2 350

推荐阅读更多精彩内容