2013-08-20 41 views
0

嗨我使用的代碼從this link,你能讓我知道爲什麼簽名驗證不起作用嗎?RSA和公鑰與dotnet互操作

Java的簽名是使用BouncyCastleProviderSHA1withRSA,這裏是DOTNET驗證碼....

using System; 
using System.IO; 
using System.Collections.Generic; 
using System.Linq; 
using System.Text; 
using System.Security.Cryptography; 
using System.Security.Cryptography.X509Certificates; 
using Org.BouncyCastle.Asn1; 
using Org.BouncyCastle.Crypto; 
using Org.BouncyCastle.Crypto.Parameters; 
using Org.BouncyCastle.OpenSsl; 
using Org.BouncyCastle.Security; 
using Org.BouncyCastle.Utilities.Encoders; 

namespace ConsoleApplication1 
{ 
    class Program 
    { 
     static void Main(string[] args) 
     { 
      string pubkey = @"MFwwDQYJKoZIhvcNAQEBBQADSwAwSAJBAMf54mcK3EYJn9tT9BhRoTX+8AkqojIyeSfog9ncYEye0VXyBULGg2lAQsDRt8lZsvPioORZW7eB6IKawshoWUsCAwEAAQ=="; 
      String signature = "770bb2610bf6b2602ce2b3ad8489054f4ed59c9b0c9299327f76ecbc60a8bb9a725cfae901fc189d4bafcf73a2f4aed8dffe9842f7b6196ddfcd040c7271c7ca"; 
      String signData = "C2:AE:D6:2B:DF:A4"; 
      byte[] expectedSig = System.Convert.FromBase64String(signature); 
      byte[] baKey = System.Convert.FromBase64String(pubkey); 

      byte[] data = Encoding.UTF8.GetBytes(signData); 

      //Console.WriteLine(p.VerifyData(data, new SHA1CryptoServiceProvider(), expectedSig)); 

      /* Init alg */ 
      ISigner signer = SignerUtilities.GetSigner("SHA1withRSA"); 
      /* Populate key */ 
      signer.Init(false, DecodeX509PublicKey2(baKey)); 
      /* Calculate the signature and see if it matches */ 
      signer.BlockUpdate(data, 0, data.Length); 

      Console.WriteLine(signer.VerifySignature(expectedSig)); 
      Console.In.ReadLine(); 
     } 

     public static RsaKeyParameters DecodeX509PublicKey2(byte[] x509key) 
     { 
      byte[] SeqOID = { 0x2A, 0x86, 0x48, 0x86, 0xF7, 0x0D, 0x01, 0x01, 0x01 }; 

      MemoryStream ms = new MemoryStream(x509key); 
      BinaryReader reader = new BinaryReader(ms); 

      if (reader.ReadByte() == 0x30) 
       ReadASNLength(reader); //skip the size 
      else 
       return null; 

      int identifierSize = 0; //total length of Object Identifier section 
      if (reader.ReadByte() == 0x30) 
       identifierSize = ReadASNLength(reader); 
      else 
       return null; 

      if (reader.ReadByte() == 0x06) //is the next element an object identifier? 
      { 
       int oidLength = ReadASNLength(reader); 
       byte[] oidBytes = new byte[oidLength]; 
       reader.Read(oidBytes, 0, oidBytes.Length); 
       if (oidBytes.SequenceEqual(SeqOID) == false) //is the object identifier rsaEncryption PKCS#1? 
        return null; 

       int remainingBytes = identifierSize - 2 - oidBytes.Length; 
       reader.ReadBytes(remainingBytes); 
      } 

      if (reader.ReadByte() == 0x03) //is the next element a bit string? 
      { 
       ReadASNLength(reader); //skip the size 
       reader.ReadByte(); //skip unused bits indicator 
       if (reader.ReadByte() == 0x30) 
       { 
        ReadASNLength(reader); //skip the size 
        if (reader.ReadByte() == 0x02) //is it an integer? 
        { 
         int modulusSize = ReadASNLength(reader); 
         byte[] modulus = new byte[modulusSize]; 
         reader.Read(modulus, 0, modulus.Length); 
         if (modulus[0] == 0x00) //strip off the first byte if it's 0 
         { 
          byte[] tempModulus = new byte[modulus.Length - 1]; 
          Array.Copy(modulus, 1, tempModulus, 0, modulus.Length - 1); 
          modulus = tempModulus; 
         } 
         Array.Reverse(modulus); //convert to big-endian 

         if (reader.ReadByte() == 0x02) //is it an integer? 
         { 
          int exponentSize = ReadASNLength(reader); 
          byte[] exponent = new byte[exponentSize]; 
          reader.Read(exponent, 0, exponent.Length); 
          Array.Reverse(exponent); //convert to big-endian 

          //RSAParameters RSAKeyInfo = new RSAParameters(); 
          //RSAKeyInfo.Modulus = modulus; 
          //RSAKeyInfo.Exponent = exponent; 

          return MakeKey(BitConverter.ToString(modulus).Replace("-", string.Empty), BitConverter.ToString(exponent).Replace("-", string.Empty), false); 
         } 
        } 
       } 
      } 
      return null; 
     } 

