这段代码如何提取签名?

时间:2012-03-08 22:26:35

标签: php certificate x509certificate x509

我必须从离开公司的开发人员那里调试旧的PHP脚本。我理解代码的大部分内容,除了以下函数。我的问题:什么......

  

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

...表示从X.509证书中提取签名的上下文?

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;
}

5 个答案:

答案 0 :(得分:5)

X.509证书包含多个部分的数据(称为Tag-Length-Value三元组)。每个部分都以Tag字节开头,表示该部分的数据格式。您可以看到这些数据类型的列表here

0x03 BIT STRING数据类型的标记字节, 0x30 SEQUENCE数据类型的标记字节。

因此,此代码旨在处理BIT STRING和SEQUENCE数据类型。如果你看这个部分:

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

你可以看到该函数被设计为跳过序列(0x30),直到它找到一个位串(0x03),此时它返回位串的值。

您可能想知道为什么位字符串 3 序列 2 。这是因为在位串中,第一个值字节是一个特殊的额外字段,表示在数据的最后一个字节中未使用的位数。 (例如,如果要发送13位数据,则​​需要2个字节= 16位,“未使用位”字段将为3。)

下一期:长度字段。当Value的长度小于128个字节时,使用单个字节简单地指定长度(最高有效位将为0)。如果长度为128或更大,则第一个长度字节的位设置为7,其余7位表示后续字节包含的长度(以大端顺序排列)。更多说明here。长度字段的解析发生在代码的这一部分:

$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]);
    }
}

之后,$bytes包含长度字段使用的额外字节数,$len包含值字段的长度(以字节为单位)。

您是否发现了代码中的错误?请记住,

  

如果长度为128或更大,则第一个长度字节的位为7   设置,剩余的 7位表示后续字节数   包含长度。

但代码显示$bytes = ($len & 0x0f),只占用字节的低4位!它应该是:

$bytes = ($len & 0x7f);

当然,这个错误只是长消息的问题:只要长度值适合0x0f = 15字节,它就能正常工作,这意味着数据必须小于256 ^ 15个字节。这大约是一万亿个字节,对任何人来说应该足够了。

答案 1 :(得分:2)

正如佩特曼上面所说,你只是有一个合乎逻辑的,我们只是检查$seq是0x30还是0x03。

我有一种感觉你已经知道了,所以这里。 $seq是证书的第一个字节,可能是证书的版本或幻数,表示该文件是证书(也称为“我猜这是因为10:45不是是时候开始阅读RFC了。)。

在这种情况下,我们将与0x30和0x03进行比较。这些数字以十六进制表示(以每个以0x开头的数字表示),即base-16。这实际上是二进制的一种非常方便的简写,因为每个十六进制数字恰好对应于四个二进制位。快速表是这样的:

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

同样,我们可以说if($seq == 3 || $seq == 48),但在这种情况下,十六进制只是更容易阅读和理解。

答案 2 :(得分:2)

我猜测它是x.509证书中版本标识符“3”的字节顺序无关的检查。见RFC 1422,第7页。其余的是逐字节拉取签名。

答案 3 :(得分:1)

ord()获取您传递的ASCII字符的值。在这种情况下,它会检查ASCII字符是0还是文本结尾(根据此ASCII table)。

答案 4 :(得分:-1)

0x03和0x30是十六进制值。看一下,你会得到$ seq与

相匹配的东西