Example #1
0
 public function format(PublicKeyInterface $key)
 {
     if (!$key->getCurve() instanceof NamedCurveFp) {
         throw new \RuntimeException('Not implemented for unnamed curves');
     }
     $sequence = new Sequence(new Sequence(new ObjectIdentifier(DerPublicKeySerializer::X509_ECDSA_OID), CurveOidMapper::getCurveOid($key->getCurve())), new BitString($this->encodePoint($key->getPoint())));
     return $sequence->getBinary();
 }
Example #2
0
 public function addAttribute($objectIdentifier, Set $attribute)
 {
     if (is_string($objectIdentifier)) {
         $objectIdentifier = new ObjectIdentifier($objectIdentifier);
     }
     $attributeSequence = new Sequence($objectIdentifier, $attribute);
     $attributeSequence->getNumberOfLengthOctets();
     // length and number of length octets is calculated
     $this->addChild($attributeSequence);
 }
Example #3
0
 /**
  * @param NamedCurveFp $c
  * @param GeneratorPoint $G
  * @return string
  */
 public function serialize(NamedCurveFp $c, GeneratorPoint $G)
 {
     $math = $G->getAdapter();
     $fieldID = $this->getFieldIdAsn($math, $c);
     $curve = $this->getCurveAsn($math, $c);
     $domain = new Sequence(new Integer(1), $fieldID, $curve, new OctetString($this->pointSerializer->serialize($G)), new Integer($G->getOrder()), new Integer(1));
     $payload = $domain->getBinary();
     $content = self::HEADER . PHP_EOL . trim(chunk_split(base64_encode($payload), 64, PHP_EOL)) . PHP_EOL . self::FOOTER;
     return $content;
 }
Example #4
0
 public function testParseBase64()
 {
     $sequence = new Sequence(new Set(new ObjectIdentifier('1.2.250.1.16.9'), new Sequence(new Integer(42), new BitString('A0 12 00 43'))));
     $data = base64_encode($sequence->getBinary());
     $template = [Identifier::SEQUENCE => [Identifier::SET => [Identifier::OBJECT_IDENTIFIER, Identifier::SEQUENCE => [Identifier::INTEGER, Identifier::BITSTRING]]]];
     $parser = new TemplateParser();
     $object = $parser->parseBase64($data, $template);
     $this->assertInstanceOf(Set::class, $object[0]);
     $this->assertInstanceOf(ObjectIdentifier::class, $object[0][0]);
     $this->assertInstanceOf(Sequence::class, $object[0][1]);
     $this->assertInstanceOf(Integer::class, $object[0][1][0]);
     $this->assertInstanceOf(BitString::class, $object[0][1][1]);
 }
 public static function fromBinary(&$binaryData, &$offsetIndex = 0)
 {
     self::parseIdentifier($binaryData[$offsetIndex], Identifier::OCTETSTRING, $offsetIndex++);
     $contentLength = self::parseContentLength($binaryData, $offsetIndex);
     if ($contentLength < 2) {
         throw new ParserException('Can not parse Subject Alternative Names: The Sequence within the octet string after the Object identifier ' . OID::CERT_EXT_SUBJECT_ALT_NAME . " is too short ({$contentLength} octets)", $offsetIndex);
     }
     $offsetOfSequence = $offsetIndex;
     $sequence = Sequence::fromBinary($binaryData, $offsetIndex);
     $offsetOfSequence += $sequence->getNumberOfLengthOctets() + 1;
     if ($sequence->getObjectLength() != $contentLength) {
         throw new ParserException('Can not parse Subject Alternative Names: The Sequence length does not match the length of the surrounding octet string', $offsetIndex);
     }
     $parsedObject = new self();
     /** @var \FG\ASN1\Object $object */
     foreach ($sequence as $object) {
         if ($object->getType() == DNSName::IDENTIFIER) {
             $domainName = DNSName::fromBinary($binaryData, $offsetOfSequence);
             $parsedObject->addDomainName($domainName);
         } elseif ($object->getType() == IPAddress::IDENTIFIER) {
             $ip = IPAddress::fromBinary($binaryData, $offsetOfSequence);
             $parsedObject->addIP($ip);
         } else {
             throw new ParserException('Could not parse Subject Alternative Name: Only DNSName and IP SANs are currently supported', $offsetIndex);
         }
     }
     $parsedObject->getBinary();
     // Determine the number of content octets and object sizes once (just to let the equality unit tests pass :/ )
     return $parsedObject;
 }
