STM32利用AES加密数据、解密数据
STM32利用AES加密数据、解密数据
- MD5在线工具
- Chapter1 STM32利用AES加密数据、解密数据
- 一、头文件
- 二、源文件
- 三、使用
- Chapter2 stm32 的 md5计算函数
- Chapter3 STM32 应用程序加密的一种设计方案
- 前言
- 一、计算AppKey
- 二、自动配置流程
- 三、出厂固件合并
- 个人总结
MD5在线工具
https://www.md5ma.com/md5-generator
Chapter1 STM32利用AES加密数据、解密数据
原文链接
一、头文件
#ifndef _AES_H
#define _AES_H// 以bit为单位的密钥长度,只能为 128,192 和 256 三种
#define AES_KEY_LENGTH 128// 加解密模式
#define AES_MODE_ECB 0 // 电子密码本模式(一般模式)
#define AES_MODE_CBC 1 // 密码分组链接模式
#define AES_MODE AES_MODE_CBC///
// 函数名: aes_init
// 描述: 初始化,在此执行扩展密钥操作。
// 输入参数: pKey -- 原始密钥,其长度必须为 AES_KEY_LENGTH/8 字节。
// 输出参数: 无。
// 返回值: 无。
///
void aes_init(const void *pKey);//
// 函数名: aes_encrypt
// 描述: 加密数据
// 输入参数: pPlainText -- 明文,即需加密的数据,其长度为nDataLen字节。
// nDataLen -- 数据长度,以字节为单位,必须为AES_KEY_LENGTH/8的整倍数。
// pIV -- 初始化向量,如果使用ECB模式,可设为NULL。
// 输出参数: pCipherText -- 密文,即由明文加密后的数据,可以与pPlainText相同。
// 返回值: 无。
//
void aes_encrypt(const unsigned char *pPlainText, unsigned char *pCipherText, unsigned int nDataLen, const unsigned char *pIV);//
// 函数名: aes_decrypt
// 描述: 解密数据
// 输入参数: pCipherText -- 密文,即需解密的数据,其长度为nDataLen字节。
// nDataLen -- 数据长度,以字节为单位,必须为AES_KEY_LENGTH/8的整倍数。
// pIV -- 初始化向量,如果使用ECB模式,可设为NULL。
// 输出参数: pPlainText -- 明文,即由密文解密后的数据,可以与pCipherText相同。
// 返回值: 无。
//
void aes_decrypt( const unsigned char *pCipherText,unsigned char *pPlainText, unsigned int nDataLen, const unsigned char *pIV);
#endif /* _AES_H */
二、源文件
#include "aes.h"
#include "string.h"// 为了能针对C51进行优化,并且又使代码可用于ARM和PC等环境,
// 在非C51环境(没有定义__C51__)下需要把C51特定的关键字定义为空
#ifndef __C51__#define code#define data#define idata#define xdata#define pdatatypedef unsigned char BOOL;
#elsetypedef bit BOOL;
#endif#define Nk (AES_KEY_LENGTH / 32) // 以“字”(4字节)为单位的密钥长度
#define Nb 4 // 以“字”(4字节)为单位的加解密数据块大小,固定为4// Nr:加密的轮数
#if AES_KEY_LENGTH == 128#define Nr 10
#elif AES_KEY_LENGTH == 192#define Nr 12
#elif AES_KEY_LENGTH == 256#define Nr 14
#else#error AES_KEY_LENGTH must be 128, 192 or 256 BOOLs!
#endif// GF(28) 多项式
#define BPOLY 0x1B // Lower 8 BOOLs of (x^8 + x^4 + x^3 + x + 1), ie. (x^4 + x^3 + x + 1)./*****************************************************************************
* Typedef Enum 枚举定义
*****************************************************************************//*****************************************************************************
* Struct 数据结构定义
******************************************************************************//*****************************************************************************
* Local variable 局部变量
*****************************************************************************/// AES子密钥表,当密钥长度为128位时,占用176字节空间
static xdata unsigned char g_roundKeyTable[4*Nb*(Nr+1)];// 加密用的SBox
static code const unsigned char sBox[256] =
{0x63, 0x7c, 0x77, 0x7b, 0xf2, 0x6b, 0x6f, 0xc5, 0x30, 0x01, 0x67, 0x2b, 0xfe, 0xd7, 0xab, 0x76,0xca, 0x82, 0xc9, 0x7d, 0xfa, 0x59, 0x47, 0xf0, 0xad, 0xd4, 0xa2, 0xaf, 0x9c, 0xa4, 0x72, 0xc0,0xb7, 0xfd, 0x93, 0x26, 0x36, 0x3f, 0xf7, 0xcc, 0x34, 0xa5, 0xe5, 0xf1, 0x71, 0xd8, 0x31, 0x15,0x04, 0xc7, 0x23, 0xc3, 0x18, 0x96, 0x05, 0x9a, 0x07, 0x12, 0x80, 0xe2, 0xeb, 0x27, 0xb2, 0x75,0x09, 0x83, 0x2c, 0x1a, 0x1b, 0x6e, 0x5a, 0xa0, 0x52, 0x3b, 0xd6, 0xb3, 0x29, 0xe3, 0x2f, 0x84,0x53, 0xd1, 0x00, 0xed, 0x20, 0xfc, 0xb1, 0x5b, 0x6a, 0xcb, 0xbe, 0x39, 0x4a, 0x4c, 0x58, 0xcf,0xd0, 0xef, 0xaa, 0xfb, 0x43, 0x4d, 0x33, 0x85, 0x45, 0xf9, 0x02, 0x7f, 0x50, 0x3c, 0x9f, 0xa8,0x51, 0xa3, 0x40, 0x8f, 0x92, 0x9d, 0x38, 0xf5, 0xbc, 0xb6, 0xda, 0x21, 0x10, 0xff, 0xf3, 0xd2,0xcd, 0x0c, 0x13, 0xec, 0x5f, 0x97, 0x44, 0x17, 0xc4, 0xa7, 0x7e, 0x3d, 0x64, 0x5d, 0x19, 0x73,0x60, 0x81, 0x4f, 0xdc, 0x22, 0x2a, 0x90, 0x88, 0x46, 0xee, 0xb8, 0x14, 0xde, 0x5e, 0x0b, 0xdb,0xe0, 0x32, 0x3a, 0x0a, 0x49, 0x06, 0x24, 0x5c, 0xc2, 0xd3, 0xac, 0x62, 0x91, 0x95, 0xe4, 0x79,0xe7, 0xc8, 0x37, 0x6d, 0x8d, 0xd5, 0x4e, 0xa9, 0x6c, 0x56, 0xf4, 0xea, 0x65, 0x7a, 0xae, 0x08,0xba, 0x78, 0x25, 0x2e, 0x1c, 0xa6, 0xb4, 0xc6, 0xe8, 0xdd, 0x74, 0x1f, 0x4b, 0xbd, 0x8b, 0x8a,0x70, 0x3e, 0xb5, 0x66, 0x48, 0x03, 0xf6, 0x0e, 0x61, 0x35, 0x57, 0xb9, 0x86, 0xc1, 0x1d, 0x9e,0xe1, 0xf8, 0x98, 0x11, 0x69, 0xd9, 0x8e, 0x94, 0x9b, 0x1e, 0x87, 0xe9, 0xce, 0x55, 0x28, 0xdf,0x8c, 0xa1, 0x89, 0x0d, 0xbf, 0xe6, 0x42, 0x68, 0x41, 0x99, 0x2d, 0x0f, 0xb0, 0x54, 0xbb, 0x16
};// 解密用的SBox
static code const unsigned char invSBox[256] =
{0x52, 0x09, 0x6a, 0xd5, 0x30, 0x36, 0xa5, 0x38, 0xbf, 0x40, 0xa3, 0x9e, 0x81, 0xf3, 0xd7, 0xfb,0x7c, 0xe3, 0x39, 0x82, 0x9b, 0x2f, 0xff, 0x87, 0x34, 0x8e, 0x43, 0x44, 0xc4, 0xde, 0xe9, 0xcb,0x54, 0x7b, 0x94, 0x32, 0xa6, 0xc2, 0x23, 0x3d, 0xee, 0x4c, 0x95, 0x0b, 0x42, 0xfa, 0xc3, 0x4e,0x08, 0x2e, 0xa1, 0x66, 0x28, 0xd9, 0x24, 0xb2, 0x76, 0x5b, 0xa2, 0x49, 0x6d, 0x8b, 0xd1, 0x25,0x72, 0xf8, 0xf6, 0x64, 0x86, 0x68, 0x98, 0x16, 0xd4, 0xa4, 0x5c, 0xcc, 0x5d, 0x65, 0xb6, 0x92,0x6c, 0x70, 0x48, 0x50, 0xfd, 0xed, 0xb9, 0xda, 0x5e, 0x15, 0x46, 0x57, 0xa7, 0x8d, 0x9d, 0x84,0x90, 0xd8, 0xab, 0x00, 0x8c, 0xbc, 0xd3, 0x0a, 0xf7, 0xe4, 0x58, 0x05, 0xb8, 0xb3, 0x45, 0x06,0xd0, 0x2c, 0x1e, 0x8f, 0xca, 0x3f, 0x0f, 0x02, 0xc1, 0xaf, 0xbd, 0x03, 0x01, 0x13, 0x8a, 0x6b,0x3a, 0x91, 0x11, 0x41, 0x4f, 0x67, 0xdc, 0xea, 0x97, 0xf2, 0xcf, 0xce, 0xf0, 0xb4, 0xe6, 0x73,0x96, 0xac, 0x74, 0x22, 0xe7, 0xad, 0x35, 0x85, 0xe2, 0xf9, 0x37, 0xe8, 0x1c, 0x75, 0xdf, 0x6e,0x47, 0xf1, 0x1a, 0x71, 0x1d, 0x29, 0xc5, 0x89, 0x6f, 0xb7, 0x62, 0x0e, 0xaa, 0x18, 0xbe, 0x1b,0xfc, 0x56, 0x3e, 0x4b, 0xc6, 0xd2, 0x79, 0x20, 0x9a, 0xdb, 0xc0, 0xfe, 0x78, 0xcd, 0x5a, 0xf4,0x1f, 0xdd, 0xa8, 0x33, 0x88, 0x07, 0xc7, 0x31, 0xb1, 0x12, 0x10, 0x59, 0x27, 0x80, 0xec, 0x5f,0x60, 0x51, 0x7f, 0xa9, 0x19, 0xb5, 0x4a, 0x0d, 0x2d, 0xe5, 0x7a, 0x9f, 0x93, 0xc9, 0x9c, 0xef,0xa0, 0xe0, 0x3b, 0x4d, 0xae, 0x2a, 0xf5, 0xb0, 0xc8, 0xeb, 0xbb, 0x3c, 0x83, 0x53, 0x99, 0x61,0x17, 0x2b, 0x04, 0x7e, 0xba, 0x77, 0xd6, 0x26, 0xe1, 0x69, 0x14, 0x63, 0x55, 0x21, 0x0c, 0x7d
};///
// 函数名: rotation_word
// 描述: 对一个“字”数据进行循环右移。
// 输入参数: pWord -- 要右移的4字节数据。
// 输出参数: pWord -- 右移后的4字节数据。
// 返回值: 无。
///
static void rotation_word(unsigned char *pWord)
{unsigned char temp = pWord[0];pWord[0] = pWord[1];pWord[1] = pWord[2];pWord[2] = pWord[3];pWord[3] = temp;
}///
// 函数名: xor_bytes
// 描述: 批量异或两组数据。
// 输入参数: pData1 -- 要异或的第一组数据。
// pData1 -- 要异或的第二组数据。
// nCount -- 要异或的数据长度。
// 输出参数: pData1 -- 异或后的结果。
// 返回值: 无。
///
static void xor_bytes(unsigned char *pData1, const unsigned char *pData2, unsigned char nCount)
{unsigned char i;for (i = 0; i < nCount; i++){pData1[i] ^= pData2[i];}
}///
// 函数名: AddRoundKey
// 描述: 把 中间状态数据 加上(异或)子密钥,数据长度为16字节。
// 输入参数: pState -- 状态数据。
// pRoundKey -- 子密钥数据。
// 输出参数: pState -- 加上子密钥后的状态数据。
// 返回值: 无。
///
// static void AddRoundKey(unsigned char *pState, const unsigned char *pRoundKey)
// {
// xor_bytes(pState, pRoundKey, 4*Nb);
// }// AddRoundKey的宏形式,比函数形式可以节省4字节的data数据
#define AddRoundKey(pState, pRoundKey) \xor_bytes((pState), (pRoundKey), 4*Nb)///
// 函数名: sub_bytes
// 描述: 通过S盒子置换状态数据。
// 输入参数: pState -- 状态数据。
// nCount -- 状态数据长度。
// bInvert -- 是否使用反向S盒子(解密时使用)。
// 输出参数: pState -- 置换后的状态数据。
// 返回值: 无。
///
static void sub_bytes(unsigned char *pState, unsigned char nCount, BOOL bInvert)
{unsigned char i;const unsigned char code *pSBox = bInvert ? invSBox : sBox;for (i = 0; i < nCount; i++){pState[i] = pSBox[pState[i]];}
}///
// 函数名: shift_rows
// 描述: 把状态数据移行。
// 输入参数: pState -- 状态数据。
// bInvert -- 是否反向移行(解密时使用)。
// 输出参数: pState -- 移行后的状态数据。
// 返回值: 无。
///
static void shift_rows(unsigned char *pState, BOOL bInvert)
{// 注意:状态数据以列形式存放!unsigned char r; // row, 行unsigned char c; // column,列unsigned char temp;unsigned char rowData[4];for (r = 1; r < 4; r++){// 备份一行数据for (c = 0; c < 4; c++){rowData[c] = pState[r + 4*c];}temp = bInvert ? (4 - r) : r;for (c = 0; c < 4; c++){pState[r + 4*c] = rowData[(c + temp) % 4];}}
}///
// 函数名: gf_mult_by02
// 描述: 在GF(28)域的 乘2 运算。
// 输入参数: num -- 乘数。
// 输出参数: 无。
// 返回值: num乘以2的结果。
///
static unsigned char gf_mult_by02(unsigned char num)
{if ((num & 0x80) == 0){num = num << 1;}else{num = (num << 1) ^ BPOLY;}return num;
}///
// 函数名: mix_columns
// 描述: 混合状态各列数据。
// 输入参数: pState -- 状态数据。
// bInvert -- 是否反向混合(解密时使用)。
// 输出参数: pState -- 混合列后的状态数据。
// 返回值: 无。
///
static void mix_columns(unsigned char *pState, BOOL bInvert)
{unsigned char i;unsigned char temp;unsigned char a0Pa2_M4; // 4(a0 + a2)unsigned char a1Pa3_M4; // 4(a1 + a3)unsigned char result[4];for (i = 0; i < 4; i++, pState += 4){// b0 = 2a0 + 3a1 + a2 + a3 // = (a0 + a1 + a2 + a3) + 2(a0 + a1) + a0temp = pState[0] ^ pState[1] ^ pState[2] ^ pState[3];result[0] = temp ^ pState[0] ^ gf_mult_by02((unsigned char) (pState[0] ^ pState[1]));result[1] = temp ^ pState[1] ^ gf_mult_by02((unsigned char) (pState[1] ^ pState[2]));result[2] = temp ^ pState[2] ^ gf_mult_by02((unsigned char) (pState[2] ^ pState[3]));result[3] = temp ^ pState[3] ^ gf_mult_by02((unsigned char) (pState[3] ^ pState[0]));if (bInvert){// b0' = 14a0 + 11a1 + 13a2 + 9a3 // = (a0 + a1 + a2 + a3) + 2(a0 + a1) + a0 (这部分为b0)// + 2(4(a0 + a2) + 4(a1 + a3))// + 4(a0 + a2)a0Pa2_M4 = gf_mult_by02(gf_mult_by02((unsigned char) (pState[0] ^ pState[2])));a1Pa3_M4 = gf_mult_by02(gf_mult_by02((unsigned char) (pState[1] ^ pState[3])));temp = gf_mult_by02((unsigned char) (a0Pa2_M4 ^ a1Pa3_M4));result[0] ^= temp ^ a0Pa2_M4;result[1] ^= temp ^ a1Pa3_M4;result[2] ^= temp ^ a0Pa2_M4;result[3] ^= temp ^ a1Pa3_M4;}memcpy(pState, result, 4);}
}///
// 函数名: block_encrypt
// 描述: 对单块数据加密。
// 输入参数: pState -- 状态数据。
// 输出参数: pState -- 加密后的状态数据。
// 返回值: 无。
///
static void block_encrypt(unsigned char *pState)
{unsigned char i;AddRoundKey(pState, g_roundKeyTable);for (i = 1; i <= Nr; i++) // i = [1, Nr]{sub_bytes(pState, 4*Nb, 0);shift_rows(pState, 0);if (i != Nr){mix_columns(pState, 0);}AddRoundKey(pState, &g_roundKeyTable[4*Nb*i]);}// 为了节省代码,合并到循化执行
// sub_bytes(pState, 4*Nb);
// shift_rows(pState, 0);
// AddRoundKey(pState, &g_roundKeyTable[4*Nb*Nr]);
}///
// 函数名: block_decrypt
// 描述: 对单块数据解密。
// 输入参数: pState -- 状态数据。
// 输出参数: pState -- 解密后的状态数据。
// 返回值: 无。
///
static void block_decrypt(unsigned char *pState)
{unsigned char i;AddRoundKey(pState, &g_roundKeyTable[4*Nb*Nr]);for (i = Nr; i > 0; i--) // i = [Nr, 1]{shift_rows(pState, 1);sub_bytes(pState, 4*Nb, 1);AddRoundKey(pState, &g_roundKeyTable[4*Nb*(i-1)]);if (i != 1){mix_columns(pState, 1);}}// 为了节省代码,合并到循化执行
// shift_rows(pState, 1);
// sub_bytes(pState, 4*Nb, 1);
// AddRoundKey(pState, g_roundKeyTable);
}/*****************************************************************************
* Global Functions 全局函数
******************************************************************************////
// 函数名: aes_init
// 描述: 初始化,在此执行扩展密钥操作。
// 输入参数: pKey -- 原始密钥,其长度必须为 AES_KEY_LENGTH/8 字节。
// 输出参数: 无。
// 返回值: 无。
///
void aes_init(const void *pKey)
{// 扩展密钥unsigned char i;unsigned char *pRoundKey;unsigned char Rcon[4] = {0x01, 0x00, 0x00, 0x00};memcpy(g_roundKeyTable, pKey, 4*Nk);pRoundKey = &g_roundKeyTable[4*Nk];for (i = Nk; i < Nb*(Nr+1); pRoundKey += 4, i++){memcpy(pRoundKey, pRoundKey - 4, 4);if (i % Nk == 0){rotation_word(pRoundKey);sub_bytes(pRoundKey, 4, 0);xor_bytes(pRoundKey, Rcon, 4);Rcon[0] = gf_mult_by02(Rcon[0]);}else if (Nk > 6 && i % Nk == Nb){sub_bytes(pRoundKey, 4, 0);}xor_bytes(pRoundKey, pRoundKey - 4*Nk, 4);}
}//
// 函数名: aes_encrypt
// 描述: 加密数据
// 输入参数: pPlainText -- 明文,即需加密的数据,其长度为nDataLen字节。
// nDataLen -- 数据长度,以字节为单位,必须为AES_KEY_LENGTH/8的整倍数。
// pIV -- 初始化向量,如果使用ECB模式,可设为NULL。
// 输出参数: pCipherText -- 密文,即由明文加密后的数据,可以与pPlainText相同。
// 返回值: 无。
//
void aes_encrypt(const unsigned char *pPlainText, unsigned char *pCipherText, unsigned int nDataLen, const unsigned char *pIV)
{unsigned int i;if (pPlainText != pCipherText){memcpy(pCipherText, pPlainText, nDataLen);}for (i = nDataLen/(4*Nb); i > 0 ; i--, pCipherText += 4*Nb){#if AES_MODE == AES_MODE_CBCxor_bytes(pCipherText, pIV, 4*Nb);#endifblock_encrypt(pCipherText);pIV = pCipherText;}
}//
// 函数名: aes_decrypt
// 描述: 解密数据
// 输入参数: pCipherText -- 密文,即需解密的数据,其长度为nDataLen字节。
// nDataLen -- 数据长度,以字节为单位,必须为AES_KEY_LENGTH/8的整倍数。
// pIV -- 初始化向量,如果使用ECB模式,可设为NULL。
// 输出参数: pPlainText -- 明文,即由密文解密后的数据,可以与pCipherText相同。
// 返回值: 无。
//
void aes_decrypt( const unsigned char *pCipherText,unsigned char *pPlainText, unsigned int nDataLen, const unsigned char *pIV)
{unsigned int i;if (pPlainText != pCipherText){memcpy(pPlainText, pCipherText, nDataLen);}// 从最后一块数据开始解密,这样不用开辟空间来保存IVpPlainText += nDataLen - 4*Nb;for (i = nDataLen/(4*Nb); i > 0 ; i--, pPlainText -= 4*Nb){block_decrypt(pPlainText);#if AES_MODE == AES_MODE_CBCif (i == 1){// 最后一块数据xor_bytes(pPlainText, pIV, 4*Nb);}else{xor_bytes(pPlainText, pPlainText - 4*Nb, 4*Nb);}#endif}
}
三、使用
使用注意点:
1、AES_KEY_LENGTH取值只能是128,192 和 256
2、密钥和向量表长度为AES_KEY_LENGTH/8个字节
3、加密、解密数据长度为AES_KEY_LENGTH/8的整数倍字节
int main(void)
{ u8 buf[16],saveBuf[16],descryptBuf[16];u16 i;unsigned char AES128key[16] = "123456789abcdefa";//秘钥unsigned char AES_IV[16]= "0102030405123456";//向量表delay_init(168); //初始化延时函数LED_Init(); //初始化LED端口aes_init(AES128key);//AES初始化for(i=0;i<sizeof(buf);i++){buf[i]=i;}while(1){aes_encrypt(buf,saveBuf,sizeof(buf), AES_IV);aes_decrypt(saveBuf,descryptBuf, sizeof(buf), AES_IV);GPIO_ResetBits(GPIOF,GPIO_Pin_9); //LED0对应引脚GPIOF.9拉低,亮 等同LED0=0;GPIO_SetBits(GPIOF,GPIO_Pin_10); //LED1对应引脚GPIOF.10拉高,灭 等同LED1=1;delay_ms(500); //延时300msGPIO_SetBits(GPIOF,GPIO_Pin_9); //LED0对应引脚GPIOF.0拉高,灭 等同LED0=1;GPIO_ResetBits(GPIOF,GPIO_Pin_10); //LED1对应引脚GPIOF.10拉低,亮 等同LED1=0;delay_ms(500); //延时300ms}
}
Chapter2 stm32 的 md5计算函数
原文链接:https://blog.csdn.net/joyopirate/article/details/122006598
假如你用的是stm32cubeide,可以考虑使用官方的库。使用方法可以看我写的这个【加密库使用方法】
参考:
https://www.cnblogs.com/xuning/p/4686021.html
根据上面那个网站提供方法稍加改造得到的
#define ROTATELEFT(value, bits) (((value) << (bits)) | ((value) >> (32 - (bits))))/*** @desc: convert message and mes_bkp string into integer array and store them in w*/
static void md5_process_part1(uint32_t *w, unsigned char *message, uint32_t *pos, uint32_t mes_len, const unsigned char *mes_bkp)
{uint32_t i; // used in for loopfor(i = 0; i <= 15; i++){int32_t count = 0;while(*pos < mes_len && count <= 24){w[i] += (((uint32_t)message[*pos]) << count);(*pos)++;count += 8;}while(count <= 24){w[i] += (((uint32_t)mes_bkp[*pos - mes_len]) << count);(*pos)++;count += 8;}}
}/*** @desc: start encryption based on w*/
static void md5_process_part2(uint32_t abcd[4], uint32_t *w, const uint32_t k[64], const uint32_t s[64])
{uint32_t i; // used in for loopuint32_t a = abcd[0];uint32_t b = abcd[1];uint32_t c = abcd[2];uint32_t d = abcd[3];uint32_t f = 0;uint32_t g = 0;for(i = 0; i < 64; i++){if(i >= 0 && i <= 15){f = (b & c) | ((~b) & d);g = i;}else if(i >= 16 && i <= 31){f = (d & b) | ((~d) & c);g = (5 * i + 1) % 16;}else if(i >= 32 && i <= 47){f = b ^ c ^ d;g = (3 * i + 5) % 16;}else if(i >= 48 && i <= 63){f = c ^ (b | (~d));g = (7 * i) % 16;}uint32_t temp = d;d = c;c = b;b = ROTATELEFT((a + f + k[i] + w[g]), s[i]) + b;a = temp;}abcd[0] += a;abcd[1] += b;abcd[2] += c;abcd[3] += d;
}static const uint32_t k_table[]={0xd76aa478,0xe8c7b756,0x242070db,0xc1bdceee,0xf57c0faf,0x4787c62a,0xa8304613,0xfd469501,0x698098d8,0x8b44f7af,0xffff5bb1,0x895cd7be,0x6b901122,0xfd987193,0xa679438e,0x49b40821,0xf61e2562,0xc040b340,0x265e5a51,0xe9b6c7aa,0xd62f105d,0x02441453,0xd8a1e681,0xe7d3fbc8,0x21e1cde6,0xc33707d6,0xf4d50d87,0x455a14ed,0xa9e3e905,0xfcefa3f8,0x676f02d9,0x8d2a4c8a,0xfffa3942,0x8771f681,0x6d9d6122,0xfde5380c,0xa4beea44,0x4bdecfa9,0xf6bb4b60,0xbebfbc70,0x289b7ec6,0xeaa127fa,0xd4ef3085,0x04881d05,0xd9d4d039,0xe6db99e5,0x1fa27cf8,0xc4ac5665,0xf4292244,0x432aff97,0xab9423a7,0xfc93a039,0x655b59c3,0x8f0ccc92,0xffeff47d,0x85845dd1,0x6fa87e4f,0xfe2ce6e0,0xa3014314,0x4e0811a1,0xf7537e82,0xbd3af235,0x2ad7d2bb,0xeb86d391
};static const uint32_t s_table[]={7,12,17,22,7,12,17,22,7,12,17,22,7,12,17,22,5,9,14,20,5,9,14,20,5,9,14,20,5,9,14,20,4,11,16,23,4,11,16,23,4,11,16,23,4,11,16,23,6,10,15,21,6,10,15,21,6,10,15,21,6,10,15,21
};int32_t cal_md5(unsigned char *result, unsigned char *data, int length){if (result == NULL){return 1;}uint32_t w[16];uint32_t i; // used in for loopuint32_t mes_len = length;uint32_t looptimes = (mes_len + 8) / 64 + 1;uint32_t abcd[] = {0x67452301, 0xEFCDAB89, 0x98BADCFE, 0x10325476};uint32_t pos = 0; // position pointer for messageuint32_t bkp_len = 64 * looptimes - mes_len; // 经过计算发现不超过72// unsigned char *bkp_mes = (unsigned char *)calloc(1, bkp_len);unsigned char bkp_mes[80];for(int i = 0; i < 80; i++) //初始化{bkp_mes[i] = 0;}bkp_mes[0] = (unsigned char)(0x80);uint64_t mes_bit_len = ((uint64_t)mes_len) * 8;for(i = 0; i < 8; i++){bkp_mes[bkp_len-i-1] = (unsigned char)((mes_bit_len & (0x00000000000000FF << (8 * (7 - i)))) >> (8 * (7 - i)));}for(i = 0; i < looptimes; i++){for(int j = 0; j < 16; j++) //初始化{w[j] = 0x00000000;}md5_process_part1(w, data, &pos, mes_len, bkp_mes); // compute wmd5_process_part2(abcd, w, k_table, s_table); // calculate md5 and store the result in abcd}for(int i = 0; i < 16; i++){result[i] = ((unsigned char*)abcd)[i];}return 0;
}
Chapter3 STM32 应用程序加密的一种设计方案
原文链接:https://blog.csdn.net/u010058695/article/details/101014672
前言
STM32编译后的代码存在FLASH中,通过外部工具可以读出来全部数据,一旦硬件抄板一样,再将FLASH数据全部拷贝至抄板单片机中,既可以完全实现硬件和软件功能抄袭。因此,需要对自己的应用程序加密,即使被抄板和读取FLASH数据,拷贝过后仍然无法执行相应的功能。【ps:没有解不开的单片机,主要看解密的经济效益】
主要思路:利用每个芯片的96位唯一ID,进行一定的计算和单向加密,得到唯一的应用AppKey并保存至FLASH,运行APP时读取该FLASH位置的AppKey,并与计算得到的AppKey比较是否相同,如果相同则正常运行,不相同则退出。
因此,在正确运行APP之前,需要保证FLASH保存有AppKey,可以在设备出厂前提前通过代码方式写入FLASH,再烧写正常的APP代码。为了减小代码烧写的工作量,采用IAP的方式实现自动出厂配置,方案包括三个工程:BootLoader,Encrypt,APP。三个工程在FLASH中的内存分配与OTA-IAP相同。
一、计算AppKey
step1 :为避免出现UID_BASE的明文,在读取UID时,对其地址进行一定的简单运算,再读取UID。
#define ADDRSEED (0x20170620)
#define METHORDADDR (ADDRSEED^1+1)
#define METHORDEUID (ADDRSEED|0x12345678)volatile const uint32_t uidaddr[3] = {UID_BASE+METHORDADDR,UID_BASE+4-METHORDADDR,UID_BASE+8+METHORDADDR};//get uid, by calc uidbase
//uid: ptr, 96byte length
static void Getuid(volatile uint32_t *uid)
{volatile uint32_t addrtemp;addrtemp = uidaddr[0] - METHORDADDR;uid[0] = *(volatile uint32_t*)(addrtemp);addrtemp = uidaddr[1] + METHORDADDR;uid[1] = *(volatile uint32_t*)(addrtemp);addrtemp = uidaddr[2] - METHORDADDR;uid[2] = *(volatile uint32_t*)(addrtemp);
}
step2 :对UID进一步简单加密。
//encrypt uid
//euid: ptr, 96byte length
static void Encryptuid(volatile uint32_t *euid)
{uint32_t uid[3];Getuid(uid);euid[0] = uid[0] + METHORDEUID;euid[0] ^= ADDRSEED;euid[1] = uid[1] + METHORDEUID;euid[1] ^= ADDRSEED;euid[2] = uid[2] + METHORDEUID;euid[2] ^= ADDRSEED;
}
step3 :对EncryptUID计算MD5,计算128bit单向散列值。
//generate md5 by euid
//key: ptr, 16byte length
void GenerateMD5(uint8_t *md5)
{uint32_t *euid;Encryptuid(euid);MD5_Init(&Context);MD5_Update(&Context,(uint8_t *)euid,96);MD5_Final(&Context,md5);
}
step4 :根据FLASH页容量大小,将MD5扩充至1024Byte,采用随机数扩充。
//generate key
//key: ptr, 1024 length, md5 extend to 1024
void GenerateKey(uint8_t *key)
{uint16_t i,j;uint8_t md5val[16];GenerateMD5(md5val);j = 0;for(i = 0; i < 1024; i++){if(i%64 == 0){key[i] = md5val[j++];}else{srand1(HAL_GetTick());key[i] = randr(0,0xFF);}}
}
二、自动配置流程
Encrypt工程代码只在出厂时运行一次,目的是利用UID产生AppKey,并将其提前写入指定FLASH中,因此,该段代码执行一次后将擦除。可设置Encrypt在APPA区中运行,利用IAP功能,将APPB区的APP固件在执行完Encrypt固件后,搬移至APPA区。
具体地,出厂自动配置密钥流程如下:
Step1:上电启动单片机,首先执行BootLoader程序。
Step2:BootLoader读取Parameter参数区,此时参数区设置为无升级任务,BootLoader执行APPA区的代码,进入 Step3 。存在升级任务时,执行 Step6, Step7 。
Step3:BootLoader跳转至APPA,执行Encrypt代码,根据UID计算AppKey,执行 Step4 和 Step5 。
Step4:将计算的AppKey存入Parameter区指定的位置,并写入升级标志和APPB区相关固件参数。
Step5:重启单片机,进入BootLoader。
Step6:清除升级任务标志。
Step7:拷贝APPB代码至APPA区,即擦除Encrypt代码,最后执行重启进入 Step5 ,重启后运行真正的APP代码。
三、出厂固件合并
出厂前需要在STM32中烧写BootLoader、Encrypt和APP三份Hex固件,加大了时间开销,三份Hex固件存在FlASH的不同位置,地址容易出错。因此,可将三份Hex进行合并成一个Hex,进行一次烧写,按照自动配置流程完成加密和代码搬运工作。
BootLoader工程在程序一开始运行,其地址和空间大小分配仍然按照0x08000000和20kB分配,编译生成boot.hex。Encrypt工程和APP都在APPA区运行,因此,两者地址和空间大小分配为相同的0x08005000和50kB。APP.Hex只是保存在APPB区,带加密执行完成后通知Bootloader进行搬移。
出厂固件按照boot.Hex,encrypt.Hex,app.Hex的顺序合并,打开Hex文件,分别用后一个文件的全部内容替换前一个文件的最后一行,保存为hex格式就可以。
个人总结
头文件AppCryptography.h
#ifndef __BSP_APPCRYPTOGRAPHY_H__
#define __BSP_APPCRYPTOGRAPHY_H__#include <stdint.h>/*** 函数功能: 计算一组数据的MD5值* 输入参数: data:指向待计算数据的首地址;length:待计算数据的长度;* 返 回 值: result:MD5结果返回值,16字节,128位* 说 明: 无*/
int32_t cal_md5(unsigned char *result, unsigned char *data, int length);
void Getuid(void);#endif
#include "AppCryptography/AppCryptography.h"
#include <stdio.h>
#include <stdlib.h>
#include <stm32f410tx.h>#define ROTATELEFT(value, bits) (((value) << (bits)) | ((value) >> (32 - (bits))))#define ADDRSEED (0x20170620)
#define METHORDADDR (ADDRSEED^1+1)
#define METHORDEUID (ADDRSEED|0x12345678)volatile const uint32_t uidaddr[3] = {UID_BASE+METHORDADDR,UID_BASE+4-METHORDADDR,UID_BASE+8+METHORDADDR};
uint32_t uid[3] = {0x00,0x00,0x00};//get uid, by calc uidbase
//uid: ptr, 96byte length
void Getuid(void)
//void Getuid(volatile uint32_t *uid)
{
// volatile uint32_t addrtemp;
// addrtemp = uidaddr[0] - METHORDADDR;
// uid[0] = *(volatile uint32_t*)(addrtemp);
// addrtemp = uidaddr[1] + METHORDADDR;
// uid[1] = *(volatile uint32_t*)(addrtemp);
// addrtemp = uidaddr[2] - METHORDADDR;
// uid[2] = *(volatile uint32_t*)(addrtemp);HAL_GetUID(uid); //STM32不同系列,UID_BASE不一样
}源文件AppCryptography.c
/*** @desc: convert message and mes_bkp string into integer array and store them in w*/
static void md5_process_part1(uint32_t *w, unsigned char *message, uint32_t *pos, uint32_t mes_len, const unsigned char *mes_bkp)
{uint32_t i; // used in for loopfor(i = 0; i <= 15; i++){int32_t count = 0;while(*pos < mes_len && count <= 24){w[i] += (((uint32_t)message[*pos]) << count);(*pos)++;count += 8;}while(count <= 24){w[i] += (((uint32_t)mes_bkp[*pos - mes_len]) << count);(*pos)++;count += 8;}}
}/*** @desc: start encryption based on w*/
static void md5_process_part2(uint32_t abcd[4], uint32_t *w, const uint32_t k[64], const uint32_t s[64])
{uint32_t i; // used in for loopuint32_t a = abcd[0];uint32_t b = abcd[1];uint32_t c = abcd[2];uint32_t d = abcd[3];uint32_t f = 0;uint32_t g = 0;for(i = 0; i < 64; i++){if(i >= 0 && i <= 15){f = (b & c) | ((~b) & d);g = i;}else if(i >= 16 && i <= 31){f = (d & b) | ((~d) & c);g = (5 * i + 1) % 16;}else if(i >= 32 && i <= 47){f = b ^ c ^ d;g = (3 * i + 5) % 16;}else if(i >= 48 && i <= 63){f = c ^ (b | (~d));g = (7 * i) % 16;}uint32_t temp = d;d = c;c = b;b = ROTATELEFT((a + f + k[i] + w[g]), s[i]) + b;a = temp;}abcd[0] += a;abcd[1] += b;abcd[2] += c;abcd[3] += d;
}static const uint32_t k_table[]={0xd76aa478,0xe8c7b756,0x242070db,0xc1bdceee,0xf57c0faf,0x4787c62a,0xa8304613,0xfd469501,0x698098d8,0x8b44f7af,0xffff5bb1,0x895cd7be,0x6b901122,0xfd987193,0xa679438e,0x49b40821,0xf61e2562,0xc040b340,0x265e5a51,0xe9b6c7aa,0xd62f105d,0x02441453,0xd8a1e681,0xe7d3fbc8,0x21e1cde6,0xc33707d6,0xf4d50d87,0x455a14ed,0xa9e3e905,0xfcefa3f8,0x676f02d9,0x8d2a4c8a,0xfffa3942,0x8771f681,0x6d9d6122,0xfde5380c,0xa4beea44,0x4bdecfa9,0xf6bb4b60,0xbebfbc70,0x289b7ec6,0xeaa127fa,0xd4ef3085,0x04881d05,0xd9d4d039,0xe6db99e5,0x1fa27cf8,0xc4ac5665,0xf4292244,0x432aff97,0xab9423a7,0xfc93a039,0x655b59c3,0x8f0ccc92,0xffeff47d,0x85845dd1,0x6fa87e4f,0xfe2ce6e0,0xa3014314,0x4e0811a1,0xf7537e82,0xbd3af235,0x2ad7d2bb,0xeb86d391
};static const uint32_t s_table[]={7,12,17,22,7,12,17,22,7,12,17,22,7,12,17,22,5,9,14,20,5,9,14,20,5,9,14,20,5,9,14,20,4,11,16,23,4,11,16,23,4,11,16,23,4,11,16,23,6,10,15,21,6,10,15,21,6,10,15,21,6,10,15,21
};/* 函数体 --------------------------------------------------------------------*/
/*** 函数功能: 计算一组数据的MD5值* 输入参数: data:指向待计算数据的首地址;length:待计算数据的长度;* 返 回 值: result:MD5结果返回值,16字节,128位* 说 明: 无*/
int32_t cal_md5(unsigned char *result, unsigned char *data, int length){if (result == NULL){return 1;}uint32_t w[16];uint32_t i; // used in for loopuint32_t mes_len = length;uint32_t looptimes = (mes_len + 8) / 64 + 1;uint32_t abcd[] = {0x67452301, 0xEFCDAB89, 0x98BADCFE, 0x10325476};uint32_t pos = 0; // position pointer for messageuint32_t bkp_len = 64 * looptimes - mes_len; // 经过计算发现不超过72// unsigned char *bkp_mes = (unsigned char *)calloc(1, bkp_len);unsigned char bkp_mes[80];for(int i = 0; i < 80; i++) //初始化{bkp_mes[i] = 0;}bkp_mes[0] = (unsigned char)(0x80);uint64_t mes_bit_len = ((uint64_t)mes_len) * 8;for(i = 0; i < 8; i++){bkp_mes[bkp_len-i-1] = (unsigned char)((mes_bit_len & (0x00000000000000FF << (8 * (7 - i)))) >> (8 * (7 - i)));}for(i = 0; i < looptimes; i++){for(int j = 0; j < 16; j++) //初始化{w[j] = 0x00000000;}md5_process_part1(w, data, &pos, mes_len, bkp_mes); // compute wmd5_process_part2(abcd, w, k_table, s_table); // calculate md5 and store the result in abcd}for(int i = 0; i < 16; i++){result[i] = ((unsigned char*)abcd)[i];}return 0;
}
具体使用方法
uint8_t data1[16] = "abcdefg012345678";
uint8_t data_result[16] ={0};int k = sizeof(data1);
cal_md5(data_result, data1, k);
相关文章:
STM32利用AES加密数据、解密数据
STM32利用AES加密数据、解密数据 MD5在线工具Chapter1 STM32利用AES加密数据、解密数据一、头文件二、源文件三、使用 Chapter2 stm32 的 md5计算函数Chapter3 STM32 应用程序加密的一种设计方案前言一、计算AppKey二、自动配置流程三、出厂固件合并 个人总结 MD5在线工具 htt…...
C语言技能数(知识点汇总)
C语言技能数(知识点汇总) C 语言概述特点不足之处 标准编程机制 数据类型变量数据类型字符类型整数类型符号位二进制的原码、反码和补码 浮点类型布尔类型 常量字面常量const 修饰的常变量#define定义的标识符常量枚举常量 sizeofsizeof(结构体)不要对 v…...
Vue.js+SpringBoot开发企业项目合同信息系统
目录 一、摘要1.1 项目介绍1.2 项目录屏 二、功能模块2.1 数据中心模块2.2 合同审批模块2.3 合同签订模块2.4 合同预警模块2.5 数据可视化模块 三、系统设计3.1 用例设计3.2 数据库设计3.2.1 合同审批表3.2.2 合同签订表3.2.3 合同预警表 四、系统展示五、核心代码5.1 查询合同…...
Linux Shell中的echo命令详解
Linux Shell中的echo命令详解 在Linux Shell中,echo命令是一个常用的内置命令,用于在终端上显示文本或字符串。它主要用于显示变量的值,创建文件的内容,或者简单地输出一些信息。在本文中,我们将详细探讨echo命令的用…...
php 页面中下载文件|图片
一、需求 页面中点击下载图片 二、实现 protected function pageLoad(){$filePath $_GET[file];$host $_SERVER[HTTP_HOST];$file http://.$host.$filePath;$fileName basename($filePath);$content file_get_contents($file);ob_clean();$suffixArr explode(., $file…...
2024年企业经济管理与大数据国际会议(ICEEMBD 2024)
2024年企业经济管理与大数据国际会议(ICEEMBD 2024) 2024 International Conference on Enterprise Economic Management and Big Data 会议简介: 大数据分析在经济管理中发挥着至关重要的作用,它不仅能够提高决策效率和准确性…...
数新网络助阵哈工大国家级项目,共绘数智化新篇章,打造大数据法务平台
2024年1月23日,由哈尔滨工业大学牵头的国家重点研发计划“基于多元关联分析的大数据法律监督关键技术研究”项目2023年度项目进展报告会在杭州成功举办。 本次会议通过线上线下同步开展的形式进行,旨在对项目的进展情况进行深入总结探讨及成果展示,确保项目的研发方向与实际法律…...
React+umi+dva 项⽬实战-lesson6
lesson4-react全家桶及原理解析.mov 项⽬实战 项⽬实战 课堂⽬标资源知识要点起步Generatorredux-sagaumi why umidvadva+umi 的约定安装Umi基本使⽤理解dva移动端cra项⽬简介课堂⽬标 掌握企业级应⽤框架 - umi掌握数据流⽅案 - dva掌握⽣成器函数 - generator掌握redux异步⽅…...
Vue el-table 合并单元格
一般常见的就是下图这种的单列,上下重复进行合并。 有时候可能也会需要多行多列的合并。 <!DOCTYPE html> <html lang"en"><head><meta charset"UTF-8"><meta http-equiv"X-UA-Compatible" content&qu…...
面试算法-61-二叉树的右视图
题目 给定一个二叉树的 根节点 root,想象自己站在它的右侧,按照从顶部到底部的顺序,返回从右侧所能看到的节点值。 示例 1: 输入: [1,2,3,null,5,null,4] 输出: [1,3,4] 解 class Solution {public List<Integer> rightSideView(T…...
【鸿蒙HarmonyOS开发笔记】动画过渡效果之布局更新动画
概述 动画的原理是在一个时间段内,多次改变UI外观,由于人眼会产生视觉暂留,所以最终看到的就是一个“连续”的动画。UI的一次改变称为一个动画帧,对应一次屏幕刷新,而决定动画流畅度的一个重要指标就是帧率FPS&#x…...
过路费的题解
目录 原题描述: 题目描述 输入格式: 输出格式: 样例输入: 样例输出: 数据范围: 提示: 主要思路: code: 原题描述: 题目描述 在某个遥远的国家里,有…...
51单片机LED8*8点阵显示坤坤跳舞打篮球画面
我们作为一名合格的 ikun,专业的小黑子,这个重要的知识必须学会。 先看效果: 51LED点阵_鸡你太美 这里我们首先要用到延时函数Delay: void Delay(unsigned int xms) {unsigned char i, j;while(xms--){ i 2;j 239;do{while (-…...
C++_day6:2024/3/18
作业1:编程题: 以下是一个简单的比喻,将多态概念与生活中的实际情况相联系: 比喻:动物园的讲解员和动物表演 想象一下你去了一家动物园,看到了许多不同种类的动物,如狮子、大象、猴子等。现在…...
汇编语言和IBM的关系
一 缺乏汇编的硬件没有灵魂 1964年,在IBM没有发明System 360大型计算机之前,IBM已经发明了很多计算机。如IBM 1952年发布的第一台商用计算机:701计算机。1959年,IBM首次利用晶体管、磁芯存储器、印刷电路技术,发明了小…...
堆(数据结构)
堆的概念及结构 如果有一个关键码的集合K { , , ,…, },把它的所有元素按完全二叉树的顺序存储方式存储在一个一维数组中,并满足: < 且 < ( > 且 > ) i 0,1ÿ…...
医药工厂5G智能制造数字孪生可视化平台,推进医药企业数字化转型
医药工厂5G智能制造数字孪生可视化平台,推进医药企业数字化转型。随着科技的不断发展,数字化转型已成为医药企业不可或缺的一部分。5G智能制造医药工厂数字孪生可视化平台作为数字化转型的重要工具,正在逐步改变医药企业的生产方式和管理模式…...
C语言学习--八种排序算法
目录 排序的概念 1.直接插入排序 基本思想 代码实现 算法分析 2.希尔排序 基本思想 代码实现 算法分析 3.冒泡排序 基本思想 代码实现 算法分析 4.快速排序 基本思想 代码实现 算法分析 5.简单选择排序 基本思想 代码实现 算法分析 6.堆排序 基本思想 代…...
Infineon_TC264智能车代码初探及C语言深度学习(二)
本篇文章记录我在智能车竞赛中,对 Infineon_TC264 这款芯片的底层库函数的学习分析。通过深入地对其库函数进行分析,C语言深入的知识得以再次在编程中呈现和运用。故觉得很有必要在此进行记录分享一下。 目录 编辑 一、代码段分析 NO.1 指向结构体…...
第十三届蓝桥杯(C/C++ 大学B组)
目录 试题 A: 九进制转十进制 试题 B: 顺子日期 试题 C: 刷题统计 试题 D: 修剪灌木 试题 E: X 进制减法 试题 F: 统计子矩阵 试题 G: 积木画 试题 H: 扫雷 试题 I: 李白打酒加强版 试题 J: 砍竹子 试题 A: 九进制转十进制 九进制正整数 ( 2022 )转换成十进制等于多…...
数据结构从入门到精通——排序的概念及运用
排序的概念及运用 前言一、排序的概念排序稳定性内部排序外部排序 二、排序运用三、常见的排序算法四、排序性能检测代码srand()clock() 五、oj排序测试代码 前言 排序是将数据按照一定规则重新排列的过程,常见规则有升序、降序等。排序算法如冒泡排序、快速排序等…...
react面试题总结
1、当调用 setState的时候,发生了什么操作? 当调用 setState时, React做的第一件事是将传递给setState的对象合并到组件的当前状态,这将启动一个称为和解( reconciliation)的过程。 和解的最终目标是&#…...
5_springboot_shiro_jwt_多端认证鉴权_禁用Cookie
1. Cookie是什么 Cookie是一种在客户端(通常是用户的Web浏览器)和服务器之间进行状态管理的技术。当用户访问Web服务器时,服务器可以向用户的浏览器发送一个名为Cookie的小数据块。浏览器会将这个Cookie存储在客户端,为这个Co…...
条形码申请指南:外地人如何成功注册香港条形码
香港条形码是打造的通行证,消费者对香港条码有一定的认知,拥有香港条形码就获得消费者对产品的认可,香港条形码是全球条码中具有防伪功能的条形码,化妆品、护肤品、保健品、包装食品等行业的产品认证,就有必要申请香港…...
Covalent Network借助大规模的历史Web3数据集,推动人工智能发展
人工智能在众多领域中增强了区块链的实用性,反之亦然,区块链确保了 AI 模型所使用的数据的来源和质量。人工智能带来的生产力提升,将与区块链系统固有的安全性和透明度融合。 Covalent Network(CQT)正位于这两项互补技…...
test测试类-变量学习
test测试类 作用:标记到类上成为测试类,标记到方法上成为测试方法 变量:测试类的变量,在测试类括号中应用 1、invocationCount变量 意思是这个方法应该被调用的次数。 在测试框架中,特别是当使用参数化测试或数据驱动…...
【DL经典回顾】激活函数大汇总(二十七)(Bent Identity附代码和详细公式)
激活函数大汇总(二十七)(Bent Identity附代码和详细公式) 更多激活函数见激活函数大汇总列表 一、引言 欢迎来到我们深入探索神经网络核心组成部分——激活函数的系列博客。在人工智能的世界里,激活函数扮演着不可或…...
element-plus el-table表格默认选中某一行
需求:进入页面时默认选中表格第一行 <el-tableref"singleTableRef":data"tableData"highlight-current-rowrow-click"handleCurrentChange" ><el-table-column property"date" label"日期" /><…...
Vue+SpringBoot打造民宿预定管理系统
目录 一、摘要1.1 项目介绍1.2 项目录屏 二、功能模块2.1 用例设计2.2 功能设计2.2.1 租客角色2.2.2 房主角色2.2.3 系统管理员角色 三、系统展示四、核心代码4.1 查询民宿4.2 新增民宿4.3 新增民宿评价4.4 查询留言4.5 新增民宿订单 五、免责说明 一、摘要 1.1 项目介绍 基于…...
基于单片机的模糊PID炉温控制系统设计
摘 要 电热炉是在工业热处理的生产中广泛使用的一种设备,电热炉的温度控制系统存在时变性,非线性,滞后性等特征,难以用常规PID的控制器对系统达到很好的控制效果。当控温精度的要求高时,使用传统的控制理论方法难以达…...
wordpress 邮件防骚扰/品牌推广活动策划方案
开篇 AgileEAS.NET5.0平台,预计这个月的中旬就会发布,这次发布里面相比上次的AgileEAS.NET4.0的版本主要的变化是以下几块内容: 本文,主要是针对其中的工作流这块,进行讲述基本的说明,这个月的中旬,大家就可…...
淄博网站建设企业/关键词优化排名怎么做
http2的问题: 队头阻塞 http2 解决http队头阻塞问题,但是没有解决tcp队头阻塞问题 http1.1引入了持久化连接:在一个tcp连接上可以传送多个http的请求和相应,减少建立连接和关闭连接的消耗和延迟 http1.1在持久化连接上支持请求…...
乐清网站设计制作/网站百度不收录
IOS7 webview全屏导致状态栏悬浮在页面上解决方案:打开 ios项目/classes/MainViewController.m,修改viewWillAppear方法- (void)viewWillAppear:(BOOL)animated{// View defaults to full size. If you want to customize the views size, or its subvie…...
运城 网站建设/域名注册网站系统
项目中遇到这么一个问题,在easyui的datagrid中,删除一条记录成功,重新加载datagrid后,删除其他的不行了,点击删除返回的是删除那一行的id,要解决这个问题,应该在删除数据重新加载datagrid后再清…...
淘宝客怎样做网站/百度人工投诉电话是多少
规范 RESTful API RESTful 是⼀种架构设计⻛格。核⼼思想是⼀切皆资源。RESTful架构中,将所有的东⻄都视为资源, 每个资源对应了⼀个URI(统⼀资源标识符)所有的操作都是对资源的增删改查,这些操作对应了 HTTP 的 GET、…...
做内贸哪个网站找客户/大数据培训机构排名前十
2019独角兽企业重金招聘Python工程师标准>>> 1、Integer 相等比较 public static void main(String[] args) {Integer d 127;Integer f 127;Integer e 130;Integer g 130;System.out.println(df);//trueSystem.out.println(eg);// false} 上述:总所周…...