     public static RsaKeyParameters MakeKey(String modulusHexString, String exponentHexString, bool isPrivateKey) 
     { 
      var modulus = new Org.BouncyCastle.Math.BigInteger(modulusHexString, 16); 
      var exponent = new Org.BouncyCastle.Math.BigInteger(exponentHexString, 16); 

      return new RsaKeyParameters(isPrivateKey, modulus, exponent); 
     } 

     public static RSACryptoServiceProvider DecodeX509PublicKey(byte[] x509key) 
     { 
      byte[] SeqOID = { 0x2A, 0x86, 0x48, 0x86, 0xF7, 0x0D, 0x01, 0x01, 0x01 }; 

      MemoryStream ms = new MemoryStream(x509key); 
      BinaryReader reader = new BinaryReader(ms); 

      if (reader.ReadByte() == 0x30) 
       ReadASNLength(reader); //skip the size 
      else 
       return null; 

      int identifierSize = 0; //total length of Object Identifier section 
      if (reader.ReadByte() == 0x30) 
       identifierSize = ReadASNLength(reader); 
      else 
       return null; 

      if (reader.ReadByte() == 0x06) //is the next element an object identifier? 
      { 
       int oidLength = ReadASNLength(reader); 
       byte[] oidBytes = new byte[oidLength]; 
       reader.Read(oidBytes, 0, oidBytes.Length); 
       if (oidBytes.SequenceEqual(SeqOID) == false) //is the object identifier rsaEncryption PKCS#1? 
        return null; 

       int remainingBytes = identifierSize - 2 - oidBytes.Length; 
       reader.ReadBytes(remainingBytes); 
      } 

      if (reader.ReadByte() == 0x03) //is the next element a bit string? 
      { 
       ReadASNLength(reader); //skip the size 
       reader.ReadByte(); //skip unused bits indicator 
       if (reader.ReadByte() == 0x30) 
       { 
        ReadASNLength(reader); //skip the size 
        if (reader.ReadByte() == 0x02) //is it an integer? 
        { 
         int modulusSize = ReadASNLength(reader); 
         byte[] modulus = new byte[modulusSize]; 
         reader.Read(modulus, 0, modulus.Length); 
         if (modulus[0] == 0x00) //strip off the first byte if it's 0 
         { 
          byte[] tempModulus = new byte[modulus.Length - 1]; 
          Array.Copy(modulus, 1, tempModulus, 0, modulus.Length - 1); 
          modulus = tempModulus; 
         } 
         Array.Reverse(modulus); //convert to big-endian 

         if (reader.ReadByte() == 0x02) //is it an integer? 
         { 
          int exponentSize = ReadASNLength(reader); 
          byte[] exponent = new byte[exponentSize]; 
          reader.Read(exponent, 0, exponent.Length); 
          Array.Reverse(exponent); //convert to big-endian 

          RSACryptoServiceProvider RSA = new RSACryptoServiceProvider(); 
          RSAParameters RSAKeyInfo = new RSAParameters(); 
          RSAKeyInfo.Modulus = modulus; 
          RSAKeyInfo.Exponent = exponent; 
          RSA.ImportParameters(RSAKeyInfo); 
          return RSA; 
         } 
        } 
       } 
      } 
      return null; 
     } 

     public static int ReadASNLength(BinaryReader reader) 
     { 
      //Note: this method only reads lengths up to 4 bytes long as 
      //this is satisfactory for the majority of situations. 
      int length = reader.ReadByte(); 
      if ((length & 0x00000080) == 0x00000080) //is the length greater than 1 byte 
      { 
       int count = length & 0x0000000f; 
       byte[] lengthBytes = new byte[4]; 
       reader.Read(lengthBytes, 4 - count, count); 
       Array.Reverse(lengthBytes); // 
       length = BitConverter.ToInt32(lengthBytes, 0); 
      } 
      return length; 
     } 
    } 
} 
用於簽名的簽名數據

Java代碼:

private static final java.security.Signature signer; 
static final String transformation = "RSA/ECB/PKCS1Padding"; 
static { 
    try { 
     signer = java.security.Signature.getInstance("SHA1withRSA"); 
    } catch (NoSuchAlgorithmException e) { 
     e.printStackTrace(); 
    } 
} 

static String sign(String clearText) { 
    String signed = null; 
    try { 
     Security.addProvider(new org.bouncycastle.jce.provider.BouncyCastleProvider()); 
     byte[] data = clearText.getBytes("UTF-8"); 
     signer.initSign(getPrivateKey()); 
     signer.update(data); 
     byte[] digitalSignature = signer.sign(); 
     //--toHex 
     signed = org.apache.commons.codec.binary.Hex.encodeHexString(digitalSignature); 
    } catch (Exception e) { 
     e.printStackTrace(); 
    } 
    return signed; 
} 

