ECDH с Надувной Замок
У меня есть требование, в котором говорится.
используйте для вычисления общего секрета Z метод статического унифицированного кода C(0e, 2s, ECC CDH) (как указано в специальной публикации NIST 800-56Ar214, за исключением требования обнуления общего секрета) с помощью:
одноэтапная функция получения ключа (KDF) на основе SHA-256, как указано в специальной публикации NIST 800-56Ar2; а также
кривая P-256 для операций с эллиптической кривой
Я уже прочитал и попытался реализовать то, что я нашел здесь, но это не работает.
На этом этапе я могу убедиться, что общий секретный ключ верен, но не могу получить правильный ключ и не могу (без редактирования источника 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 ответ
Для меня решение - написать одноступенчатый код 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();
}