Example #6
0
 /**
  * @param ObjectIdentifier|string $objIdentifier
  * @param \FG\ASN1\Object $value
  */
 public function __construct($objIdentifier, Object $value)
 {
     if ($objIdentifier instanceof ObjectIdentifier == false) {
         $objIdentifier = new ObjectIdentifier($objIdentifier);
     }
     parent::__construct($objIdentifier, $value);
 }
 public function testREADME_encoding()
 {
     $this->expectOutputString('MBgCAwHiQAEB/woBARYLSGVsbG8gd29ybGQxODAYAgMB4kABAf8KAQEWC0hlbGxvIHdvcmxkBQAGBiqBegEQCQYJKoZIhvcNAQEBEwdGb28gYmFy');
     $integer = new Integer(123456);
     $boolean = new Boolean(true);
     $enum = new Enumerated(1);
     $ia5String = new IA5String('Hello world');
     $asnNull = new NullObject();
     $objectIdentifier1 = new ObjectIdentifier('1.2.250.1.16.9');
     $objectIdentifier2 = new ObjectIdentifier(OID::RSA_ENCRYPTION);
     $printableString = new PrintableString('Foo bar');
     $sequence = new Sequence($integer, $boolean, $enum, $ia5String);
     $set = new Set($sequence, $asnNull, $objectIdentifier1, $objectIdentifier2, $printableString);
     $myBinary = $sequence->getBinary();
     $myBinary .= $set->getBinary();
     echo base64_encode($myBinary);
 }
Example #8
0
 /**
  * @param string $commonName
  * @param string $email
  * @param string $organization
  * @param string $locality
  * @param string $state
  * @param string $country
  * @param string $organizationalUnit
  */
 public function __construct($commonName, $email, $organization, $locality, $state, $country, $organizationalUnit)
 {
     parent::__construct(new RDNString(OID::COUNTRY_NAME, $country), new RDNString(OID::STATE_OR_PROVINCE_NAME, $state), new RDNString(OID::LOCALITY_NAME, $locality), new RDNString(OID::ORGANIZATION_NAME, $organization), new RDNString(OID::OU_NAME, $organizationalUnit), new RDNString(OID::COMMON_NAME, $commonName), new RDNString(OID::PKCS9_EMAIL, $email));
     $this->commonName = $commonName;
     $this->email = $email;
     $this->organization = $organization;
     $this->locality = $locality;
     $this->state = $state;
     $this->country = $country;
     $this->organizationalUnit = $organizationalUnit;
 }
Example #9
0
 /**
  * @depends testFromBinary
  */
 public function testFromBinaryWithOffset()
 {
     $originalObject1 = new Sequence(new Boolean(true), new Integer(123));
     $originalObject2 = new Sequence(new Integer(64), new Boolean(false));
     $binaryData = $originalObject1->getBinary();
     $binaryData .= $originalObject2->getBinary();
     $offset = 0;
     $parsedObject = Sequence::fromBinary($binaryData, $offset);
     $this->assertEquals($originalObject1, $parsedObject);
     $this->assertEquals(8, $offset);
     $parsedObject = Sequence::fromBinary($binaryData, $offset);
     $this->assertEquals($originalObject2, $parsedObject);
     $this->assertEquals(16, $offset);
 }
