Ваш ввод - это формат подписи 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, которые излишне дополняются слева нулевыми байтами.
Ни одна из этих проблем не должна нарушать безопасность алгоритма, но другие библиотеки могут и должны быть менее разрешительными.
person
Maarten Bodewes
schedule
11.06.2018