打开APP
userphoto
未登录

开通VIP,畅享免费电子书等14项超值服

开通VIP
[Delphi]一个功能完备的国密SM4类(TSM4)

 本软件使用Delphi 10.3.3编写和测试, 源码中用到了System.NetEncoding和Generics.Collections两个单元, 因此本程序仅支持Delphi XE及更新的版本.

支持6种加密模式: ECB, CBC, CFB, OFB, PCBC, CTR; 默认为ECB;

支持7种填充模式(ZERO, PKCS5, PKCS7, ISO10126, ANSIX923, OneAndZero); 默认为PKCS7;

SM4要求密码长度的长度为16个字节(128bit), 如果长度不足, 程序就填充0x00来补足

SM4要求初始向量的长度为16个字节(128bit), 如果长度不足, 程序就填充0x00来补足

SrcBuffer, KeyBuffer, IVBuffer, DestBuffer: TBuffer 说明:

SrcBuffer存放待加密/解密的数据,

KeyBuffer存放密码数据,

IVBuffer存放初始向量数据,

DestBuffer存放已加密/解密的数据

TBuffer本身带有各种数据转换函数, 数据转换非常方便,不需要另外再写代码,例如:

function ToString(Encoding: TEncoding): String;

function ToHexString: String;

function ToDelimitedHexString(Prefix: String; Delimitor: String): String;

function ToBase64String: String;

procedure ToBytes(var OutBytes: TBytes; ByteLen: Integer);

procedure FromString(const Str: String);

procedure FromString(const Str: String; Encoding: TEncoding);

procedure FromHexString(const Str: String);

