您的位置:首页 > 编程语言 > C#

C#中.snk文件的作用及从.snk文件导出密钥 (网上整理)

2013-05-10 15:22 183 查看
SNK,作为程序后缀的时候,是.net中的强密匙加密文件!

当你把一个程序集放进GAC(全局程序集缓存)时,就要加强名(也就是签名),在GAC中的程序集可以被所有程序引用访问(相当于以前COM组件在注册表里注册一样),如果不放进GAC,刚所有使用这个程序集都要复制这个程序集(也就是私有程序集),所以用不用强名要看情况而定.

如果不是要把.dll共享可以不使用强名
强名称是为了你的Dll文件的安全,一般我们的dll文件,随便在哪只要引用就可以用
但如果用了强名称的话,dll就会有一个秘钥,其它人就不能引用了

如何创建带有强名称的程序集
可以向程序集分配称为强名称的加密签名,它为程序集提供名称的唯一性并防止他人借用您的程序集名称(名称欺骗)。如果您正在部署一个将被同一台计算机上的多个应用程序共享的程序集,则该程序集必须有强名称。即使只在您的应用程序中使用该程序集,使用强名称也可确保加载该程序集的正确版本。

生成具有强名称的程序集的第一步是获得加密密钥对。.NETFrameworkSDK包括一个可用来生成密钥对的“强名称”工具(Sn.exe)。由“强名称”工具生成的密钥对可以保留在文件中,您也可以将它存储在本地计算机的“加密服务提供程序”(CSP)中。下面的命令使用“强名称”工具生成一个新的密钥对并将它存储在名为TestKey.snk的文件中:

sn-kTestkey.snk

在获得密钥对后,需要向源文件中添加正确的自定义属性,以便编译器发出带有强名称的程序集。根据用于签名的密钥对是包含在文件中还是CSP内的密钥容器中,正确地选择属性。对于存储在文件中的密钥,请使用System.Reflection.AssemblyKeyFileAttribute。对于存储在CSP中的密钥,请使用System.Reflection.AssemblyKeyNameAttribute。

(*)如果未指定密钥,则程序集不会被签名。
//(*)KeyName是指已经安装在
//计算机上的加密服务提供程序(CSP)中的密钥。KeyFile是指包含
//密钥的文件。
//(*)如果KeyFile和KeyName值都已指定,则
//发生下面的处理:
//(1)如果在CSP中可以找到KeyName,则使用该密钥。
//(2)如果KeyName不存在而KeyFile存在,则
//KeyFile中的密钥安装到CSP中并且使用该密钥。
//(*)要创建KeyFile,可以使用sn.exe(强名称)实用工具。
//在指定KeyFile时,KeyFile的位置应该
//相对于“项目输出目录”。项目输出
//目录的位置取决于您是在使用本地项目还是Web项目。
//对于本地项目,项目输出目录定义为
//<ProjectDirectory>\obj\<Configuration>。例如,如果KeyFile位于该
//项目目录中,应将AssemblyKeyFile
//属性指定为[assembly:AssemblyKeyFile("..\\..\\mykey.snk")]
//对于Web项目,项目输出目录定义为
//%HOMEPATH%\VSWebCache\<MachineName>\<ProjectDirectory>\obj\<Configuration>。

从.snk文件导出密钥

先声明该文的实用性不强,要产生一对密钥可以有更简单的方法。该文简单解释了.snk文件的格式,并给出了从中提取密钥的C#代码。

.snk文件(StrongNameKey)也可以叫签名文件,它一般用来给DotNet程序集进行强命名。它的好处是非常容易产生,也非常容易管理。我们可以在VisualStudio的项目设置中新建一个签名文件,也可以用sn.exe-kkey.snk命令行来产生一个。

.snk可以只包含一个公钥,也可以同时包含公钥和私钥。其中私钥可以用来对程序集进行签名,而公钥则可以用来验证一个签名。由于该密钥实际上就是一个1024位的RSA密钥,我们也可以用.snk文件来进行RSA应用。比如进行非对称加密,进行Xml数字签名和验证等等。

目前就我知道的,snk格式没有正式公开。不过包含公钥和私钥的.snk实际上就是一个PRIVATEKEYBLOB结构。该结构的定义见:

http://msdn.microsoft.com/en-us/library/aa387401(v=vs.85).aspx

该结构由一个BLOBHEADER头结构,一个RSAPUBKEY结构,以及一些RSA钥匙数据组成。其中BLOBHEADER.bType为PRIVATEKEYBLOB(0x7),指明该文件包含了私钥;而BLOBHEADER.aiKeyAlg固定为CALG_RSA_SIGN,指明该钥匙用来签名;RSAPUBKEY.bitlen指定了RSA密钥的长度,该长度将影响那一些钥匙数据的长度,这里我们把它固定为1024位。

下列C#代码示例如何将带私钥的签名文件读入到的结构中,并导出一个RSA钥匙:


[StructLayout(LayoutKind.Sequential,Pack=1)]

structBLOBHEADER

{

publicbytebType;//PRIVATEKEYBLOB=0x7

publicbytebVersion;//DigitalSignatureStandard=3;CUR_BLOB_VERSION=2

publicshortreserved;//

publicuintaiKeyAlg;//CALG_RSA_KEYX=0xa400;CALG_RSA_SIGN=0x2400

}


