关于微软CryptoAPI用PKI加解密的问题 (200分)

  • 主题发起人 devil-li
  • 开始时间
下面是我对crpytoapi加密方法的理解,不知道是否正确。在你的加密解密函数中的key参数只是一个口令,而不是实际完成加密的密匙。如果我想实现对称加密的话,使用CryptDeriveKey 函数即可,返回的是指向密匙的句柄。<br><br>下面是我对你的函数的修改,实现了对称加密,好像觉得调用Ecrypt时对key 并没有限制<br>甚至为空也可以,可能把为空的情况夜作为一个字符串处理,我们可以一起讨论一下<br>不需要在MakeKeyFromStr过程中使用导出密匙CryptGetUserKey,CryptExportKey<br>修改如下:<br>Function &nbsp;TForm1.Encrypt(const Data,Key :string):string;<br>var<br>&nbsp; &nbsp; hProv : HCRYPTPROV; &nbsp;//指向csp提供者的句柄<br>&nbsp; &nbsp; hKey &nbsp;: HCRYPTKEY ; &nbsp; //指向密匙的句柄<br>&nbsp; &nbsp; hHash :HCRYPTHASH; &nbsp; &nbsp; //指向散列表的句柄,用于创建散列表<br>&nbsp; &nbsp; Buffer,Temp :string;<br>&nbsp; &nbsp; Index : integer;<br>&nbsp; &nbsp; dwCount :DWORD;<br>&nbsp; &nbsp; Endof : Boolean;<br>&nbsp; &nbsp; BufferLen :DWORD;<br><br>procedure Init ;<br>begin<br>&nbsp; &nbsp; hProv:=0;<br>&nbsp; &nbsp; hKey :=0;<br>&nbsp; &nbsp; hHash:=0;<br>&nbsp; &nbsp; Endof:=False;<br>end;<br><br>procedure clear;<br>begin<br>&nbsp; &nbsp; &nbsp; &nbsp; if boolean(hKey) &nbsp;then<br>&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp;CryptDestroyKey(hKey); //先销毁指向key的句柄<br>&nbsp; &nbsp; &nbsp; &nbsp; if boolean(hHash) then<br>&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp;CryptDestroyHash(hHash); //销毁指向散列的句柄<br>&nbsp; &nbsp; &nbsp; &nbsp; if boolean(hProv) then<br>&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp;CryptReleaseContext(hProv,0); //销毁指向提供者的句柄<br>end;<br>Function Encrypt53(Data53 :string):string;<br>begin<br>&nbsp; &nbsp; &nbsp; &nbsp; Init;<br>&nbsp; &nbsp; &nbsp; &nbsp;if not &nbsp; CryptAcquireContext(@hProv, nil, nil,PROV_RSA_FULL, 0) then &nbsp; &nbsp; &nbsp;//连接缺省的csp<br><br>&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; begin<br>&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp;windows.MessageBox(0,'Erro during CryptAcquireContext','error..',MB_OK);<br>&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp;clear;<br>&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp;exit;<br>&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; end;<br><br>&nbsp; &nbsp; &nbsp; &nbsp;//Key 为加密口令,用口令派生出的密匙加密<br><br>&nbsp; &nbsp; &nbsp; &nbsp;//建立散列表<br>&nbsp; &nbsp; &nbsp; if not &nbsp;CryptCreateHash(hProv,CALG_MD5,0,0,@hHash) &nbsp;then<br><br>&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; begin<br>&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp;clear;<br>&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp;raise Exception.Create('Error during CryptCreateHash');<br>&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; end;<br><br>&nbsp; &nbsp; &nbsp; &nbsp; //散列口令<br>&nbsp; &nbsp; &nbsp; if &nbsp;not &nbsp;CryptHashData(hHash,PBYTE(Pchar(Key)),Length(Key),0) &nbsp;then &nbsp; //PBYTE(pchar(Key))<br><br>&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; begin<br>&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp;windows.MessageBox(0,'Error during CryptHashData','Error..,',MB_OK);<br>&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp;clear;<br>&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp;exit;<br>&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; end;<br><br>&nbsp; &nbsp; &nbsp; //从散列口令中派生出密匙<br>&nbsp; &nbsp; &nbsp; &nbsp;if not CryptDeriveKey(hProv,CALG_RC2 ,hHash,0,@hKey) &nbsp;then<br>&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; begin<br>&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp;windows.MessageBox(0,'Erroe during CryptDeriveKey','Error..',MB_OK);<br>&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp;clear;<br>&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp;exit;<br>&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; end;<br><br>&nbsp; &nbsp; &nbsp; &nbsp;CryptDestroyHash(hHash);<br>&nbsp; &nbsp; &nbsp; &nbsp;hHash := 0;<br><br>&nbsp; &nbsp; &nbsp; &nbsp;Index:=1;<br>&nbsp; &nbsp; &nbsp; &nbsp;Result:='';<br>&nbsp; &nbsp; &nbsp; &nbsp;BufferLen:=BlockLen+8; &nbsp; //使用块编码,则需要额外空间<br>&nbsp; &nbsp; &nbsp; &nbsp;repeat<br>&nbsp; &nbsp; &nbsp; &nbsp; &nbsp;Buffer:=Copy(Data53,Index,BlockLen); &nbsp;//为Buffer赋值<br>&nbsp; &nbsp; &nbsp; &nbsp; &nbsp;dwCount:=Length(Buffer); &nbsp;//buffer的长度<br>&nbsp; &nbsp; &nbsp; &nbsp; &nbsp;Inc(Index,dwCount); &nbsp; //Index:=Index+dwCount<br>&nbsp; &nbsp; &nbsp; &nbsp; &nbsp;Endof:=Index&gt;Length(Data53); &nbsp; //Index是否大于需要加密的数据的长度<br>&nbsp; &nbsp; &nbsp; &nbsp; &nbsp;SetLength(Buffer,BufferLen); &nbsp; // 为buffer分配长度空间 bufferlen<br>&nbsp; &nbsp; &nbsp; &nbsp; &nbsp;if not CryptEncrypt(hKey,0,Endof,0,PByte(PChar(Buffer)),@dwCount,BufferLen) then<br>&nbsp; &nbsp; &nbsp; &nbsp; &nbsp;begin<br>&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp;clear;<br>&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp;Raise Exception.Create('Error during CryptEncrypt');<br>&nbsp; &nbsp; &nbsp; &nbsp; &nbsp;end;<br>&nbsp; &nbsp; &nbsp; &nbsp; &nbsp;SetLength(temp,dwCount); &nbsp;//为temp 分配长度空间dwCount<br>&nbsp; &nbsp; &nbsp; &nbsp; &nbsp;Move(PChar(Buffer)^,Pointer(temp)^,dwCount); &nbsp;//??<br>&nbsp; &nbsp; &nbsp; &nbsp; &nbsp;Result:=Result+temp;<br>&nbsp; &nbsp; &nbsp; &nbsp; &nbsp;until Endof;<br>&nbsp; &nbsp; &nbsp; &nbsp; &nbsp;Clear;<br>&nbsp; &nbsp; &nbsp; end;<br>var<br>&nbsp; &nbsp; &nbsp; &nbsp; i: integer;<br>&nbsp; &nbsp; &nbsp; &nbsp; SegStr :string;<br>begin<br>&nbsp; &nbsp; &nbsp; &nbsp; Result:='';<br>&nbsp; &nbsp; &nbsp; &nbsp; if ord(Key[1])=1 then<br>&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; Result:=Encrypt53(Data)<br>&nbsp; &nbsp; &nbsp; &nbsp; else<br>&nbsp; &nbsp; &nbsp; &nbsp; begin<br>&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp;for i:=0 to Length(Data) div EncryptLen + 1 &nbsp;do<br>&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp;begin<br>&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp;SegStr:=Copy(Data,i*EncryptLen + 1,EncryptLen );<br>&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp;SegStr:=Encrypt53(SegStr);<br>&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp;Result:=Result+Chr(Length(SegStr))+ SegStr;<br>&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp;end;<br>&nbsp; &nbsp; &nbsp; &nbsp; end;<br>end;<br><br>对解密过程做相应的改动即可。
 
