In diesem Abschnitt erfahren Sie mehr über die Header und Nachrichtenkörper der Benachrichtigungsanforderung eines Webhooks. Es ist die Aufgabe Ihrer Anwendung, die Anforderung zu analysieren, die Signatur zu verifizieren und eine Antwort zu senden.
Anforderung
Wenn eine Meldung ausgelöst wird, sendet das Secure Edge Portal eine HTTP-POST-Anfrage an die konfigurierte URL des Webhooks. Die Header und der JSON-Body der POST-Anfrage sind nachfolgend beschrieben.
Die Benachrichtigungsanforderung des Webhooks sieht wie folgt aus:
curl -X POST \
-H '<your_additional_headers>' \
-H "Content-type:application/json"
-H "X-Iotium-Signature:<IOTIUM_SIGNATURE>" \
-H "X-Iotium-Timestamp:<TIMESTAMP>" \
-d '{<WEBHOOK_NOTIFICATION_POST_BODY>}' \
'<your_webhook_url>'
Header
Die Benachrichtigungsanforderung des Webhooks beinhaltet die folgenden ioTium-spezifischen Header:
Header | Beschreibung |
---|---|
Ihre zusätzlichen Header | Zusätzliche Header der HTTP-Anfrage, die Sie bei einer Benachrichtigungsanforderung integrieren möchten. Bei dem Hinzufügen des Webhooks können Sie bis zu drei Header mit einer Gesamtlänge von bis zu 2.048 Zeichen festlegen. |
X-Iotium-Signatur | Die Signatur wird durch den Zeitstempel und den Nachrichtenkörper dieser Benachrichtigungsanforderung erstellt. Sehen Sie sich Verifying Webhook Signature an, um weitere Informationen zu erhalten. |
X-Iotium-Zeitstempel | POSIX-Zeitstempel in Millisekunden, der festlegt, zu welchem Zeitpukt wir Ihnen diese Benachrichtigungsanforderung schicken. |
Nachrichtenkörper
Der Nachrichtenkörper des Webhooks (Payload) ist ein JSON-Objekt, das die Meldung beschreibt, die die Benachrichtigung ausgelöst hat. Er beschreibt ebenfalls die Zielressource (iNode, iNode-Netzwerk, Dienst etc.), an die sich die Meldung richtet.
In den folgenden Abschnitten wird der Payload für jeden Meldungstyp beschrieben.
iNode-Status
Der Payload iNode-Status der Meldung beinhaltet die folgenden Felder:
Feld | Beschreibung |
---|---|
type | Metriktyp der ausgelösten Meldung. Für diese Meldung ist der Metriktyp NODE_STATE_CHANGE. |
display_type | Anzeigbarer Text in dem Feld typ. Für diese Meldung ist der anzeigbare Text iNode-Status. |
id | Einzigartiger Identifikator für Ihre Benachrichtigung. Wenn wir versuchen, die Benachrichtigung erneut zu senden, bleibt die ID dieselbe. |
alert | Details Ihrer ausgelösten Meldung: |
event_occurred_at | Verfallszeit (Datum und Uhrzeit im ISO-8601-Format) Ihrer ausgelösten Meldung. |
created_at | Verfallszeit (Datum und Uhrzeit im ISO-8601-Format), als wir Ihre ausgelöste Meldung erkannt haben. |
organization | Details Ihrer Organisation: |
node | Details Ihres iNode, das die Meldung ausgelöst hat: |
profile | Details Ihres iNode-Profils: |
hardware_serial_number | Seriennummer Ihres iNode, das die Meldung ausgelöst hat. |
status | Status Ihres iNode, das die Meldung ausgelöst hat: |
Der Payload iNode-Status der Meldung sieht wie folgt aus:
{
"type":"NODE_STATE_CHANGE",
"node":{
"id":"<your_inode_id>",
"name":"<your_inode_name>"
},
"profile":{
"id":"<your_inode_profile_id>",
"name":"<your_inode_profile_name>"
},
"id":"<your_notification_id>",
"status":"<your_inode_status>",
"display_type":"iNode Status",
"hardware_serial_number":"<your_inode_serial_number>",
"organization":{
"id":"<your_org_id>",
"name":"<your_org_name>"
},
"alert":{
"id":"<your_alert_id>",
"name":"<your_alert_name>"
},
"created_at":"2020-01-01T00:00:00.000000000Z",
"event_occured_at":"2020-01-01T00:00:00.000000000Z"
}
Verbindungsstatus des Remote-Netzwerks
Der Payload Verbindungsstatus des Remote-Netzwerks der Meldung beinhaltet die folgenden Felder:
Feld | Beschreibung |
---|---|
type | Metriktyp der ausgelösten Meldung. Für diese Meldung ist der Metriktyp TUNNEL_STATE_CHANGE. |
display_type | Anzeigbarer Text in dem Feld typ. Für diese Meldung ist der anzeigbare Text Verbindungsstatus des Remote-Netzwerks. |
id | Einzigartiger Identifikator für Ihre Benachrichtigung. Wenn wir versuchen, die Benachrichtigung erneut zu senden, bleibt die ID dieselbe. |
alert | Details Ihrer ausgelösten Meldung: |
event_occurred_at | Verfallszeit (Datum und Uhrzeit im ISO-8601-Format) Ihrer ausgelösten Meldung. |
created_at | Verfallszeit (Datum und Uhrzeit im ISO-8601-Format), als wir Ihre ausgelöste Meldung erkannt haben. |
organization | Details Ihrer Organisation: |
node | Details Ihres iNode, das die Meldung ausgelöst hat: |
profile | Details Ihres iNode-Profils: |
hardware_serial_number | Seriennummer Ihres iNode, das die Meldung ausgelöst hat. |
Netzwerk | Details Ihres lokalen Netzwerks: |
peer_node | Details Ihres Remote-iNode: |
peer_node_profile | Details des Profils Ihres Remote-iNode: |
peer_network | Details des Remote-Netzwerks in Ihrem Remote-iNode: |
Tunnel | Details der Verbindung Ihres lokalen Netzwerks: |
peer_tunnel | Details der Verbindung Ihres Remote-Netzwerks: |
status | Status Ihrer Verbindung mit dem Remote-Netzwerk, die die Meldung ausgelöst hat: |
Der Payload Verbindungsstatus des Remote-Netzwerks der Meldung sieht wie folgt aus:
{
"type":"TUNNEL_STATE_CHANGE",
"node":{
"id":"<your_inode_id>",
"name":"<your_inode_name>"
},
"profile":{
"id":"<your_inode_profile_id>",
"name":"<your_inode_profile_name>"
},
"network":{
"id":"<your_network_id>",
"name":"<your_network_name>"
},
"peer_node":{
"id":"<your_remote_inode_id>",
"name":"<your_remote_inode_name>"
},
"peer_node_profile":{
"id":"<your_remote_inode_profile_id>",
"name":"<your_remote_inode_profile_name>"
},
"peer_network":{
"id":"<your_remote_network_id>",
"name":"<your_remote_network_name>"
},
"tunnel":{
"id":"<your_network_connection_id>",
"name":"<your_network_connection_name>"
},
"peer_tunnel":{
"id":"<your_remote_network_connection_id>",
"name":"<your_remote_network_connection_name>"
},
"id":"<your_notification_id>",
"status":"<your_remote_network_connection_status>",
"display_type":"Remote Network Connection Status",
"hardware_serial_number":"<your_inode_serial_number>",
"organization":{
"id":"<your_org_id>",
"name":"<your_org_name>"
},
"alert":{
"id":"<your_alert_id>",
"name":"<your_alert_name>"
},
"created_at":"2020-01-01T00:00:00.000000000Z",
"event_occured_at":"2020-01-01T00:00:00.000000000Z"
}
Dienst-Status
Der Payload Dienst-Status der Meldung beinhaltet die folgenden Felder:
Feld | Beschreibung |
---|---|
type | Metriktyp der ausgelösten Meldung. Für diese Meldung ist der Metriktyp SERVICE_STATE_CHANGE. |
display_type | Anzeigbarer Text in dem Feld typ. Für diese Meldung ist der anzeigbare Text Dienst-Status. |
id | Einzigartiger Identifikator für Ihre Benachrichtigung. Wenn wir versuchen, die Benachrichtigung erneut zu senden, bleibt die ID dieselbe. |
alert | Details Ihrer ausgelösten Meldung: |
event_occurred_at | Verfallszeit (Datum und Uhrzeit im ISO-8601-Format) Ihrer ausgelösten Meldung. |
created_at | Verfallszeit (Datum und Uhrzeit im ISO-8601-Format), als wir Ihre ausgelöste Meldung erkannt haben. |
organization | Details Ihrer Organisation: |
node | Details Ihres iNode, das die Meldung ausgelöst hat: |
profile | Details Ihres iNode-Profils: |
hardware_serial_number | Seriennummer Ihres iNode. |
service | Details Ihres Dienstes, der das Ereignis ausgelöst hat: |
container_status | Status aller Container in Ihrem Dienst, als Schlüssel-Wert-Paare aus dem Container-Namen und dem Container-Status ausgedrückt. Dies sind die möglichen Status eines Containers: |
running_containers | Anzahl der in Betrieb befindlichen Container und die gesamte Anzahl an Containern, getrennt durch einen Schrägstrich ( / ). |
status | Status Ihres Dienstes, der die Meldung ausgelöst hat: |
Der Payload Dienst-Status der Meldung sieht wie folgt aus:
{
"type":"SERVICE_STATE_CHANGE",
"node":{
"id":"<your_inode_id>",
"name":"<your_inode_name>"
},
"profile":{
"id":"<your_inode_profile_id>",
"name":"<your_inode_profile_name>"
},
"service":{
"id":"<your_service_id>",
"name":"<your_service_name>"
},
"id":"<your_notification_id>",
"status":"<your_service_status>",
"display_type":"Service Status",
"hardware_serial_number":"<your_inode_serial_number>",
"container_status":{"<your_container_name>":"<your_container_status>"},
"running_containers":"<your_running_containers_count>/<your_total_containers_count>",
"organization":{
"id":"<your_org_id>",
"name":"<your_org_name>"
},
"alert":{
"id":"<your_alert_id>",
"name":"<your_alert_name>"
},
"created_at":"2020-01-01T00:00:00.000000000Z",
"event_occured_at":"2020-01-01T00:00:00.000000000Z"
}
Verfallszeit des eigenständigen Modus
Der Payload Verfallszeit des eigenständigen Modus der Meldung beinhaltet die folgenden Felder:
Feld | Beschreibung |
---|---|
type | Metriktyp der ausgelösten Meldung. Für diese Meldung ist der Metriktyp HEADLESS_EXPIRY. |
display_type | Anzeigbarer Text in dem Feld typ. Für diese Meldung ist der anzeigbare Text Verfallszeit des eigenständigen Modus. |
id | Einzigartiger Identifikator für Ihre Benachrichtigung. Wenn wir versuchen, die Benachrichtigung erneut zu senden, bleibt die ID dieselbe. |
alert | Details Ihrer ausgelösten Meldung: |
event_occurred_at | Verfallszeit (Datum und Uhrzeit im ISO-8601-Format) Ihrer ausgelösten Meldung. |
created_at | Verfallszeit (Datum und Uhrzeit im ISO-8601-Format), als wir Ihre ausgelöste Meldung erkannt haben. |
organization | Details Ihrer Organisation: |
node | Details Ihres iNode, das die Meldung ausgelöst hat: |
profile | Details Ihres iNode-Profils: |
hardware_serial_number | Seriennummer Ihres iNode, das das Ereignis ausgelöst hat. |
max_headless_time | Die Verfallszeit des eigenständigen Modus Ihres iNode in Minuten. |
headless_expire_at | Verfallszeit (Datum und Uhrzeit im ISO-8601-Format) des eigenständigen Modus Ihres iNode. |
headless_expire_percent | Verbrauchte Verfallszeit des eigenständigen Modus Ihres iNode in Prozent. Falls 80 % oder ein höherer Anteil der Verfallszeit abgelaufen ist, wird die Meldung ausgelöst. |
is_headless_expired | Ist der eigenständige Modus Ihres iNode abgelaufen? (true or false.) Falls dies true ist, wird die Meldung ausgelöst. |
Der Payload Verfallszeit des eigenständigen Modus der Meldung sieht wie folgt aus:
{
"type":"HEADLESS_EXPIRY",
"node":{
"id":"<your_inode_id>",
"name":"<your_inode_name>"
},
"profile":{
"id":"<your_inode_profile_id>",
"name":"<your_inode_profile_name>"
},
"id":"<your_notification_id>",
"max_headless_time":10,
"headless_expire_at":"2020-01-01T00:00:00.000000000Z",
"headless_expire_percent":80,
"is_headless_expired":false,
"last_contact_at":"2020-01-01T00:00:00.000000000Z",
"hardware_serial_number":"<your_inode_serial_number>",
"display_type":"Standalone Mode Expiry"
"organization":{
"id":"<your_org_id>",
"name":"<your_org_name>"
},
"alert":{
"id":"<your_alert_id>",
"name":"<your_alert_name>"
},
"created_at":"2020-01-01T00:00:00.000000000Z",
"event_occured_at":"2020-01-01T00:00:00.000000000Z"
}
Verfallszeit des iNode-Zertifikats
Der Payload Verfallszeit des iNode-Zertifikats der Meldung beinhaltet die folgenden Felder:
Feld | Beschreibung |
---|---|
type | Metriktyp der ausgelösten Meldung. Für diese Meldung ist der Metriktyp CERT_EXPIRY. |
display_type | Anzeigbarer Text in dem Feld typ. Für diese Meldung ist der anzeigbare Text Verfallszeit des iNode-Zertifikats. |
id | Einzigartiger Identifikator für Ihre Benachrichtigung. Wenn wir versuchen, die Benachrichtigung erneut zu senden, bleibt die ID dieselbe. |
alert | Details Ihrer ausgelösten Meldung: |
event_occurred_at | Verfallszeit (Datum und Uhrzeit im ISO-8601-Format) Ihrer ausgelösten Meldung. |
created_at | Verfallszeit (Datum und Uhrzeit im ISO-8601-Format), als wir Ihre ausgelöste Meldung erkannt haben. |
organization | Details Ihrer Organisation: |
node | Details Ihres iNode, das die Meldung ausgelöst hat: |
profile | Details Ihres iNode-Profils: |
hardware_serial_number | Seriennummer Ihres iNode, das das Ereignis ausgelöst hat. |
cert_expire_at | Verfallszeit (Datum und Uhrzeit im ISO-8601-Format) Ihres iNode-Zertifikats. |
cert_expire_percent | Verbrauchte Verfallszeit Ihres iNode-Zertifikats in Prozent. Falls 80 % oder ein höherer Anteil der Verfallszeit abgelaufen ist, wird die Meldung ausgelöst. |
is_cert_expired | Ist Ihr iNode-Zertifikat abgelaufen? (true or false.) Falls dies true ist, wird die Meldung ausgelöst. |
Der Payload Verfallszeit des iNode-Zertifikats der Meldung sieht wie folgt aus:
{
"type":"CERT_EXPIRY",
"id":"<your_notification_id>",
"certs":[{
"node":{
"id":"<your_inode_id>",
"name":"<your_inode_name>"
},
"profile":{
"id":"<your_inode_profile_id>",
"name":"<your_inode_profile_name>"
},
"organization":{
"id":"<your_org_id>",
"name":"<your_org_name>"
},
"cert_expire_at":"2020-01-01T00:00:00.000000000Z",
"hardware_serial_number":"<your_inode_serial_number>",
"cert_expire_percent":80,
"is_cert_expired":false
}],
"alert":{
"id":"<your_alert_id>",
"name":"<your_alert_name>"
},
"display_type":"iNode Certificate Expiry",
"created_at":"2020-01-01T00:00:00.000000000Z",
"event_occured_at":"2020-01-01T00:00:00.000000000Z"
}
Änderung der IP-Adresse des iNode
Der Payload Änderung der IP-Adresse des iNode der Meldung beinhaltet die folgenden Felder:
Feld | Beschreibung |
---|---|
type | Metriktyp der ausgelösten Meldung. Für diese Meldung ist der Metriktyp NODE_IP_CHANGE. |
display_type | Anzeigbarer Text in dem Feld typ. Für diese Meldung ist der anzeigbare Text Änderung der IP-Adresse des iNode. |
id | Einzigartiger Identifikator für Ihre Benachrichtigung. Wenn wir versuchen, die Benachrichtigung erneut zu senden, bleibt die ID dieselbe. |
alert | Details Ihrer ausgelösten Meldung: |
event_occurred_at | Verfallszeit (Datum und Uhrzeit im ISO-8601-Format) Ihrer ausgelösten Meldung. |
created_at | Verfallszeit (Datum und Uhrzeit im ISO-8601-Format), als wir Ihre ausgelöste Meldung erkannt haben. |
organization | Details Ihrer Organisation: |
node | Details Ihres iNode, das die Meldung ausgelöst hat: |
profile | Details Ihres iNode-Profils: |
hardware_serial_number | Seriennummer Ihres iNode, das das Ereignis ausgelöst hat. |
from | IP-Adresse Ihres iNode vor der Änderung. |
to | IP-Adresse Ihres iNode nach der Änderung. |
status | Die Änderung der IP-Adresse Ihres iNode, die die Meldung ausgelöst hat: |
Der Payload Änderung der IP-Adresse des iNode der Meldung sieht wie folgt aus:
{
"type":"NODE_IP_CHANGE",
"node":{
"id":"<your_inode_id>",
"name":"<your_inode_name>"
},
"profile":{
"id":"<your_inode_profile_id>",
"name":"<your_inode_profile_name>"
},
"id":"<your_notification_id>",
"status":"<your_inode_which_ip_changed>",
"display_type":"iNode IP Address Change",
"hardware_serial_number":"<your_inode_serial_number>",
"from":"<your_from_ip_address>",
"to":"<your_to_ip_address>",
"organization":{
"id":"<your_org_id>",
"name":"<your_org_name>"
},
"alert":{
"id":"<your_alert_id>",
"name":"<your_alert_name>"
},
"created_at":"2020-01-01T00:00:00.000000000Z",
"event_occured_at":"2020-01-01T00:00:00.000000000Z"
}
Upgrade-Status des iNode
Der Payload Upgrade-Status des iNode der Meldung beinhaltet die folgenden Felder:
Feld | Beschreibung |
---|---|
type | Metriktyp der ausgelösten Meldung. Für diese Meldung ist der Metriktyp NODE_UPGRADE. |
display_type | Anzeigbarer Text in dem Feld typ. Für diese Meldung ist der anzeigbare Text Upgrade-Status des iNode. |
id | Einzigartiger Identifikator für Ihre Benachrichtigung. Wenn wir versuchen, die Benachrichtigung erneut zu senden, bleibt die ID dieselbe. |
alert | Details Ihrer ausgelösten Meldung: |
event_occurred_at | Verfallszeit (Datum und Uhrzeit im ISO-8601-Format) Ihrer ausgelösten Meldung. |
created_at | Verfallszeit (Datum und Uhrzeit im ISO-8601-Format), als wir Ihre ausgelöste Meldung erkannt haben. |
organization | Details Ihrer Organisation: |
node | Details Ihres iNode, das die Meldung ausgelöst hat: |
profile | Details Ihres iNode-Profils: |
hardware_serial_number | Seriennummer Ihres iNode, das das Ereignis ausgelöst hat. |
from | Version Ihres iNode vor dem Upgrade (falls der Upgrade-Status ERFOLGREICH ist). |
to | Version Ihres iNode nach dem Upgrade (falls der Upgrade-Status ERFOLGREICH ist). |
status | Upgrade-Status Ihres iNode, der die Meldung ausgelöst hat: |
Der Payload Upgrade-Status des iNode der Meldung sieht wie folgt aus:
{
"type":"NODE_UPGRADE",
"node":{
"id":"<your_inode_id>",
"name":"<your_inode_name>"
},
"profile":{
"id":"<your_inode_profile_id>",
"name":"<your_inode_profile_name>"
},
"id":"<your_notification_id>",
"status":"<your_inode_upgrade_status>",
"display_type":"iNode Upgrade Status",
"hardware_serial_number":"<your_inode_serial_number>",
"from":"<your_from_version>",
"to":"<your_to_version>",
"organization":{
"id":"<your_org_id>",
"name":"<your_org_name>"
},
"alert":{
"id":"<your_alert_id>",
"name":"<your_alert_name>"
},
"created_at":"2020-01-01T00:00:00.000000000Z",
"event_occured_at":"2020-01-01T00:00:00.000000000Z"
}
Antwort auf eine Benachrichtigungsanforderung des Webhooks
Bei allen Benachrichtigungsanforderungen des Webhooks bedeutet die Antwort 2XX, dass diese erfolgreich waren. Die Antwort 3XX und eine Weiterleitung der Anforderung können bis zu fünfmal erfolgen. Alle anderen Anforderungen gelten als fehlgeschlagen.
Zeitüberschreitung und erneuter Versuch
Es kann bis zu fünf Sekunden dauern, bis eine Benachrichtigungsanforderung des Webhooks abgeschlossen ist. Falls die Verbindung fehlschlägt, die Zeit überschritten wird oder die Anforderung mit einer 5XX-Antwort fehlschlägt, wird nach einer Minute ein erneuter Versuch unternommen. Sollte die Anforderung nach dem erneuten Versuch weiterhin fehlschlagen, wird abermals ein erneuter Versuch unternommen.
Sollte die Benachrichtigungsanforderung des Webhooks 10-mal nacheinander fehlschlagen, tritt ein WEBHOOK_FAILED-Ereignis auf und der Webhook wird deaktiviert.
Webhook-Signatur verifizieren
Die HTTP-POST-Anfrage des Webhooks beinhaltet einen Header mit einer Signatur, die von einem Hashwert des Bodys der Benachrichtigungsanforderung erstellt wird. Wir fordern Sie, sobald Ihre Anwendung die Anfrage erhält, auf, die Signatur zu verifizieren, indem Sie denselben Hashwert berechnen und mit dem Hashwert in dem Header vergleichen. Durch eine Verifizierung der Signatur können Sie die Integrität und Herkunft der Webhook-Benachrichtigung validieren.
Befolgen Sie diese Schritte, um die Signatur zu verifizieren:
- Extrahieren Sie den Zeitstempel aus dem Header-Feld X-Iotium-Zeitstempel und die Signatur aus dem Header-Feld X-Iotium-Signatur der Benachrichtigungsanforderung des Webhooks.
- Berechnen Sie die Differenz zwischen dem aktuellen Zeitstempel und dem extrahierten Zeitstempel. Vertrauen Sie nicht auf den Payload, falls die Differenz außerhalb Ihres Toleranzbereichs liegt.
- Berechnen Sie auf folgende Art und Weise die erwartete Signatur:Shell
Das Secret ist der Wert des Felds Secret, den Sie aus der Antwort auf die POST-Anfrage zum Hinzufügen des Webhooks gespeichert haben. Nutzen Sie für diese Berechnung keinen formatierten Anfrage-Body.signature = Hex-encoded(HMACSHA256(<>,>,>
Dies ist der Ausschnitt eines Codes zur Berechnung der erwarteten Signatur mit Python (Version 3.8.2):Python
Dies ist der Ausschnitt eines Codes zur Berechnung der erwarteten Signatur mit Java (Version 1.8.0_191):def genSignature(secret:str, timestamp:str, body:str) -> str: import hmac import hashlib message:str = "%s.%s" % (timestamp, body) return hmac.new(secret.encode(), message.encode(), hashlib.sha256).hexdigest()
Java
4. Vergleichen Sie die erwartete Signatur mit der aus dem Header-Feld X-Iotium-Signatur extrahierten Signatur. Falls diese identisch sind, können Sie auf den Payload vertrauen. Andernfalls vertrauen Sie nicht auf den Payload.import java.nio.charset.Charset; import java.security.GeneralSecurityException; import javax.crypto.Mac; import javax.crypto.spec.SecretKeySpec; public String getSignature(String secret, String timestamp, String body) { String HMAC_SHA256_ALGORITHM = "HmacSHA256"; String hmac_hex = null; try { SecretKeySpec signingKey = new SecretKeySpec(secret.getBytes(Charset.defaultCharset()), HMAC_SHA256_ALGORITHM); Mac mac = Mac.getInstance(HMAC_SHA256_ALGORITHM); mac.init(signingKey); String message = timestamp.concat(".").concat(body); byte[] rawHmac = mac.doFinal(message.getBytes(Charset.defaultCharset())); hmac_hex = bytesToHex(rawHmac); } catch (GeneralSecurityException e) { throw new IllegalArgumentException(); } return hmac_hex; } public String bytesToHex(byte[] bytes) { char[] hexArray = "0123456789abcdef".toCharArray(); char[] hexChars = new char[bytes.length * 2]; for (int j = 0; j < bytes.length; j++) { int v = bytes[j] & 0xFF; hexChars[j * 2] = hexArray[v >>> 4]; hexChars[j * 2 + 1] = hexArray[v & 0x0F]; } return new String(hexChars); }