[StructLayout(LayoutKind.Sequential,Pack=1)]

structRSAPUBKEY

{

publicintmagic;//0x32415352("RSA2")forpublic/privatekey;("RSA1")forpublickeyonly

publicintbitlen;//Numberofbitsinthemodulus.Amultipleofeight.

publicintpubexp;//Thepublicexponent

}


[StructLayout(LayoutKind.Sequential,Pack=1)]

structPRIVATEKEYBLOB

{

publicBLOBHEADERblobheader;

publicRSAPUBKEYrsapubkey;

[MarshalAs(UnmanagedType.ByValArray,SizeConst=128)]

publicbyte[]modulus;//[rsapubkey.bitlen/8];asModulus

[MarshalAs(UnmanagedType.ByValArray,SizeConst=64)]

publicbyte[]prime1;//[rsapubkey.bitlen/16];asP

[MarshalAs(UnmanagedType.ByValArray,SizeConst=64)]

publicbyte[]prime2;//[rsapubkey.bitlen/16];asQ

[MarshalAs(UnmanagedType.ByValArray,SizeConst=64)]

publicbyte[]exponent1;//[rsapubkey.bitlen/16];asDmod(P-1)".

[MarshalAs(UnmanagedType.ByValArray,SizeConst=64)]

publicbyte[]exponent2;//[rsapubkey.bitlen/16];asDmod(Q-1)".

[MarshalAs(UnmanagedType.ByValArray,SizeConst=64)]

publicbyte[]coefficient;//[rsapubkey.bitlen/16];asInverseQ

[MarshalAs(UnmanagedType.ByValArray,SizeConst=128)]

publicbyte[]privateExponent;//[rsapubkey.bitlen/8];asD


publicRSAParametersGetRSAKey()

{

RSAParameterskey=newRSAParameters()

{

Modulus=this.modulus,

P=this.prime1,

Q=this.prime2,

DP=this.exponent1,

DQ=this.exponent2,

InverseQ=this.coefficient,

D=this.privateExponent,

Exponent=BitConverter.GetBytes(this.rsapubkey.pubexp),

};

Array[]arrays={key.Modulus,key.P,key.Q,key.DP,key.DQ,key.InverseQ,key.D,key.Exponent};

foreach(Arrayainarrays)

{

Array.Reverse(a);

}

returnkey;

}


publicstaticRSAParametersLoadFromSNK(stringfilename)

{

byte[]raw=File.ReadAllBytes(filename);

if(raw.Length!=Marshal.SizeOf(typeof(PRIVATEKEYBLOB)))

{

thrownewInvalidOperationException("notavalidsnkfilewithpublic/privatekey");

}

GCHandlegc=GCHandle.Alloc(raw,GCHandleType.Pinned);

PRIVATEKEYBLOBb=(PRIVATEKEYBLOB)Marshal.PtrToStructure(gc.AddrOfPinnedObject(),typeof(PRIVATEKEYBLOB));

gc.Free();

returnb.GetRSAKey();

}

}



只包含公钥的.snk文件可以通过sn.exe-pkey.snkpublic.snk来产生。这种只包含公钥的.snk文件格式则是PUBLICKEYBLOB的基础上,加上一些额外包装。PUBLICKEYBLOB结构的定义见:

http://msdn.microsoft.com/en-us/library/aa387459(v=vs.85).aspx

额外包装为4个字节的签名算法;4个字节的散列算法;以及4个字节的钥匙长度。这额外的12字节,加上PUBLICKEYBLOB的148字节,总好是(只包含公钥的).snk的总长度,160字节。

下列为代码表示:

[StructLayout(LayoutKind.Sequential,Pack=1)]

structPUBLICKEYBLOB

{

//.Netwrapper

publicintsignatureAlg;//CALG_RSA_SIGN=0x2400

publicinthashAlg;//CALG_SHA1=0x8004

publicintblobLength;//148


publicBLOBHEADERblobheader;

publicRSAPUBKEYrsapubkey;

[MarshalAs(UnmanagedType.ByValArray,SizeConst=128)]

publicbyte[]modulus;//[rsapubkey.bitlen/8];asModulus


publicRSAParametersGetRSAKey()

{

RSAParameterskey=newRSAParameters()

{

Modulus=this.modulus,

Exponent=BitConverter.GetBytes(this.rsapubkey.pubexp),

};

Array[]arrays={key.Modulus,key.Exponent};

foreach(Arrayainarrays)

{

Array.Reverse(a);

}

returnkey;

}

}


导出公钥还有其他的办法。一个办法是将.snk导入到当前系统的钥匙容器中,既sn.exe-ikey.snkmykeycontainer。然后再用RSACrytoServiceProvider.ExportParameters来导出公钥:

CspParameterscp=newCspParameters(1);

cp.KeyContainerName="mykeycontainer";

cp.KeyNumber=(int)KeyNumber.Signature;

RSACryptoServiceProviderrsa=newRSACryptoServiceProvider(cp);

RSAParameterspublicKey=rsa.ExportParameters(false);


不过这种方法对私钥无效。虽然sn.exe可以将钥匙导入到容器的KeyNumber.Signature上,但该钥匙被标志为不可导出。也就是说调用rsa.ExportParameters(true)将抛出异常。
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: