155 self::TYPE_BOOLEAN =>
true,
156 self::TYPE_INTEGER =>
true,
157 self::TYPE_BIT_STRING =>
'bitString',
158 self::TYPE_OCTET_STRING =>
'octetString',
159 self::TYPE_NULL =>
'null',
160 self::TYPE_OBJECT_IDENTIFIER =>
'objectIdentifier',
161 self::TYPE_REAL =>
true,
162 self::TYPE_ENUMERATED =>
'enumerated',
163 self::TYPE_UTF8_STRING =>
'utf8String',
164 self::TYPE_NUMERIC_STRING =>
'numericString',
165 self::TYPE_PRINTABLE_STRING =>
'printableString',
166 self::TYPE_TELETEX_STRING =>
'teletexString',
167 self::TYPE_VIDEOTEX_STRING =>
'videotexString',
168 self::TYPE_IA5_STRING =>
'ia5String',
169 self::TYPE_UTC_TIME =>
'utcTime',
170 self::TYPE_GENERALIZED_TIME =>
'generalTime',
171 self::TYPE_GRAPHIC_STRING =>
'graphicString',
172 self::TYPE_VISIBLE_STRING =>
'visibleString',
173 self::TYPE_GENERAL_STRING =>
'generalString',
174 self::TYPE_UNIVERSAL_STRING =>
'universalString',
176 self::TYPE_BMP_STRING =>
'bmpString' 189 self::TYPE_UTF8_STRING => 0,
190 self::TYPE_BMP_STRING => 2,
191 self::TYPE_UNIVERSAL_STRING => 4,
192 self::TYPE_PRINTABLE_STRING => 1,
193 self::TYPE_TELETEX_STRING => 1,
194 self::TYPE_IA5_STRING => 1,
195 self::TYPE_VISIBLE_STRING => 1,
209 if ($encoded instanceof
Element) {
210 $encoded = $encoded->element;
237 $constructed = (
$type >> 5) & 1;
244 $loop = ord($encoded[0]) >> 7;
254 if ($length == 0x80) {
257 $length = strlen($encoded);
258 } elseif ($length & 0x80) {
264 $current+= array(
'headerlength' => $length + 2);
266 extract(unpack(
'Nlength', substr(str_pad($temp, 4, chr(0), STR_PAD_LEFT), -4)));
268 $current+= array(
'headerlength' => 2);
271 if ($length > strlen($encoded)) {
288 $class = (
$type >> 6) & 3;
290 case self::CLASS_APPLICATION:
291 case self::CLASS_PRIVATE:
292 case self::CLASS_CONTEXT_SPECIFIC:
297 'content' => $content,
302 $newcontent = array();
303 $remainingLength = $length;
304 while ($remainingLength > 0) {
306 $length = $temp[
'length'];
308 if (substr($content, $length, 2) ==
"\0\0") {
311 $newcontent[] = $temp;
315 $remainingLength-= $length;
316 $newcontent[] = $temp;
324 'content' => $newcontent,
336 case self::TYPE_BOOLEAN:
341 $current[
'content'] = (bool) ord($content[0]);
343 case self::TYPE_INTEGER:
344 case self::TYPE_ENUMERATED:
347 case self::TYPE_REAL:
349 case self::TYPE_BIT_STRING:
357 $length-= strlen($content);
358 $last = count($temp) - 1;
359 for (
$i = 0;
$i < $last;
$i++) {
364 $current[
'content'].= substr($temp[
$i][
'content'], 1);
370 $current[
'content'] = $temp[$last][
'content'][0] .
$current[
'content'] . substr($temp[
$i][
'content'], 1);
373 case self::TYPE_OCTET_STRING:
379 while (substr($content, 0, 2) !=
"\0\0") {
386 $current[
'content'].= $temp[
'content'];
387 $length+= $temp[
'length'];
389 if (substr($content, 0, 2) ==
"\0\0") {
394 case self::TYPE_NULL:
400 case self::TYPE_SEQUENCE:
404 while (strlen($content)) {
407 if (!isset(
$current[
'headerlength']) && substr($content, 0, 2) ==
"\0\0") {
408 $length = $offset + 2;
414 $offset+= $temp[
'length'];
417 case self::TYPE_OBJECT_IDENTIFIER:
419 $current[
'content'] = sprintf(
'%d.%d', floor($temp / 40), $temp % 40);
422 while (strlen($content)) {
425 $valuen |= $temp & 0x7F;
443 case self::TYPE_NUMERIC_STRING:
445 case self::TYPE_PRINTABLE_STRING:
448 case self::TYPE_TELETEX_STRING:
451 case self::TYPE_VIDEOTEX_STRING:
453 case self::TYPE_VISIBLE_STRING:
455 case self::TYPE_IA5_STRING:
457 case self::TYPE_GRAPHIC_STRING:
459 case self::TYPE_GENERAL_STRING:
461 case self::TYPE_UTF8_STRING:
463 case self::TYPE_BMP_STRING:
466 case self::TYPE_UTC_TIME:
467 case self::TYPE_GENERALIZED_TIME:
491 function asn1map($decoded, $mapping, $special = array())
493 if (isset($mapping[
'explicit']) && is_array($decoded[
'content'])) {
494 $decoded = $decoded[
'content'][0];
498 case $mapping[
'type'] == self::TYPE_ANY:
499 $intype = $decoded[
'type'];
500 if (isset($decoded[
'constant']) || !isset($this->ANYmap[$intype]) || ($this->encoded[$decoded[
'start']] & 0x20)) {
501 return new Element(substr($this->encoded, $decoded[
'start'], $decoded[
'length']));
503 $inmap = $this->ANYmap[$intype];
504 if (is_string($inmap)) {
505 return array($inmap => $this->
asn1map($decoded, array(
'type' => $intype) + $mapping, $special));
508 case $mapping[
'type'] == self::TYPE_CHOICE:
509 foreach ($mapping[
'children'] as
$key => $option) {
511 case isset($option[
'constant']) && $option[
'constant'] == $decoded[
'constant']:
512 case !isset($option[
'constant']) && $option[
'type'] == $decoded[
'type']:
513 $value = $this->
asn1map($decoded, $option, $special);
515 case !isset($option[
'constant']) && $option[
'type'] == self::TYPE_CHOICE:
516 $v = $this->
asn1map($decoded, $option, $special);
522 if (isset($special[
$key])) {
523 $value = call_user_func($special[$key], $value);
525 return array($key => $value);
529 case isset($mapping[
'implicit']):
530 case isset($mapping[
'explicit']):
531 case $decoded[
'type'] == $mapping[
'type']:
537 case $decoded[
'type'] < 18:
538 case $decoded[
'type'] > 30:
539 case $mapping[
'type'] < 18:
540 case $mapping[
'type'] > 30:
545 if (isset($mapping[
'implicit'])) {
546 $decoded[
'type'] = $mapping[
'type'];
549 switch ($decoded[
'type']) {
550 case self::TYPE_SEQUENCE:
554 if (isset($mapping[
'min']) && isset($mapping[
'max'])) {
555 $child = $mapping[
'children'];
556 foreach ($decoded[
'content'] as $content) {
557 if ((
$map[] = $this->
asn1map($content, $child, $special)) === null) {
565 $n = count($decoded[
'content']);
568 foreach ($mapping[
'children'] as
$key => $child) {
571 $temp = $decoded[
'content'][
$i];
573 if ($child[
'type'] != self::TYPE_CHOICE) {
575 $childClass = $tempClass = self::CLASS_UNIVERSAL;
577 if (isset($temp[
'constant'])) {
578 $tempClass = isset($temp[
'class']) ? $temp[
'class'] : self::CLASS_CONTEXT_SPECIFIC;
580 if (isset($child[
'class'])) {
581 $childClass = $child[
'class'];
582 $constant = $child[
'cast'];
583 } elseif (isset($child[
'constant'])) {
584 $childClass = self::CLASS_CONTEXT_SPECIFIC;
585 $constant = $child[
'constant'];
588 if (isset($constant) && isset($temp[
'constant'])) {
590 $maymatch = $constant == $temp[
'constant'] && $childClass == $tempClass;
593 $maymatch = !isset($child[
'constant']) && array_search($child[
'type'], array($temp[
'type'], self::TYPE_ANY, self::TYPE_CHOICE)) !==
false;
600 $candidate = $this->
asn1map($temp, $child, $special);
601 $maymatch = $candidate !== null;
606 if (isset($special[
$key])) {
607 $candidate = call_user_func($special[$key], $candidate);
611 } elseif (isset($child[
'default'])) {
613 } elseif (!isset($child[
'optional'])) {
626 if (isset($mapping[
'min']) && isset($mapping[
'max'])) {
627 $child = $mapping[
'children'];
628 foreach ($decoded[
'content'] as $content) {
629 if ((
$map[] = $this->
asn1map($content, $child, $special)) === null) {
637 for (
$i = 0;
$i < count($decoded[
'content']);
$i++) {
638 $temp = $decoded[
'content'][
$i];
639 $tempClass = self::CLASS_UNIVERSAL;
640 if (isset($temp[
'constant'])) {
641 $tempClass = isset($temp[
'class']) ? $temp[
'class'] : self::CLASS_CONTEXT_SPECIFIC;
644 foreach ($mapping[
'children'] as
$key => $child) {
649 if ($child[
'type'] != self::TYPE_CHOICE) {
650 $childClass = self::CLASS_UNIVERSAL;
652 if (isset($child[
'class'])) {
653 $childClass = $child[
'class'];
654 $constant = $child[
'cast'];
655 } elseif (isset($child[
'constant'])) {
656 $childClass = self::CLASS_CONTEXT_SPECIFIC;
657 $constant = $child[
'constant'];
660 if (isset($constant) && isset($temp[
'constant'])) {
662 $maymatch = $constant == $temp[
'constant'] && $childClass == $tempClass;
665 $maymatch = !isset($child[
'constant']) && array_search($child[
'type'], array($temp[
'type'], self::TYPE_ANY, self::TYPE_CHOICE)) !==
false;
671 $candidate = $this->
asn1map($temp, $child, $special);
672 $maymatch = $candidate !== null;
680 if (isset($special[$key])) {
681 $candidate = call_user_func($special[$key], $candidate);
688 foreach ($mapping[
'children'] as
$key => $child) {
690 if (isset($child[
'default'])) {
692 } elseif (!isset($child[
'optional'])) {
698 case self::TYPE_OBJECT_IDENTIFIER:
699 return isset($this->oids[$decoded[
'content']]) ? $this->oids[$decoded[
'content']] : $decoded[
'content'];
700 case self::TYPE_UTC_TIME:
701 case self::TYPE_GENERALIZED_TIME:
702 if (isset($mapping[
'implicit'])) {
703 $decoded[
'content'] = $this->
_decodeTime($decoded[
'content'], $decoded[
'type']);
705 return @date($this->format, $decoded[
'content']);
706 case self::TYPE_BIT_STRING:
707 if (isset($mapping[
'mapping'])) {
708 $offset = ord($decoded[
'content'][0]);
709 $size = (strlen($decoded[
'content']) - 1) * 8 - $offset;
718 $bits = count($mapping[
'mapping']) ==
$size ? array() : array_fill(0, count($mapping[
'mapping']) -
$size,
false);
719 for (
$i = strlen($decoded[
'content']) - 1;
$i > 0;
$i--) {
721 for ($j = $offset; $j < 8; $j++) {
722 $bits[] = (bool) (
$current & (1 << $j));
727 $map = array_reverse($mapping[
'mapping']);
728 foreach (
$map as
$i => $value) {
735 case self::TYPE_OCTET_STRING:
736 return base64_encode($decoded[
'content']);
737 case self::TYPE_NULL:
739 case self::TYPE_BOOLEAN:
740 return $decoded[
'content'];
741 case self::TYPE_NUMERIC_STRING:
742 case self::TYPE_PRINTABLE_STRING:
743 case self::TYPE_TELETEX_STRING:
744 case self::TYPE_VIDEOTEX_STRING:
745 case self::TYPE_IA5_STRING:
746 case self::TYPE_GRAPHIC_STRING:
747 case self::TYPE_VISIBLE_STRING:
748 case self::TYPE_GENERAL_STRING:
749 case self::TYPE_UNIVERSAL_STRING:
750 case self::TYPE_UTF8_STRING:
751 case self::TYPE_BMP_STRING:
752 return $decoded[
'content'];
753 case self::TYPE_INTEGER:
754 case self::TYPE_ENUMERATED:
755 $temp = $decoded[
'content'];
756 if (isset($mapping[
'implicit'])) {
757 $temp =
new BigInteger($decoded[
'content'], -256);
759 if (isset($mapping[
'mapping'])) {
760 $temp = (int) $temp->toString();
761 return isset($mapping[
'mapping'][$temp]) ?
762 $mapping[
'mapping'][$temp] :
785 $this->location = array();
805 if (isset($mapping[
'default']) &&
$source === $mapping[
'default']) {
810 if (isset($special[$idx])) {
813 $this->location[] = $idx;
816 $tag = $mapping[
'type'];
820 case self::TYPE_SEQUENCE:
825 if (isset($mapping[
'min']) && isset($mapping[
'max'])) {
826 $child = $mapping[
'children'];
828 foreach (
$source as $content) {
829 $temp = $this->
_encode_der($content, $child, null, $special);
830 if ($temp ===
false) {
838 foreach ($mapping[
'children'] as
$key => $child) {
840 if (!isset($child[
'optional'])) {
847 if ($temp ===
false) {
858 if (isset($child[
'constant'])) {
868 if (isset($child[
'explicit']) || $child[
'type'] == self::TYPE_CHOICE) {
869 $subtag = chr((self::CLASS_CONTEXT_SPECIFIC << 6) | 0x20 | $child[
'constant']);
870 $temp = $subtag . $this->
_encodeLength(strlen($temp)) . $temp;
872 $subtag = chr((self::CLASS_CONTEXT_SPECIFIC << 6) | (ord($temp[0]) & 0x20) | $child[
'constant']);
873 $temp = $subtag . substr($temp, 1);
879 case self::TYPE_CHOICE:
882 foreach ($mapping[
'children'] as
$key => $child) {
888 if ($temp ===
false) {
898 $tag = ord($temp[0]);
901 if (isset($child[
'constant'])) {
902 if (isset($child[
'explicit']) || $child[
'type'] == self::TYPE_CHOICE) {
903 $subtag = chr((self::CLASS_CONTEXT_SPECIFIC << 6) | 0x20 | $child[
'constant']);
904 $temp = $subtag . $this->
_encodeLength(strlen($temp)) . $temp;
906 $subtag = chr((self::CLASS_CONTEXT_SPECIFIC << 6) | (ord($temp[0]) & 0x20) | $child[
'constant']);
907 $temp = $subtag . substr($temp, 1);
913 array_pop($this->location);
916 if ($temp && isset($mapping[
'cast'])) {
917 $temp[0] = chr(($mapping[
'class'] << 6) | (
$tag & 0x20) | $mapping[
'cast']);
921 case self::TYPE_INTEGER:
922 case self::TYPE_ENUMERATED:
923 if (!isset($mapping[
'mapping'])) {
927 $value =
$source->toBytes(
true);
929 $value = array_search(
$source, $mapping[
'mapping']);
930 if ($value ===
false) {
934 $value = $value->toBytes(
true);
936 if (!strlen($value)) {
940 case self::TYPE_UTC_TIME:
941 case self::TYPE_GENERALIZED_TIME:
942 $format = $mapping[
'type'] == self::TYPE_UTC_TIME ?
'y' :
'Y';
944 $value = @gmdate($format, strtotime(
$source)) .
'Z';
946 case self::TYPE_BIT_STRING:
947 if (isset($mapping[
'mapping'])) {
948 $bits = array_fill(0, count($mapping[
'mapping']), 0);
950 for (
$i = 0;
$i < count($mapping[
'mapping']);
$i++) {
951 if (in_array($mapping[
'mapping'][
$i],
$source)) {
957 if (isset($mapping[
'min']) && $mapping[
'min'] >= 1 &&
$size < $mapping[
'min']) {
958 $size = $mapping[
'min'] - 1;
961 $offset = 8 - ((
$size + 1) & 7);
962 $offset = $offset !== 8 ? $offset : 0;
964 $value = chr($offset);
966 for (
$i =
$size + 1;
$i < count($mapping[
'mapping']);
$i++) {
970 $bits = implode(
'', array_pad($bits,
$size + $offset + 1, 0));
971 $bytes = explode(
' ', rtrim(chunk_split($bits, 8,
' ')));
972 foreach ($bytes as $byte) {
973 $value.= chr(bindec($byte));
978 case self::TYPE_OCTET_STRING:
983 $value = base64_decode(
$source);
985 case self::TYPE_OBJECT_IDENTIFIER:
987 if ($oid ===
false) {
988 user_error(
'Invalid OID');
992 $parts = explode(
'.', $oid);
993 $value = chr(40 * $parts[0] + $parts[1]);
994 for (
$i = 2;
$i < count($parts);
$i++) {
1000 $temp = chr(0x80 | ($parts[$i] & 0x7F)) . $temp;
1003 $temp[strlen($temp) - 1] = $temp[strlen($temp) - 1] & chr(0x7F);
1008 case self::TYPE_ANY:
1011 array_pop($this->location);
1016 return $this->
_encode_der(null, array(
'type' => self::TYPE_NULL) + $mapping, null, $special);
1019 return $this->
_encode_der(
$source, array(
'type' => self::TYPE_INTEGER) + $mapping, null, $special);
1021 return $this->
_encode_der(
$source, array(
'type' => self::TYPE_REAL) + $mapping, null, $special);
1023 return $this->
_encode_der(
$source, array(
'type' => self::TYPE_BOOLEAN) + $mapping, null, $special);
1025 $typename = implode(
'', array_keys(
$source));
1026 $outtype = array_search($typename, $this->ANYmap,
true);
1027 if ($outtype !==
false) {
1028 return $this->
_encode_der(
$source[$typename], array(
'type' => $outtype) + $mapping, null, $special);
1033 foreach ($loc as $part) {
1034 if (!isset($filters[$part])) {
1038 $filters = $filters[$part];
1040 if ($filters ===
false) {
1041 user_error(
'No filters defined for ' . implode(
'/', $loc));
1045 case self::TYPE_NULL:
1048 case self::TYPE_NUMERIC_STRING:
1049 case self::TYPE_TELETEX_STRING:
1050 case self::TYPE_PRINTABLE_STRING:
1051 case self::TYPE_UNIVERSAL_STRING:
1052 case self::TYPE_UTF8_STRING:
1053 case self::TYPE_BMP_STRING:
1054 case self::TYPE_IA5_STRING:
1055 case self::TYPE_VISIBLE_STRING:
1056 case self::TYPE_VIDEOTEX_STRING:
1057 case self::TYPE_GRAPHIC_STRING:
1058 case self::TYPE_GENERAL_STRING:
1061 case self::TYPE_BOOLEAN:
1062 $value =
$source ?
"\xFF" :
"\x00";
1065 user_error(
'Mapping provides no type definition for ' . implode(
'/', $this->location));
1070 array_pop($this->location);
1073 if (isset($mapping[
'cast'])) {
1074 if (isset($mapping[
'explicit']) || $mapping[
'type'] == self::TYPE_CHOICE) {
1076 $tag = ($mapping[
'class'] << 6) | 0x20 | $mapping[
'cast'];
1078 $tag = ($mapping[
'class'] << 6) | (ord($temp[0]) & 0x20) | $mapping[
'cast'];
1097 if ($length <= 0x7F) {
1098 return chr($length);
1101 $temp = ltrim(pack(
'N', $length), chr(0));
1102 return pack(
'Ca*', 0x80 | strlen($temp), $temp);
1125 $pattern =
$tag == self::TYPE_UTC_TIME ?
1126 '#(..)(..)(..)(..)(..)(..)(.*)#' :
1127 '#(....)(..)(..)(..)(..)(..).*([Z+-].*)$#';
1129 preg_match($pattern, $content, $matches);
1131 list(, $year, $month, $day, $hour, $minute, $second, $timezone) = $matches;
1133 if (
$tag == self::TYPE_UTC_TIME) {
1134 $year = $year >= 50 ?
"19$year" :
"20$year";
1137 if ($timezone ==
'Z') {
1138 $mktime =
'gmmktime';
1140 } elseif (preg_match(
'#([+-])(\d\d)(\d\d)#', $timezone, $matches)) {
1141 $mktime =
'gmmktime';
1142 $timezone = 60 * $matches[3] + 3600 * $matches[2];
1143 if ($matches[1] ==
'-') {
1144 $timezone = -$timezone;
1151 return @$mktime($hour, $minute, $second, $month, $day, $year) + $timezone;
1177 $this->oids =
$oids;
1205 $substr = substr($string, 0,
$index);
1206 $string = substr($string,
$index);
1224 if (!isset($this->stringTypeSize[
$from]) || !isset($this->stringTypeSize[$to])) {
1227 $insize = $this->stringTypeSize[
$from];
1228 $outsize = $this->stringTypeSize[$to];
1229 $inlength = strlen(
$in);
1232 for (
$i = 0;
$i < $inlength;) {
1233 if ($inlength -
$i < $insize) {
1247 case (
$c & 0x80) == 0x00:
1249 case (
$c & 0x40) == 0x00:
1254 if ($bit > 25 ||
$i >= $inlength || (ord(
$in[
$i]) & 0xC0) != 0x80) {
1257 $c = (
$c << 6) | (ord(
$in[$i++]) & 0x3F);
1260 }
while (
$c & $bit);
1269 $v .= chr(
$c & 0xFF);
1271 $v .= chr(
$c & 0xFF);
1274 $v .= chr(
$c & 0xFF);
1277 $v .= chr(
$c & 0xFF);
1283 case (
$c & 0x80000000) != 0:
1285 case $c >= 0x04000000:
1286 $v .= chr(0x80 | (
$c & 0x3F));
1287 $c = (
$c >> 6) | 0x04000000;
1288 case $c >= 0x00200000:
1289 $v .= chr(0x80 | (
$c & 0x3F));
1290 $c = (
$c >> 6) | 0x00200000;
1291 case $c >= 0x00010000:
1292 $v .= chr(0x80 | (
$c & 0x3F));
1293 $c = (
$c >> 6) | 0x00010000;
1294 case $c >= 0x00000800:
1295 $v .= chr(0x80 | (
$c & 0x3F));
1296 $c = (
$c >> 6) | 0x00000800;
1297 case $c >= 0x00000080:
1298 $v .= chr(0x80 | (
$c & 0x3F));
1299 $c = (
$c >> 6) | 0x000000C0;
const TYPE_VISIBLE_STRING
loadFilters($filters)
Load filters.
const CLASS_CONTEXT_SPECIFIC
const TYPE_NUMERIC_STRING
#-
const TYPE_PRINTABLE_STRING
asn1map($decoded, $mapping, $special=array())
ASN.1 Map.
const CLASS_UNIVERSAL
#+ Tag Classes
_encode_der($source, $mapping, $idx=null, $special=array())
ASN.1 Encode (Helper function)
decodeBER($encoded)
Parse BER-encoding.
convert($in, $from=self::TYPE_UTF8_STRING, $to=self::TYPE_UTF8_STRING)
String type conversion.
const TYPE_UNIVERSAL_STRING
_string_shift(&$string, $index=1)
String Shift.
const TYPE_OBJECT_IDENTIFIER
const TYPE_GENERALIZED_TIME
loadOIDs($oids)
Load OIDs.
const TYPE_GRAPHIC_STRING
encodeDER($source, $mapping, $special=array())
ASN.1 Encode.
if(php_sapi_name() !='cli') $in
const TYPE_TELETEX_STRING
_encodeLength($length)
DER-encode the length.
Pure-PHP arbitrary precision integer arithmetic library.
_decode_ber($encoded, $start=0)
Parse BER-encoding (Helper function)
const TYPE_VIDEOTEX_STRING
_decodeTime($content, $tag)
BER-decode the time.
const TYPE_GENERAL_STRING
if(function_exists('posix_getuid') &&posix_getuid()===0) if(!array_key_exists('t', $options)) $tag
setTimeFormat($format)
Set the time format.