1.密钥不是任意的字符串,是要满足一定格式的,要得到满足条件的字符串,参见我以下的过程)<br>&gt;&gt;我的key取的是类似与‘11111111111’的数字字符串,那请问字符串要满足的格式是什么<br>2。在我的程序里 EncryptLen取的是1 <br>3。1。能否解释一下为什么 &nbsp;if Ord(Key[1]) = 1 &nbsp; 则是 &nbsp;对称密钥解密:<br>》》对称密钥加密的密钥第一个字节是1<br>》》》我还是不是很理解,key[1]是字符串key的第一个字符,ord是获得字符的ascii 码,是否<br>是说对称加密中密匙的第一个字节ascii码必须是一。<br>4。按照你的理解 &nbsp;key参数即为加密的密匙,但是我认为key是口令 ,密匙是根据key由CryptDeriveKey<br>和CryptGenKey 得到 &nbsp;不知道是不是正确?应该怎样理解,如果你比较清楚的话,能否将原理解释一下<br><br>另外是否可以留下你的邮箱进行联系 jimiking@163.com
 
1。直接用纯文本字符串是无法加密的:因为需要一个密钥句柄hkey。因此要用纯文本加密必需要将纯文本变成key结构。我之所以用MakeKeyFromStr是考虑了公开密钥加密的情况:公开密钥加密是不可能由你自己指定纯文本字符串的密钥的。如果你只需要对称密钥加密,就用你的方法也可以:CryptDeriveKey以后直接用hkey而不用导出。我只不过是CryptDeriveKey然后导出,Encrypt再导入。结果都是一样的。<br>2。密钥不是任意的字符串。我指的密钥不是纯文本的密钥,而是hkey的密钥。(就是CryptDeriveKey之后的结果)对称密钥加密的密钥第一个字节是1,这里的密钥也是指的hkey指向的密钥。<br>3。按照你的理解 &nbsp;key参数即为加密的密匙,但是我认为key是口令 ,密匙是根据key由CryptDeriveKey和CryptGenKey 得到 &nbsp;不知道是不是正确?<br>》》在我的函数中,key参数即为加密的密匙。key就是从CryptDeriveKey和CryptGenKey得到的,因此你的理解和我的意思是一样的。<br>4。我的纯文本的密钥加密的过程:1.有一个纯文本[red]密码[/red],通过MakeKeyFromStr得到密码对应的密钥(key)。调用Encrypt,key参数就是MakeKeyFromStr的结果。在Encrypt里面会调用CryptImportKey导入key而得到hkey。然后加密。<br>5。我的EMail:blacwet@163.com
 
你好 &nbsp;: 我的对称加密在2000 下可以通过,为什么到98下 总是连接不到CSP<br><br>现在我参照你的代码改为对称加密后,在2000环境下可以通过,但是在98下时候,总是连接不到CSP,<br><br>Function Encrypt53(Data53 :string):string;<br>begin<br>&nbsp; &nbsp; &nbsp; &nbsp; Init;<br>&nbsp; &nbsp; &nbsp; &nbsp;if not &nbsp; CryptAcquireContext(@hProv, nil, nil,PROV_RSA_FULL, 0) then &nbsp; &nbsp; &nbsp;//连接缺省的csp<br><br>&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; begin<br>&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp;windows.MessageBox(0,'Erro during CryptAcquireContext','error..',MB_OK);<br>&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp;clear;<br>&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp;exit;<br>&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; end;<br><br>CryptAcquireContext(@hProv, nil, nil,PROV_RSA_FULL, 0)执行会有错误,请你指教。我做的对称加密在2000下通过,到98下调试的时候需要注意些什么问题呢 &nbsp; 谢谢<br><br>
 
因为你的98没有container。<br>你可以这样写:<br>class procedure TPvtCAPICrypto.CryptAcquireContextEx(var hProv: HCRYPTPROV);<br>begin<br>&nbsp; if not CryptAcquireContext(@hProv, nil, nil, PROV_RSA_FULL, 0) then<br>&nbsp; &nbsp; if not CryptAcquireContext(@hProv, 'PvtContainer', nil,<br>&nbsp; &nbsp; &nbsp; PROV_RSA_FULL, CRYPT_NEWKEYSET) then<br>&nbsp; &nbsp; &nbsp; raise Exception.Create('Error during CryptAcquireContext');<br>end;<br><br>其中'PvtContainer'这个名字是随便起的。
 
那之后的调用方法还一样么 &nbsp; &nbsp;PvtContainer参数在后面怎么引用<br><br>我后来改为 &nbsp;CryptAcquireContext(@hProv, nil, nil,<br>&nbsp; &nbsp; &nbsp;PROV_RSA_FULL, CRYPT_NEWKEYSET) &nbsp;也不行的
 
'PvtContainer'这个是Container的名字,一定要指定的。随便什么名字都可以。<br>以后访问直接用<br>if not CryptAcquireContext(@hProv, nil, nil, PROV_RSA_FULL, 0) then<br>所以直接像我那样封装就行了,使用时直接调用CryptAcquireContextEx
 
下面是我进行调试时候发现的一些问题,和你讨论一下:<br>1。不能有两个相同的container 名字。如 &nbsp;<br>if &nbsp;not CryptAcquireContext(@hProv, 'wer', nil,PROV_RSA_FULL,CRYPT_NEWKEYSET)then &nbsp; &nbsp;在第一次执行时,跳出Exception处理。当第二次执行时就会执行Exception。<br>(这是只有这一句的情况)<br>2.如果有上一句代码的话就必须要有<br>if not CryptAcquireContext(@hProv, nil, nil, PROV_RSA_FULL, 0) then<br><br>即像你上面所写:<br>if not CryptAcquireContext(@hProv, nil, nil, PROV_RSA_FULL, 0) then &nbsp; //(1)<br>&nbsp; &nbsp;if not CryptAcquireContext(@hProv, 'PvtContainer', nil,<br>&nbsp; &nbsp; &nbsp;PROV_RSA_FULL, CRYPT_NEWKEYSET) then &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp;//(2)<br>&nbsp; &nbsp; &nbsp;raise Exception.Create('Error during CryptAcquireContext');<br>end;<br>当第一次执行时,执行的是(2),而再一次调用Encrypt53时,执行到(1)的时,就执行完毕,这时候CryptAcquireContext(@hProv, nil, nil, PROV_RSA_FULL, 0) 中的第二个参数应理解为缺省值,即是我们刚刚创建的container 的名字'PvtContainer'<br><br>不知道我的理解是否正确 &nbsp; 谢谢<br><br>&nbsp; &nbsp;
 
另外 创建的这个container的名字,有没有存在生命期的问题,即我运行了第一句代码后<br><br>是不是在系统里永远的建立了这个container,或者机器重启后 &nbsp;就销毁,再进行建立
 
应该是不重装系统就永远存在
 
to devil-li<br>请把你的程序给我发一份吧,我现在也在做这样一个东西.非常感谢.<br>jnbutian@sohu.com
 
对不起,这个是商业程序,不能发给你。通过上面的讨论,所有的关键源代码都有了。你自己研究一下吧
 
顶部