嗨,我正在使用代码,您能否让我知道为什么签名验证无效?
Java签署者将 BouncyCastleProvider 与 SHA1withRSA 结合 使用,这是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); }
这里有几个问题。
String signature = "770bb ... 1c7ca"; ... byte[] expectedSig = System.Convert.FromBase64String(signature);
您正在对签名进行Base64解码,但不是Base64编码,而是十六进制编码。
第二个问题是DecodeX509PublicKey方法中的(这是我的错误,因为我在另一个答案中提供了此代码。)特定的问题行是
DecodeX509PublicKey
Array.Reverse(modulus); //convert to big-endian
和
Array.Reverse(exponent); //convert to big-endian
我反复读到ASN.1和.Net API的密钥使用相反的字节序,因此,我给人的印象是,需要对此字节序进行反转以解决此问题。(确实,我确实应该像签名验证一样进行测试,而不是仅仅查看内存中的键值>。<)无论如何,删除这些行,解决编码问题,然后您的签名将正确验证(已成功测试)使用您的示例数据以及我自己的示例数据)。
另外,sign方法中的这一行不太正确:
sign
Security.addProvider(new org.bouncycastle.jce.provider.BouncyCastleProvider());
到代码中的那一点时,该signer对象已经使用默认提供程序实例化了。同样,您不需要每次想要对某些数据进行签名时都添加Bouncy Castle提供程序,它实际上只会在您第一次进行此调用时第一次添加该提供程序,并且在所有后续调用中都将忽略它。
signer
此外,该signer对象已被解密static,但是您对它的使用不是线程安全的。
static
您更可能想做的是在静态块中添加提供程序,然后使用Bouncy Castle提供程序显式实例化签名者。如果您未明确指定Bouncy Castle作为提供程序(或使用来将Bouncy Castle添加为最高优先级insertProviderAt),则将使用默认提供程序。
insertProviderAt
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()); ...