zoukankan      html  css  js  c++  java
  • Silverlight中非对称加密及数字签名RSA算法的实现

    RSA算法是第一个既能用于数据加密也能用于数字签名的算法。它易于理解和操作,也很流行。它的安全性是基于大整数素因子分解的困难性,而大整数因子分解问题是数学上的著名难题,至今没有有效的方法予以解决,因此可以确保RSA算法的安全性。

        到目前Silverlight4 Beta发布为止,Silverlight中仍然没有提供非对称加密及数字签名相关的算法。而.NET Framework中提供的RSA等算法,都是通过操作系统提供的相关API实现的,没法移植到Silverlight中使用。因此很难实现一个健壮点的Silverlight纯客户端的注册验证算法。这几天抽空写了个Silverlight下可用的RSA算法,使用非对称加密和数字签名使Silverlight纯客户端的注册验证算法健壮了不少。关于这个Silverlight下可用的RSA算法的具体实现,记录在下面,欢迎大家拍砖。在进行Silverlight开发时,还可以借助一些开发工具。ComponentOne Studio for Silverlight 拥有50多个基于Microsoft Silverlight的控件,能够满足开发最流行Web界面的需要。

        RSA算法实现主要分为三部分:包括公钥和私钥的产生,非对称加密和解密,数字签名和验证,下面将逐个介绍RSA算法的工作原理及我的实现方法。

     

     

        1,公钥和私钥的产生

        随意选择两个大素数p、q,p不等于q,计算n = p * q。
        随机选择一个整数e,满足e和( p – 1 ) * ( q – 1 )互质。(注:e很容易选择,如3, 17, 65537等都可以。.NET Framework中e默认选择的就是65537)
    利用Euclid算法计算解密密钥d,满足
          e * d ≡ 1 ( mod ( p - 1 ) * ( q - 1 ) )
        其中n和d也要互质。

        其中e和n就是公钥,d和n就是私钥。P、q销毁。

        在.NET Framework的RSA算法中,e对应RSAParameters.Exponent;d对应RSAParameters.D;n对应RSAParameters.ModulusExponent。.NET Framework中的RSA算法默认使用1024位长的密钥。公钥和私钥是利用.NET Framework的RSACryptoServiceProvider生成公钥xml文件和私钥xml文件来实现的。生成公钥和私钥xml文件的程序本身不是Silverlight程序。

    RSACryptoServiceProvider rsa = new RSACryptoServiceProvider();
    
        //生成公钥XML字符串
        string publicKeyXmlString = rsa.ToXmlString(false);
    
        //生成私钥XML字符串
        string privateKeyXmlString = rsa.ToXmlString(true);

    公钥和私钥将从生成的公钥xml文件和私钥xml文件中导入。

    public class RSAPublicKey
        {
            public byte[] Modulus;
            public byte[] Exponent;
    
            public static RSAPublicKey FromXmlString(string xmlString)
            {
                if (string.IsNullOrEmpty(xmlString))
                {
                    return null;
                }
    
                try
                {
                    using (XmlReader reader = XmlReader.Create(new StringReader(xmlString)))
                    {
                        if (!reader.ReadToFollowing("RSAKeyValue"))
                        {
                            return null;
                        }
    
                        if (reader.LocalName != "Modulus" && !reader.ReadToFollowing("Modulus"))
                        {
                            return null;
                        }
                        string modulus = reader.ReadElementContentAsString();
    
                        if (reader.LocalName != "Exponent" && !reader.ReadToFollowing("Exponent"))
                        {
                            return null;
                        }
                        string exponent = reader.ReadElementContentAsString();
    
                        RSAPublicKey publicKey = new RSAPublicKey();
                        publicKey.Modulus = Convert.FromBase64String(modulus);
                        publicKey.Exponent = Convert.FromBase64String(exponent);
    
                        return publicKey;
                    }
                }
                catch
                {
                    return null;
                }
            }        
        }
    public class RSAPrivateKey
        {
            public byte[] Modulus;
            public byte[] D;
    
            public static RSAPrivateKey FromXmlString(string xmlString)
            {
                if (string.IsNullOrEmpty(xmlString))
                {
                    return null;
                }
    
                try
                {
                    using (XmlReader reader = XmlReader.Create(new StringReader(xmlString)))
                    {
                        if (!reader.ReadToFollowing("RSAKeyValue"))
                        {
                            return null;
                        }
    
                        if (reader.LocalName != "Modulus" && !reader.ReadToFollowing("Modulus"))
                        {
                            return null;
                        }
                        string modulus = reader.ReadElementContentAsString();
    
                        if (reader.LocalName != "D" && !reader.ReadToFollowing("D"))
                        {
                            return null;
                        }
                        string d = reader.ReadElementContentAsString();
    
                        RSAPrivateKey privateKey = new RSAPrivateKey();
                        privateKey.Modulus = Convert.FromBase64String(modulus);
                        privateKey.D = Convert.FromBase64String(d);
    
                        return privateKey;
                    }
                }
                catch
                {
                    return null;
                }
            }        
        }

    2,非对称加密和解密
        私钥加密m(二进制表示)时,首先把m分成长s的数据块 m1, m2 ... mi,其中 2^s <= n, s 尽可能的大。执行如下计算:
            ci = mi ^ d (mod n)
        公钥解密c(二进制表示)时,也需要将c分成长s的数据块c1, c2 ... ci,执行如下计算:
            mi = ci ^ e (mod n)

        在某些情况下,也会使用公钥加密->私钥解密。原理和私钥加密->公钥解密一样。下面是私钥计算和公钥计算的算法。其中利用到了Chew Keong TAN的BigInteger类。.NET Framework 4中提供的BigInteger.ModPow方法好像有问题。 

    private static byte[] Compute(byte[] data, RSAPublicKey publicKey, int blockSize)
            {
                //
                // 公钥加密/解密公式为:ci = mi^e ( mod n )            
                // 
                // 先将 m(二进制表示)分成数据块 m1, m2, ..., mi ,然后进行运算。
                //
                BigInteger e = new BigInteger(publicKey.Exponent);
                BigInteger n = new BigInteger(publicKey.Modulus);
    
                int blockOffset = 0;
                using (MemoryStream stream = new MemoryStream())
                {
                    while (blockOffset < data.Length)
                    {
                        int blockLen = Math.Min(blockSize, data.Length - blockOffset);
                        byte[] blockData = new byte[blockLen];
                        Buffer.BlockCopy(data, blockOffset, blockData, 0, blockLen);
    
                        BigInteger mi = new BigInteger(blockData);
                        BigInteger ci = mi.modPow(e, n);//ci = mi^e ( mod n )
    
                        byte[] block = ci.getBytes();
                        stream.Write(block, 0, block.Length);
                        blockOffset += blockLen;
                    }
    
                    return stream.ToArray();
                }
            }
    
            private static byte[] Compute(byte[] data, RSAPrivateKey privateKey, int blockSize)
            {
                //
                // 私钥加密/解密公式为:mi = ci^d ( mod n )
                // 
                // 先将 c(二进制表示)分成数据块 c1, c2, ..., ci ,然后进行运算。            
                //
                BigInteger d = new BigInteger(privateKey.D);
                BigInteger n = new BigInteger(privateKey.Modulus);
    
                int blockOffset = 0;
    
                using (MemoryStream stream = new MemoryStream())
                {
                    while (blockOffset < data.Length)
                    {
                        int blockLen = Math.Min(blockSize, data.Length - blockOffset);
                        byte[] blockData = new byte[blockLen];
                        Buffer.BlockCopy(data, blockOffset, blockData, 0, blockLen);
    
                        BigInteger ci = new BigInteger(blockData);
                        BigInteger mi = ci.modPow(d, n);//mi = ci^d ( mod n )
    
                        byte[] block = mi.getBytes();
                        stream.Write(block, 0, block.Length);
                        blockOffset += blockLen;
                    }
    
                    return stream.ToArray();
                }
            }

    下面是私钥加密->公钥解密的实现。

    public static byte[] Encrypt(byte[] data, RSAPublicKey publicKey)
            {
                if (data == null)
                {
                    throw new ArgumentNullException("data");
                }
    
                if (publicKey == null)
                {
                    throw new ArgumentNullException("publicKey");
                }
    
                int blockSize = publicKey.Modulus.Length - 1;
                return Compute(data, publicKey, blockSize);
            }
    
            public static byte[] Decrypt(byte[] data, RSAPrivateKey privateKey)
            {
                if (data == null)
                {
                    throw new ArgumentNullException("data");
                }
    
                if (privateKey == null)
                {
                    throw new ArgumentNullException("privateKey");
                }
    
                int blockSize = privateKey.Modulus.Length;
                return Compute(data, privateKey, blockSize);
            }

    下面是公钥加密->私钥解密的实现。 

    public static byte[] Encrypt(byte[] data, RSAPrivateKey privateKey)
            {
                if (data == null)
                {
                    throw new ArgumentNullException("data");
                }
    
                if (privateKey == null)
                {
                    throw new ArgumentNullException("privateKey");
                }
    
                int blockSize = privateKey.Modulus.Length - 1;
                return Compute(data, privateKey, blockSize);
            }
    
            public static byte[] Decrypt(byte[] data, RSAPublicKey publicKey)
            {
                if (data == null)
                {
                    throw new ArgumentNullException("data");
                }
    
                if (publicKey == null)
                {
                    throw new ArgumentNullException("publicKey");
                }
    
                int blockSize = publicKey.Modulus.Length;
                return Compute(data, publicKey, blockSize);
            }
     

    3,数字签名和验证
        私钥签名数据m时,先对m进行hash计算,得到计算结果h。然后将h使用私钥加密,得到加密后的密文s即为签名。
        公钥验证签名s时,先将m进行hash计算,得到计算结果h。然后使用公钥解密s得到结果h’。如果h==h’即验证成功,否则验证失败。

        在某些情况下,也会使用公钥签名->私钥验证。原理和私钥签名->公钥验证一样。

        下面是私钥签名->公钥验证的实现。 

     public static byte[] Sign(byte[] data, RSAPublicKey publicKey, HashAlgorithm hash)
            {
                if (data == null)
                {
                    throw new ArgumentNullException("data");
                }
    
                if (publicKey == null)
                {
                    throw new ArgumentNullException("publicKey");
                }
    
                if (hash == null)
                {
                    throw new ArgumentNullException("hash");
                }
    
                byte[] hashData = hash.ComputeHash(data);
                byte[] signature = Encrypt(hashData, publicKey);
                return signature;
            }
    
            public static bool Verify(byte[] data, RSAPrivateKey privateKey, HashAlgorithm hash, byte[] signature)
            {
                if (data == null)
                {
                    throw new ArgumentNullException("data");
                }
    
                if (privateKey == null)
                {
                    throw new ArgumentNullException("privateKey");
                }
    
                if (hash == null)
                {
                    throw new ArgumentNullException("hash");
                }
    
                if (signature == null)
                {
                    throw new ArgumentNullException("signature");
                }
    
                byte[] hashData = hash.ComputeHash(data);
                byte[] signatureHashData = Decrypt(signature, privateKey);
    
                if (signatureHashData != null && signatureHashData.Length == hashData.Length)
                {
                    for (int i = 0; i < signatureHashData.Length; i++)
                    {
                        if (signatureHashData[i] != hashData[i])
                        {
                            return false;
                        }
                    }
                    return true;
                }
    
                return false;
            }

    下面是公钥签名->私钥验证的实现。

    public static byte[] Sign(byte[] data, RSAPrivateKey privateKey, HashAlgorithm hash)
            {
                if (data == null)
                {
                    throw new ArgumentNullException("data");
                }
    
                if (privateKey == null)
                {
                    throw new ArgumentNullException("privateKey");
                }
    
                if (hash == null)
                {
                    throw new ArgumentNullException("hash");
                }
    
                byte[] hashData = hash.ComputeHash(data);
                byte[] signature = Encrypt(hashData, privateKey);
                return signature;
            }
    
            public static bool Verify(byte[] data, RSAPublicKey publicKey, HashAlgorithm hash, byte[] signature)
            {
                if (data == null)
                {
                    throw new ArgumentNullException("data");
                }
    
                if (publicKey == null)
                {
                    throw new ArgumentNullException("publicKey");
                }
    
                if (hash == null)
                {
                    throw new ArgumentNullException("hash");
                }
    
                if (signature == null)
                {
                    throw new ArgumentNullException("signature");
                }
    
                byte[] hashData = hash.ComputeHash(data);
    
                byte[] signatureHashData = Decrypt(signature, publicKey);
    
                if (signatureHashData != null && signatureHashData.Length == hashData.Length)
                {
                    for (int i = 0; i < signatureHashData.Length; i++)
                    {
                        if (signatureHashData[i] != hashData[i])
                        {
                            return false;
                        }
                    }
                    return true;
                }
    
                return false;
            }




    本文是由葡萄城技术开发团队发布,转载请注明出处:葡萄城官网


  • 相关阅读:
    file is universal (3 slices) but does not contain a(n) armv7s slice error for static libraries on iOS
    WebImageButton does not change images after being enabled in Javascript
    ajax OPTION
    编程遍历页面上所有TextBox控件并给它赋值为string.Empty?
    获取海洋天气预报
    C#线程系列教程(1):BeginInvoke和EndInvoke方法
    js控制只能输入数字和小数点
    Response.AddHeader(,)
    ManualResetEvent的理解
    Convert.ToInt32、int.Parse(Int32.Parse)、int.TryParse、(int) 区别
  • 原文地址:https://www.cnblogs.com/powertoolsteam/p/1764616.html
Copyright © 2011-2022 走看看