How does this code extract the signature?

余生长醉 提交于 2019-12-08 03:02:48

问题


I have to debug an old PHP script from a developer who has left the company. I understand the most part of the code, except the following function. My question: What does...

if($seq == 0x03 || $seq == 0x30)

...mean in context of extracting the signature out of an X.509 certificate?

public function extractSignature($certPemString) {

    $bin = $this->ConvertPemToBinary($certPemString);

    if(empty($certPemString) || empty($bin))
    {
        return false;
    }    

    $bin = substr($bin,4);

    while(strlen($bin) > 1) 
    {            
        $seq = ord($bin[0]); 
        if($seq == 0x03 || $seq == 0x30) 
        {            
            $len = ord($bin[1]);
            $bytes = 0;

            if ($len & 0x80)
            {
                $bytes = ($len & 0x0f);
                $len = 0;
                for ($i = 0; $i < $bytes; $i++)
                {
                    $len = ($len << 8) | ord($bin[$i + 2]);
                }
            }

            if($seq == 0x03)
            {
                return substr($bin,3 + $bytes, $len);
            }
            else 
            {
                $bin = substr($bin,2 + $bytes + $len);                  
            }                                                    
        }
        else 
        {                            
            return false;                
        }
    }
    return false;
}

回答1:


An X.509 certificate contains data in multiple sections (called Tag-Length-Value triplets). Each section starts with a Tag byte, which indicates the data format of the section. You can see a list of these data types here.

0x03 is the Tag byte for the BIT STRING data type, and 0x30 is the Tag byte for the SEQUENCE data type.

So this code is designed to handle the BIT STRING and SEQUENCE data types. If you look at this part:

if($seq == 0x03)
{
    return substr($bin,3 + $bytes, $len);
}
else // $seq == 0x30
{
    $bin = substr($bin,2 + $bytes + $len);                  
}

you can see that the function is designed to skip over Sequences (0x30), until it finds a Bit String (0x03), at which point it returns the value of the Bit String.

You might be wondering why the magic number is 3 for Bit String and 2 for Sequence. That is because in a Bit String, the first value byte is a special extra field which indicates how many bits are unused in the last byte of the data. (For example, if you're sending 13 bits of data, it will take up 2 bytes = 16 bits, and the "unused bits" field will be 3.)

Next issue: the Length field. When the length of the Value is less than 128 bytes, the length is simply specified using a single byte (the most significant bit will be 0). If the length is 128 or greater, then the first length byte has bit 7 set, and the remaining 7 bits indicates how many following bytes contain the length (in big-endian order). More description here. The parsing of the length field happens in this section of the code:

$len = ord($bin[1]);
$bytes = 0;

if ($len & 0x80)
{
    // length is greater than 127!
    $bytes = ($len & 0x0f);
    $len = 0;
    for ($i = 0; $i < $bytes; $i++)
    {
         $len = ($len << 8) | ord($bin[$i + 2]);
    }
}

After that, $bytes contains the number of extra bytes used by the length field, and $len contains the length of the Value field (in bytes).

Did you spot the error in the code? Remember,

If the length is 128 or greater, then the first length byte has bit 7 set, and the remaining 7 bits indicates how many following bytes contain the length.

but the code says $bytes = ($len & 0x0f), which only takes the lower 4 bits of the byte! It should be:

$bytes = ($len & 0x7f);

Of course, this error is only a problem for extremely long messages: it will work fine as long as the length value will fit within 0x0f = 15 bytes, meaning the data has to be less than 256^15 bytes. That's about a trillion yottabytes, which ought to be enough for anybody.




回答2:


As Pateman says above, you just have a logical if, we're just checking if $seq is either 0x30 or 0x03.

I have a feeling you already know that though, so here goes. $seq is the first byte of the certificate, which is probably either the version of the certificate or the magic number to denote that the file is a certificate (also known as "I'm guessing this because 10:45 is no time to start reading RFCs").

In this case, we're comparing against 0x30 and 0x03. These numbers are expressed in hexadecimal (as is every number starting with 0x), which is base-16. This is just really a very convenient shorthand for binary, as each hex digit corresponds to exactly four binary bits. A quick table is this:

0 = 0000
1 = 0001
2 = 0010
3 = 0011
...
...
E = 1110
F = 1111

Equally well, we could have said if($seq == 3 || $seq == 48), but hex is just much easier to read and understand in this case.




回答3:


I'd hazard a guess that it's a byte-order-independent check for version identifier '3' in an x.509 certificate. See RFC 1422, p7. The rest is pulling the signature byte-by-byte.




回答4:


ord() gets the value of the ASCII character you pass it. In this case it's checking to see if the ASCII character is either a 0 or end of text (according to this ASCII table).




回答5:


0x03 and 0x30 are hex values. Look that up and you'll have what $seq is matching to



来源:https://stackoverflow.com/questions/9626048/how-does-this-code-extract-the-signature

易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!