DSA数字签名算法
1 引言
为了确保数据传输的安全性,不得不采取一系列的安全技术,如加密技术、数字签名、身份认证、密钥管理、防火墙、安全协议等。其中数字签名就是实现网上交易安全的核心技术之一,它可以保证信息传输的保密性、数据交换的完整性、发送信息的不可否认性、交易者身份的确定性等。DSA(Digital Signature Algorithm,数字签名算法,用作数字签名标准的一部分),它是另一种公开密钥算法,它不能用作加密,只用作数字签名。DSA使用公开密钥,为接受者验证数据的完整性和数据发送者的身份。它也可用于由第三方去确定签名和所签数据的真实性。DSA算法的安全性基于解离散对数的困难性,这类签字标准具有较大的兼容性和适用性,成为网络安全体系的基本构件之一。
2. 数字签名
2.1 数字签名的概念
数字签名在ISO7498—2标准中定义为:“附加在数据单元上的一些数据,或是对数据单元所作的密码变换,这种数据和变换允许数据单元的接收者用以确认数据单元来源和数据单元的完整性,并保护数据,防止被人(例如接收者)进行伪造”。
数字签名是通过一个单向函数对要传送的信息进行处理得到的用以认证信息来源并核实信息在传送过程中是否发生变化的一个字母数字串。数字签名提供了对信息来源的确定并能检测信息是否被篡改。
数字签名要实现的功能是我们平常的手写签名要实现功能的扩展。平常在书面文件上签名的主要作用有两点,一是因为对自己的签名本人难以否认,从而确定了文件已被自己签署这一事实;二是因为自己的签名不易被别人模仿,从而确定了文件是真的这一事实。采用数字签名,也能完成这些功能:
(1)确认信息是由签名者发送的;
(2)确认信息自签名后到收到为止,未被修改过;
签名者无法否认信息是由自己发送的。
数字签名和手签的区别是:手签是模拟的,易伪造,而数字签名是基于数学原理的,更难伪造。
数字签名的技术基础是公钥密码技术。密钥必须以某种安全的方式告诉解密方。大家熟悉的DES加密标准就是一种对称加密技术。1976年,Diffie和Hellman在一篇名叫“New Direction in Cryptog raphy(密码学的新方向)”一文中提出了一个新的思想,即:不仅加密算法本身可以公开,就是加密用的密钥本身也可以公开。这就是公钥密码体制。其中使用的密钥被分解为一对:一把公钥和一把私钥。只要私钥保密就可以了,公钥可以发到因特网(如网站的黄页)等公开地方供别人查询和下载。
2.2 数字签名的原理
图1. 数字签名的原理图
处理过程:(采用双重加密)
(1)使用SHA编码将发送文件加密产生128bit的数字摘要;
(2)发送方用自己的专用密钥对摘要再加密,形成数字签名;
(3)将原文和加密的摘要同时传给对方;
(4)接受方用发送方的公共密钥对摘要解密,同时对收到的文件用SHA编码加密产生同一摘要;
(5)将解密后的摘要和收到的文件在接受方重新加密产生的摘要相互对比,如果两者一致,则说明在传送过程中信息没有破坏和篡改。否则,则说明信息已经失去安全性和保密性。
3数字签名的方案
3.1 RSA数字签名系统
RSA算法中数字签名技术实际上是通过一个哈希函数来实现的。数字签名的特点
是它代表了文件的特征,文件如果发生改变,数字签名的值也将发生变化。不同的文件将得到不同的数字签名。
用RSA或其它公开密钥密码算法的最大方便是没有密钥分配问题。因为公开密钥加密使用两个不同的密钥,其中有一个是公开的,另一个是保密的。公开密钥可以保存在系统目录内、未加密的电子邮件信息中、电话黄页(商业电话)上或公告牌里,网上的任何用户都可获得公开密钥。
3.2 Hash签名
Hash签名是最主要的数字签名方法,也称之为数字摘要法(Digital Digest)或数字指纹法(Digital Finger Print)。它与RSA数字签名是单独的签名不同,该数字签名方法是将数字签名与要发送的信息紧密联系在一起,它更适合于电子商务活动。将一个商务合同的个体内容与签名结合在一起,比合同和签名分开传递,更增加了可信度和安全性。
一个Hash函数满足:
①H可以作用于一个任意长度的数据块;
②H产生一个固定长度的输出;
③H(x)对任意给定的x计算相对容易,无论是软件还是硬件实现;
④对任意给定码h,找到x满足H(x)=h具有计算不可行性;
⑤对任意给定的数据块x,找到满足H(y)=H(x)的y x具有计算不可行性;
⑥找到任意数据对(x,y),满足H(x) = H(y)是计算不可行的。
用Hash函数实验签名的方案如下:
发送方X:准备消息M,计算其散列码H(M),用X的私钥对散列值构成签名Kx-1[H(M)],并将消息M及签名Kx-1 [H(M)]发送给Y
接收方Y:对收到的消息M′计算用H(M′),利用公钥解密Kx-1[H(M)],然后比较Kx [Kx-1 [H(M)]]和H(M′),如果Kx [Kx-1 [H(M)]] =H(M′),则签名得到验证。
3.3 椭圆曲线数字签名算法(ECDSA)
椭圆曲线的数字签名具有与RSA数字签名和DSA数字签名基本上相同的功能,但实施起来更有效,因为椭圆曲线数字签名在生成签名和进行验证时要必RSA和DSA来得快。
椭圆曲线数字签名的速度要比RSA、DSA快,还可以用在一些较小、对资源有一定限制得设备如智能卡(含有微处理器芯片得塑料片)中。
3.4 美国数字签名标准(DSA)
数字签名算法(Digital Signature Algorithm,DSA)是Schnorr和ElGamal签名算法的变种,由美国国家标准化技术研究院(NIST)和国家安全局共同开发。DSA是基于离散对数的难度。
4 基于DSA数字签名的实现方法
建立在公钥密码技术上的数字签名方法有很多,有RSA签名、DSA签名和椭圆曲线数字签名算法(ECDSA)等等。下面对DSA签名进行详细分析。
(1)DSA算法参数说明
DSA算法中应用了下述参数:
p:L bits长的素数。L是64的倍数,范围是512到1024;
q:p - 1的160bits的素因子;
g:g = hp-1 mod p,h满足h < p - 1, h(p-1)/q mod p > 1;
x:1 y:y = gx mod p ,( p, q, g, y )为公钥; H( x ):单向Hash函数。在DSS中选用安全散列算法( Secure Hash Algorithm,SHA )[]。p, q, g:可由一组用户共享,但在实际应用中,使用公共模数可能会带来一定的威胁。(2)签名及验证协议 签名及验证协议如下: ①P产生随机数k,k < q; ②P计算r = ( gk mod p ) mod q和s = ( k-1 (H(m) + xr)) mod q 签名结果是( m, r, s )。 ③验证时 计算w = s-1mod q 计算u1 = ( H( m ) * w ) mod q 计算u2 = ( r * w ) mod q 计算v = (( gu1 * yu2 ) mod p ) mod q 若v = r,则认为签名有效。 5.结果 在VC++ 编译环境下,可实现对DSA数字签名。其中对摘要的加密和签名可以选择不同的算法。结果如图: 6.结论 DSA主要依赖于整数有限域离散对数难题。素数P 必须足够大,且p-1至少包含一个大素数因子以抵抗Pohlig & Hellman算法的攻击。M 一般都应采用信息的HASH值(官方推荐为SHA算法)。DSA的安全性主要依赖于p和g,若选取不当则签名容易伪造,应保证g 对于p-1的大素数因子不可约。DSA的安全性要次于ECC 与RSA不相上下。但是有一点,就是DSA算法的验证过程R,S 是以明文形式出现的,这点很容易被利用。但是随着加密技术的发展,椭圆曲线数字签名算法(ECDSA)是使用椭圆曲线对数字签名算法(DSA)的模拟。与普通的离散对数问题(discrete logarithm problem DLP)和大数分解问题(integer factorization problem IFP)不同,椭圆曲线离散对数问题(elliptic curve discrete logarithm problem ECDLP)没有亚指数时间的解决方法。因此椭圆曲线密码的单位比特强度要高于 其他公钥体制。 7.附录 // 数字签名Dlg.cpp : implementation file // #include "stdafx.h" #include "数字签名.h" #include "数字签名Dlg.h" #include "BigInt.h" #include "MD5.h" #include "SHA1.h" #ifdef _DEBUG #define new DEBUG_NEW #undef THIS_FILE static char THIS_FILE[] = __FILE__; #endif CBigInt sha(CBigInt y); CBigInt pow2( const int x, int y); CBigInt makerandnumber( unsigned int len); CBigInt pow3(CBigInt x, CBigInt y, CBigInt m); ///////////////////////////////////// ///////////////////////////////////// /// // CAboutDlg dialog used for App About class CAboutDlg : public CDialog { public: CAboutDlg(); // Dialog Data //{{AFX_DATA(CAboutDlg) enum { IDD = IDD_ABOUTBOX }; //}}AFX_DATA // ClassWizard generated virtual function overrides //{{AFX_VIRTUAL(CAboutDlg) protected: virtual void DoDataExchange(CDataExchange* pDX); // DDX/DDV support //}}AFX_VIRTUAL // Implementation protected: //{{AFX_MSG(CAboutDlg) virtual void OnOK(); //}}AFX_MSG DECLARE_MESSAGE_MAP() }; CAboutDlg::CAboutDlg() : CDialog(CAboutDlg::IDD) { //{{AFX_DATA_INIT(CAboutDlg) //}}AFX_DATA_INIT } void CAboutDlg::DoDataExchange(CDataExchan ge* pDX) { CDialog::DoDataExchange(pDX); //{{AFX_DATA_MAP(CAboutDlg) //}}AFX_DATA_MAP } BEGIN_MESSAGE_MAP(CAboutDlg, CDialog) //{{AFX_MSG_MAP(CAboutDlg) //}}AFX_MSG_MAP END_MESSAGE_MAP() ///////////////////////////////////// ///////////////////////////////////// /// // DMyDlg dialog DMyDlg::DMyDlg(CWnd* pParent /*=NULL*/) : CDialog(DMyDlg::IDD, pParent) { //{{AFX_DATA_INIT(DMyDlg) m_P = _T(""); m_Q = _T(""); m_G = _T(""); //mpdified in 2007.6.10 m_Y = _T(""); m_S = _T(""); m_V = _T(""); m_Len = 0; m_OUT = _T(""); m_IN = _T(""); ready = 0; m_file = _T(""); m_string = _T(""); //}}AFX_DATA_INIT // Note that LoadIcon does not require a subsequent DestroyIcon in Win32 m_hIcon = AfxGetApp()->LoadIcon(IDR_MAINFRAME); } void DMyDlg::DoDataExchange(CDataExchange* pDX) { CDialog::DoDataExchange(pDX); //{{AFX_DATA_MAP(DMyDlg) DDX_Text(pDX, IDC_P, m_P); DDX_Text(pDX, IDC_Q, m_Q); DDX_Text(pDX, IDC_G, m_G); // mpdified in 2007.6.10 DDX_Text(pDX, IDC_Y, m_Y); DDX_Text(pDX, IDC_S, m_S); DDX_Text(pDX, IDC_V, m_V); DDX_CBIndex(pDX, IDC_COMBO, m_Len); DDX_Text(pDX, IDC_OUTPUT, m_OUT); DDX_Text(pDX, IDC_INPUT, m_IN); DDX_Text(pDX, IDC_EDITFILE, m_file); DDX_Text(pDX, IDC_STRING, m_string); //}}AFX_DATA_MAP } BEGIN_MESSAGE_MAP(DMyDlg, CDialog) //{{AFX_MSG_MAP(DMyDlg) ON_WM_SYSCOMMAND() ON_WM_PAINT() ON_WM_QUERYDRAGICON() ON_BN_CLICKED(IDC_BUTTON_GET, OnButtonGet) ON_BN_CLICKED(IDC_ENCRYPT, OnEncrypt) ON_BN_CLICKED(IDC_DECRYPT, OnDecrypt) ON_BN_CLICKED(IDC_Btnfile, OnBtnfile) ON_EN_CHANGE(IDC_EDITFILE, OnChangeEditfile) ON_EN_CHANGE(IDC_STRING, OnChangeString) ON_BN_CLICKED(IDC_SHA1_RADIO, OnSha1Radio) ON_BN_CLICKED(IDC_MD5_RADIO, OnMd5Radio) ON_CBN_EDITCHANGE(IDC_COMBO, OnEditchangeCombo) ON_BN_CLICKED(IDC_BUTTON1, OnButton1) //}}AFX_MSG_MAP END_MESSAGE_MAP() ///////////////////////////////////// ///////////////////////////////////// /// // DMyDlg message handlers BOOL DMyDlg::OnInitDialog() { CDialog::OnInitDialog(); // Add "About..." menu item to system menu. // IDM_ABOUTBOX must be in the system command range. ASSERT((IDM_ABOUTBOX & 0xFFF0) == IDM_ABOUTBOX); ASSERT(IDM_ABOUTBOX < 0xF000); CMenu* pSysMenu = GetSystemMenu(FALSE); if (pSysMenu != NULL) { CString strAboutMenu; strAboutMenu.LoadString(IDS_ABOUT BOX); if (!strAboutMenu.IsEmpty()) { pSysMenu->AppendMenu(MF_SEPARATOR ); pSysMenu->AppendMenu(MF_STRING, IDM_ABOUTBOX, strAboutMenu); } } // Set the icon for this dialog. The framework does this automatically // when the application's main window is not a dialog SetIcon(m_hIcon, TRUE); // Set big icon SetIcon(m_hIcon, FALSE); // Set small icon // TODO: Add extra initialization here CheckRadioButton(IDC_MD5_RADIO,ID C_SHA1_RADIO,IDC_MD5_RADIO); UpdateData(FALSE); return TRUE; // return TRUE unless you set the focus to a control } void DMyDlg::OnSysCommand(UINT nID, LPARAM lParam) { if ((nID & 0xFFF0) == IDM_ABOUTBOX) { CAboutDlg dlgAbout; dlgAbout.DoModal(); } else { CDialog::OnSysCommand(nID, lParam); } } // If you add a minimize button to your dialog, you will need the code below // to draw the icon. For MFC applications using the document/view model, // this is automatically done for you by the framework. void DMyDlg::OnPaint() { if (IsIconic()) { CPaintDC dc(this); // device context for painting SendMessage(WM_ICONERASEBKGND, (WPARAM) dc.GetSafeHdc(), 0); // Center icon in client rectangle int cxIcon = GetSystemMetrics(SM_CXICON); int cyIcon = GetSystemMetrics(SM_CYICON); CRect rect; GetClientRect(&rect); int x = (rect.Width() - cxIcon + 1) / 2; int y = (rect.Height() - cyIcon + 1) / 2; // Draw the icon dc.DrawIcon(x, y, m_hIcon); } else { CDialog::OnPaint(); } } // The system calls this to obtain the cursor to display while the user drags // the minimized window. HCURSOR DMyDlg::OnQueryDragIcon() { return (HCURSOR) m_hIcon; } void DMyDlg::OnButtonGet() { // TODO: Add your control notification handler code here ready=1; UpdateData(TRUE); CTime t0=CTime::GetCurrentTime(); /* int len=8; //int len=2; modified in 2007 6. 9 for(int i=0;i P.Mov(0); Q.Mov(0); N.Mov(0); E.Mov(0); P.GetPrime(len); Q.GetPrime(len); N.Mov(P.Mul(Q)); N.Put(m_P); P.m_ulValue[0]--; Q.m_ulValue[0]--; P.Mov(P.Mul(Q)); D.Mov(0x10001); //mpdified in 2007.6.10 // m_D="10001"; //mpdified in 2007.6.10 E.Mov(D.Euc(P)); E.Put(m_Q); CTime t1=CTime::GetCurrentTime(); CTimeSpan t=t1-t0; m_OUT.Format("%d",t.GetTotalSecon ds()); m_OUT+=" 秒"; Q.m_ulValue[0]=0; */ CBigInt r,s,u,v[4],w,p,q,t; //,H,G,X,Y; int n=3,b=31,L=512,g=160,T=1; t.Mov(pow2(2,g)); while (T) { s.Mov(makerandnumber(g)); // r.Mov(s); // r.Mov(r.Add(1)); // r.Mov(r.Mod(t)); // u.Mov(sha(s)); q.Mov(sha(s)); q.m_ulValue[0]=q.m_ulValue[0]|0x1 ; q.m_ulValue[4]=q.m_ulValue[4]|0x8 0000000; while (q.Rab()==0) q.Mov(q.Add(2)); int C=0,N=2; for (int k=0;k<4;k++) { v[k].Mov(s); v[k].Mov(v[k].Add(N+k)); v[k].Mov(v[k].Mod(t)); v[k].Mov(sha(v[k])); } w.Mov(0); r.Mov(1); for ( k=0;k<3;k++) { v[k].Mov(v[k].Mul(r)); w.Mov(w.Add(v[k])); r.Mov(r.Mul(t)); } s.Mov(pow2(2,b)); v[k].Mov(v[k].Mod(s)); v[k].Mov(v[k].Mul(r)); w.Mov(w.Add(v[k])); r.Mov(w); r.Mov(r.Add(1)); w.Mov(w.Mod(q)); r.Mov(r.Sub(w)); p.Mov(r); while (p.Rab()==0) { C=C+1; N=N+n+1; if (C==4096) break; p.Mov(p.Add(q)); p.Mov(p.Add(q)); } if (C<4096) break; } P.Mov(p); Q.Mov(q); H.Mov(makerandnumber(g)); X.Mov(makerandnumber(g)); X.Mov(X.Mod(Q)); t.Mov(P.Sub(1)); t.Mov(t.Div(Q)); G.Mov(H.RsaTrans(t, Q)); Y.Mov(G.RsaTrans(X, P)); P.Put(m_P); Q.Put(m_Q); G.Put(m_G); Y.Put(m_Y); // m_D="10001"; CTime t1=CTime::GetCurrentTime(); CTimeSpan t2=t1-t0; m_OUT.Format("%d",t2.GetTotalSeco nds()); m_OUT+=" 秒"; UpdateData(FALSE); } void DMyDlg::OnEncrypt() { // TODO: Add your control notification handler code here CBigInt g,t; if(ready==0) { m_OUT=_T("请先输入或生成N、D、E"); UpdateData(FALSE); return; } UpdateData(TRUE); if(m_IN.GetLength()>256) { m_OUT=_T("N不得大于256位"); UpdateData(FALSE); return; } K.Mov(makerandnumber(160)); R.Mov(G.RsaTrans(K,P)); R.Mov(R.Mod(Q)); S.Mov(K.Euc(Q)); g.Get(m_IN); t.Mov(X.Mul(R)); t.Mov(t.Mod(Q)); t.Mov(t.Add(g)); S.Mov(S.Mul(t)); S.Mov(S.Mod(Q)); R.Put(m_OUT); S.Put(m_S); /* for(int i=0;i { if((m_IN[i]<'0')|| ((m_IN[i]>'9')&&(m_IN[i]<'A'))|| ((m_IN[i]>'F')&&(m_IN[i]<'a'))|| (m_IN[i]>'f')) { m_OUT=_T("待加密数据必须 为0-9或A-F或a-f组成的整数"); UpdateData(FALSE); return; } }*/ // P.Get(m_IN); // if(P.Cmp(N)>=0) // { // m_OUT=_T("待加密数据必须小于N"); // UpdateData(FALSE); // return; // } // Q.Mov(P.RsaTrans(E,N)); // Q.Put(m_OUT); UpdateData(FALSE); } void DMyDlg::OnDecrypt() { CBigInt W,U1,U2,t; // TODO: Add your control notification handler code here if((ready==0)||(Q.m_ulValue[0]==0 )) { m_OUT=_T("请先进行数字签名"); UpdateData(FALSE); return; } W.Mov(S.Euc(Q)); U1.Get(m_IN); U1.Mov(U1.Mul(W)); U1.Mov(U1.Mod(Q)); U2.Mov(R.Mul(W)); U2.Mov(U2.Mod(Q)); V.Mov(G.RsaTrans(U1,P)); V.Mov(V.Mod(P)); t.Mov(Y.RsaTrans(U2,P)); t.Mov(t.Mod(P)); V.Mov(V.Mul(t)); V.Mov(V.Mod(P)); V.Mov(V.Mod(Q)); if (V.Cmp(R)==0) V.Put(m_V); R.Put(m_V); // Q.Get(m_OUT); // P.Mov(Q.RsaTrans(D,N)); // P.Put(m_OUT); UpdateData(FALSE); } void DMyDlg::OnBtnfile() { // TODO: Add your control notification handler code here CFileDialog m_FileOpen(TRUE); //设置打开窗体的标题 m_FileOpen.m_ofn.lpstrTitle = _T("打开文件"); m_FileOpen.m_ofn.lpstrFilter = _T("文件 (*.*)\0*.*\0\0"); //点击浏览按钮 if(IDOK == m_FileOpen.DoModal()) { //获取打开的文件路径 CString m_FileName = m_FileOpen.GetPathName(); CEdit* pEdit = (CEdit*)(GetDlgItem(IDC_EDITFILE)); //将选择的文件路径名显示到 IDC_EDITFILE表识的EDIT中 pEdit->SetWindowText(m_FileName); } } void byte2strhash(unsigned char *hash,int hashlen,unsigned char *out) { int i,x,y; for (i = 0;i < hashlen; i++) { y = hash[i] >> 4; y += 0x30; if (y > 0x39) y+= 0x07; x = hash[i] & 0x0F; x += 0x30; if (x > 0x39) x+= 0x07; out[i << 1] = y; out[(i << 1)+1] = x; } out[hashlen << 1] = 0x00; } void DMyDlg::OnChangeEditfile() { // TODO: If this is a RICHEDIT control, the control will not // send this notification unless you override the CDialog::OnInitDialog() // function and call CRichEditCtrl().SetEventMask() // with the ENM_CHANGE flag ORed into the mask. // TODO: Add your control notification handler code here UpdateData(TRUE); CString open=m_file; MD5_CTX md5_ctx; SHA1_CTX sha1_ctx; int hashradio; hashradio=GetCheckedRadioButton(I DC_MD5_RADIO,IDC_SHA1_RADIO); if(hashradio==IDC_MD5_RADIO) //MD5 { int i; unsigned char digest[16],output[16]; FILE* file; unsigned char buffer[16384]; MD5Init(&md5_ctx); file = fopen(open, "rb"); i = fread(buffer, 1, 16384, file); MD5Update(&md5_ctx, buffer, i); MD5Final(digest, &md5_ctx); fclose(file); byte2strhash(digest,16,output); CString str(output); SetDlgItemText(IDC_INPUT,str); } else //SHA-1 { int i; unsigned char digest[20],output[20]; FILE* file; unsigned char buffer[16384]; SHA1Init(&sha1_ctx); file = fopen(open, "rb"); i = fread(buffer, 1, 16384, file); SHA1Update(&sha1_ctx, buffer, i); SHA1Final(digest, &sha1_ctx); fclose(file); byte2strhash(digest,20,output); CString str(output); SetDlgItemText(IDC_INPUT,str); } } void DMyDlg::OnChangeString() { // TODO: If this is a RICHEDIT control, the control will not // send this notification unless you override the CDialog::OnInitDialog() // function and call CRichEditCtrl().SetEventMask() // with the ENM_CHANGE flag ORed into the mask. // TODO: Add your control notification handler code here UpdateData(TRUE); MD5_CTX md5_ctx; SHA1_CTX sha1_ctx; int hashradio; hashradio=GetCheckedRadioButton(I DC_MD5_RADIO,IDC_SHA1_RADIO); if(hashradio==IDC_MD5_RADIO) //MD5 { unsigned char digest[16],output[16]; UpdateData(TRUE); CString context=m_string; MD5Init(&md5_ctx); unsigned char *buf = (unsigned char*)context.GetBuffer(context.GetLe ngth()); MD5Update(&md5_ctx,buf, strlen(context)); MD5Final(digest, &md5_ctx); context.ReleaseBuffer(); byte2strhash(digest,16,output); CString str(output); SetDlgItemText(IDC_INPUT,str); } else //SHA1 { unsigned char digest[20],output[20]; UpdateData(TRUE); CString context=m_string; SHA1Init(&sha1_ctx); unsigned char *buf = (unsigned char*)context.GetBuffer(context.GetLe ngth()); SHA1Update(&sha1_ctx,buf, strlen(context)); SHA1Final(digest, &sha1_ctx); context.ReleaseBuffer(); byte2strhash(digest,20,output); CString str(output); SetDlgItemText(IDC_INPUT,str); } } void DMyDlg::OnSha1Radio() { // TODO: Add your control notification handler code here } void DMyDlg::OnMd5Radio() { // TODO: Add your control notification handler code here } void DMyDlg::OnEditchangeCombo() { // TODO: Add your control notification handler code here } void DMyDlg::OnButton1() { // TODO: Add your control notification handler code here CDialog::OnOK(); } void CAboutDlg::OnOK() { // TODO: Add extra validation here CDialog::OnOK(); }