Example #10
0
 public static function fromBinary(&$binaryData, &$offsetIndex = 0)
 {
     self::parseIdentifier($binaryData[$offsetIndex], Identifier::SET, $offsetIndex++);
     self::parseContentLength($binaryData, $offsetIndex);
     $tmpOffset = $offsetIndex;
     $extensions = Sequence::fromBinary($binaryData, $offsetIndex);
     $tmpOffset += 1 + $extensions->getNumberOfLengthOctets();
     $parsedObject = new self();
     foreach ($extensions as $extension) {
         if ($extension->getType() != Identifier::SEQUENCE) {
             //FIXME wrong offset index
             throw new ParserException('Could not parse Certificate Extensions: Expected ASN.1 Sequence but got ' . $extension->getTypeName(), $offsetIndex);
         }
         $tmpOffset += 1 + $extension->getNumberOfLengthOctets();
         $children = $extension->getChildren();
         if (count($children) < 2) {
             throw new ParserException('Could not parse Certificate Extensions: Needs at least two child elements per extension sequence (object identifier and octet string)', $tmpOffset);
         }
         /** @var \FG\ASN1\Object $objectIdentifier */
         $objectIdentifier = $children[0];
         /** @var OctetString $octetString */
         $octetString = $children[1];
         if ($objectIdentifier->getType() != Identifier::OBJECT_IDENTIFIER) {
             throw new ParserException('Could not parse Certificate Extensions: Expected ASN.1 Object Identifier but got ' . $extension->getTypeName(), $tmpOffset);
         }
         $tmpOffset += $objectIdentifier->getObjectLength();
         if ($objectIdentifier->getContent() == OID::CERT_EXT_SUBJECT_ALT_NAME) {
             $sans = SubjectAlternativeNames::fromBinary($binaryData, $tmpOffset);
             $parsedObject->addSubjectAlternativeNames($sans);
         } else {
             // can now only parse SANs. There might be more in the future
             $tmpOffset += $octetString->getObjectLength();
         }
     }
     $parsedObject->getBinary();
     // Determine the number of content octets and object sizes once (just to let the equality unit tests pass :/ )
     return $parsedObject;
 }
Example #11
0
<?php

require_once __DIR__ . '/../vendor/autoload.php';
use FG\ASN1\OID;
use FG\ASN1\Universal\Integer;
use FG\ASN1\Universal\Boolean;
use FG\ASN1\Universal\Enumerated;
use FG\ASN1\Universal\IA5String;
use FG\ASN1\Universal\ObjectIdentifier;
use FG\ASN1\Universal\PrintableString;
use FG\ASN1\Universal\Sequence;
use FG\ASN1\Universal\Set;
use FG\ASN1\Universal\NullObject;
$integer = new Integer(123456);
$boolean = new Boolean(true);
$enum = new Enumerated(1);
$ia5String = new IA5String('Hello world');
$asnNull = new NullObject();
$objectIdentifier1 = new ObjectIdentifier('1.2.250.1.16.9');
$objectIdentifier2 = new ObjectIdentifier(OID::RSA_ENCRYPTION);
$printableString = new PrintableString('Foo bar');
$sequence = new Sequence($integer, $boolean, $enum, $ia5String);
$set = new Set($sequence, $asnNull, $objectIdentifier1, $objectIdentifier2, $printableString);
$myBinary = $sequence->getBinary();
$myBinary .= $set->getBinary();
echo base64_encode($myBinary);
Example #12
0
 /**
  * @param Sequence $sigSection
  * @return string
  * @throws \Exception
  */
 public function parseSigAlg(Sequence $sigSection)
 {
     $sigInfo = $sigSection->getContent();
     if (!$sigInfo[0] instanceof ObjectIdentifier) {
         throw new \Exception('Invaid sig: object identfier');
     }
     return SigAlgorithmOidMapper::getAlgorithmFromOid($sigInfo[0]);
 }
Example #13
0
 /**
  *
  */
 private function initPrivateKey()
 {
     $this->addChild(new Integer(0));
     $oid_sequence = new Sequence();
     $oid_sequence->addChild(new ObjectIdentifier('1.2.840.113549.1.1.1'));
     $oid_sequence->addChild(new NullObject());
     $this->addChild($oid_sequence);
     $v = new Integer(0);
     $n = new Integer($this->fromBase64ToInteger($this->n));
     $e = new Integer($this->fromBase64ToInteger($this->e));
     $d = new Integer($this->fromBase64ToInteger($this->d));
     $p = new Integer($this->fromBase64ToInteger($this->p));
     $q = new Integer($this->fromBase64ToInteger($this->q));
     $dp = new Integer($this->fromBase64ToInteger($this->dp));
     $dq = new Integer($this->fromBase64ToInteger($this->dq));
     $qi = new Integer($this->fromBase64ToInteger($this->qi));
     $key_sequence = new Sequence();
     $key_sequence->addChild($v);
     $key_sequence->addChild($n);
     $key_sequence->addChild($e);
     $key_sequence->addChild($d);
     $key_sequence->addChild($p);
     $key_sequence->addChild($q);
     $key_sequence->addChild($dp);
     $key_sequence->addChild($dq);
     $key_sequence->addChild($qi);
     $key_octet_string = new OctetString(bin2hex($key_sequence->getBinary()));
     $this->addChild($key_octet_string);
 }
 /**
  * {@inheritDoc}
  * @see \Mdanter\Ecc\Serializer\PrivateKeySerializerInterface::serialize()
  */
 public function serialize(PrivateKeyInterface $key)
 {
     $privateKeyInfo = new Sequence(new Integer(self::VERSION), new OctetString($this->formatKey($key)), new ExplicitlyTaggedObject(0, CurveOidMapper::getCurveOid($key->getPoint()->getCurve())), new ExplicitlyTaggedObject(1, $this->encodePubKey($key)));
     return $privateKeyInfo->getBinary();
 }
