如何在C#中使用ANSI X9.31 RSA创建数字签名?

时间:2014-04-03 17:11:21

标签: c# rsa digital-signature ansi

我试图弄清楚如何使用ANSI X9.31版本的RSA数字签名算法创建数字签名。这与PKCS#1实现不同,主要是由于填充方案。

我以为我可以使用RSACryptoServiceProvider来SignHash;但是,这使用PKCS#1或OAEP。我无法弄清楚如何使用ANS X9.31填充。帮助

编辑:如果我使用BouncyCastle,我是否需要使用" RSABlindingEngine"或者我可以只使用" RSAEngine"?见下面的例子:

using System.Security.Cryptography;

public byte[] SignSha256AnsiX931Rsa(
    byte[] data, 
    System.Security.Cryptography.X509Certificates.X509Certificate2 certificate)
{
    // Do a SHA-256 hash of the data, using CNG
    SHA256Cng sha = new SHA256Cng();
    byte[] digest = sha.ComputeHash(data);

    // Pad the hash using ANSI X9.31 padding scheme
    byte[] paddedHash = AnsiX931PadData(digest);

    // Encrypt the data using RSA encryption
    return RsaEncryptData(paddedHash, certificate);
}

/// <summary>
/// Pads the passed in SHA-256 hash (32-bytes) using the padding scheme
/// specified for ANS X9.31 RSA digital signatures.
/// </summary>
/// <param name="sha256HashedData">The 32-byte hash</param>
public byte[] AnsiX931PadData(byte[] sha256HashedData)
{
    // N = Modulus Length (in bytes) = 256, where modulus is 2048-bits (length of private key)
    // M = Message
    // k = Hash Output = Digest = Hash(M)
    // z = Len(k) (in bytes) = 32, where using Hash = SHA-256
    // p = number of fixed padding bytes, in this case 4 (6b, ba, 34, and cc)
    // 
    // Padded Hash = 6b [bb]*x ba [k] 34 cc, where x = 220 (N - z - 4 = 256 - 32 - 4 = 220)
    // [6b bb bb ... bb ba] [Hash(M)] [hashID=34] [cc]
    // 
    // See: 
    // http://www.drdobbs.com/rsa-digital-signatures/184404605?pgno=6
    // http://books.google.com/books?id=7gBn_gEBQesC&lpg=PA388&dq=x9.31%20padding&pg=PA386#v=onepage&q=x9.31%20padding&f=false

    const int MOD_LENGTH = 256; // 2048-bit private key length
    const int PAD_LENGTH = 4; // number of fixed padding bytes, in this case 4 (6b, ba, 34, and cc)
    const int HASH_LENGTH = 32; // where using Hash = SHA-256

    if (sha256HashedData.Length != HASH_LENGTH) throw new ArgumentException("The hash provided is the incorrect length");

    List<byte> paddedHash = new List<byte>(MOD_LENGTH);

    paddedHash.Add(0x6b);

    int bbRepeats = MOD_LENGTH - HASH_LENGTH - PAD_LENGTH;

    for (int ii = 0; ii < bbRepeats; ii++)
    {
        paddedHash.Add(0xbb);
    }

    paddedHash.Add(0xba);
    paddedHash.AddRange(sha256HashedData);
    paddedHash.Add(0x34); // SHA-256 Hash ID; 0x33 = SHA-1
    paddedHash.Add(0xcc);

    return paddedHash.ToArray();
}

/// <summary>
/// Encrypts the data using an X509 certificate and the RSA encryption scheme 
/// </summary>
public byte[] RsaEncryptData(
    byte[] data, 
    System.Security.Cryptography.X509Certificates.X509Certificate2 certificate)
{
    Org.BouncyCastle.Crypto.Engines.RsaBlindedEngine engine = new Org.BouncyCastle.Crypto.Engines.RsaBlindedEngine();
    engine.Init(true, getBouncyPrivateKey(certificate));
    return engine.ProcessBlock(data, 0, data.Length);
}

/// <summary>
/// Gets the private key from an X509Certificate
/// </summary>
private Org.BouncyCastle.Crypto.AsymmetricKeyParameter getBouncyPrivateKey(
    System.Security.Cryptography.X509Certificates.X509Certificate2 certificate)
{
    // Need to transform the key from X509 to RSACrypto so we can do SHA-256 since
    // X509 only supports SHA-1:
    System.Security.Cryptography.RSACryptoServiceProvider key = new System.Security.Cryptography.RSACryptoServiceProvider(2048);

    // Round-trip the key to XML and back, there might be a better way but this works
    key.FromXmlString(certificate.PrivateKey.ToXmlString(true));

    return Org.BouncyCastle.Security.DotNetUtilities.GetKeyPair(key).Private;
}

1 个答案:

答案 0 :(得分:0)

棘手。您可以使用或从Bouncy Castle for C#中提取RSA致盲引擎,然后自己执行填充/取消填充。它在this age old Dr Dobbs article中描述。