草庐IT

密码学:AES算法C语言完整、简单实现

正义的伙伴-L 2023-06-02 原文

文章目录


前言

最近密码学学到了分组密码体制。本文主要分享了AES算法的基础知识和C语言的简单实现。


一、AES简介

AES是分组密码,安全性良好。
与3DES比较:比3DES快、至少和3DES一样安全。
AES分组长度:128 bit
密钥长度:128/192/256 bit
对应的加密轮数:10/12/14 轮
【本文是以分组长度128bit,密钥长度128bit 为例】

二、加解密过程

1.常规版

AES加解密流程图:

以密钥长度128bit(10轮变换)为例
文字描述如下:

准备:明文plaintext [16]	
	 初始密钥和 10 个扩展密钥   函数:密钥扩展函数 KeyExpansion()

一:初始变换。
	轮密钥加             函数:AddRoundKey()
二:9轮循环
	1.S盒字节代换        函数:SubBytes()    InvSubBytes() 
	2.行移位 			函数:ShiftRows()    InvShiftRows() 
	3.列混合			函数: MixColumns()  InvMixColumns() 
		字节乘法		    函数:Mul() 
	4.轮密钥加 			函数:AddRoundKey()
三:最终轮
	1.S盒字节代换        函数:SubBytes()    InvSubBytes() 
	2.行移位 			函数:ShiftRows()    InvShiftRows() 
	3.轮密钥加			函数:AddRoundKey()
	
AES解密算法顺序 从下往上,当然使用密钥顺序也是反的 。

2.优化版

AES加解密优化版流程图:

两个流程图找找不同?

  1. 优化版AES算法加解密是对称结构
  2. 优化版AES算法每轮使用的密钥有所改变:MRK= InvMixColumns(ERK)。

原因:

  1. ByteSub( )与ShiftRows( )运算次序可交换
  2. MixColumns( )、 InvMixColumns( )关于异或有分配律
    结论:MRK= InvMixColumns(ERK)。对于具体运算,感兴趣的朋友可以找找资料。

三、 各函数简要介绍

补充:

  1. bit、字、字节
    1字=4字节=32bit
    1个字母占一个字节,8bit
  2. AES 分组用字节为单位的正方形矩阵描述,16字节的明文、密文和轮子密钥都以一个4x4的矩阵表示。该矩阵一列表示一字,4个字符。
    例:

    在这之后,就将对应的字符转化成16进制数(状态矩阵)。
  3. 异或的知识:相同为0,不同为1

1.密钥编排、密钥扩展函数 KeyExpansion()

密钥编排功能:首先初始条件里面应该有一个128bit(16个字母) 的初始密钥Key[16],由于AES有10轮变换,所以需要由初始密钥扩展出10轮密钥,分别进行每一轮变换。
结果:最终得到 扩展秘钥矩阵 k[11][16],其中k[0][16]就是初始密钥Key[16]。

过程:从初始密钥开始,矩阵按列展开,根据1个字分wi
对wi的操作:

  1. i!=4的倍数 w[i]=w[i-4]⊕ w[i-1]
  2. i==4的倍数 w[i]= w[i-4]⊕ T[i-1]
    其中T变换:
    ①字循环(左移)
    ②字节代换(S盒)
    ③轮常量异或RC
    RC的计算:RC:RC[1]=1 ; RC[i]=0x02*RC[i-1]
    轮常量Rcon[i]是一个字,详细见下表:

    注意:
    1. T[i]变换是对w[i]一列进行操作。
    2. 矩阵按列展开看下面第4点的描述
    3. 字节代换需要先看下面第3点对S盒的介绍。
    4. RC[i]的计算要先看下面第5点对字节乘法的介绍

从上图可以看出当 i=4 的倍数 时,wi对应矩阵k[i]的下标为{0,1,2 ,3}

下面以计算 w4一列中第一个数 为例来具体阐述 i=4的倍数 时的变换,信息如图:

w[4]=w[0]⊕ T[3]
T[3]: 对w[3]变换
①字循环(左移): (n p q m) 对应下标 {13,14,15,12}
②字节代换(S盒) : (n p q m)
③轮常量异或RC ^( 0x01 0 0 0)

代码编写需与k[11][16]矩阵联系起来:

代码如下:

void KeyExpansion(unsigned char K[16],unsigned char k[11][16])     
{
    unsigned char RC[10];
	RC[0]=1;
	int i;
	for(i=1;i<10;i++)
		RC[i]=Mul(0x02,RC[i-1]);
	for(i=0;i<16;i++)
		k[0][i]=K[i];
	for(i=1;i<11;i++)
	{
	    k[i][0]=k[i-1][0]^S[k[i-1][13]]^RC[i-1];
		k[i][1]=k[i-1][1]^S[k[i-1][14]];
		k[i][2]=k[i-1][2]^S[k[i-1][15]];
		k[i][3]=k[i-1][3]^S[k[i-1][12]];
		k[i][4]=k[i-1][4]^k[i][0];
		k[i][5]=k[i-1][5]^k[i][1];
		k[i][6]=k[i-1][6]^k[i][2];
		k[i][7]=k[i-1][7]^k[i][3];
		k[i][8]=k[i-1][8]^k[i][4];
		k[i][9]=k[i-1][9]^k[i][5];
		k[i][10]=k[i-1][10]^k[i][6];
		k[i][11]=k[i-1][11]^k[i][7];
		k[i][12]=k[i-1][12]^k[i][8];
		k[i][13]=k[i-1][13]^k[i][9];
		k[i][14]=k[i-1][14]^k[i][10];
		k[i][15]=k[i-1][15]^k[i][11];
	}
}

2. 轮密钥加 函数:AddRoundKey()

解释:就是进行异或操作

密钥加运算的逆运算是自己。

void AddRoundKey( unsigned char *a , unsigned char *Key )  {  //  轮密钥加  
	for( int i = 0 ; i < 16 ; i ++ )
		a[i] ^= Key[i] ;
}

3. S盒字节代换 函数:SubBytes()

性质:关于字节的非线性变换
利用:代换表(即S盒)。AES有S盒和逆S盒。
解释:在矩阵中,根据当前字符的下标,去找S盒中对应的字符,并进行替换。

S盒参数:

unsigned char S[256] = {             // S盒
	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
	};
unsigned char IS[256] =  {                // S盒的逆
	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
	};	 

代码如下:

void SubBytes( unsigned char *input )   {    //  S盒字节代换
	for( int i = 0 ; i < 16 ; i ++ )
		input[i] = S[input[i]] ;
}
void InvSubBytes( unsigned char *input )    {  //   S盒字节代换逆变换
	for( int i = 0 ; i < 16 ; i ++ )
		input[i] = IS[input[i]] ;
}

4. 行移位 函数:ShiftRows()

解释:在分组长度为128bit的情况下,第0行不动,第一行循环左移1个字节,第二行循环左移2个字节,第三行循环左移3个字节
注意:不同分组长度的位移量不同。
行移位前提:根据AES分组的特点,首先要将原矩阵按列展开。搞清楚行列关系。下图给出了对应的下标,有助于理解代码。

代码如下:

void ShiftRows( unsigned char *a )     {   //   行移位—矩阵按列展开
	unsigned char b[16] ;
	b[ 0] = a[ 0] ;	b[ 4] = a[ 4] ;	b[ 8] = a[ 8] ;	b[12] = a[12] ;
	b[ 1] = a[ 5] ;	b[ 5] = a[ 9] ;	b[ 9] = a[13] ;	b[13] = a[ 1] ;
	b[ 2] = a[10] ;	b[ 6] = a[14] ;	b[10] = a[ 2] ;	b[14] = a[ 6] ;
	b[ 3] = a[15] ;	b[ 7] = a[ 3] ;	b[11] = a[ 7] ;	b[15] = a[11] ;
	for( int i = 0 ; i < 16 ; i ++ )
		a[i] = b[i] ;
} 
void InvShiftRows( unsigned char *a )     {    //   行移位逆变换  
	unsigned char b[16] ;
	b[ 0] = a[ 0] ;	b[ 4] = a[ 4] ;	b[ 8] = a[ 8] ;	b[12] = a[12] ;
	b[ 1] = a[13] ;	b[ 5] = a[ 1] ;	b[ 9] = a[ 5] ;	b[13] = a[ 9] ;
	b[ 2] = a[10] ;	b[ 6] = a[14] ;	b[10] = a[ 2] ;	b[14] = a[ 6] ;
	b[ 3] = a[ 7] ;	b[ 7] = a[11] ;	b[11] = a[15] ;	b[15] = a[ 3] ;
	for( int i = 0 ; i < 16 ; i ++ )
		a[i] = b[i] ;
} 

