ECDH与Bouncy Castle

时间:2016-09-22 21:02:00

标签: c# bouncycastle kdf

我有一个要求说明。

使用,计算共享密钥Z,静态统一模型,C(0e,2s,ECC CDH)密钥协商技术(在NIST特刊800-56Ar214中指定,除了将共享密钥归零的要求) :

  • 基于SHA-256的单步密钥导出函数(KDF),如中所述 NIST特刊800-56Ar2;以及

  • 椭圆曲线运算的P-256曲线

我已阅读并尝试实施我发现的here,但它不起作用。

此时我可以验证共享密钥是否正确,但我无法获得正确的密钥,也不能(不编辑Bouncy Castle源代码)计算如何将OtherInfo带入计算器。我搜查了一下......

代码非常简单

   private static Byte[] getSingleStepKDF_SHA256(  Byte[] OtherInfo, 
                                                    Byte[] PrivateKey, 
                                                    Byte[] PublicKey,
                                                    Int32  DesiredKeyBitLength
                                                 )
    {
      BigInteger                  bi              = null;
      X9ECParameters              curve           = null;
      ECDomainParameters          ecParam         = null;
      ECPrivateKeyParameters      privKey         = null;
      ECPublicKeyParameters       pubKey          = null;
      ECDHWithKdfBasicAgreement   agree           = null;
      ECPoint                     point           = null;
      ECDHKekGenerator            ecGen           = null;

      /***********************************************************************
       * 
       * I currently do not know how to include OtherInfo into the 
       * calculation.  I have tried actually modifying ECDHKekGenerator by
       * overloading CalculateAgreement to accept OtherInfo. This had no
       * affect on the resulting key.  I have tried using KdfParameters but
       * ECDHWithKdfBasicAgreement raises an exception when I do that.
       * 
       * The shared seceret is always correct.
       * 
       ************************************************************************/

      curve     = NistNamedCurves.GetByName( "P-256" );
      ecParam   = new ECDomainParameters( curve.Curve, curve.G, curve.N, curve.H, curve.GetSeed() );
      privKey   = new ECPrivateKeyParameters( new BigInteger( PrivateKey ), ecParam );            
      point     = ecParam.Curve.DecodePoint( PublicKey );   
      pubKey    = new ECPublicKeyParameters( point, ecParam );
      ecGen     = new ECDHKekGenerator( DigestUtilities.GetDigest( "SHA256" ) );
      agree     = new ECDHWithKdfBasicAgreement( NistObjectIdentifiers.IdAes256Cbc.ToString(), ecGen );

      agree.Init( privKey );

      //  The shared secret is calculated in this method as well as the key
      bi = agree.CalculateAgreement( pubKey );

      return bi.ToByteArrayUnsigned().Take( ( int )( DesiredKeyBitLength / 8 ) ).ToArray();
    }

我很难过,并且会对我做错的任何帮助表示感谢。感谢

1 个答案:

答案 0 :(得分:2)

解决方案是我编写单步KDF代码而不是使用Bouncy Castle而不是生成共享密钥。希望这有助于其他人努力实现这项工作

/// <summary>
/// Gets the single step KDF using Hash SHA256.
/// NIST SP800 56Ar2 Section 5.8.1.1
/// </summary>
/// <param name="OtherInfo">The other information.</param>
/// <param name="PrivateKey">The private key.</param>
/// <param name="PublicKey">The public key.</param>
/// <param name="DesiredKeyBitLength">Length of the desired key bit.</param>
/// <returns>Byte[].</returns>
private static Byte[] getSingleStepKDF_SHA256(  Byte[]  OtherInfo,
                                                Byte[]  PrivateKey,
                                                Byte[]  PublicKey,
                                                Int32   DesiredKeyBitLength
                                             )
{
  ByteAccumulator             ba                      = null;
  Byte[]                      data                    = null;
  Byte[]                      secret                  = null;
  int                         keyDataLenInBits        = 0;
  int                         keyLenInBytes           = 0;
  uint                        reps                    = 0;
  uint                        cntr                    = 0;

  secret = getSharedSecret( PrivateKey, PublicKey );

  if( secret != null )
  {
    #region Single-Step KDF
    keyDataLenInBits  = DesiredKeyBitLength;
    keyLenInBytes     = ( int )( DesiredKeyBitLength / 8 );

    reps = ( uint )( keyDataLenInBits / 128 ); //  Our hash length is 128 bytes

    if( reps > ( UInt32.MaxValue - 1 ) )
      new Exception( "reps too large" );

    cntr = 1;

    if( ( 4 + ( secret.Length * 8 ) + ( OtherInfo.Length * 8 ) ) > 256 )
      new Exception( "data is too large" );

    ba = new ByteAccumulator();
    ba.IsBigEndian = true;

    data = General.CatArray<Byte>( BitConverter.GetBytes( cntr ).Reverse().ToArray(),
                                    secret,
                                    OtherInfo );

    for( int i = 1; i <= reps; i++ )
    {
      ba.AddBlock( SecureHashAlgorithm.GetSha256_BouncyCastle( data ), 32 );

      //  Increment counter modulo 2^32
      cntr = ( uint )( cntr++ % 32 );

      data = General.CatArray<Byte>( BitConverter.GetBytes( cntr ).Reverse().ToArray(),
                                      secret,
                                      OtherInfo );
    }

    return ba.ToArray().Take( keyLenInBytes ).ToArray();
    #endregion Single-Step KDF
  }
  else
    return null;
}

/// <summary>
/// Gets the shared secret.
/// </summary>
/// <param name="PrivateKeyIn">The private key in.</param>
/// <param name="PublicKeyIn">The public key in.</param>
/// <returns>Byte[].</returns>
private static Byte[] getSharedSecret( Byte[] PrivateKeyIn, Byte[] PublicKeyIn )
{
  ECDHCBasicAgreement         agreement             = new ECDHCBasicAgreement();
  X9ECParameters              curve                 = null;
  ECDomainParameters          ecParam               = null;
  ECPrivateKeyParameters      privKey               = null;
  ECPublicKeyParameters       pubKey                = null;
  ECPoint                     point                 = null;

  curve     = NistNamedCurves.GetByName( "P-256" );
  ecParam   = new ECDomainParameters( curve.Curve, curve.G, curve.N, curve.H, curve.GetSeed() );
  privKey   = new ECPrivateKeyParameters( new BigInteger( PrivateKeyIn ), ecParam );
  point     = ecParam.Curve.DecodePoint( PublicKeyIn );
  pubKey    = new ECPublicKeyParameters( point, ecParam );

  agreement.Init( privKey );

  BigInteger secret = agreement.CalculateAgreement( pubKey );

  return secret.ToByteArrayUnsigned();
}