zoukankan      html  css  js  c++  java
  • C#中.snk文件的作用及从.snk文件导出密钥 (网上整理)

    SNK,作为程序后缀的时候,是.net中的强密匙加密文件!

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

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

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

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

    sn -k Testkey.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 项目。
    // 对于本地项目,项目输出目录定义为
    // <Project Directory>\obj\<Configuration>。例如,如果 KeyFile 位于该
    // 项目目录中,应将 AssemblyKeyFile
    // 属性指定为 [assembly: AssemblyKeyFile("..\\..\\mykey.snk")]
    // 对于 Web 项目,项目输出目录定义为
    // %HOMEPATH%\VSWebCache\<Machine Name>\<Project Directory>\obj\<Configuration>。

    从.snk文件导出密钥

     

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

      .snk文件(Strong Name Key)也可以叫签名文件,它一般用来给DotNet程序集进行强命名。它的好处是非常容易产生,也非常容易管理。我们可以在Visual Studio的项目设置中新建一个签名文件,也可以用sn.exe -k key.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)]
    struct BLOBHEADER
    {
        public byte bType;      //PRIVATEKEYBLOB = 0x7
        public byte bVersion;   //Digital Signature Standard=3; CUR_BLOB_VERSION=2
        public short reserved;  //
        public uint aiKeyAlg;   //CALG_RSA_KEYX=0xa400; CALG_RSA_SIGN=0x2400
    } 
     
    [StructLayout( LayoutKind.Sequential, Pack=1)]
    struct RSAPUBKEY 
    {
        public int magic;       //0x32415352 ("RSA2") for public/private key; ("RSA1") for public key only
        public int bitlen;      //Number of bits in the modulus. A multiple of eight.
        public int pubexp;      //The public exponent
    }
     
    [StructLayout( LayoutKind.Sequential, Pack=1)]
    struct PRIVATEKEYBLOB 
    {
        public BLOBHEADER blobheader;
        public RSAPUBKEY rsapubkey;
        [MarshalAs( UnmanagedType.ByValArray, SizeConst=128)]
        public byte[] modulus;          //[rsapubkey.bitlen/8];  as Modulus
        [MarshalAs( UnmanagedType.ByValArray, SizeConst=64)]
        public byte[] prime1;           //[rsapubkey.bitlen/16]; as P
        [MarshalAs( UnmanagedType.ByValArray, SizeConst=64)]
        public byte[] prime2;           //[rsapubkey.bitlen/16]; as Q
        [MarshalAs( UnmanagedType.ByValArray, SizeConst=64)]
        public byte[] exponent1;        //[rsapubkey.bitlen/16]; as D mod (P - 1)".
        [MarshalAs( UnmanagedType.ByValArray, SizeConst=64)]
        public byte[] exponent2;        //[rsapubkey.bitlen/16]; as D mod (Q - 1)".
        [MarshalAs( UnmanagedType.ByValArray, SizeConst=64)]
        public byte[] coefficient;      //[rsapubkey.bitlen/16]; as InverseQ
        [MarshalAs( UnmanagedType.ByValArray, SizeConst=128)]
        public byte[] privateExponent;  //[rsapubkey.bitlen/8];  as D
     
        public RSAParameters GetRSAKey()
        {
            RSAParameters key = new RSAParameters()
            {
                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(Array a in arrays)
            {
                Array.Reverse(a);
            }
            return key;
        }
     
        public static RSAParameters LoadFromSNK(string filename)
        {
            byte[] raw = File.ReadAllBytes(filename);
            if (raw.Length != Marshal.SizeOf(typeof(PRIVATEKEYBLOB)))
            {
                throw new InvalidOperationException("not a valid snk file with public/private key");
            }
            GCHandle gc = GCHandle.Alloc(raw, GCHandleType.Pinned);
            PRIVATEKEYBLOB b = (PRIVATEKEYBLOB)Marshal.PtrToStructure(gc.AddrOfPinnedObject(), typeof(PRIVATEKEYBLOB));
            gc.Free();
            return b.GetRSAKey();
        }
    }
     

      只包含公钥的.snk文件可以通过sn.exe -p key.snk public.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)]
    struct PUBLICKEYBLOB
    {
        //.Net wrapper
        public int signatureAlg;             //CALG_RSA_SIGN=0x2400
        public int hashAlg;                  //CALG_SHA1=0x8004
        public int blobLength;               //148
     
        public BLOBHEADER blobheader;
        public RSAPUBKEY rsapubkey;
        [MarshalAs(UnmanagedType.ByValArray, SizeConst = 128)]
        public byte[] modulus;               //[rsapubkey.bitlen/8];  as Modulus
     
        public RSAParameters GetRSAKey()
        {
            RSAParameters key = new RSAParameters()
            {
                Modulus = this.modulus,
                Exponent = BitConverter.GetBytes(this.rsapubkey.pubexp),
            };
            Array[] arrays = { key.Modulus, key.Exponent };
            foreach (Array a in arrays)
            {
                Array.Reverse(a);
            }
            return key;
        }
    }

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

    CspParameters cp = new CspParameters(1);
    cp.KeyContainerName = "mykeycontainer";
    cp.KeyNumber = (int)KeyNumber.Signature;
    RSACryptoServiceProvider rsa = new RSACryptoServiceProvider(cp);
    RSAParameters publicKey = rsa.ExportParameters(false);

      不过这种方法对私钥无效。虽然sn.exe可以将钥匙导入到容器的KeyNumber.Signature上,但该钥匙被标志为不可导出。也就是说调用rsa.ExportParameters(true)将抛出异常。

  • 相关阅读:
    KMP算法
    Python 正则表达式
    Python 装饰器
    C/C++ 之输入输出
    PAT(Basic Level)--个位数统计
    Java 接口与抽象类
    Java集合-01概述
    数据结构--红黑树
    数据结构--(AVL)平衡二叉树
    数据结构--二叉搜索树
  • 原文地址:https://www.cnblogs.com/gyxdbk/p/3071271.html
Copyright © 2011-2022 走看看