procedure FromDelimitedHexString(HexStr: String; Prefix: String; Delimitor: String');

procedure FromBase64String(const Str: String);

procedure FromBytes(const InBytes: TBytes; ByteLen: Integer);

最简单的TSM4使用示范代码:

  1. //最简单的使用示范
  2. procedure TMainForm.Test;
  3. var
  4. S1, S2: String;
  5. begin
  6. with TSM4.Create(cmCBC, pmPKCS5) do
  7. begin
  8. SrcBuffer.FromString('先学着让自己值钱');
  9. KeyBuffer.FromBase64String('MTIzNDU2Nzg5MDEyMzQ1Ng=='); //1234567890123456
  10. //或KeyBuffer.FromString('1234567890123456');
  11. IVBuffer.FromHexString('6162636465666768696A6B6C6D6E6F70'); //abcdefghijklmnop
  12. Encrypt; //加密
  13. S1 := DestBuffer.ToHexString; //6C19FEC30147DBDE9539DC1DF0F3ACF09B6E6210F0F220D3D923F200DC5A44C4
  14. SrcBuffer.FromHexString(S1);
  15. Decrypt; //解密
  16. S2 := DestBuffer.ToString; //'先学着让自己值钱'
  17. Free;
  18. end;
  19. end;

完整的TSM4代码(包括测试代码)

  1. unit uMain;
  2. interface
  3. uses
  4. {$IF CompilerVersion <= 22}
  5. Windows, Messages, Generics.Collections, SysUtils,
  6. Variants, Classes, Graphics, Controls, Forms,
  7. Dialogs, NetEncoding, StdCtrls, uSM4;
  8. {$ELSE}
  9. Winapi.Windows, Winapi.Messages, Generics.Collections, System.SysUtils,
  10. System.Variants, System.Classes, Vcl.Graphics, Vcl.Controls, Vcl.Forms,
  11. Vcl.Dialogs, System.NetEncoding, Vcl.StdCtrls, uSM4;
  12. {$ENDIF}
  13. type
  14. TMainForm = class(TForm)
  15. LabelBlockMode: TLabel;
  16. LabelPaddingmode: TLabel;
  17. ComboBoxCipherMode: TComboBox;
  18. ComboBoxPaddingmode: TComboBox;
  19. LabelStr: TLabel;
  20. LabelIV: TLabel;
  21. LabelEnc: TLabel;
  22. LabelDec: TLabel;
  23. EditKey: TEdit;
  24. EditIV : TEdit;
  25. EditDec: TEdit;
  26. EditEnc: TEdit;
  27. EditStr: TEdit;
  28. LabelKey: TLabel;
  29. ButtonEncrypt: TButton;
  30. ButtonDecrypt: TButton;
  31. ComboBoxStr: TComboBox;
  32. ComboBoxKey: TComboBox;
  33. ComboBoxIV: TComboBox;
  34. ComboBoxEnc: TComboBox;
  35. ComboBoxDec: TComboBox;
  36. LabelStrCnt: TLabel;
  37. LabelKeyCnt: TLabel;
  38. LabelIVCnt : TLabel;
  39. LabelEncCnt: TLabel;
  40. LabelDecCnt: TLabel;
  41. EditSrc: TEdit;
  42. ButtonToHex: TButton;
  43. ButtonToBase64: TButton;
  44. EditDest: TEdit;
  45. RzEdit1: TEdit;
  46. Label1: TLabel;
  47. ButtonFromHex: TButton;
  48. ButtonFramBase64: TButton;
  49. Label2: TLabel;
  50. Label3: TLabel;
  51. procedure FormCreate(Sender: TObject);
  52. procedure EditChange(Sender: TObject);
  53. procedure ButtonClick(Sender: TObject);
  54. procedure FormDestroy(Sender: TObject);
  55. procedure ButtonConvertClick(Sender: TObject);
  56. procedure ButtonFromClick(Sender: TObject);
  57. private type
  58. TProc = procedure(const S: String) of object;
  59. Tfunc = function(): String of object;
  60. TFromDict = TDictionary<String, TProc>;
  61. TToDict = TDictionary<String, TFunc>;
  62. TCountDict = TDictionary<TEdit, TLabel>;
  63. private
  64. SM4: TSM4;
  65. DIctCm: TDictionary<String, TCipherMode>;
  66. DIctPm: TDictionary<String, TPaddingMode>;
  67. DictFromStr, DictFromKey, DictFromIV, DictFromEnc: TFromDict;
  68. DictToEnc, DictToDec: TToDict;
  69. DictCount: TCountDict;
  70. procedure FillDicts;
  71. public
  72. { Public declarations }
  73. end;
  74. var
  75. MainForm: TMainForm;
  76. implementation
  77. {$R *.dfm}
  78. uses uBuffer;
  79. procedure TMainForm.FormCreate(Sender: TObject);
  80. begin
  81. ReportMemoryLeaksOnShutDown := True;
  82. SM4 := TSM4.Create;
  83. DIctCm := TDictionary<String, TCipherMode>.Create;
  84. DIctPm := TDictionary<String, TPaddingMode>.Create;
  85. DictFromStr := TFromDict.Create;
  86. DictFromKey := TFromDict.Create;
  87. DictFromIV := TFromDict.Create;
  88. DictFromEnc := TFromDict.Create;
  89. DictToEnc := TToDict.Create;
  90. DictToDec := TToDict.Create;
  91. DictCount := TCountDict.Create;
  92. FillDicts;
  93. EditChange(nil);
  94. end;
  95. procedure TMainForm.FormDestroy(Sender: TObject);
  96. begin
  97. DIctCm.Free;
  98. DIctPm.Free;
  99. DictFromStr.Free;
  100. DictFromKey.Free;
  101. DictFromIV.Free;
  102. DictFromEnc.Free;
  103. DictToEnc.Free;
  104. DictToDec.Free;
  105. DictCount.Free;
  106. SM4.Free;
  107. end;
  108. procedure TMainForm.FillDicts;
  109. var
  110. SrcBuffer, KeyBuffer, IVBuffer, DestBuffer: TBuffer;
  111. begin
  112. SrcBuffer := SM4.SrcBuffer;
  113. KeyBuffer := SM4.KeyBuffer;
  114. IVBuffer := SM4.IVBuffer;
  115. DestBuffer:= SM4.DestBuffer;
  116. with DIctCm do
  117. begin
  118. Add('ECB' , cmECB);
  119. Add('CBC' , cmCBC);
  120. Add('CFB' , cmCFB);
  121. Add('OFB' , cmOFB);
  122. Add('PCBC', cmPCBC);
  123. Add('CTR' , cmCTR);
  124. end;
  125. with DIctPm do
  126. begin
  127. Add('ZERO' , pmZERO);
  128. Add('PKCS5' , pmPKCS5);
  129. Add('PKCS7' , pmPKCS7);
  130. Add('ISO10126' , pmISO10126);
  131. Add('ANSIX923' , pmANSIX923);
  132. Add('OneAndZero', pmOneAndZero);
  133. end;
  134. with DictFromStr do
  135. begin
  136. Add('String', SrcBuffer.FromString);
  137. Add('Hex' , SrcBuffer.FromHexString);
  138. Add('Base64', SrcBuffer.FromBase64String);
  139. end;
  140. with DictFromKey do
  141. begin
  142. Add('String', KeyBuffer.FromString);
  143. Add('Hex' , KeyBuffer.FromHexString);
  144. Add('Base64', KeyBuffer.FromBase64String);
  145. end;
  146. with DictFromIV do
  147. begin
  148. Add('String', IVBuffer.FromString);
  149. Add('Hex' , IVBuffer.FromHexString);
  150. Add('Base64', IVBuffer.FromBase64String);
  151. end;
  152. with DictFromEnc do
  153. begin
  154. Add('Hex' , SrcBuffer.FromHexString);
  155. Add('Base64', SrcBuffer.FromBase64String);
  156. end;
  157. with DictToEnc do
  158. begin
  159. Add('Hex' , DestBuffer.ToHexString);
  160. Add('Base64', DestBuffer.ToBase64String);
  161. end;
  162. with DictToDec do
  163. begin
  164. Add('String', DestBuffer.ToString);
  165. Add('Hex' , DestBuffer.ToHexString);
  166. Add('Base64', DestBuffer.ToBase64String);
  167. end;
  168. with DictCount do
  169. begin
  170. Add(EditStr, LabelStrCnt);
  171. Add(EditKey, LabelKeyCnt);
  172. Add(EditIV , LabelIVCnt );
  173. Add(EditEnc, LabelEncCnt);
  174. Add(EditDec, LabelDecCnt);
  175. end;
  176. end;
  177. procedure TMainForm.ButtonConvertClick(Sender: TObject);
  178. begin
  179. with TBuffer.Create do
  180. begin
  181. FromString(EditSrc.Text);
  182. if Sender = ButtonToHex then
  183. EditDest.Text := ToHexString
  184. else
  185. EditDest.Text := ToBase64String;
  186. Free;
  187. end;
  188. end;
  189. procedure TMainForm.ButtonFromClick(Sender: TObject);
  190. begin
  191. with TBuffer.Create do
  192. begin
  193. if Sender = ButtonFromHex then
  194. FromHexString(EditDest.Text)
  195. else
  196. FromBase64String(EditDest.Text);
  197. EditSrc.Text := ToString;
  198. Free;
  199. end;
  200. end;
  201. procedure TMainForm.ButtonClick(Sender: TObject);
  202. begin
  203. SM4.CipherMode := DIctCm.Items[ComboBoxCipherMode.Text];
  204. SM4.PaddingMode := DIctPm.Items[ComboBoxPaddingMode.Text];
  205. DictFromKey.Items[ComboBoxKey.Text](EditKey.Text);
  206. DictFromIV.Items[ComboBoxIV.Text](EditIV.Text);
  207. if Sender = ButtonEncrypt then
  208. begin
  209. DictFromStr.Items[ComboBoxStr.Text](EditStr.Text);
  210. SM4.Encrypt;
  211. EditEnc.Text := DictToEnc.Items[ComboBoxEnc.Text]();
  212. end
  213. else
  214. begin
  215. DictFromEnc.Items[ComboBoxEnc.Text](EditEnc.Text);
  216. SM4.Decrypt;
  217. EditDec.Text := DictToDec.Items[ComboBoxDec.Text]();
  218. end;
  219. end;
  220. procedure TMainForm.EditChange(Sender: TObject);
  221. var
  222. Edit: TEdit;
  223. begin
  224. if Sender <> nil then
  225. begin
  226. Edit := TEdit(Sender);
  227. DictCount.Items[Edit].Caption := Length(Edit.Text).ToString;
  228. end
  229. else
  230. begin
  231. for Edit in DictCount.Keys do
  232. begin
  233. DictCount.Items[Edit].Caption := Length(Edit.Text).ToString;
  234. end;
  235. end;
  236. end;
  237. end.
  1. object MainForm: TMainForm
  2. Left = 0
  3. Top = 0
  4. Caption = 'SM4'#21152#23494#35299#23494#27979#35797
  5. ClientHeight = 452
  6. ClientWidth = 788
  7. Color = clBtnFace
  8. Font.Charset = DEFAULT_CHARSET
  9. Font.Color = clWindowText
  10. Font.Height = -11
  11. Font.Name = 'Tahoma'
  12. Font.Style = []
  13. OldCreateOrder = False
  14. Position = poScreenCenter
  15. OnCreate = FormCreate
  16. OnDestroy = FormDestroy
  17. DesignSize = (
  18. 788
  19. 452)
  20. PixelsPerInch = 96
  21. TextHeight = 13
  22. object LabelBlockMode: TLabel
  23. Left = 5
  24. Top = 108
  25. Width = 48
  26. Height = 13
  27. Caption = #21152#23494#27169#24335
  28. end
  29. object LabelPaddingmode: TLabel
  30. Left = 132
  31. Top = 108
  32. Width = 48
  33. Height = 13
  34. Caption = #22635#20805#27169#24335
  35. end
  36. object LabelStr: TLabel
  37. Left = 28
  38. Top = 149
  39. Width = 24
  40. Height = 13
  41. Caption = #26126#25991
  42. end
  43. object LabelIV: TLabel
  44. Left = 4
  45. Top = 234
  46. Width = 48
  47. Height = 13
  48. Caption = #21021#22987#21521#37327
  49. end
  50. object LabelEnc: TLabel
  51. Left = 26
  52. Top = 314
  53. Width = 24
  54. Height = 13
  55. Caption = #23494#25991
  56. end
  57. object LabelDec: TLabel
  58. Left = 26
  59. Top = 402
  60. Width = 24
  61. Height = 13
  62. Caption = #26126#25991
  63. end
  64. object LabelKey: TLabel
  65. Left = 26
  66. Top = 193
  67. Width = 24
  68. Height = 13
  69. Caption = #23494#30721
  70. end
  71. object LabelStrCnt: TLabel
  72. Left = 758
  73. Top = 150
  74. Width = 24
  75. Height = 13
  76. Anchors = [akTop, akRight]
  77. Caption = #23383#25968
  78. end
  79. object LabelKeyCnt: TLabel
  80. Left = 758
  81. Top = 192
  82. Width = 24
  83. Height = 13
  84. Anchors = [akTop, akRight]
  85. Caption = #23383#25968
  86. end
  87. object LabelIVCnt: TLabel
  88. Left = 758
  89. Top = 234
  90. Width = 24
  91. Height = 13
  92. Anchors = [akTop, akRight]
  93. Caption = #23383#25968
  94. end
  95. object LabelEncCnt: TLabel
  96. Left = 758
  97. Top = 314
  98. Width = 24
  99. Height = 13
  100. Anchors = [akTop, akRight]
  101. Caption = #23383#25968
  102. end
  103. object LabelDecCnt: TLabel
  104. Left = 758
  105. Top = 400
  106. Width = 24
  107. Height = 13
  108. Anchors = [akTop, akRight]
  109. Caption = #23383#25968
  110. end
  111. object Label1: TLabel
  112. Left = 5
  113. Top = 35
  114. Width = 48
  115. Height = 13
  116. Caption = #23545#27604#32467#26524
  117. end
  118. object Label2: TLabel
  119. Left = 512
  120. Top = 1
  121. Width = 28
  122. Height = 13
  123. Caption = 'String'
  124. end
  125. object Label3: TLabel
  126. Left = 494
  127. Top = 64
  128. Width = 89
  129. Height = 13
  130. Caption = 'Hex/Base64 [utf8]'
  131. end
  132. object ComboBoxCipherMode: TComboBox
  133. Left = 59
  134. Top = 105
  135. Width = 54
  136. Height = 21
  137. TabOrder = 0
  138. Text = 'ECB'
  139. Items.Strings = (
  140. 'ECB'
  141. 'CBC'
  142. 'PCBC'
  143. 'CFB'
  144. 'OFB'
  145. 'CTR')
  146. end
  147. object ComboBoxPaddingmode: TComboBox
  148. Left = 185
  149. Top = 105
  150. Width = 68
  151. Height = 21
  152. TabOrder = 1
  153. Text = 'PKCS7'
  154. Items.Strings = (
  155. 'PKCS5'
  156. 'PKCS7'
  157. 'ANSIX923'
  158. 'OneAndZero'
  159. 'ISO10126'
  160. 'ZERO'
  161. '')
  162. end
  163. object EditKey: TEdit
  164. Left = 119
  165. Top = 188
  166. Width = 633
  167. Height = 21
  168. Anchors = [akLeft, akTop, akRight]
  169. TabOrder = 2
  170. Text = '1234567890123456'
  171. OnChange = EditChange
  172. end
  173. object EditIV: TEdit
  174. Left = 119
  175. Top = 230
  176. Width = 633
  177. Height = 21
  178. Anchors = [akLeft, akTop, akRight]
  179. TabOrder = 3
  180. Text = 'abcdefghijklmnop'
  181. OnChange = EditChange
  182. end
  183. object EditDec: TEdit
  184. Left = 119
  185. Top = 396
  186. Width = 633
  187. Height = 21
  188. Anchors = [akLeft, akTop, akRight]
  189. TabOrder = 4
  190. OnChange = EditChange
  191. end
  192. object EditEnc: TEdit
  193. Left = 119
  194. Top = 310
  195. Width = 633
  196. Height = 21
  197. Anchors = [akLeft, akTop, akRight]
  198. TabOrder = 5
  199. OnChange = EditChange
  200. end
  201. object EditStr: TEdit
  202. Left = 119
  203. Top = 146
  204. Width = 633
  205. Height = 21
  206. Anchors = [akLeft, akTop, akRight]
  207. TabOrder = 6
  208. Text = #20808#23398#30528#35753#33258#24049#20540#38065
  209. OnChange = EditChange
  210. end
  211. object ButtonEncrypt: TButton
  212. Left = 329
  213. Top = 268
  214. Width = 137
  215. Height = 24
  216. Caption = #21152#23494
  217. TabOrder = 7
  218. OnClick = ButtonClick
  219. end
  220. object ButtonDecrypt: TButton
  221. Left = 329
  222. Top = 351
  223. Width = 137
  224. Height = 24
  225. Caption = #35299#23494
  226. TabOrder = 8
  227. OnClick = ButtonClick
  228. end
  229. object ComboBoxStr: TComboBox
  230. Left = 57
  231. Top = 146
  232. Width = 58
  233. Height = 21
  234. TabOrder = 9
  235. Text = 'String'
  236. Items.Strings = (
  237. 'String'
  238. 'Hex'
  239. 'Base64')
  240. end
  241. object ComboBoxKey: TComboBox
  242. Left = 57
  243. Top = 188
  244. Width = 58
  245. Height = 21
  246. TabOrder = 10
  247. Text = 'String'
  248. Items.Strings = (
  249. 'String'
  250. 'Hex'
  251. 'Base64')
  252. end
  253. object ComboBoxIV: TComboBox
  254. Left = 57
  255. Top = 230
  256. Width = 58
  257. Height = 21
  258. TabOrder = 11
  259. Text = 'String'
  260. Items.Strings = (
  261. 'String'
  262. 'Hex'
  263. 'Base64')
  264. end
  265. object ComboBoxEnc: TComboBox
  266. Left = 57
  267. Top = 310
  268. Width = 58
  269. Height = 21
  270. TabOrder = 12
  271. Text = 'Hex'
  272. Items.Strings = (
  273. 'Hex'
  274. 'Base64')
  275. end
  276. object ComboBoxDec: TComboBox
  277. Left = 57
  278. Top = 396
  279. Width = 58
  280. Height = 21
  281. TabOrder = 13
  282. Text = 'String'
  283. Items.Strings = (
  284. 'String'
  285. 'Hex'
  286. 'Base64')
  287. end
  288. object EditSrc: TEdit
  289. Left = 361
  290. Top = 16
  291. Width = 345
  292. Height = 21
  293. Anchors = [akLeft, akTop, akRight]
  294. TabOrder = 14
  295. Text = #20808#23398#30528#35753#33258#24049#20540#38065
  296. end
  297. object ButtonToHex: TButton
  298. Left = 293
  299. Top = 41
  300. Width = 62
  301. Height = 22
  302. Caption = 'ToHex'
  303. TabOrder = 15
  304. OnClick = ButtonConvertClick
  305. end
  306. object ButtonToBase64: TButton
  307. Left = 713
  308. Top = 41
  309. Width = 65
  310. Height = 22
  311. Anchors = [akTop, akRight]
  312. Caption = 'ToBase64'
  313. TabOrder = 16
  314. OnClick = ButtonConvertClick
  315. end
  316. object EditDest: TEdit
  317. Left = 361
  318. Top = 41
  319. Width = 345
  320. Height = 21
  321. Anchors = [akLeft, akTop, akRight]
  322. TabOrder = 17
  323. end
  324. object RzEdit1: TEdit
  325. Left = 57
  326. Top = 32
  327. Width = 196
  328. Height = 21
  329. TabOrder = 18
  330. Text = 'https://javalang.cn/crypto/sm4.html'
  331. end
  332. object ButtonFromHex: TButton
  333. Left = 294
  334. Top = 16
  335. Width = 61
  336. Height = 22
  337. Caption = 'FromHex'
  338. TabOrder = 19
  339. OnClick = ButtonFromClick
  340. end
  341. object ButtonFramBase64: TButton
  342. Left = 713
  343. Top = 15
  344. Width = 65
  345. Height = 22
  346. Anchors = [akTop, akRight]
  347. Caption = 'FromBase64'
  348. TabOrder = 20
  349. OnClick = ButtonFromClick
  350. end
  351. end
  1. unit uSM4;
  2. interface
  3. uses
  4. {$IF CompilerVersion <= 22}
  5. Forms, Classes, Windows, SysUtils, NetEncoding,
  6. {$ELSE}
  7. Vcl.Forms, System.Classes, Winapi.Windows, System.SysUtils, System.NetEncoding,
  8. {$ENDIF}
  9. uConst, uBuffer;
  10. type
  11. TCipherMode = (cmECB, cmCBC, cmPCBC, cmCFB, cmOFB, cmCTR);
  12. TPaddingMode = (pmZERO, pmPKCS5, pmPKCS7, pmISO10126, pmANSIX923, pmOneAndZero);
  13. TSM4 = class(TObject)
  14. private type
  15. TWord = UInt32;
  16. TBlock = array[0..4 -1] of TWord;
  17. TRoundKey = array[0..32-1] of TWord;
  18. TCryptType = (ctEncrypt, ctDecrypt);
  19. TProc = procedure (var A: TBlock) of object;
  20. private
  21. RK: TRoundKey;
  22. MK, DataBlock, IVBlock: TBlock;
  23. BlockSize, BlockLen, RKLen: Integer;
  24. function SBoxMap(A: TWord): TWord;
  25. function ROTL(A: TWord; N: Byte): TWord;
  26. procedure CryptBlock(const CryptType: TCryptType; var A: TBlock);
  27. procedure ExpandKey;
  28. procedure ReverseEndian(var A: TBlock);
  29. procedure AppendPadding;
  30. procedure RemovePadding;
  31. procedure CheckKeyBufferAndIVBuffer;
  32. procedure XorBlock(var A: TBlock; const B: TBlock); overload;
  33. procedure XorBlock(var A: TBlock; const B, C: TBlock); overload;
  34. procedure IncBlock(Var A: TBlock);
  35. procedure Crypt(CryptType: TCryptType);
  36. public
  37. CipherMode : TCipherMode;
  38. PaddingMode: TPaddingMode;
  39. SrcBuffer, KeyBuffer, IVBuffer, DestBuffer: TBuffer;
  40. procedure Encrypt;
  41. procedure Decrypt;
  42. constructor Create(aCipherMode: TCipherMode = cmECB; aPaddingMode: TPaddingMode = pmPKCS7);
  43. destructor Destroy; override;
  44. end;
  45. implementation
  46. constructor TSM4.Create(aCipherMode: TCipherMode; aPaddingMode: TPaddingMode);
  47. begin
  48. inherited Create;
  49. CipherMode := aCipherMode;
  50. PaddingMode := aPaddingMode;
  51. SrcBuffer := TBuffer.Create;
  52. KeyBuffer := TBuffer.Create;
  53. IVBuffer := TBuffer.Create;
  54. DestBuffer := TBuffer.Create;
  55. BlockSize := SizeOf(TBlock); //16
  56. BlockLen := SizeOf(TBlock) div Sizeof(UInt32); //4
  57. RKLen := SizeOf(TRoundKey) div Sizeof(UInt32); //32
  58. end;
  59. destructor TSM4.Destroy;
  60. begin
  61. SrcBuffer.Free;
  62. KeyBuffer.Free;
  63. IVBuffer.Free;
  64. DestBuffer.Free;
  65. inherited;
  66. end;
  67. procedure TSM4.Encrypt;
  68. begin
  69. AppendPadding;
  70. Crypt(ctEncrypt);
  71. end;
  72. procedure TSM4.Decrypt;
  73. begin
  74. Crypt(ctDecrypt);
  75. RemovePadding;
  76. end;
  77. procedure TSM4.Crypt(CryptType: TCryptType);
  78. var
  79. InBlock: TBlock;
  80. DataLen, OffSet: Integer;
  81. begin
  82. CheckKeyBufferAndIVBuffer;
  83. DataLen := SrcBuffer.Length;
  84. DestBuffer.Length := DataLen;
  85. KeyBuffer.ToBytes(MK, BlockSize);
  86. if CipherMode <> cmECB then
  87. begin
  88. IVBuffer.ToBytes(IVBlock, BlockSize);
  89. end;
  90. ExpandKey;
  91. OffSet := 0;
  92. while OffSet < DataLen do
  93. begin
  94. SrcBuffer.OffsetToBytes(OffSet, DataBlock, BlockSize);
  95. case CipherMode of
  96. cmECB: //Electronic Codebook (ECB)
  97. begin
  98. CryptBlock(CryptType, DataBlock);
  99. end;
  100. cmCBC: //Cipher Block Chaining (CBC)
  101. begin
  102. if CryptType = ctEncrypt then
  103. begin
  104. XorBlock(DataBlock, IVBlock);
  105. CryptBlock(CryptType, DataBlock);
  106. IVBlock := DataBlock;
  107. end
  108. else
  109. begin
  110. InBlock := DataBlock;
  111. CryptBlock(CryptType, DataBlock);
  112. XorBlock(DataBlock, IVBlock);
  113. IVBlock := InBlock;
  114. end;
  115. end;
  116. cmPCBC: //Propagating Cipher Block Chaining (PCBC)
  117. begin
  118. InBlock := DataBlock;
  119. if CryptType = ctEncrypt then
  120. begin
  121. XorBlock(DataBlock, IVBlock);
  122. CryptBlock(CryptType, DataBlock);
  123. end
  124. else
  125. begin
  126. CryptBlock(CryptType, DataBlock);
  127. XorBlock(DataBlock, IVBlock);
  128. end;
  129. XorBlock(IVBlock, DataBlock, InBlock);
  130. end;
  131. cmCFB: //Cipher Feedback (CFB)
  132. begin
  133. InBlock := DataBlock;
  134. CryptBlock(ctEncrypt, IVBlock); //不管是加密还是解密,这里都是用Encrypt对IV进行操作
  135. XorBlock(DataBlock, IVBlock);
  136. if CryptType = ctEncrypt then
  137. IVBlock := DataBlock
  138. else
  139. IVBlock := InBlock;
  140. end;
  141. cmOFB: //Output Feedback (OFB)
  142. begin
  143. CryptBlock(ctEncrypt, IVBlock); //不管是加密还是解密,这里都是用Encrypt对IV进行操作
  144. XorBlock(DataBlock, IVBlock);
  145. end;
  146. cmCTR: //Counter (CTR)
  147. begin
  148. InBlock := IVBlock;
  149. CryptBlock(ctEncrypt, InBlock); //不管是加密还是解密,这里都是用Encrypt对IV进行操作
  150. XorBlock(DataBlock, InBlock);
  151. IncBlock(IVBlock);
  152. end;
  153. end;
  154. DestBuffer.OffsetFromBytes(OffSet, DataBlock, BlockSize);
  155. Inc(OffSet, BlockSize);
  156. end;
  157. end;
  158. procedure TSM4.AppendPadding; //添加填充
  159. var
  160. I, M, N, Len, NewLen: Integer;
  161. begin
  162. Len := Length(SrcBuffer.Data);
  163. M := (Len mod BlockSize);
  164. if (M = 0) and (PaddingMode = pmZero) then
  165. N := 0
  166. else
  167. N := BlockSize - M;
  168. NewLen := Len + N;
  169. SetLength(SrcBuffer.Data, NewLen);
  170. case PaddingMode of
  171. pmPKCS5, pmPKCS7:
  172. begin
  173. for I := Len to NewLen-1 do SrcBuffer.Data[I] := N;
  174. end;
  175. pmANSIX923:
  176. begin
  177. for I := Len to NewLen-1 do
  178. begin
  179. if I < NewLen-1 then
  180. SrcBuffer.Data[I] := 0
  181. else
  182. SrcBuffer.Data[I] := N;
  183. end;
  184. end;
  185. pmISO10126:
  186. begin
  187. Randomize;
  188. for I := Len to NewLen-1 do
  189. begin
  190. if I < NewLen-1 then
  191. SrcBuffer.Data[I] := Random(255)
  192. else
  193. SrcBuffer.Data[I] := N;
  194. end;
  195. end;
  196. pmZERO:
  197. begin
  198. for I := Len to NewLen-1 do SrcBuffer.Data[I] := 0;
  199. end;
  200. pmOneAndZero:
  201. begin
  202. for I := Len to NewLen-1 do
  203. begin
  204. if I = Len then
  205. SrcBuffer.Data[I] := $80
  206. else
  207. SrcBuffer.Data[I] := 0;
  208. end;
  209. end;
  210. end;
  211. end;
  212. procedure TSM4.RemovePadding; //移除填充
  213. var
  214. I, M, Len: Integer;
  215. begin
  216. Len := Length(DestBuffer.Data);
  217. case PaddingMode of
  218. pmPKCS5, pmPKCS7, pmANSIX923, pmISO10126:
  219. begin
  220. M := DestBuffer.Data[Len-1];
  221. SetLength(DestBuffer.Data, Len-M);
  222. end;
  223. pmZERO:
  224. begin
  225. for I := Len-1 downto 0 do
  226. begin
  227. if DestBuffer.Data[I] = 0 then
  228. Dec(Len)
  229. else
  230. Break;
  231. end;
  232. SetLength(DestBuffer.Data, Len);
  233. end;
  234. pmOneAndZero:
  235. begin
  236. for I := Len-1 downto 0 do
  237. begin
  238. if DestBuffer.Data[I] <> $80 then
  239. Dec(Len)
  240. else
  241. Break;
  242. end;
  243. SetLength(DestBuffer.Data, Len-1);
  244. end;
  245. end;
  246. end;
  247. procedure TSM4.CheckKeyBufferAndIVBuffer; //对Key和IV不足16Bytes的情况以0填足
  248. var
  249. I, Len: Integer;
  250. begin
  251. Len := Length(KeyBuffer.Data);
  252. SetLength(KeyBuffer.Data, BlockSize);
  253. for I := Len to BlockSize - 1 do
  254. begin
  255. KeyBuffer.Data[I] := 0;
  256. end;
  257. if CipherMode <> cmECB then //ECB模式不需要初始矢量IV
  258. begin
  259. Len := Length(IVBuffer.Data);
  260. SetLength(IVBuffer.Data, BlockSize);
  261. for I := Len to BlockSize - 1 do
  262. begin
  263. IVBuffer.Data[I] := 0;
  264. end;
  265. end;
  266. end;
  267. procedure TSM4.ExpandKey;
  268. function Transform(A: TWord): TWord; //合成置换
  269. begin
  270. A := SBoxMap(A); //非线性变换
  271. Result := A xor ROTL(A, 13) xor ROTL(A, 23); //线性变换
  272. end;
  273. var
  274. K: TBlock;
  275. I: Integer;
  276. M0, M1, M2, M3: TWord;
  277. begin
  278. ReverseEndian(MK); //32整数位大小端转换
  279. for I := 0 to BlockLen-1 do
  280. begin
  281. K[I] := MK[I] xor FK[I];
  282. end;
  283. for I := 0 to RKLen-1 do //32次迭代运算
  284. begin
  285. M0 := (I ) mod 4;
  286. M1 := (I+1) mod 4;
  287. M2 := (I+2) mod 4;
  288. M3 := (I+3) mod 4;
  289. K[M0] := K[M0] xor Transform(K[M1] xor K[M2] xor K[M3] xor CK[I]); //迭代运算
  290. RK[I] := K[M0];
  291. end;
  292. end;
  293. procedure TSM4.CryptBlock(const CryptType: TCryptType; var A: TBlock);
  294. function TransForm(A: TWord): TWord; //合成置换
  295. begin
  296. A := SBoxMap(A); //非线性变换
  297. Result := A xor ROTL(A, 2) xor ROTL(A, 10) xor ROTL(A, 18) xor ROTL(A, 24); //线性变换
  298. end;
  299. var
  300. T: TBlock;
  301. I, R: Integer;
  302. M0, M1, M2, M3: TWord;
  303. begin
  304. ReverseEndian(A); //32位整数大小端(Endian)转换
  305. for I := 0 to RKLen-1 do //32次迭代运算
  306. begin
  307. if CryptType = ctEncrypt then
  308. R := I
  309. else
  310. R := 31 - I; //解密时使用反序的RK
  311. M0 := (I ) mod 4;
  312. M1 := (I+1) mod 4;
  313. M2 := (I+2) mod 4;
  314. M3 := (I+3) mod 4;
  315. A[M0] := A[M0] xor TransForm(A[M1] xor A[M2] xor A[M3] xor RK[R]); //迭代运算
  316. end;
  317. ReverseEndian(A); //32整数位大小端(Endian)转换
  318. T := A;
  319. for I := 0 to 3 do //Block内部的4个32位整数的位置做反序变换
  320. begin
  321. A[I] := T[3 - I];
  322. end;
  323. end;
  324. function TSM4.SBoxMap(A: TWord): TWord; //S盒非线性变换(Byte to Byte的固定映射}
  325. var
  326. I: Integer;
  327. B: array[0..3] of Byte absolute A;
  328. R: array[0..3] of Byte absolute Result;
  329. begin
  330. for I := 0 to 3 do
  331. begin
  332. R[I] := SBox[B[I]];
  333. end;
  334. end;
  335. procedure TSM4.ReverseEndian(var A: TBlock); //32位大小端(Endian)变换
  336. var
  337. I: Integer;
  338. begin
  339. for I := 0 to BlockLen-1 do
  340. begin
  341. A[I] := ((A[I] and $FF000000) shr 24) or
  342. ((A[I] and $00FF0000) shr 8 ) or
  343. ((A[I] and $0000FF00) shl 8 ) or
  344. ((A[I] and $000000FF) shl 24);
  345. end;
  346. end;
  347. function TSM4.ROTL(A: TWord; N: Byte): TWord; //32位循环左移N位
  348. begin
  349. Result := (A shl N) or (A shr (32-N));
  350. end;
  351. procedure TSM4.XorBlock(var A: TBlock; const B: TBlock); //两个Block异或
  352. var
  353. I: Integer;
  354. begin
  355. for I := 0 to BlockLen-1 do
  356. begin
  357. A[I] := A[I] xor B[I];
  358. end;
  359. end;
  360. procedure TSM4.XorBlock(var A: TBlock; const B, C: TBlock); //两个Block异或
  361. var
  362. I: Integer;
  363. begin
  364. for I := 0 to BlockLen-1 do
  365. begin
  366. A[I] := B[I] xor C[I];
  367. end;
  368. end;
  369. procedure TSM4.IncBlock(var A: TBlock); //Block值加1
  370. var
  371. I: Integer;
  372. begin
  373. for I := 0 to BlockLen-1 do
  374. begin
  375. if I > 0 then
  376. begin
  377. A[I-1] := 0;
  378. end;
  379. if (I = BlockLen-1) or (A[I] < $FFFFFFFF) then
  380. begin
  381. Inc(A[0]);
  382. Exit;
  383. end;
  384. end;
  385. end;
  386. end.
  1. unit uConst;
  2. interface
  3. const
  4. // S盒
  5. Sbox: array[0..255] of Byte = (
  6. $d6,$90,$e9,$fe,$cc,$e1,$3d,$b7,$16,$b6,$14,$c2,$28,$fb,$2c,$05,
  7. $2b,$67,$9a,$76,$2a,$be,$04,$c3,$aa,$44,$13,$26,$49,$86,$06,$99,
  8. $9c,$42,$50,$f4,$91,$ef,$98,$7a,$33,$54,$0b,$43,$ed,$cf,$ac,$62,
  9. $e4,$b3,$1c,$a9,$c9,$08,$e8,$95,$80,$df,$94,$fa,$75,$8f,$3f,$a6,
  10. $47,$07,$a7,$fc,$f3,$73,$17,$ba,$83,$59,$3c,$19,$e6,$85,$4f,$a8,
  11. $68,$6b,$81,$b2,$71,$64,$da,$8b,$f8,$eb,$0f,$4b,$70,$56,$9d,$35,
  12. $1e,$24,$0e,$5e,$63,$58,$d1,$a2,$25,$22,$7c,$3b,$01,$21,$78,$87,
  13. $d4,$00,$46,$57,$9f,$d3,$27,$52,$4c,$36,$02,$e7,$a0,$c4,$c8,$9e,
  14. $ea,$bf,$8a,$d2,$40,$c7,$38,$b5,$a3,$f7,$f2,$ce,$f9,$61,$15,$a1,
  15. $e0,$ae,$5d,$a4,$9b,$34,$1a,$55,$ad,$93,$32,$30,$f5,$8c,$b1,$e3,
  16. $1d,$f6,$e2,$2e,$82,$66,$ca,$60,$c0,$29,$23,$ab,$0d,$53,$4e,$6f,
  17. $d5,$db,$37,$45,$de,$fd,$8e,$2f,$03,$ff,$6a,$72,$6d,$6c,$5b,$51,
  18. $8d,$1b,$af,$92,$bb,$dd,$bc,$7f,$11,$d9,$5c,$41,$1f,$10,$5a,$d8,
  19. $0a,$c1,$31,$88,$a5,$cd,$7b,$bd,$2d,$74,$d0,$12,$b8,$e5,$b4,$b0,
  20. $89,$69,$97,$4a,$0c,$96,$77,$7e,$65,$b9,$f1,$09,$c5,$6e,$c6,$84,
  21. $18,$f0,$7d,$ec,$3a,$dc,$4d,$20,$79,$ee,$5f,$3e,$d7,$cb,$39,$48
  22. );
  23. // 密钥扩展算法的常数FK
  24. FK: array[0..3] of UInt32 = ($a3b1bac6, $56aa3350, $677d9197, $b27022dc);
  25. // 密钥扩展算法的固定参数CK
  26. CK: array[0..31] of UInt32 = (
  27. $00070e15, $1c232a31, $383f464d, $545b6269,
  28. $70777e85, $8c939aa1, $a8afb6bd, $c4cbd2d9,
  29. $e0e7eef5, $fc030a11, $181f262d, $343b4249,
  30. $50575e65, $6c737a81, $888f969d, $a4abb2b9,
  31. $c0c7ced5, $dce3eaf1, $f8ff060d, $141b2229,
  32. $30373e45, $4c535a61, $686f767d, $848b9299,
  33. $a0a7aeb5, $bcc3cad1, $d8dfe6ed, $f4fb0209,
  34. $10171e25, $2c333a41, $484f565d, $646b7279
  35. );
  36. implementation
  37. end.
  1. unit uBuffer;
  2. interface
  3. uses
  4. {$IF CompilerVersion <= 22}
  5. Forms, Classes, Windows, SysUtils, NetEncoding;
  6. {$ELSE}
  7. Vcl.Forms, System.Classes, Winapi.Windows, System.SysUtils, System.NetEncoding;
  8. {$ENDIF}
  9. type
  10. TBuffer = class
  11. private
  12. function GetDataLength: Integer; inline;
  13. procedure SetDataLength(Len: Integer); inline;
  14. function GetItem(Index: Integer): Byte; inline;
  15. procedure SetItem(Index: Integer; Value: Byte); inline;
  16. public
  17. Data: TBytes; //TBytes = array of Byte;
  18. procedure FromString(const Str: String); overload; //默认为utf8
  19. procedure FromString(const Str: String; Encoding: TEncoding); overload;
  20. procedure FromHexString(const Str: String);
  21. procedure FromDelimitedHexString(HexStr: String; Prefix: String = '$'; Delimitor: String = ',');
  22. procedure FromBase64String(const Str: String);
  23. procedure FromBytes(const InBytes: TBytes; ByteLen: Integer = -1); overload;
  24. procedure FromBytes(const InBytes: array of Byte; ByteLen: Integer = -1); overload;
  25. procedure FromBytes(const Ints: array of UInt32; ByteLen: Integer); overload;
  26. procedure OffsetFromBytes(Offset: Integer; const Ints: array of UInt32; ByteLen: Integer);
  27. procedure FromStream(const Stream: TStream; ByteLen: Integer = -1);
  28. procedure FromFile(const FileName: String);
  29. function ToString: String; reintroduce; overload; //默认为utf8
  30. function ToString(Encoding: TEncoding): String; reintroduce; overload;
  31. function ToHexString: String;
  32. function ToDelimitedHexString(Prefix: String = '$'; Delimitor: String = ', '): String;
  33. function ToBase64String: String;
  34. procedure ToBytes(var OutBytes: TBytes; ByteLen: Integer = -1); overload;
  35. procedure ToBytes(var OutBytes: array of Byte; ByteLen: Integer = -1); overload;
  36. procedure ToBytes(var Ints: array of UInt32; ByteLen: Integer = 1); overload;
  37. procedure OffsetToBytes(Offset: Integer; var Ints: array of UInt32; ByteLen: Integer);
  38. procedure ToStream(const Stream: TStream);
  39. procedure ToFile(const FileName: String; Warning: Boolean = True);
  40. property Length: Integer read GetDataLength write SetDataLength;
  41. property Bytes[Index: Integer]: Byte read GetItem write SetItem; default;
  42. end;
  43. resourcestring
  44. SInvalidBufSize = 'Invalid buffer size for ouput';
  45. implementation
  46. function TBuffer.GetDataLength: Integer;
  47. begin
  48. Result := System.Length(Data);
  49. end;
  50. procedure TBuffer.SetDataLength(Len: Integer);
  51. begin
  52. System.SetLength(Data, Len);
  53. end;
  54. function TBuffer.GetItem(Index: Integer): Byte;
  55. begin
  56. Result := Data[Index];
  57. end;
  58. procedure TBuffer.SetItem(Index: Integer; Value: Byte);
  59. begin
  60. Data[Index] := Value;
  61. end;
  62. procedure TBuffer.FromString(const Str: String);
  63. begin
  64. Data := TEncoding.UTF8.GetBytes(Str);
  65. end;
  66. procedure TBuffer.FromString(const Str: String; Encoding: TEncoding);
  67. begin
  68. Data := Encoding.GetBytes(Str);
  69. end;
  70. procedure TBuffer.FromHexString(const Str: String);
  71. var
  72. Len: Integer;
  73. begin
  74. Len := System.Length(Str) div 2;
  75. SetLength(Data, Len);
  76. HexToBin(PChar(Str), @Data[0], Len)
  77. end;
  78. procedure TBuffer.FromDelimitedHexString(HexStr: String; Prefix: String; Delimitor: String);
  79. var
  80. Len: Integer;
  81. begin
  82. HexStr := HexStr.Replace(Prefix , '');
  83. HexStr := HexStr.Replace(Delimitor , '');
  84. HexStr := HexStr.Replace(' ' , '');
  85. Len := System.Length(HexStr) div 2;
  86. SetLength(Data, Len);
  87. HexToBin(PChar(HexStr), @Data[0], Len)
  88. end;
  89. procedure TBuffer.FromBase64String(const Str: String);
  90. var
  91. Base64Encoding: TBase64Encoding;
  92. begin
  93. //Base64Encoding := TBase64Encoding.Create; //含换行符
  94. Base64Encoding := TBase64Encoding.Create(0); //不含换行符
  95. Data := Base64Encoding.DecodeStringToBytes(Str);
  96. Base64Encoding.Free;
  97. end;
  98. procedure TBuffer.FromBytes(const InBytes: array of Byte; ByteLen: Integer);
  99. begin
  100. if (ByteLen = -1) then ByteLen := System.Length(InBytes);
  101. SetLength(Data, ByteLen);
  102. Move(InBytes[0], Data[0], ByteLen);
  103. end;
  104. procedure TBuffer.FromBytes(const InBytes: TBytes; ByteLen: Integer);
  105. begin
  106. if (ByteLen = -1) then ByteLen := System.Length(InBytes);
  107. SetLength(Data, ByteLen);
  108. Move(InBytes[0], Data[0], ByteLen);
  109. end;
  110. procedure TBuffer.FromBytes(const Ints: array of UInt32; ByteLen: Integer);
  111. begin
  112. SetLength(Data, ByteLen);
  113. Move(Ints[0], Data[0], ByteLen);
  114. end;
  115. procedure TBuffer.OffsetFromBytes(Offset: Integer; const Ints: array of UInt32; ByteLen: Integer);
  116. begin
  117. Move(Ints[0], Data[Offset], ByteLen);
  118. end;
  119. procedure TBuffer.FromStream(const Stream: TStream; ByteLen: Integer);
  120. begin
  121. if (ByteLen = -1) then ByteLen := Stream.Size;
  122. SetLength(Data, ByteLen);
  123. Stream.Read(Data, ByteLen);
  124. end;
  125. procedure TBuffer.FromFile(const FileName: String);
  126. var
  127. Stream: TFileStream;
  128. begin
  129. Stream := TFileStream.Create(FileName, fmOpenRead);
  130. SetLength(Data, Stream.Size);
  131. Stream.Read(Data, Stream.Size);
  132. Stream.Free;
  133. end;
  134. function TBuffer.ToString: String;
  135. begin
  136. Result := TEncoding.UTF8.GetString(Data);
  137. end;
  138. function TBuffer.ToString(Encoding: TEncoding): String;
  139. begin
  140. Result := Encoding.GetString(Data);
  141. end;
  142. function TBuffer.ToHexString: String;
  143. var
  144. Len: Integer;
  145. begin
  146. Len := System.Length(Data);
  147. SetLength(Result, 2*Len);
  148. BinToHex(@Data[0], PChar(Result), Len);
  149. end;
  150. function TBuffer.ToDelimitedHexString(Prefix: String; Delimitor: String): String;
  151. var
  152. I, Len: Integer;
  153. begin
  154. Result := '';
  155. Len := System.Length(Data);
  156. for I := 0 to Len-1 do
  157. begin
  158. Result := Result + Prefix + IntToHex(Data[I], 2);
  159. if I < Len-1 then
  160. Result := Result + Delimitor;
  161. end;
  162. end;
  163. function TBuffer.ToBase64String: String;
  164. var
  165. Base64Encoding: TBase64Encoding;
  166. begin
  167. //Base64Encoding := TBase64Encoding.Create; //含换行符
  168. Base64Encoding := TBase64Encoding.Create(0); //不含换行符
  169. Result := Base64Encoding.EncodeBytesToString(Data);
  170. Base64Encoding.Free;
  171. end;
  172. procedure TBuffer.ToBytes(var OutBytes: array of Byte; ByteLen: Integer);
  173. begin
  174. if (ByteLen = -1) then ByteLen := System.Length(Data);
  175. if (ByteLen > System.Length(OutBytes)) then
  176. raise Exception.Create(SInvalidBufSize);
  177. Move(Data[0], OutBytes[0], ByteLen);
  178. end;
  179. procedure TBuffer.ToBytes(var OutBytes: TBytes; ByteLen: Integer);
  180. begin
  181. if ByteLen = -1 then ByteLen := System.Length(Data);
  182. SetLength(OutBytes, ByteLen);
  183. Move(Data[0], OutBytes[0], ByteLen);
  184. end;
  185. procedure TBuffer.ToBytes(var Ints: array of UInt32; ByteLen: Integer);
  186. begin
  187. if ByteLen = -1 then ByteLen := System.Length(Data);
  188. Move(Data[0], Ints[0], ByteLen);
  189. end;
  190. procedure TBuffer.OffsetToBytes(Offset: Integer; var Ints: array of UInt32; ByteLen: Integer);
  191. begin
  192. Move(Data[Offset], Ints[0], ByteLen);
  193. end;
  194. procedure TBuffer.ToStream(const Stream: TStream);
  195. begin
  196. Stream.Write(Data, System.Length(Data));
  197. end;
  198. procedure TBuffer.ToFile(const FileName: String; Warning: Boolean);
  199. var
  200. Stream: TFileStream;
  201. begin
  202. if Warning and FileExists(FileName) and
  203. (Application.MessageBox(PChar('File ' + FileName + ' Exists, Overwrite It?'),
  204. 'Warning: File Exists', MB_YESNO) = IDNO) then Exit;
  205. Stream := TFileStream.Create(FileName, fmCreate);
  206. Stream.Write(Data, System.Length(Data));
  207. Stream.Free;
  208. end;
  209. end.
本站仅提供存储服务,所有内容均由用户发布,如发现有害或侵权内容,请点击举报
打开APP,阅读全文并永久保存 查看更多类似文章
猜你喜欢
类似文章
【热】打开小程序,算一算2024你的财运
Delphi常用的函数库
Delphi通过 RTTI信息重构类方法的原始声明
《网络吸管》开发手记
字符串操作中较常用的函数
使用拼音首字母序列实现检索功能
bpl插件系统开发
更多类似文章 >>
生活服务
热点新闻
分享 收藏 导长图 关注 下载文章
绑定账号成功
后续可登录账号畅享VIP特权!
如果VIP功能使用有故障,
可点击这里联系客服!

联系客服