Ваш ввод - это формат подписи X9.62, который представляет собой ПОСЛЕДОВАТЕЛЬНОСТЬ, содержащую две подписи в кодировке ASN.1 / DER.Эти целые числа имеют переменные размеры, числа со знаком и порядковые номера.Они кодируются в минимальном количестве байтов.Это означает, что размер кодировки может варьироваться.
139 байтов являются общими, поскольку они предполагают максимальный размер кодирования для r
и s
.Эти значения вычисляются с использованием модульной арифметики, и поэтому они могут содержать любое количество битов, вплоть до количества битов порядка n
, которое совпадает с размером ключа, 521 бит.
132 байта определены в стандарте ISO / IEC 7816-8 / IEEE P1363, который является стандартом, касающимся подписей для смарт-карт.Подпись состоит из конкатенации r
и s
, где r
и s
кодируются как минимальное количество байтов для отображения значения того же размера, что и порядок, в байтах.r
и s
имеют статические размеры, без знака, числа с прямым порядком байтов.
Вычисление количества байтов r
или s
равно ceil((double) n / 8)
или (n + 8 - 1) / 8
, где 8 равноколичество бит в байте.Таким образом, если эллиптическая кривая составляет 521 бит, то результирующий размер составляет 66 байтов, и вместе они, следовательно, потребляют 132 байта.
Теперь перейдем к декодированию.Существует несколько способов справиться с этим: выполнить полный синтаксический анализ ASN.1, получить целые числа и затем снова закодировать их в форме ISO 7816-8. Это наиболее логичный способ.
Однако вы также можете увидетьчто вы можете просто скопировать байты как r
и s
всегда будут неотрицательными (и, следовательно, без знака) и с прямым порядком байтов.Так что вам просто нужно компенсировать размер.В противном случае единственная сложная часть - это возможность декодировать длину компонентов в структуре X9.62.
Предупреждение : код на C # вместо C ++, как я ожидал,основной язык .NET;язык, не указанный в вопросе, когда я писал основную часть ответа.
class ConvertECDSASignature
{
private static int BYTE_SIZE_BITS = 8;
private static byte ASN1_SEQUENCE = 0x30;
private static byte ASN1_INTEGER = 0x02;
public static byte[] lightweightConvertSignatureFromX9_62ToISO7816_8(int orderInBits, byte[] x9_62)
{
int offset = 0;
if (x9_62[offset++] != ASN1_SEQUENCE)
{
throw new IllegalSignatureFormatException("Input is not a SEQUENCE");
}
int sequenceSize = parseLength(x9_62, offset, out offset);
int sequenceValueOffset = offset;
int nBytes = (orderInBits + BYTE_SIZE_BITS - 1) / BYTE_SIZE_BITS;
byte[] iso7816_8 = new byte[2 * nBytes];
// retrieve and copy r
if (x9_62[offset++] != ASN1_INTEGER)
{
throw new IllegalSignatureFormatException("Input is not an INTEGER");
}
int rSize = parseLength(x9_62, offset, out offset);
copyToStatic(x9_62, offset, rSize, iso7816_8, 0, nBytes);
offset += rSize;
// --- retrieve and copy s
if (x9_62[offset++] != ASN1_INTEGER)
{
throw new IllegalSignatureFormatException("Input is not an INTEGER");
}
int sSize = parseLength(x9_62, offset, out offset);
copyToStatic(x9_62, offset, sSize, iso7816_8, nBytes, nBytes);
offset += sSize;
if (offset != sequenceValueOffset + sequenceSize)
{
throw new IllegalSignatureFormatException("SEQUENCE is either too small or too large for the encoding of r and s");
}
return iso7816_8;
}
/**
* Copies an variable sized, signed, big endian number to an array as static sized, unsigned, big endian number.
* Assumes that the iso7816_8 buffer is zeroized from the iso7816_8Offset for nBytes.
*/
private static void copyToStatic(byte[] sint, int sintOffset, int sintSize, byte[] iso7816_8, int iso7816_8Offset, int nBytes)
{
// if the integer starts with zero, then skip it
if (sint[sintOffset] == 0x00)
{
sintOffset++;
sintSize--;
}
// after skipping the zero byte then the integer must fit
if (sintSize > nBytes)
{
throw new IllegalSignatureFormatException("Number format of r or s too large");
}
// copy it into the right place
Array.Copy(sint, sintOffset, iso7816_8, iso7816_8Offset + nBytes - sintSize, sintSize);
}
/*
* Standalone BER decoding of length value, up to 2^31 -1.
*/
private static int parseLength(byte[] input, int startOffset, out int offset)
{
offset = startOffset;
byte l1 = input[offset++];
// --- return value of single byte length encoding
if (l1 < 0x80)
{
return l1;
}
// otherwise the first byte of the length specifies the number of encoding bytes that follows
int end = offset + l1 & 0x7F;
uint result = 0;
// --- skip leftmost zero bytes (for BER)
while (offset < end)
{
if (input[offset] != 0x00)
{
break;
}
offset++;
}
// --- test against maximum value
if (end - offset > sizeof(uint))
{
throw new IllegalSignatureFormatException("Length of TLV is too large");
}
// --- parse multi byte length encoding
while (offset < end)
{
result = (result << BYTE_SIZE_BITS) ^ input[offset++];
}
// --- make sure that the uint isn't larger than an int can handle
if (result > Int32.MaxValue)
{
throw new IllegalSignatureFormatException("Length of TLV is too large");
}
// --- return multi byte length encoding
return (int) result;
}
}
Обратите внимание, что код является несколько допустимым в том смысле, что он не требует кодирования длины минимальная для кодирования длины SEQUENCE и INTEGER (что и должно быть).
Это также позволяет неправильно кодировать значения INTEGER, которые неоправданно дополняются слева нулевыми байтами.
Ни одна из этих проблем не должна нарушать безопасностьалгоритма, но другие библиотеки могут и должны быть менее разрешительными.