Example #15
0
 private function initPublicKey()
 {
     $oid_sequence = new Sequence();
     $oid_sequence->addChild(new ObjectIdentifier('1.2.840.10045.2.1'));
     $oid_sequence->addChild(new ObjectIdentifier($this->getOID($this->values['crv'])));
     $this->addChild($oid_sequence);
     $bits = '04';
     $bits .= bin2hex(Base64Url::decode($this->values['x']));
     $bits .= bin2hex(Base64Url::decode($this->values['y']));
     $this->addChild(new BitString($bits));
 }
Example #16
0
    // this should contain a sequence of extensions
    $certExtensions = $certExtensions->getFirstChild();
    assert($certExtensions->getType() == Identifier::SEQUENCE);
    // now check all extensions and search for the SAN
    /** @var Object $extensionSequence */
    foreach ($certExtensions as $extensionSequence) {
        assert($extensionSequence->getType() == Identifier::SEQUENCE);
        assert($extensionSequence->getNumberofChildren() >= 2);
        $extensionSequenceChildren = $extensionSequence->getChildren();
        $objectIdentifier = $extensionSequenceChildren[0];
        /* @var ObjectIdentifier $objectIdentifier */
        assert($objectIdentifier->getType() == Identifier::OBJECT_IDENTIFIER);
        if ($objectIdentifier->getContent() == OID::CERT_EXT_SUBJECT_ALT_NAME) {
            // now we have the wanted octet string
            $octetString = $extensionSequenceChildren[1];
            /* @var OctetString $octetString */
            $octetStringBinary = $octetString->getBinaryContent();
            // At this point you may want to create the sequence from the binary value of
            // the octet string and parse its structure like we did so far.
            // However a more general approach would be to understand the format of the
            // contained SAN fields and implement them in SubjectAlternativeNames.
            $sequence = Sequence::fromBinary($octetStringBinary);
            echo 'This is the parsed content of the SAN certificate extension field so far:' . PHP_EOL;
            printObject($sequence);
            // The following does not work yet because PHPASN1 SAn does only support DNS and IP
            //SubjectAlternativeNames::fromBinary($octetStringBinary);
        }
    }
} catch (\Exception $exception) {
    echo '[ERROR] Caught exception:' . PHP_EOL . $exception->getMessage() . PHP_EOL;
}
Example #17
0
 /**
  * @param string $binaryData
  * @param int $offsetIndex
  *
  * @throws ParserException
  *
  * @return \FG\ASN1\Object
  */
 public static function fromBinary(&$binaryData, &$offsetIndex = 0)
 {
     if (strlen($binaryData) <= $offsetIndex) {
         throw new ParserException('Can not parse binary from data: Offset index larger than input size', $offsetIndex);
     }
     $identifierOctet = ord($binaryData[$offsetIndex]);
     if (Identifier::isContextSpecificClass($identifierOctet) && Identifier::isConstructed($identifierOctet)) {
         return ExplicitlyTaggedObject::fromBinary($binaryData, $offsetIndex);
     }
     switch ($identifierOctet) {
         case Identifier::BITSTRING:
             return BitString::fromBinary($binaryData, $offsetIndex);
         case Identifier::BOOLEAN:
             return Boolean::fromBinary($binaryData, $offsetIndex);
         case Identifier::ENUMERATED:
             return Enumerated::fromBinary($binaryData, $offsetIndex);
         case Identifier::INTEGER:
             return Integer::fromBinary($binaryData, $offsetIndex);
         case Identifier::NULL:
             return NullObject::fromBinary($binaryData, $offsetIndex);
         case Identifier::OBJECT_IDENTIFIER:
             return ObjectIdentifier::fromBinary($binaryData, $offsetIndex);
         case Identifier::RELATIVE_OID:
             return RelativeObjectIdentifier::fromBinary($binaryData, $offsetIndex);
         case Identifier::OCTETSTRING:
             return OctetString::fromBinary($binaryData, $offsetIndex);
         case Identifier::SEQUENCE:
             return Sequence::fromBinary($binaryData, $offsetIndex);
         case Identifier::SET:
             return Set::fromBinary($binaryData, $offsetIndex);
         case Identifier::UTC_TIME:
             return UTCTime::fromBinary($binaryData, $offsetIndex);
         case Identifier::GENERALIZED_TIME:
             return GeneralizedTime::fromBinary($binaryData, $offsetIndex);
         case Identifier::IA5_STRING:
             return IA5String::fromBinary($binaryData, $offsetIndex);
         case Identifier::PRINTABLE_STRING:
             return PrintableString::fromBinary($binaryData, $offsetIndex);
         case Identifier::NUMERIC_STRING:
             return NumericString::fromBinary($binaryData, $offsetIndex);
         case Identifier::UTF8_STRING:
             return UTF8String::fromBinary($binaryData, $offsetIndex);
         case Identifier::UNIVERSAL_STRING:
             return UniversalString::fromBinary($binaryData, $offsetIndex);
         case Identifier::CHARACTER_STRING:
             return CharacterString::fromBinary($binaryData, $offsetIndex);
         case Identifier::GENERAL_STRING:
             return GeneralString::fromBinary($binaryData, $offsetIndex);
         case Identifier::VISIBLE_STRING:
             return VisibleString::fromBinary($binaryData, $offsetIndex);
         case Identifier::GRAPHIC_STRING:
             return GraphicString::fromBinary($binaryData, $offsetIndex);
         case Identifier::BMP_STRING:
             return BMPString::fromBinary($binaryData, $offsetIndex);
         case Identifier::T61_STRING:
             return T61String::fromBinary($binaryData, $offsetIndex);
         case Identifier::OBJECT_DESCRIPTOR:
             return ObjectDescriptor::fromBinary($binaryData, $offsetIndex);
         default:
             // At this point the identifier may be >1 byte.
             if (Identifier::isConstructed($identifierOctet)) {
                 return new UnknownConstructedObject($binaryData, $offsetIndex);
             } else {
                 $identifier = self::parseBinaryIdentifier($binaryData, $offsetIndex);
                 $lengthOfUnknownObject = self::parseContentLength($binaryData, $offsetIndex);
                 $offsetIndex += $lengthOfUnknownObject;
                 return new UnknownObject($identifier, $lengthOfUnknownObject);
             }
     }
 }