KeyPair generateKeyPair() { 
    KeyPair kp = null; 
    // Generate a key-pair 
    KeyPairGenerator kpg; 
    SecureRandom secureRandom; 
    try { 
     kpg = KeyPairGenerator.getInstance("RSA"); 
     secureRandom = SecureRandom.getInstance("SHA1PRNG", "SUN"); 
     secureRandom.setSeed(secureRandomSeed); 
     kpg.initialize(512, secureRandom); 
     kp = kpg.generateKeyPair(); 
    } catch (Exception e) { 
     e.printStackTrace(); 
    } 
    return kp; 
} 

這裏是代碼在C#中籤名和驗證:

static void test3() 
{ 
    AsymmetricCipherKeyPair keys = generateNewKeys(); 
    /* Init alg */ 
    ISigner sig = SignerUtilities.GetSigner("SHA1withRSA"); 

    /* Populate key */ 
    sig.Init(true, keys.Private); 

    /* Get the bytes to be signed from the string */ 
    var bytes = Encoding.UTF8.GetBytes(signData); 

    /* Calc the signature */ 
    sig.BlockUpdate(bytes, 0, bytes.Length); 
    byte[] signature = sig.GenerateSignature(); 

    /* Base 64 encode the sig so its 8-bit clean */ 
    var signedString = Convert.ToBase64String(signature); 
    Console.WriteLine(signedString); 

    string expectedSignature = signedString; 
    /* Init alg */ 
    ISigner signer = SignerUtilities.GetSigner("SHA1withRSA"); 

    /* Populate key */ 
    signer.Init(false, keys.Public); 

    /* Get the signature into bytes */ 
    var expectedSig = Convert.FromBase64String(expectedSignature); 

    /* Get the bytes to be signed from the string */ 
    var msgBytes = Encoding.UTF8.GetBytes(signData); 

    /* Calculate the signature and see if it matches */ 
    signer.BlockUpdate(msgBytes, 0, msgBytes.Length); 
    /*Verify*/ 
    bool result= signer.VerifySignature(expectedSig); 
    Console.WriteLine(result); 
} 
+0

你能解釋一下「簽名驗證」是什麼嗎? –

+0

簽名由Java使用與提供者BouncyCastleProvider相同的算法SHA1withRSA生成。驗證正在使用'signData'來驗證預期的簽名。 – gpa

+0

您可以發佈用於生成簽名的Java代碼以及任何相關的測試數據嗎? (所以我們有一些東西要測試。) – Syon

回答

3

這裏有幾個問題。

String signature = "770bb ... 1c7ca"; 
... 
byte[] expectedSig = System.Convert.FromBase64String(signature); 

你是Base64解碼簽名,但它不是Base64編碼,它是十六進制編碼。

第二個問題是在DecodeX509PublicKey方法(這誠然是我的錯,因爲我在another answer提供此代碼。)具體問題線條

Array.Reverse(modulus); //convert to big-endian 

Array.Reverse(exponent); //convert to big-endian 

我反覆讀ASN.1和.Net API對他們的密鑰使用相反的endieness,所以我的印象是需要顛倒endieness來解決這個問題。 (我真的應該像你的簽名驗證一樣進行測試,以確保它不會只看內存中的關鍵值)。無論如何,刪除這些行,修正編碼問題,並且簽名將被正確驗證(已成功測試使用您的示例數據以及我自己的)。

另外,該線路在sign方法是不完全正確:

Security.addProvider(new org.bouncycastle.jce.provider.BouncyCastleProvider()); 

通過你在代碼點時,signer對象已經使用默認提供實例化。另外,您不需要在每次要簽署某些數據時添加Bouncy Castle提供程序,它只會在第一次進行此調用時實際添加提供程序,並且會在所有後續調用中忽略它。

此外,signer對象被聲明爲static,但您的使用不是線程安全的。

更可能要做的事情是在靜態塊中添加提供程序,然後使用Bouncy Castle提供程序明確地實例化簽名者。如果您沒有明確指定Bouncy Castle作爲提供者(或者使用insertProviderAt將Bouncy Castle添加爲最高優先級),則將使用默認提供者。

static { 
    try { 
     Security.addProvider(new org.bouncycastle.jce.provider.BouncyCastleProvider()); 
    } catch (Exception e) { 
     e.printStackTrace(); 
    } 
} 

... 

String signed = null; 
try { 
    java.security.Signature signer = java.security.Signature.getInstance("SHA1withRSA", "BC"); 
    byte[] data = clearText.getBytes("UTF-8"); 
    signer.initSign(getPrivateKey()); 

...