5. 列混合 函数: MixColumns()

字节乘法:利用x乘法实现

解释:原矩阵与一个固定矩阵的乘法

(1)固定矩阵c(x) (也是16进制):

  1. c(x)={03} x3+{01} x2+{01}x +{02}
    对应4字节向量为( 03 01 01 02),循环左移三次得到(02 03 01 01)
  2. c(x) 的逆多项式d (x) = {0B}x3 + {0D}x2 + {09}x + {0E}
    对应4字节向量为( 0B 0D 09 0E),循环左移三次得到(0E 0B 0D 09)
    【对应下面列混合的代码。】

(2)运算:乘法运算

  1. 矩阵乘法
    概括:字节乘法 和 异或操作

  2. 字节乘法
    x乘法:

    解释:
    M–左移一位(最低位为0)–>N
    If :0bM(M的二进制形式)的最高位为0,N为原值
    Else:0bM的最高位为1,N⊕00011011

字节乘法举例:
计算: 57 * 83 = ? (两个16进制数相乘:0x57*0x83)

字节乘法实现代码如下:

unsigned char Mul(unsigned char a, unsigned char b)
{
	unsigned char i;
	unsigned char temp;
	unsigned char result[8];
	unsigned char sum=0;
	result[0] = a;
	for(i=1;i<8;i++)       //x乘法 
	{
		temp=result[i-1];  //>>右移,<<左移,^异或 ,&与操作(同1为1) 
		temp=temp>>7;     //temp为最高位,判断 最高位 是0还是1 
		if(temp==1)
		{
			result[i]=result[i-1]<<1^0x1b;    // 0x1b=0b 00011011
		}
		else
		{
			result[i]=result[i-1]<<1;
		}
	}
	for(i=0;i<8;i++){			//左移之后补零 
		temp=b<<i&0x80;       //判断 b二进制的哪几位为1 
		if(temp==0x80)
		{
			sum^=result[7-i];
		}
	}
	return sum;
 }

列混合代码如下:

// 列混合 
void MixColumns( unsigned char *a )     {    
	unsigned char b[16] ;
	b[ 0] = Mul(0x02,a[0]) ^ Mul(0x03,a[1]) ^ a[2] ^ a[3];
	b[ 1] = Mul(0x02,a[1]) ^ Mul(0x03,a[2]) ^ a[3] ^ a[0];
	b[ 2] = Mul(0x02,a[2]) ^ Mul(0x03,a[3]) ^ a[0] ^ a[1];
	b[ 3] = Mul(0x02,a[3]) ^ Mul(0x03,a[0]) ^ a[1] ^ a[2];
	b[ 4] = Mul(0x02,a[4]) ^ Mul(0x03,a[5]) ^ a[6] ^ a[7];
	b[ 5] = Mul(0x02,a[5]) ^ Mul(0x03,a[6]) ^ a[7] ^ a[4];
	b[ 6] = Mul(0x02,a[6]) ^ Mul(0x03,a[7]) ^ a[4] ^ a[5];
	b[ 7] = Mul(0x02,a[7]) ^ Mul(0x03,a[4]) ^ a[5] ^ a[6];
	b[ 8] = Mul(0x02,a[8]) ^ Mul(0x03,a[9]) ^ a[10] ^ a[11];
	b[ 9] = Mul(0x02,a[9]) ^ Mul(0x03,a[10]) ^ a[11] ^ a[8];
	b[10] = Mul(0x02,a[10]) ^ Mul(0x03,a[11]) ^ a[8] ^ a[9];
	b[11] = Mul(0x02,a[11]) ^ Mul(0x03,a[8]) ^ a[9] ^ a[10];
	b[12] = Mul(0x02,a[12]) ^ Mul(0x03,a[13]) ^ a[14] ^ a[15];
	b[13] = Mul(0x02,a[13]) ^ Mul(0x03,a[14]) ^ a[15] ^ a[12];
	b[14] = Mul(0x02,a[14]) ^ Mul(0x03,a[15]) ^ a[12] ^ a[13];
	b[15] = Mul(0x02,a[15]) ^ Mul(0x03,a[12]) ^ a[13] ^ a[14];
	for( int i = 0 ; i < 16 ; i ++ )
		a[i] = b[i] ;
}
//列混合的逆
 void InvMixColumns(unsigned char *a)   
{
	unsigned char b[16];
	b[0] = Mul(0x0E, a[0]) ^ Mul(0x0B, a[1]) ^ Mul(0x0D, a[2]) ^ Mul(0x09, a[3]);
	b[1] = Mul(0x0E, a[1]) ^ Mul(0x0B, a[2]) ^ Mul(0x0D, a[3]) ^ Mul(0x09, a[0]);
	b[2] = Mul(0x0E, a[2]) ^ Mul(0x0B, a[3]) ^ Mul(0x0D, a[0]) ^ Mul(0x09, a[1]);
	b[3] = Mul(0x0E, a[3]) ^ Mul(0x0B, a[0]) ^ Mul(0x0D, a[1]) ^ Mul(0x09, a[2]);
	b[4] = Mul(0x0E, a[4]) ^ Mul(0x0B, a[5]) ^ Mul(0x0D, a[6]) ^ Mul(0x09, a[7]);
	b[5] = Mul(0x0E, a[5]) ^ Mul(0x0B, a[6]) ^ Mul(0x0D, a[7]) ^ Mul(0x09, a[4]);
	b[6] = Mul(0x0E, a[6]) ^ Mul(0x0B, a[7]) ^ Mul(0x0D, a[4]) ^ Mul(0x09, a[5]);
	b[7] = Mul(0x0E, a[7]) ^ Mul(0x0B, a[4]) ^ Mul(0x0D, a[5]) ^ Mul(0x09, a[6]);
	b[8] = Mul(0x0E, a[8]) ^ Mul(0x0B, a[9]) ^ Mul(0x0D, a[10]) ^ Mul(0x09, a[11]);
	b[9] = Mul(0x0E, a[9]) ^ Mul(0x0B, a[10]) ^ Mul(0x0D, a[11]) ^ Mul(0x09, a[8]);
	b[10] = Mul(0x0E, a[10]) ^ Mul(0x0B, a[11]) ^ Mul(0x0D, a[8]) ^ Mul(0x09, a[9]);
	b[11] = Mul(0x0E, a[11]) ^ Mul(0x0B, a[8]) ^ Mul(0x0D, a[9]) ^ Mul(0x09, a[10]);
	b[12] = Mul(0x0E, a[12]) ^ Mul(0x0B, a[13]) ^ Mul(0x0D, a[14]) ^ Mul(0x09, a[15]);
	b[13] = Mul(0x0E, a[13]) ^ Mul(0x0B, a[14]) ^ Mul(0x0D, a[15]) ^ Mul(0x09, a[12]);
	b[14] = Mul(0x0E, a[14]) ^ Mul(0x0B, a[15]) ^ Mul(0x0D, a[12]) ^ Mul(0x09, a[13]);
	b[15] = Mul(0x0E, a[15]) ^ Mul(0x0B, a[12]) ^ Mul(0x0D, a[13]) ^ Mul(0x09, a[14]);
	for (int i = 0; i < 16; i++)
		a[i] = b[i];
}

6. AES加密函数和解密函数

上文提到了AES加解密顺序,具体代码如下:

//AES算法加密函数  
void AES(unsigned char plaintext[16], unsigned char ciphertext[16], unsigned char k[11][16],int Round) 
{
	int i,round;
	for(i=0;i<16;i++){
		ciphertext[i]=plaintext[i];}      //此处注意代码思想 
	AddRoundKey(ciphertext,k[0]);
	for(round=1;round<Round;round++){
		SubBytes(ciphertext);
		ShiftRows(ciphertext);
		MixColumns(ciphertext);
		AddRoundKey(ciphertext,k[round]);
	}
	SubBytes(ciphertext);
	ShiftRows(ciphertext);
	AddRoundKey(ciphertext,k[Round]);
}
//AES解密常规模式 
void RAES(unsigned char plaintext[16], unsigned char ciphertext[16], unsigned char k[11][16], int Round)
{
	int i,round;
	for(i=0;i<16;i++){ 
		plaintext[i]=ciphertext[i];}    
	AddRoundKey(plaintext,k[Round]);       
	InvShiftRows(plaintext);               
	InvSubBytes(plaintext);
	for(round=9;round>0;round--){
		AddRoundKey(plaintext,k[round]);
		InvMixColumns(plaintext);
		InvShiftRows(plaintext);
		InvSubBytes(plaintext);
	}
	AddRoundKey(plaintext,k[0]); 
}

另外给出主函数:

int main()
{
	int i;
	unsigned char k[11][16];   //扩展秘钥 
	unsigned char ciphertext[16];  //密文
	unsigned char plaintext[16]={
		0x01,0x23,0x45,0x67,     // 明文
    	0x89,0xab,0xcd,0xef,
    	0x01,0x23,0x45,0x67,     
   	 	0x89,0xab,0xcd,0xef
	};
	unsigned char Key[16]={
		0x01,0x23,0x45,0x67,     // 初始密钥 
    	0x89,0xab,0xcd,0xef,
    	0x01,0x23,0x45,0x67,     
    	0x89,0xab,0xcd,0xef
	};
	printf("明文为:\n");
	for(i=0;i<16;i++){
	 	printf("%02x ",plaintext[i]);}  //明文 
	printf("\n");
	
	KeyExpansion(Key,k);  
	//加密 
	AES(plaintext,ciphertext,k,10);
	printf("加密后的密文为:\n");
	for(i=0;i<16;i++){
	 	printf("%02x ",ciphertext[i]);
	} 
	printf("\n");
	//解密 
	RAES(plaintext,ciphertext,k,10);
	printf("解密后的明文为:\n");
	for(i=0;i<16;i++){
	 	printf("%02x ",plaintext[i]);
	} 
	printf("\n");
	return 0;                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                        	
}

四、总结

纵观全文,本文主要还是介绍了AES的一些基础的东西,介绍了AES算法流程,详细介绍了主要函数的原理,并给出了代码参考。
完整代码参考请见:AES加解密(C语言优化版)
AES算法内容比较复杂,但是安全性较高,还有很多算法可以优化AES算法并提高AES的运行效率。如果还需要用到AES,我还会再进行下一步的学习。
感谢评论区伙伴们的反馈,部分错误已经修改~

参考资料:

https://www.sohu.com/a/201169795_466846
http://www.uooc.net.cn/course/2117859688?cycleid=1216670014
https://blog.csdn.net/gulang03/article/details/81175854