Example #18
0
 /**
  * @param \Jose\Object\JWKInterface $key
  * @param string                    $data
  * @param string                    $R
  * @param string                    $S
  *
  * @return bool
  */
 private function verifyOpenSSLSignature(JWKInterface $key, $data, $R, $S)
 {
     $pem = ECKey::toPublic(new ECKey($key))->toPEM();
     $oid_sequence = new Sequence();
     $oid_sequence->addChildren([new Integer(gmp_strval($this->convertHexToGmp($R), 10)), new Integer(gmp_strval($this->convertHexToGmp($S), 10))]);
     return 1 === openssl_verify($data, $oid_sequence->getBinary(), $pem, $this->getHashAlgorithm());
 }
Example #19
0
 /**
  * @param string $hexKey
  * @param \FG\ASN1\Object|string $algorithmIdentifierString
  */
 public function __construct($hexKey, $algorithmIdentifierString = OID::RSA_ENCRYPTION)
 {
     parent::__construct(new Sequence(new ObjectIdentifier($algorithmIdentifierString), new NullObject()), new BitString($hexKey));
 }
Example #20
0
 private function initPrivateKey()
 {
     $this->addChild(new Integer(0));
     $oid_sequence = new Sequence();
     $oid_sequence->addChild(new ObjectIdentifier('1.2.840.113549.1.1.1'));
     $oid_sequence->addChild(new NullObject());
     $this->addChild($oid_sequence);
     $v = new Integer(0);
     $n = new Integer($this->fromBase64ToInteger($this->values['n']));
     $e = new Integer($this->fromBase64ToInteger($this->values['e']));
     $d = new Integer($this->fromBase64ToInteger($this->values['d']));
     $p = new Integer($this->fromBase64ToInteger($this->values['p']));
     $q = new Integer($this->fromBase64ToInteger($this->values['q']));
     $dp = array_key_exists('dp', $this->values) ? new Integer($this->fromBase64ToInteger($this->values['dp'])) : new Integer(0);
     $dq = array_key_exists('dq', $this->values) ? new Integer($this->fromBase64ToInteger($this->values['dq'])) : new Integer(0);
     $qi = array_key_exists('qi', $this->values) ? new Integer($this->fromBase64ToInteger($this->values['qi'])) : new Integer(0);
     $key_sequence = new Sequence();
     $key_sequence->addChild($v);
     $key_sequence->addChild($n);
     $key_sequence->addChild($e);
     $key_sequence->addChild($d);
     $key_sequence->addChild($p);
     $key_sequence->addChild($q);
     $key_sequence->addChild($dp);
     $key_sequence->addChild($dq);
     $key_sequence->addChild($qi);
     $key_octet_string = new OctetString(bin2hex($key_sequence->getBinary()));
     $this->addChild($key_octet_string);
 }
Example #21
0
mk+8uP4/x61UWBliqW08/1jMA83Qj2yuCAvhMhmuq8BvyWP4jnXoxTy3iTZFDXIj
5Vg2FlHWna0TMsU5jlmv8W/mu9U3+q7i1+339JrEZkrDLNDpJ23gqKBgC1OaNH7+
vicBRmoP5kj2X62XMaKT5TMFBZzTtSF7IaUjt9SVXQjG3aHIy7D1mOypvEw4LSS+
RvvJTezSQeCoLpX7HziRnoVUNkJWZHL2wG5rb/SJzpXwHkRa7R250vN8TFtSSsp4
YXzK6aks/qkxRJ1UkBLUAZlRasUD+zu8gyTIz0sNgFrOtx8EOzjZRiJ/vBiVOn88
Brf4i0D+fVXmJQIDAQABow8wDTALBgNVHQ8EBAMCAAIwCwYJKoZIhvcNAQEFA4IB
AQBfubKPTvmDGrDxoqCbPwFoPRC0STwPL2GV8f5sD/Sbyc0NoJdygUO2DvquGGn5
6UJCfLo1u6Dn4zuuDs3m6if86HTpAf9Z3a72ok2Tor/NFwYt+vDOrFY5F4bXDZkf
u4zuDLmjpj26Dk4te3BVohsLTXbvJ5a/TT2VanwNOyx85lXPxy3V8Rr1AwlmHZoz
DDbUGbe/noUDJCgMjvaKKvLykIhIcW+g6W7SOcKRflw5H8kzDv816XFODSC3X1Uw
o3aVy9du/0mH+g4HvyVVplO90tdoHD1gHUMZwuen4dbTzhWv4dtLFelWM5lGWbLE
Wn7kJghclgIxv10nkGyfrowt';
// OCSP response status according to
// https://tools.ietf.org/html/rfc6960#section-4.2.1
$validResponseStatuses = array(0 => 'Response has valid confirmations', 1 => 'Illegal confirmation request', 2 => 'Internal error in issuer', 3 => 'Try again later', 5 => 'Must sign the request', 6 => 'Request unauthorized');
$ocspResponse = Sequence::fromBinary(base64_decode($data));
/* @var Enumerated $responseStatus */
$elements = $ocspResponse->getChildren();
$responseStatus = $elements[0];
$responseStatusCode = $responseStatus->getContent();
echo PHP_EOL;
echo "OCSP response status: {$responseStatusCode} ({$validResponseStatuses[$responseStatusCode]})" . PHP_EOL;
/** @var ExplicitlyTaggedObject $responseBytes */
$responseBytes = $elements[1];
/** @var Sequence $responseBytesSequence */
$responseBytesSequence = $responseBytes->getContent();
/** @var ObjectIdentifier $responseType */
$responseType = $responseBytesSequence->getChildren()[0];
echo "ResponseType: {$responseType}" . PHP_EOL;
$response = $responseBytesSequence->getChildren()[1];
echo "Response (octet string): {$response}" . PHP_EOL;