[Delphi]一个功能完备的国密SM4类(TSM4)
Posted BlueStorm
tags:
篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了[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使用示范代码:
//最简单的使用示范
procedure TMainForm.Test;
var
S1, S2: String;
begin
with TSM4.Create(cmCBC, pmPKCS5) do
begin
SrcBuffer.FromString('先学着让自己值钱');
KeyBuffer.FromBase64String('MTIzNDU2Nzg5MDEyMzQ1Ng=='); //1234567890123456
//或KeyBuffer.FromString('1234567890123456');
IVBuffer.FromHexString('6162636465666768696A6B6C6D6E6F70'); //abcdefghijklmnop
Encrypt; //加密
S1 := DestBuffer.ToHexString; //6C19FEC30147DBDE9539DC1DF0F3ACF09B6E6210F0F220D3D923F200DC5A44C4
SrcBuffer.FromHexString(S1);
Decrypt; //解密
S2 := DestBuffer.ToString; //'先学着让自己值钱'
Free;
end;
end;
完整的TSM4代码(包括测试代码)
unit uMain;
interface
uses
$IF CompilerVersion <= 22
Windows, Messages, Generics.Collections, SysUtils,
Variants, Classes, Graphics, Controls, Forms,
Dialogs, NetEncoding, StdCtrls, uSM4;
$ELSE
Winapi.Windows, Winapi.Messages, Generics.Collections, System.SysUtils,
System.Variants, System.Classes, Vcl.Graphics, Vcl.Controls, Vcl.Forms,
Vcl.Dialogs, System.NetEncoding, Vcl.StdCtrls, uSM4;
$ENDIF
type
TMainForm = class(TForm)
LabelBlockMode: TLabel;
LabelPaddingmode: TLabel;
ComboBoxCipherMode: TComboBox;
ComboBoxPaddingmode: TComboBox;
LabelStr: TLabel;
LabelIV: TLabel;
LabelEnc: TLabel;
LabelDec: TLabel;
EditKey: TEdit;
EditIV : TEdit;
EditDec: TEdit;
EditEnc: TEdit;
EditStr: TEdit;
LabelKey: TLabel;
ButtonEncrypt: TButton;
ButtonDecrypt: TButton;
ComboBoxStr: TComboBox;
ComboBoxKey: TComboBox;
ComboBoxIV: TComboBox;
ComboBoxEnc: TComboBox;
ComboBoxDec: TComboBox;
LabelStrCnt: TLabel;
LabelKeyCnt: TLabel;
LabelIVCnt : TLabel;
LabelEncCnt: TLabel;
LabelDecCnt: TLabel;
EditSrc: TEdit;
ButtonToHex: TButton;
ButtonToBase64: TButton;
EditDest: TEdit;
RzEdit1: TEdit;
Label1: TLabel;
ButtonFromHex: TButton;
ButtonFramBase64: TButton;
Label2: TLabel;
Label3: TLabel;
procedure FormCreate(Sender: TObject);
procedure EditChange(Sender: TObject);
procedure ButtonClick(Sender: TObject);
procedure FormDestroy(Sender: TObject);
procedure ButtonConvertClick(Sender: TObject);
procedure ButtonFromClick(Sender: TObject);
private type
TProc = procedure(const S: String) of object;
Tfunc = function(): String of object;
TFromDict = TDictionary<String, TProc>;
TToDict = TDictionary<String, TFunc>;
TCountDict = TDictionary<TEdit, TLabel>;
private
SM4: TSM4;
DIctCm: TDictionary<String, TCipherMode>;
DIctPm: TDictionary<String, TPaddingMode>;
DictFromStr, DictFromKey, DictFromIV, DictFromEnc: TFromDict;
DictToEnc, DictToDec: TToDict;
DictCount: TCountDict;
procedure FillDicts;
public
Public declarations
end;
var
MainForm: TMainForm;
implementation
$R *.dfm
uses uBuffer;
procedure TMainForm.FormCreate(Sender: TObject);
begin
ReportMemoryLeaksOnShutDown := True;
SM4 := TSM4.Create;
DIctCm := TDictionary<String, TCipherMode>.Create;
DIctPm := TDictionary<String, TPaddingMode>.Create;
DictFromStr := TFromDict.Create;
DictFromKey := TFromDict.Create;
DictFromIV := TFromDict.Create;
DictFromEnc := TFromDict.Create;
DictToEnc := TToDict.Create;
DictToDec := TToDict.Create;
DictCount := TCountDict.Create;
FillDicts;
EditChange(nil);
end;
procedure TMainForm.FormDestroy(Sender: TObject);
begin
DIctCm.Free;
DIctPm.Free;
DictFromStr.Free;
DictFromKey.Free;
DictFromIV.Free;
DictFromEnc.Free;
DictToEnc.Free;
DictToDec.Free;
DictCount.Free;
SM4.Free;
end;
procedure TMainForm.FillDicts;
var
SrcBuffer, KeyBuffer, IVBuffer, DestBuffer: TBuffer;
begin
SrcBuffer := SM4.SrcBuffer;
KeyBuffer := SM4.KeyBuffer;
IVBuffer := SM4.IVBuffer;
DestBuffer:= SM4.DestBuffer;
with DIctCm do
begin
Add('ECB' , cmECB);
Add('CBC' , cmCBC);
Add('CFB' , cmCFB);
Add('OFB' , cmOFB);
Add('PCBC', cmPCBC);
Add('CTR' , cmCTR);
end;
with DIctPm do
begin
Add('ZERO' , pmZERO);
Add('PKCS5' , pmPKCS5);
Add('PKCS7' , pmPKCS7);
Add('ISO10126' , pmISO10126);
Add('ANSIX923' , pmANSIX923);
Add('OneAndZero', pmOneAndZero);
end;
with DictFromStr do
begin
Add('String', SrcBuffer.FromString);
Add('Hex' , SrcBuffer.FromHexString);
Add('Base64', SrcBuffer.FromBase64String);
end;
with DictFromKey do
begin
Add('String', KeyBuffer.FromString);
Add('Hex' , KeyBuffer.FromHexString);
Add('Base64', KeyBuffer.FromBase64String);
end;
with DictFromIV do
begin
Add('String', IVBuffer.FromString);
Add('Hex' , IVBuffer.FromHexString);
Add('Base64', IVBuffer.FromBase64String);
end;
with DictFromEnc do
begin
Add('Hex' , SrcBuffer.FromHexString);
Add('Base64', SrcBuffer.FromBase64String);
end;
with DictToEnc do
begin
Add('Hex' , DestBuffer.ToHexString);
Add('Base64', DestBuffer.ToBase64String);
end;
with DictToDec do
begin
Add('String', DestBuffer.ToString);
Add('Hex' , DestBuffer.ToHexString);
Add('Base64', DestBuffer.ToBase64String);
end;
with DictCount do
begin
Add(EditStr, LabelStrCnt);
Add(EditKey, LabelKeyCnt);
Add(EditIV , LabelIVCnt );
Add(EditEnc, LabelEncCnt);
Add(EditDec, LabelDecCnt);
end;
end;
procedure TMainForm.ButtonConvertClick(Sender: TObject);
begin
with TBuffer.Create do
begin
FromString(EditSrc.Text);
if Sender = ButtonToHex then
EditDest.Text := ToHexString
else
EditDest.Text := ToBase64String;
Free;
end;
end;
procedure TMainForm.ButtonFromClick(Sender: TObject);
begin
with TBuffer.Create do
begin
if Sender = ButtonFromHex then
FromHexString(EditDest.Text)
else
FromBase64String(EditDest.Text);
EditSrc.Text := ToString;
Free;
end;
end;
procedure TMainForm.ButtonClick(Sender: TObject);
begin
SM4.CipherMode := DIctCm.Items[ComboBoxCipherMode.Text];
SM4.PaddingMode := DIctPm.Items[ComboBoxPaddingMode.Text];
DictFromKey.Items[ComboBoxKey.Text](EditKey.Text);
DictFromIV.Items[ComboBoxIV.Text](EditIV.Text);
if Sender = ButtonEncrypt then
begin
DictFromStr.Items[ComboBoxStr.Text](EditStr.Text);
SM4.Encrypt;
EditEnc.Text := DictToEnc.Items[ComboBoxEnc.Text]();
end
else
begin
DictFromEnc.Items[ComboBoxEnc.Text](EditEnc.Text);
SM4.Decrypt;
EditDec.Text := DictToDec.Items[ComboBoxDec.Text]();
end;
end;
procedure TMainForm.EditChange(Sender: TObject);
var
Edit: TEdit;
begin
if Sender <> nil then
begin
Edit := TEdit(Sender);
DictCount.Items[Edit].Caption := Length(Edit.Text).ToString;
end
else
begin
for Edit in DictCount.Keys do
begin
DictCount.Items[Edit].Caption := Length(Edit.Text).ToString;
end;
end;
end;
end.
object MainForm: TMainForm
Left = 0
Top = 0
Caption = 'SM4'#21152#23494#35299#23494#27979#35797
ClientHeight = 452
ClientWidth = 788
Color = clBtnFace
Font.Charset = DEFAULT_CHARSET
Font.Color = clWindowText
Font.Height = -11
Font.Name = 'Tahoma'
Font.Style = []
OldCreateOrder = False
Position = poScreenCenter
OnCreate = FormCreate
OnDestroy = FormDestroy
DesignSize = (
788
452)
PixelsPerInch = 96
TextHeight = 13
object LabelBlockMode: TLabel
Left = 5
Top = 108
Width = 48
Height = 13
Caption = #21152#23494#27169#24335
end
object LabelPaddingmode: TLabel
Left = 132
Top = 108
Width = 48
Height = 13
Caption = #22635#20805#27169#24335
end
object LabelStr: TLabel
Left = 28
Top = 149
Width = 24
Height = 13
Caption = #26126#25991
end
object LabelIV: TLabel
Left = 4
Top = 234
Width = 48
Height = 13
Caption = #21021#22987#21521#37327
end
object LabelEnc: TLabel
Left = 26
Top = 314
Width = 24
Height = 13
Caption = #23494#25991
end
object LabelDec: TLabel
Left = 26
Top = 402
Width = 24
Height = 13
Caption = #26126#25991
end
object LabelKey: TLabel
Left = 26
Top = 193
Width = 24
Height = 13
Caption = #23494#30721
end
object LabelStrCnt: TLabel
Left = 758
Top = 150
Width = 24
Height = 13
Anchors = [akTop, akRight]
Caption = #23383#25968
end
object LabelKeyCnt: TLabel
Left = 758
Top = 192
Width = 24
Height = 13
Anchors = [akTop, akRight]
Caption = #23383#25968
end
object LabelIVCnt: TLabel
Left = 758
Top = 234
Width = 24
Height = 13
Anchors = [akTop, akRight]
Caption = #23383#25968
end
object LabelEncCnt: TLabel
Left = 758
Top = 314
Width = 24
Height = 13
Anchors = [akTop, akRight]
Caption = #23383#25968
end
object LabelDecCnt: TLabel
Left = 758
Top = 400
Width = 24
Height = 13
Anchors = [akTop, akRight]
Caption = #23383#25968
end
object Label1: TLabel
Left = 5
Top = 35
Width = 48
Height = 13
Caption = #23545#27604#32467#26524
end
object Label2: TLabel
Left = 512
Top = 1
Width = 28
Height = 13
Caption = 'String'
end
object Label3: TLabel
Left = 494
Top = 64
Width = 89
Height = 13
Caption = 'Hex/Base64 [utf8]'
end
object ComboBoxCipherMode: TComboBox
Left = 59
Top = 105
Width = 54
Height = 21
TabOrder = 0
Text = 'ECB'
Items.Strings = (
'ECB'
'CBC'
'PCBC'
'CFB'
'OFB'
'CTR')
end
object ComboBoxPaddingmode: TComboBox
Left = 185
Top = 105
Width = 68
Height = 21
TabOrder = 1
Text = 'PKCS7'
Items.Strings = (
'PKCS5'
'PKCS7'
'ANSIX923'
'OneAndZero'
'ISO10126'
'ZERO'
'')
end
object EditKey: TEdit
Left = 119
Top = 188
Width = 633
Height = 21
Anchors = [akLeft, akTop, akRight]
TabOrder = 2
Text = '1234567890123456'
OnChange = EditChange
end
object EditIV: TEdit
Left = 119
Top = 230
Width = 633
Height = 21
Anchors = [akLeft, akTop, akRight]
TabOrder = 3
Text = 'abcdefghijklmnop'
OnChange = EditChange
end
object EditDec: TEdit
Left = 119
Top = 396
Width = 633
Height = 21
Anchors = [akLeft, akTop, akRight]
TabOrder = 4
OnChange = EditChange
end
object EditEnc: TEdit
Left = 119
Top = 310
Width = 633
Height = 21
Anchors = [akLeft, akTop, akRight]
TabOrder = 5
OnChange = EditChange
end
object EditStr: TEdit
Left = 119
Top = 146
Width = 633
Height = 21
Anchors = [akLeft, akTop, akRight]
TabOrder = 6
Text = #20808#23398#30528#35753#33258#24049#20540#38065
OnChange = EditChange
end
object ButtonEncrypt: TButton
Left = 329
Top = 268
Width = 137
Height = 24
Caption = #21152#23494
TabOrder = 7
OnClick = ButtonClick
end
object ButtonDecrypt: TButton
Left = 329
Top = 351
Width = 137
Height = 24
Caption = #35299#23494
TabOrder = 8
OnClick = ButtonClick
end
object ComboBoxStr: TComboBox
Left = 57
Top = 146
Width = 58
Height = 21
TabOrder = 9
Text = 'String'
Items.Strings = (
'String'
'Hex'
'Base64')
end
object ComboBoxKey: TComboBox
Left = 57
Top = 188
Width = 58
Height = 21
TabOrder = 10
Text = 'String'
Items.Strings = (
'String'
'Hex'
'Base64')
end
object ComboBoxIV: TComboBox
Left = 57
Top = 230
Width = 58
Height = 21
TabOrder = 11
Text = 'String'
Items.Strings = (
'String'
'Hex'
'Base64')
end
object ComboBoxEnc: TComboBox
Left = 57
Top = 310
Width = 58
Height = 21
TabOrder = 12
Text = 'Hex'
Items.Strings = (
'Hex'
'Base64')
end
object ComboBoxDec: TComboBox
Left = 57
Top = 396
Width = 58
Height = 21
TabOrder = 13
Text = 'String'
Items.Strings = (
'String'
'Hex'
'Base64')
end
object EditSrc: TEdit
Left = 361
Top = 16
Width = 345
Height = 21
Anchors = [akLeft, akTop, akRight]
TabOrder = 14
Text = #20808#23398#30528#35753#33258#24049#20540#38065
end
object ButtonToHex: TButton
Left = 293
Top = 41
Width = 62
Height = 22
Caption = 'ToHex'
TabOrder = 15
OnClick = ButtonConvertClick
end
object ButtonToBase64: TButton
Left = 713
Top = 41
Width = 65
Height = 22
Anchors = [akTop, akRight]
Caption = 'ToBase64'
TabOrder = 16
OnClick = ButtonConvertClick
end
object EditDest: TEdit
Left = 361
Top = 41
Width = 345
Height = 21
Anchors = [akLeft, akTop, akRight]
TabOrder = 17
end
object RzEdit1: TEdit
Left = 57
Top = 32
Width = 196
Height = 21
TabOrder = 18
Text = 'https://javalang.cn/crypto/sm4.html'
end
object ButtonFromHex: TButton
Left = 294
Top = 16
Width = 61
Height = 22
Caption = 'FromHex'
TabOrder = 19
OnClick = ButtonFromClick
end
object ButtonFramBase64: TButton
Left = 713
Top = 15
Width = 65
Height = 22
Anchors = [akTop, akRight]
Caption = 'FromBase64'
TabOrder = 20
OnClick = ButtonFromClick
end
end
unit uSM4;
interface
uses
$IF CompilerVersion <= 22
Forms, Classes, Windows, SysUtils, NetEncoding,
$ELSE
Vcl.Forms, System.Classes, Winapi.Windows, System.SysUtils, System.NetEncoding,
$ENDIF
uConst, uBuffer;
type
TCipherMode = (cmECB, cmCBC, cmPCBC, cmCFB, cmOFB, cmCTR);
TPaddingMode = (pmZERO, pmPKCS5, pmPKCS7, pmISO10126, pmANSIX923, pmOneAndZero);
TSM4 = class(TObject)
private type
TWord = UInt32;
TBlock = array[0..4 -1] of TWord;
TRoundKey = array[0..32-1] of TWord;
TCryptType = (ctEncrypt, ctDecrypt);
TProc = procedure (var A: TBlock) of object;
private
RK: TRoundKey;
MK, DataBlock, IVBlock: TBlock;
BlockSize, BlockLen, RKLen: Integer;
function SBoxMap(A: TWord): TWord;
function ROTL(A: TWord; N: Byte): TWord;
procedure CryptBlock(const CryptType: TCryptType; var A: TBlock);
procedure ExpandKey;
procedure ReverseEndian(var A: TBlock);
procedure AppendPadding;
procedure RemovePadding;
procedure CheckKeyBufferAndIVBuffer;
procedure XorBlock(var A: TBlock; const B: TBlock); overload;
procedure XorBlock(var A: TBlock; const B, C: TBlock); overload;
procedure IncBlock(Var A: TBlock);
procedure Crypt(CryptType: TCryptType);
public
CipherMode : TCipherMode;
PaddingMode: TPaddingMode;
SrcBuffer, KeyBuffer, IVBuffer, DestBuffer: TBuffer;
procedure Encrypt;
procedure Decrypt;
constructor Create(aCipherMode: TCipherMode = cmECB; aPaddingMode: TPaddingMode = pmPKCS7);
destructor Destroy; override;
end;
implementation
constructor TSM4.Create(aCipherMode: TCipherMode; aPaddingMode: TPaddingMode);
begin
inherited Create;
CipherMode := aCipherMode;
PaddingMode := aPaddingMode;
SrcBuffer := TBuffer.Create;
KeyBuffer := TBuffer.Create;
IVBuffer := TBuffer.Create;
DestBuffer := TBuffer.Create;
BlockSize := SizeOf(TBlock); //16
BlockLen := SizeOf(TBlock) div Sizeof(UInt32); //4
RKLen := SizeOf(TRoundKey) div Sizeof(UInt32); //32
end;
destructor TSM4.Destroy;
begin
SrcBuffer.Free;
KeyBuffer.Free;
IVBuffer.Free;
DestBuffer.Free;
inherited;
end;
procedure TSM4.Encrypt;
begin
AppendPadding;
Crypt(ctEncrypt);
end;
procedure TSM4.Decrypt;
begin
Crypt(ctDecrypt);
RemovePadding;
end;
procedure TSM4.Crypt(CryptType: TCryptType);
var
InBlock: TBlock;
DataLen, OffSet: Integer;
begin
CheckKeyBufferAndIVBuffer;
DataLen := SrcBuffer.Length;
DestBuffer.Length := DataLen;
KeyBuffer.ToBytes(MK, BlockSize);
if CipherMode <> cmECB then
begin
IVBuffer.ToBytes(IVBlock, BlockSize);
end;
ExpandKey;
OffSet := 0;
while OffSet < DataLen do
begin
SrcBuffer.OffsetToBytes(OffSet, DataBlock, BlockSize);
case CipherMode of
cmECB: //Electronic Codebook (ECB)
begin
CryptBlock(CryptType, DataBlock);
end;
cmCBC: //Cipher Block Chaining (CBC)
begin
if CryptType = ctEncrypt then
begin
XorBlock(DataBlock, IVBlock);
CryptBlock(CryptType, DataBlock);
IVBlock := DataBlock;
end
else
begin
InBlock := DataBlock;
CryptBlock(CryptType, DataBlock);
XorBlock(DataBlock, IVBlock);
IVBlock := InBlock;
end;
end;
cmPCBC: //Propagating Cipher Block Chaining (PCBC)
begin
InBlock := DataBlock;
if CryptType = ctEncrypt then
begin
XorBlock(DataBlock, IVBlock);
CryptBlock(CryptType, DataBlock);
end
else
begin
CryptBlock(CryptType, DataBlock);
XorBlock(DataBlock, IVBlock);
end;
XorBlock(IVBlock, DataBlock, InBlock);
end;
cmCFB: //Cipher Feedback (CFB)
begin
InBlock := DataBlock;
CryptBlock(ctEncrypt, IVBlock); //不管是加密还是解密,这里都是用Encrypt对IV进行操作
XorBlock(DataBlock, IVBlock);
if CryptType = ctEncrypt then
IVBlock := DataBlock
else
IVBlock := InBlock;
end;
cmOFB: //Output Feedback (OFB)
begin
CryptBlock(ctEncrypt, IVBlock); //不管是加密还是解密,这里都是用Encrypt对IV进行操作
XorBlock(DataBlock, IVBlock);
end;
cmCTR: //Counter (CTR)
begin
InBlock := IVBlock;
CryptBlock(ctEncrypt, InBlock); //不管是加密还是解密,这里都是用Encrypt对IV进行操作
XorBlock(DataBlock, InBlock);
IncBlock(IVBlock);
end;
end;
DestBuffer.OffsetFromBytes(OffSet, DataBlock, BlockSize);
Inc(OffSet, BlockSize);
end;
end;
procedure TSM4.AppendPadding; //添加填充
var
I, M, N, Len, NewLen: Integer;
begin
Len := Length(SrcBuffer.Data);
M := (Len mod BlockSize);
if (M = 0) and (PaddingMode = pmZero) then
N := 0
else
N := BlockSize - M;
NewLen := Len + N;
SetLength(SrcBuffer.Data, NewLen);
case PaddingMode of
pmPKCS5, pmPKCS7:
begin
for I := Len to NewLen-1 do SrcBuffer.Data[I] := N;
end;
pmANSIX923:
begin
for I := Len to NewLen-1 do
begin
if I < NewLen-1 then
SrcBuffer.Data[I] := 0
else
SrcBuffer.Data[I] := N;
end;
end;
pmISO10126:
begin
Randomize;
for I := Len to NewLen-1 do
begin
if I < NewLen-1 then
SrcBuffer.Data[I] := Random(255)
else
SrcBuffer.Data[I] := N;
end;
end;
pmZERO:
begin
for I := Len to NewLen-1 do SrcBuffer.Data[I] := 0;
end;
pmOneAndZero:
begin
for I := Len to NewLen-1 do
begin
if I = Len then
SrcBuffer.Data[I] := $80
else
SrcBuffer.Data[I] := 0;
end;
end;
end;
end;
procedure TSM4.RemovePadding; //移除填充
var
I, M, Len: Integer;
begin
Len := Length(DestBuffer.Data);
case PaddingMode of
pmPKCS5, pmPKCS7, pmANSIX923, pmISO10126:
begin
M := DestBuffer.Data[Len-1];
SetLength(DestBuffer.Data, Len-M);
end;
pmZERO:
begin
for I := Len-1 downto 0 do
begin
if DestBuffer.Data[I] = 0 then
Dec(Len)
else
Break;
end;
SetLength(DestBuffer.Data, Len);
end;
pmOneAndZero:
begin
for I := Len-1 downto 0 do
begin
if DestBuffer.Data[I] <> $80 then
Dec(Len)
else
Break;
end;
SetLength(DestBuffer.Data, Len-1);
end;
end;
end;
procedure TSM4.CheckKeyBufferAndIVBuffer; //对Key和IV不足16Bytes的情况以0填足
var
I, Len: Integer;
begin
Len := Length(KeyBuffer.Data);
SetLength(KeyBuffer.Data, BlockSize);
for I := Len to BlockSize - 1 do
begin
KeyBuffer.Data[I] := 0;
end;
if CipherMode <> cmECB then //ECB模式不需要初始矢量IV
begin
Len := Length(IVBuffer.Data);
SetLength(IVBuffer.Data, BlockSize);
for I := Len to BlockSize - 1 do
begin
IVBuffer.Data[I] := 0;
end;
end;
end;
procedure TSM4.ExpandKey;
function Transform(A: TWord): TWord; //合成置换
begin
A := SBoxMap(A); //非线性变换
Result := A xor ROTL(A, 13) xor ROTL(A, 23); //线性变换
end;
var
K: TBlock;
I: Integer;
M0, M1, M2, M3: TWord;
begin
ReverseEndian(MK); //32整数位大小端转换
for I := 0 to BlockLen-1 do
begin
K[I] := MK[I] xor FK[I];
end;
for I := 0 to RKLen-1 do //32次迭代运算
begin
M0 := (I ) mod 4;
M1 := (I+1) mod 4;
M2 := (I+2) mod 4;
M3 := (I+3) mod 4;
K[M0] := K[M0] xor Transform(K[M1] xor K[M2] xor K[M3] xor CK[I]); //迭代运算
RK[I] := K[M0];
end;
end;
procedure TSM4.CryptBlock(const CryptType: TCryptType; var A: TBlock);
function TransForm(A: TWord): TWord; //合成置换
begin
A := SBoxMap(A); //非线性变换
Result := A xor ROTL(A, 2) xor ROTL(A, 10) xor ROTL(A, 18) xor ROTL(A, 24); //线性变换
end;
var
T: TBlock;
I, R: Integer;
M0, M1, M2, M3: TWord;
begin
ReverseEndian(A); //32位整数大小端(Endian)转换
for I := 0 to RKLen-1 do //32次迭代运算
begin
if CryptType = ctEncrypt then
R := I
else
R := 31 - I; //解密时使用反序的RK
M0 := (I ) mod 4;
M1 := (I+1) mod 4;
M2 := (I+2) mod 4;
M3 := (I+3) mod 4;
A[M0] := A[M0] xor TransForm(A[M1] xor A[M2] xor A[M3] xor RK[R]); //迭代运算
end;
ReverseEndian(A); //32整数位大小端(Endian)转换
T := A;
for I := 0 to 3 do //Block内部的4个32位整数的位置做反序变换
begin
A[I] := T[3 - I];
end;
end;
function TSM4.SBoxMap(A: TWord): TWord; //S盒非线性变换(Byte to Byte的固定映射
var
I: Integer;
B: array[0..3] of Byte absolute A;
R: array[0..3] of Byte absolute Result;
begin
for I := 0 to 3 do
begin
R[I] := SBox[B[I]];
end;
end;
procedure TSM4.ReverseEndian(var A: TBlock); //32位大小端(Endian)变换
var
I: Integer;
begin
for I := 0 to BlockLen-1 do
begin
A[I] := ((A[I] and $FF000000) shr 24) or
((A[I] and $00FF0000) shr 8 ) or
((A[I] and $0000FF00) shl 8 ) or
((A[I] and $000000FF) shl 24);
end;
end;
function TSM4.ROTL(A: TWord; N: Byte): TWord; //32位循环左移N位
begin
Result := (A shl N) or (A shr (32-N));
end;
procedure TSM4.XorBlock(var A: TBlock; const B: TBlock); //两个Block异或
var
I: Integer;
begin
for I := 0 to BlockLen-1 do
begin
A[I] := A[I] xor B[I];
end;
end;
procedure TSM4.XorBlock(var A: TBlock; const B, C: TBlock); //两个Block异或
var
I: Integer;
begin
for I := 0 to BlockLen-1 do
begin
A[I] := B[I] xor C[I];
end;
end;
procedure TSM4.IncBlock(var A: TBlock); //Block值加1
var
I: Integer;
begin
for I := 0 to BlockLen-1 do
begin
if I > 0 then
begin
A[I-1] := 0;
end;
if (I = BlockLen-1) or (A[I] < $FFFFFFFF) then
begin
Inc(A[0]);
Exit;
end;
end;
end;
end.
unit uConst;
interface
const
// S盒
Sbox: array[0..255] of Byte = (
$d6,$90,$e9,$fe,$cc,$e1,$3d,$b7,$16,$b6,$14,$c2,$28,$fb,$2c,$05,
$2b,$67,$9a,$76,$2a,$be,$04,$c3,$aa,$44,$13,$26,$49,$86,$06,$99,
$9c,$42,$50,$f4,$91,$ef,$98,$7a,$33,$54,$0b,$43,$ed,$cf,$ac,$62,
$e4,$b3,$1c,$a9,$c9,$08,$e8,$95,$80,$df,$94,$fa,$75,$8f,$3f,$a6,
$47,$07,$a7,$fc,$f3,$73,$17,$ba,$83,$59,$3c,$19,$e6,$85,$4f,$a8,
$68,$6b,$81,$b2,$71,$64,$da,$8b,$f8,$eb,$0f,$4b,$70,$56,$9d,$35,
$1e,$24,$0e,$5e,$63,$58,$d1,$a2,$25,$22,$7c,$3b,$01,$21,$78,$87,
$d4,$00,$46,$57,$9f,$d3,$27,$52,$4c,$36,$02,$e7,$a0,$c4,$c8,$9e,
$ea,$bf,$8a,$d2,$40,$c7,$38,$b5,$a3,$f7,$f2,$ce,$f9,$61,$15,$a1,
$e0,$ae,$5d,$a4,$9b,$34,$1a,$55,$ad,$93,$32,$30,$f5,$8c,$b1,$e3,
$1d,$f6,$e2,$2e,$82,$66,$ca,$60,$c0,$29,$23,$ab,$0d,$53,$4e,$6f,
$d5,$db,$37,$45,$de,$fd,$8e,$2f,$03,$ff,$6a,$72,$6d,$6c,$5b,$51,
$8d,$1b,$af,$92,$bb,$dd,$bc,$7f,$11,$d9,$5c,$41,$1f,$10,$5a,$d8,
$0a,$c1,$31,$88,$a5,$cd,$7b,$bd,$2d,$74,$d0,$12,$b8,$e5,$b4,$b0,
$89,$69,$97,$4a,$0c,$96,$77,$7e,$65,$b9,$f1,$09,$c5,$6e,$c6,$84,
$18,$f0,$7d,$ec,$3a,$dc,$4d,$20,$79,$ee,$5f,$3e,$d7,$cb,$39,$48
);
// 密钥扩展算法的常数FK
FK: array[0..3] of UInt32 = ($a3b1bac6, $56aa3350, $677d9197, $b27022dc);
// 密钥扩展算法的固定参数CK
CK: array[0..31] of UInt32 = (
$00070e15, $1c232a31, $383f464d, $545b6269,
$70777e85, $8c939aa1, $a8afb6bd, $c4cbd2d9,
$e0e7eef5, $fc030a11, $181f262d, $343b4249,
$50575e65, $6c737a81, $888f969d, $a4abb2b9,
$c0c7ced5, $dce3eaf1, $f8ff060d, $141b2229,
$30373e45, $4c535a61, $686f767d, $848b9299,
$a0a7aeb5, $bcc3cad1, $d8dfe6ed, $f4fb0209,
$10171e25, $2c333a41, $484f565d, $646b7279
);
implementation
end.
unit uBuffer;
interface
uses
$IF CompilerVersion <= 22
Forms, Classes, Windows, SysUtils, NetEncoding;
$ELSE
Vcl.Forms, System.Classes, Winapi.Windows, System.SysUtils, System.NetEncoding;
$ENDIF
type
TBuffer = class
private
function GetDataLength: Integer; inline;
procedure SetDataLength(Len: Integer); inline;
function GetItem(Index: Integer): Byte; inline;
procedure SetItem(Index: Integer; Value: Byte); inline;
public
Data: TBytes; //TBytes = array of Byte;
procedure FromString(const Str: String); overload; //默认为utf8
procedure FromString(const Str: String; Encoding: TEncoding); overload;
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 = -1); overload;
procedure FromBytes(const InBytes: array of Byte; ByteLen: Integer = -1); overload;
procedure FromBytes(const Ints: array of UInt32; ByteLen: Integer); overload;
procedure OffsetFromBytes(Offset: Integer; const Ints: array of UInt32; ByteLen: Integer);
procedure FromStream(const Stream: TStream; ByteLen: Integer = -1);
procedure FromFile(const FileName: String);
function ToString: String; reintroduce; overload; //默认为utf8
function ToString(Encoding: TEncoding): String; reintroduce; overload;
function ToHexString: String;
function ToDelimitedHexString(Prefix: String = '$'; Delimitor: String = ', '): String;
function ToBase64String: String;
procedure ToBytes(var OutBytes: TBytes; ByteLen: Integer = -1); overload;
procedure ToBytes(var OutBytes: array of Byte; ByteLen: Integer = -1); overload;
procedure ToBytes(var Ints: array of UInt32; ByteLen: Integer = 1); overload;
procedure OffsetToBytes(Offset: Integer; var Ints: array of UInt32; ByteLen: Integer);
procedure ToStream(const Stream: TStream);
procedure ToFile(const FileName: String; Warning: Boolean = True);
property Length: Integer read GetDataLength write SetDataLength;
property Bytes[Index: Integer]: Byte read GetItem write SetItem; default;
end;
resourcestring
SInvalidBufSize = 'Invalid buffer size for ouput';
implementation
function TBuffer.GetDataLength: Integer;
begin
Result := System.Length(Data);
end;
procedure TBuffer.SetDataLength(Len: Integer);
begin
System.SetLength(Data, Len);
end;
function TBuffer.GetItem(Index: Integer): Byte;
begin
Result := Data[Index];
end;
procedure TBuffer.SetItem(Index: Integer; Value: Byte);
begin
Data[Index] := Value;
end;
procedure TBuffer.FromString(const Str: String);
begin
Data := TEncoding.UTF8.GetBytes(Str);
end;
procedure TBuffer.FromString(const Str: String; Encoding: TEncoding);
begin
Data := Encoding.GetBytes(Str);
end;
procedure TBuffer.FromHexString(const Str: String);
var
Len: Integer;
begin
Len := System.Length(Str) div 2;
SetLength(Data, Len);
HexToBin(PChar(Str), @Data[0], Len)
end;
procedure TBuffer.FromDelimitedHexString(HexStr: String; Prefix: String; Delimitor: String);
var
Len: Integer;
begin
HexStr := HexStr.Replace(Prefix , '');
HexStr := HexStr.Replace(Delimitor , '');
HexStr := HexStr.Replace(' ' , '');
Len := System.Length(HexStr) div 2;
SetLength(Data, Len);
HexToBin(PChar(HexStr), @Data[0], Len)
end;
procedure TBuffer.FromBase64String(const Str: String);
var
Base64Encoding: TBase64Encoding;
begin
//Base64Encoding := TBase64Encoding.Create; //含换行符
Base64Encoding := TBase64Encoding.Create(0); //不含换行符
Data := Base64Encoding.DecodeStringToBytes(Str);
Base64Encoding.Free;
end;
procedure TBuffer.FromBytes(const InBytes: array of Byte; ByteLen: Integer);
begin
if (ByteLen = -1) then ByteLen := System.Length(InBytes);
SetLength(Data, ByteLen);
Move(InBytes[0], Data[0], ByteLen);
end;
procedure TBuffer.FromBytes(const InBytes: TBytes; ByteLen: Integer);
begin
if (ByteLen = -1) then ByteLen := System.Length(InBytes);
SetLength(Data, ByteLen);
Move(InBytes[0], Data[0], ByteLen);
end;
procedure TBuffer.FromBytes(const Ints: array of UInt32; ByteLen: Integer);
begin
SetLength(Data, ByteLen);
Move(Ints[0], Data[0], ByteLen);
end;
procedure TBuffer.OffsetFromBytes(Offset: Integer; const Ints: array of UInt32; ByteLen: Integer);
begin
Move(Ints[0], Data[Offset], ByteLen);
end;
procedure TBuffer.FromStream(const Stream: TStream; ByteLen: Integer);
begin
if (ByteLen = -1) then ByteLen := Stream.Size;
SetLength(Data, ByteLen);
Stream.Read(Data, ByteLen);
end;
procedure TBuffer.FromFile(const FileName: String);
var
Stream: TFileStream;
begin
Stream := TFileStream.Create(FileName, fmOpenRead);
SetLength(Data, Stream.Size);
Stream.Read(Data, Stream.Size);
Stream.Free;
end;
function TBuffer.ToString: String;
begin
Result := TEncoding.UTF8.GetString(Data);
end;
function TBuffer.ToString(Encoding: TEncoding): String;
begin
Result := Encoding.GetString(Data);
end;
function TBuffer.ToHexString: String;
var
Len: Integer;
begin
Len := System.Length(Data);
SetLength(Result, 2*Len);
BinToHex(@Data[0], PChar(Result), Len);
end;
function TBuffer.ToDelimitedHexString(Prefix: String; Delimitor: String): String;
var
I, Len: Integer;
begin
Result := '';
Len := System.Length(Data);
for I := 0 to Len-1 do
begin
Result := Result + Prefix + IntToHex(Data[I], 2);
if I < Len-1 then
Result := Result + Delimitor;
end;
end;
function TBuffer.ToBase64String: String;
var
Base64Encoding: TBase64Encoding;
begin
//Base64Encoding := TBase64Encoding.Create; //含换行符
Base64Encoding := TBase64Encoding.Create(0); //不含换行符
Result := Base64Encoding.EncodeBytesToString(Data);
Base64Encoding.Free;
end;
procedure TBuffer.ToBytes(var OutBytes: array of Byte; ByteLen: Integer);
begin
if (ByteLen = -1) then ByteLen := System.Length(Data);
if (ByteLen > System.Length(OutBytes)) then
raise Exception.Create(SInvalidBufSize);
Move(Data[0], OutBytes[0], ByteLen);
end;
procedure TBuffer.ToBytes(var OutBytes: TBytes; ByteLen: Integer);
begin
if ByteLen = -1 then ByteLen := System.Length(Data);
SetLength(OutBytes, ByteLen);
Move(Data[0], OutBytes[0], ByteLen);
end;
procedure TBuffer.ToBytes(var Ints: array of UInt32; ByteLen: Integer);
begin
if ByteLen = -1 then ByteLen := System.Length(Data);
Move(Data[0], Ints[0], ByteLen);
end;
procedure TBuffer.OffsetToBytes(Offset: Integer; var Ints: array of UInt32; ByteLen: Integer);
begin
Move(Data[Offset], Ints[0], ByteLen);
end;
procedure TBuffer.ToStream(const Stream: TStream);
begin
Stream.Write(Data, System.Length(Data));
end;
procedure TBuffer.ToFile(const FileName: String; Warning: Boolean);
var
Stream: TFileStream;
begin
if Warning and FileExists(FileName) and
(Application.MessageBox(PChar('File ' + FileName + ' Exists, Overwrite It?'),
'Warning: File Exists', MB_YESNO) = IDNO) then Exit;
Stream := TFileStream.Create(FileName, fmCreate);
Stream.Write(Data, System.Length(Data));
Stream.Free;
end;
end.
谈谈PBOC3.0中使用的国密SM2算法
转载请注明出处
http://blog.csdn.net/pony_maggie/article/details/39780825
作者:小马
一 知识准备
SM2是国密局推出的一种他们自己说具有自主知识产权的非对称商用password算法。本身是基于ECC椭圆曲线算法的。所以要讲sm2, 先要弄懂ECC。
全然理解ECC算法须要一定的数学功底。由于涉及到射影平面坐标系,齐次方程求解, 曲线的运算规则等概念。
这里不做过多的数学分析(主要是我自己也没有全然整明确)。
想要深入了解ECC的我推荐网名为ZMWorm 的大牛在多年前写的<<椭圆曲线ECC加密算法入门介绍>>。此人是早年看雪论坛中的一个版主,对算法和password学非常有研究。
本篇的主旨还是希望能以简单通俗的语言,讲清楚PBOC3.0认证过程中,所用到的SM2的相关概念,包含它的实现,使用等。
1 椭圆曲线究竟是什么样的
图1
图2
上面是两个不同椭圆曲线在坐标系中的几何表示, 只是这个坐标系不是二维坐标系,而是射影坐标系。能够用空间思维想像一下(可是注意不是三维坐标系), 打个例如,你晚上站在一个路灯前面,地上有你的影子,你本身是在一个二维坐标系(把你想像成一个纸片),和你的影子一起构成一个射影坐标系。
曲线的每个点, 用三个參量表示, (X,Y,Z)。我们知道在二维坐标系里的每个图形都遵循一个方程,比方直接的二元一次方程是y=kx+b, 圆的方程是(x-a)2+(y-b)2=r2, 椭圆曲线在射影坐标系里也有自己的定义:
Y2Z+a1XYZ+a3YZ2=X3+a2X2Z+a4XZ2+a6Z3
全部椭圆曲线上的点都满足上述方程。a1,a2,a3,a4,a6是系数,决定曲线的形状和位置。
二维坐标和射影坐标有一个相应关系。即x=X/Z, y=Y/Z, 这样就能够把上面的方程转成普通的二维坐标系方程:
y2+a1xy+a3y= x3+a2x2+a4x+a6
2 离散的椭圆曲线
上面的坐标系都是基于实数的,椭圆曲线看起来都是平滑的,假设我们限制曲线的点都必须是整数。曲线就变成离散的了。如图3所看到的:
图3
再进一步限制。要求整数必须大于0, 小于某个大整数P, 这样就形成了一个有限域Fp.然后我们在这个有限域里定义一些点与点之间的加减乘除运算规则。比方A点加B点得到C点(记做A+B≡C (mod p))。或者A点乘以n得到K点(记做A×n≡K (mod p))。
至于详细规则细节能够不用关心,仅仅要知道有这种操作就可以。
选一条曲线,比方
y2=x3+ax+b
把它定义在Fp上, 要求a,b满足:
4a3+27b2≠0 (mod p)
我们把这种曲线记为Ep(a,b)
加解密是基于这种数学难题,K=kG,当中 K,G为Ep(a,b)上的点,k是整数,小于G点(注意区分。不是我们寻常说的那个意思)的阶(不用关心什么是点的阶)。给定k和G,计算K非常easy;但给定K和G,求k就困难了。
这样。G就叫做基点。k是私钥。K是公钥。
最后总结。描写叙述一条Fp上的椭圆曲线,有六个參量:
T=(p,a,b,G,n,h)。
p 、a 、b 用来确定一条椭圆曲线,
G为基点。
n为点G的阶。
h 是椭圆曲线上全部点的个数m与n相除的整数部分)
知识准备阶段知道这么多就能够了。
二 SM2在PBOC认证中的使用
1 签名和验签的原理
前面提到依据系数的不同,ECC曲线能够有非常多,SM2使用当中一种,这就表明它的曲线方程,以及前面说到的六个參量都是固定的。
依据国密局给出的规范定义例如以下:
y2=x3+ax+b p=FFFFFFFE FFFFFFFFFFFFFFFF FFFFFFFF FFFFFFFF 00000000 FFFFFFFF FFFFFFFF a=FFFFFFFE FFFFFFFFFFFFFFFF FFFFFFFF FFFFFFFF 00000000 FFFFFFFF FFFFFFFC b=28E9FA9E 9D9F5E344D5A9E4B CF6509A7 F39789F5 15AB8F92 DDBCBD41 4D940E93 n=FFFFFFFE FFFFFFFFFFFFFFFF FFFFFFFF 7203DF6B 21C6052B 53BBF409 39D54123 Gx=32C4AE2C 1F198119 5F990446 6A39C994 8FE30BBF F2660BE1 715A4589 334C74C7 Gy=BC3736A2 F4F6779C 59BDCEE3 6B692153 D0A9877C C62A4740 02DF32E5 2139F0A0
这里easy引起一个误解,会觉得參数都固定了,公私钥是不是仅仅能有一对?当然不是,注意前面提到的K=kG的模型,K才是公钥,所以公钥事实上是曲线在离散坐标系中,满足条件的一个曲线上的点。能够有非常多个。另外, 从这几个參量能够获知PBOC 3.0的公钥长度都是256位。
基于这样的离散椭圆曲线原理的SM2算法一般有三种使用方法。签名验签。加解密, 密钥交换。PBOC 3.0中的脱机数据认证仅仅用到签名验签的功能。
终端关心的是怎样验签,卡片则要考虑怎样实现生成签名。
2 基于openssl实现sm2
这里给出一个基于openssl的sm2实现, 假设不了解openssl,能够先搜索一下相关知识,这里不解说。
openssl已经实现ECC算法接口,也就是核心已经有了。实现sm2事实上并不难,关键是理解它里面各种接口怎样使用。以下就分析一个终端验签的sm2实现。另外须要说明这里给出的仅仅是代码片段。仅供理解用。
函数接口
int SM2_Verify(BYTE* Px,BYTE* Py, BYTE* DataIn,DWORD DataLen, BYTE* sigrs)
前两个參数是K的坐标值,也就是公钥。
Datain和datalen各自是明文数据和其长度,最后一个參数是待验证的签名。
曲线的參数常量定义例如以下:
//////////////////////////////////////////////////////////////// static const char *group_p ="FFFFFFFEFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF00000000FFFFFFFFFFFFFFFF"; static const char *group_a ="FFFFFFFEFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF00000000FFFFFFFFFFFFFFFC"; static const char *group_b ="28E9FA9E9D9F5E344D5A9E4BCF6509A7F39789F515AB8F92DDBCBD414D940E93"; static const char *group_Gx ="32C4AE2C1F1981195F9904466A39C9948FE30BBFF2660BE1715A4589334C74C7"; static const char *group_Gy ="BC3736A2F4F6779C59BDCEE36B692153D0A9877CC62A474002DF32E52139F0A0"; static const char *group_n = "FFFFFFFEFFFFFFFFFFFFFFFFFFFFFFFF7203DF6B21C6052B53BBF40939D54123"; static const char *ENTL_ID ="008031323334353637383132333435363738"; #define SM2_KEY_LENGTH 32 //256位曲线 ////////////////////////////////////////////////////////////////
ENTL_ID是pboc规范中指定的用于SM3产生摘要的报文头数据。
strcpy(szBuff, ENTL_ID); strcat(szBuff,group_a); strcat(szBuff,group_b); strcat(szBuff,group_Gx); strcat(szBuff,group_Gy); AscToBcd(szDataForDigest,(unsigned char *)szBuff, nLen); …. SM3(szDataForDigest,nLen+SM2_KEY_LENGTH*2, digestZA); memcpy(szDataForDigest,digestZA, ECC_LENGTH); memcpy(szDataForDigest+ECC_LENGTH,DataIn, DataLen); SM3(szDataForDigest,DataLen+ECC_LENGTH, digestH);第一步,对上述数据用sm3做摘要,摘要的结果与数据拼接后再做摘要。
p = BN_new(); a = BN_new(); b = BN_new(); group = EC_GROUP_new(EC_GFp_mont_method()); BN_hex2bn(&p, group_p)) BN_hex2bn(&a, group_a)) BN_hex2bn(&b, group_b))这里是把定义的曲线常量转换成大数表式,这样才干使用openssl中的接口。
Group是ECC中的曲线组,它是ECC算法的核心。为什么这么说呢? 由于这个group里的全部字段就确定了曲线的全部信息, 后面会看到,这里仅仅是用EC_GROUP_new生成一个空的group, 然后由p,a,b等參数来填充group, 再以这个group为基础去生成曲线上的点。
if (!EC_GROUP_set_curve_GFp(group, p, a, b,ctx)) { gotoerr_process; } P = EC_POINT_new(group); Q = EC_POINT_new(group); R = EC_POINT_new(group); if (!P || !Q || !R) { gotoerr_process; }这一段就确定了group的全部信息。而且依据group生成三个曲线上的点(点一定在曲线上,这个非常重要)。
<span style="white-space:pre"> </span>x = BN_new(); y= BN_new(); z= BN_new(); if(!x || !y || !z) { gotoerr_process; } //Gx if(!BN_hex2bn(&x, group_Gx)) { gotoerr_process; } if(!EC_POINT_set_compressed_coordinates_GFp(group, P, x, 0, ctx)) { gotoerr_process; } if(!BN_hex2bn(&z, group_n)) { gotoerr_process; } if(!EC_GROUP_set_generator(group, P, z, BN_value_one())) { gotoerr_process; } if(!EC_POINT_get_affine_coordinates_GFp(group, P, x, y, ctx)) { gotoerr_process; }
这一段首先是设置n到group, n前面讲过,是曲线的阶。另外就是由G点坐标x,y确定点P,这里我事实上也有点不太明确。G点坐标y本来就是已知的,为什么要再通过曲线变换表式生成y,是不是想要对照前面的group信息是否正确?仅仅是为了校验?
if ((eckey = EC_KEY_new()) == NULL) { gotoerr_process; } if(EC_KEY_set_group(eckey, group) == 0) { gotoerr_process; } EC_KEY_set_public_key(eckey, P); if(!EC_KEY_check_key(eckey)) { gotoerr_process; } if(SM2_do_verify(1, digestH, SM2_KEY_LENGTH, signature, sig_len, eckey) != 1) { gotoerr_process; }
这段比較好理解,生成公钥eckey,并由eckey终于验签。
验签的运行函数sm2_do_verify有点复杂,这里不做过多的解释(事实上是我解释不清楚, 哇哈哈),在一个国外的站点上找到一个比較好的描写叙述,拿过来用用。
3 脱机数据认证使用sm2的详细流程
我如果看这篇文章的人对PBOC 2.0中基于RSA国际算法的脱机数据认证流程已经比較了解。相关概念不再过多描写叙述。重点关注二者的差异性。
另外,简单说一下sm3,由于以下会用到。sm3是一个类似hash的杂凑算法,即给定一个输入(一般非常长),产生一个固定长度的输出(sm3是32个字节,hash是20个字节)。它有两个特点:
1 不可逆性。即无法由输出推导出输入。
2 不同的输入,产生不同的输出。
以下就拿终端SDA认证卡片来看看sm2怎样在pboc中使用的。
首先。发卡行公钥等数据(还包含公钥,算法标识,有效期等信息)被CA私钥签名(注意不是加密)生成发卡行公钥证书,这个证书会个人化到ic卡中。这些数据例如以下图所看到的:
图4
签名事实上就是对这个表里的数据做一系列运算,终于生成一个64字节的数据,分别由各32字节的r和s拼接而成(r和s仅仅是个符号而已,没有特别意思)。这64字节的签名拼接到图4后面就是发卡行公钥证书。
这里与国际算法的公钥证书就有明显的差别了。国际算法证书是密文的。发卡行公钥用rsa加密。
而国密的公钥证书全然是明文。用数据举例,以下这些数据来自pboc3.0的卡片送检指南,就是检測时要求个人化到卡里的数据。
【发卡行公钥】 : 173A31DD681C6F8FE3BA6C354AD3924A4ADFD15EB0581BC1B37A1EB1C88DA29B47155F62FCF4CCCD201B134351A049D77E81F6A6C66E9CB32664F41348DA11F 【CA哈希值】(r||s) : 3499A2A0A7FED8F74F119B416FF728BA98EF0A32A36BCCB8D0110623D466425CA44C68F8E49121D9BFA9484CAEF9B476C5EB576D1A8DD6BC4A0986AF4134ABAF 【Tag_90 】(发卡行公钥证书) : 1262280001122000000204001140173A31DD681C6F8FE3BA6C354AD3924A4ADFD15EB0581BC1B37A1EB1C88DA29B47155F62FCF4CCCD201B134351A049D77E86A6C66E9CB32664F41348DA11F63499A2A0A7FED8F74F119B416FF728BA98EF0A32A36BCCB8D0110623D466425CA44C68F8E49121D9BFA9484CAEF9B476C5E56D1A8DD6BC4A0986AF4134ABAF
能够自己的解析一下,看看是否和上面表格中的数据一致, 而且都是明文。
终端在读记录阶段获取发卡行公钥证书。国际算法须要用rsa公钥解密整个证书,然后验证hash,通过后取出发卡行公钥。而国密算法,终端仅仅要用sm2公钥验64字节的签名,通过后直接取明文发卡行公钥,所以国密的验签的动作事实上就相当于国际里的rsa解密和hash对照两个动作。
接着终端进行静态数据签名的验证,情况类似,对于国密,这些数据都是明文形式,后面拼接64字节的sm2签名,这64字节是用发卡行私钥对明文数据签名得到的。终端要做的就是拿刚刚获取的发卡行公钥对这64字节数据验证就可以,验证通过就表示SDA通过。
四 PBOC为什么要选择国密
首先从技术角度,实现相同的计算复杂度,ECC的计算量相对RSA较小,所以效率高。RSA如今在不断的添加模长,眼下都用到了2048位。并非说如今一定要用2048位才是安全的,仅仅是它的安全性更高,破解难度更大,这个要综合考虑,位数高也意味着成本高。
有些不差钱的大公司比方谷歌就已经未雨绸缪的把2048位用在了它们的gmail邮箱服务中。PBOC3.0 认证中眼下仅仅用到1984位。事实上也是相对安全的。
只是这个观点眼下还存在争议。
前段时间在清华大学听了一个关于password算法的课,清华有个教授觉得sm2并不见得比rsa更高级,仅仅是sm2的原理比rsa难理解,所以大部分人觉得它会相对安全些。一旦椭圆曲线被大家研究透了。sm2的光环也可能就此褪去。当然这个也是他个人的观点。
另外一个因素,要从国家战略的角度考虑,RSA之前一直被传与美国安全局合作,在算法中增加后门。这样的事是宁可信其有的。
国密算法咱起码是自己研发的东西,全部的过程细节都一清二楚, 不用担收后门的事情。
央行如今非常重视国产安全芯片的推进工作,前些天央行的李晓枫还公开强调未来金融IC卡芯片要国产化,国密算法是当中非常关键的一步。
未来国产芯片加国密算法,才会有真正自主, 安全的国产金融IC卡产品。
以上是关于[Delphi]一个功能完备的国密SM4类(TSM4)的主要内容,如果未能解决你的问题,请参考以下文章