有关密码学:AES算法C语言完整、简单实现的更多相关文章

  1. ruby - 如何将脚本文件的末尾读取为数据文件(Perl 或任何其他语言) - 2

    我正在寻找执行以下操作的正确语法(在Perl、Shell或Ruby中):#variabletoaccessthedatalinesappendedasafileEND_OF_SCRIPT_MARKERrawdatastartshereanditcontinues. 最佳答案 Perl用__DATA__做这个:#!/usr/bin/perlusestrict;usewarnings;while(){print;}__DATA__Texttoprintgoeshere 关于ruby-如何将脚

  2. ruby - 如何根据特征实现 FactoryGirl 的条件行为 - 2

    我有一个用户工厂。我希望默认情况下确认用户。但是鉴于unconfirmed特征,我不希望它们被确认。虽然我有一个基于实现细节而不是抽象的工作实现,但我想知道如何正确地做到这一点。factory:userdoafter(:create)do|user,evaluator|#unwantedimplementationdetailshereunlessFactoryGirl.factories[:user].defined_traits.map(&:name).include?(:unconfirmed)user.confirm!endendtrait:unconfirmeddoenden

  3. ruby - 简单获取法拉第超时 - 2

    有没有办法在这个简单的get方法中添加超时选项?我正在使用法拉第3.3。Faraday.get(url)四处寻找,我只能先发起连接后应用超时选项,然后应用超时选项。或者有什么简单的方法?这就是我现在正在做的:conn=Faraday.newresponse=conn.getdo|req|req.urlurlreq.options.timeout=2#2secondsend 最佳答案 试试这个:conn=Faraday.newdo|conn|conn.options.timeout=20endresponse=conn.get(url

  4. ruby - 寻找通过阅读代码确定编程语言的ruby gem? - 2

    几个月前,我读了一篇关于ruby​​gem的博客文章,它可以通过阅读代码本身来确定编程语言。对于我的生活,我不记得博客或gem的名称。谷歌搜索“ruby编程语言猜测”及其变体也无济于事。有人碰巧知道相关gem的名称吗? 最佳答案 是这个吗:http://github.com/chrislo/sourceclassifier/tree/master 关于ruby-寻找通过阅读代码确定编程语言的rubygem?,我们在StackOverflow上找到一个类似的问题:

  5. ruby - 用 Ruby 编写一个简单的网络服务器 - 2

    我想在Ruby中创建一个用于开发目的的极其简单的Web服务器(不,不想使用现成的解决方案)。代码如下:#!/usr/bin/rubyrequire'socket'server=TCPServer.new('127.0.0.1',8080)whileconnection=server.acceptheaders=[]length=0whileline=connection.getsheaders想法是从命令行运行这个脚本,提供另一个脚本,它将在其标准输入上获取请求,并在其标准输出上返回完整的响应。到目前为止一切顺利,但事实证明这真的很脆弱,因为它在第二个请求上中断并出现错误:/usr/b

  6. ruby-on-rails - 简单的 Ruby on Rails 问题——如何将评论附加到用户和文章? - 2

    我意识到这可能是一个非常基本的问题,但我现在已经花了几天时间回过头来解决这个问题,但出于某种原因,Google就是没有帮助我。(我认为部分问题在于我是一个初学者,我不知道该问什么......)我也看过O'Reilly的RubyCookbook和RailsAPI,但我仍然停留在这个问题上.我找到了一些关于多态关系的信息,但它似乎不是我需要的(尽管如果我错了请告诉我)。我正在尝试调整MichaelHartl'stutorial创建一个包含用户、文章和评论的博客应用程序(不使用脚手架)。我希望评论既属于用户又属于文章。我的主要问题是:我不知道如何将当前文章的ID放入评论Controller。

  7. ruby - 使用 Ruby 通过 Outlook 发送消息的最简单方法是什么? - 2

    我的工作要求我为某些测试自动生成电子邮件。我一直在四处寻找,但未能找到可以快速实现的合理解决方案。它需要在outlook而不是其他邮件服务器中,因为我们有一些奇怪的身份验证规则,我们需要保存草稿而不是仅仅发送邮件的选项。显然win32ole可以做到这一点,但我找不到任何相当简单的例子。 最佳答案 假设存储了Outlook凭据并且您设置为自动登录到Outlook,WIN32OLE可以很好地完成此操作:require'win32ole'outlook=WIN32OLE.new('Outlook.Application')message=

  8. 区块链之加解密算法&数字证书 - 2

    目录一.加解密算法数字签名对称加密DES(DataEncryptionStandard)3DES(TripleDES)AES(AdvancedEncryptionStandard)RSA加密法DSA(DigitalSignatureAlgorithm)ECC(EllipticCurvesCryptography)非对称加密签名与加密过程非对称加密的应用对称加密与非对称加密的结合二.数字证书图解一.加解密算法加密简单而言就是通过一种算法将明文信息转换成密文信息,信息的的接收方能够通过密钥对密文信息进行解密获得明文信息的过程。根据加解密的密钥是否相同,算法可以分为对称加密、非对称加密、对称加密和非

  9. 华为OD机试用Python实现 -【明明的随机数】 2023Q1A - 2

    华为OD机试题本篇题目:明明的随机数题目输入描述输出描述:示例1输入输出说明代码编写思路最近更新的博客华为od2023|什么是华为od,od薪资待遇,od机试题清单华为OD机试真题大全,用Python解华为机试题|机试宝典【华为OD机试】全流程解析+经验分享,题型分享,防作弊指南华为o

  10. Unity 热更新技术 | (三) Lua语言基本介绍及下载安装 - 2

    ?博客主页:https://xiaoy.blog.csdn.net?本文由呆呆敲代码的小Y原创,首发于CSDN??学习专栏推荐:Unity系统学习专栏?游戏制作专栏推荐:游戏制作?Unity实战100例专栏推荐:Unity实战100例教程?欢迎点赞?收藏⭐留言?如有错误敬请指正!?未来很长,值得我们全力奔赴更美好的生活✨------------------❤️分割线❤️-------------------------

随机推荐