Les traductions sont fournies par des outils de traduction automatique. En cas de conflit entre le contenu d'une traduction et celui de la version originale en anglais, la version anglaise prévaudra.
Important
Avis de fin de support : les clients existants pourront utiliser Amazon QLDB jusqu'à la fin du support le 31 juillet 2025. Pour plus de détails, consultez Migrer un registre Amazon QLDB vers Amazon Aurora PostgreSQL
Dans ce didacticiel, vous allez vérifier un hachage de révision de document et un hachage de bloc de journal dans un registre Amazon QLDB à l'aide de l'API QLDB via un SDK. AWS Vous utilisez également le pilote QLDB pour demander la révision du document.
Prenons l'exemple d'une révision de document contenant des données relatives à un véhicule dont le numéro d'identification du véhicule (VIN) est deKM8SRDHF6EU074761
. La révision du document se trouve dans une VehicleRegistration
table figurant dans un registre nommévehicle-registration
. Supposons que vous souhaitiez vérifier l'intégrité de la révision du document pour ce véhicule et du bloc de journal qui contient la révision.
Note
Pour un article de AWS blog détaillé qui aborde la valeur de la vérification cryptographique dans le contexte d'un cas d'utilisation réaliste, consultez la section Vérification cryptographique dans le monde réel avec Amazon
Rubriques
Prérequis
Avant de commencer, assurez-vous d'effectuer les opérations suivantes :
-
Configurez le pilote QLDB pour la langue de votre choix en remplissant les conditions requises ci-dessous. Commencer à utiliser le pilote Amazon QLDB Cela inclut l'inscription AWS, l'octroi d'un accès programmatique pour le développement et la configuration de votre environnement de développement.
-
Suivez les étapes 1 et 2 ci-dessous Commencer à utiliser la console Amazon QLDB pour créer un registre nommé
vehicle-registration
et le charger avec des exemples de données prédéfinis.
Passez ensuite en revue les étapes suivantes pour savoir comment fonctionne la vérification, puis exécutez l'exemple de code complet du début à la fin.
Étape 1 : demander un résumé
Avant de pouvoir vérifier les données, vous devez d'abord demander un résumé à partir de votre registre vehicle-registration
pour une utilisation ultérieure.
// Get a digest
GetDigestRequest digestRequest = new GetDigestRequest().withName(ledgerName);
GetDigestResult digestResult = client.getDigest(digestRequest);
java.nio.ByteBuffer digest = digestResult.getDigest();
// expectedDigest is the buffer we will use later to compare against our calculated digest
byte[] expectedDigest = new byte[digest.remaining()];
digest.get(expectedDigest);
Étape 2 : demander la révision du document
Utilisez le pilote QLDB pour interroger les adresses de bloc, les hachages et les IDs documents associés au VIN. KM8SRDHF6EU074761
// Retrieve info for the given vin's document revisions
Result result = driver.execute(txn -> {
final String query = String.format("SELECT blockAddress, hash, metadata.id FROM _ql_committed_%s WHERE data.VIN = '%s'", tableName, vin);
return txn.execute(query);
});
Étape 3 : demander une preuve pour la révision
Parcourez les résultats de la requête et utilisez chaque adresse de bloc et chaque identifiant de document ainsi que le nom du grand livre pour soumettre une GetRevision
demande. Pour obtenir une preuve de la révision, vous devez également fournir l'adresse du pourboire figurant dans le résumé enregistré précédemment. Cette opération d'API renvoie un objet qui inclut la révision du document et la preuve de la révision.
Pour plus d'informations sur la structure de révision et son contenu, consultezInterrogation des métadonnées d'un document.
for (IonValue ionValue : result) {
IonStruct ionStruct = (IonStruct)ionValue;
// Get the requested fields
IonValue blockAddress = ionStruct.get("blockAddress");
IonBlob hash = (IonBlob)ionStruct.get("hash");
String metadataId = ((IonString)ionStruct.get("id")).stringValue();
System.out.printf("Verifying document revision for id '%s'%n", metadataId);
String blockAddressText = blockAddress.toString();
// Submit a request for the revision
GetRevisionRequest revisionRequest = new GetRevisionRequest()
.withName(ledgerName)
.withBlockAddress(new ValueHolder().withIonText(blockAddressText))
.withDocumentId(metadataId)
.withDigestTipAddress(digestResult.getDigestTipAddress());
// Get a result back
GetRevisionResult revisionResult = client.getRevision(revisionRequest);
...
}
Ensuite, récupérez la preuve de la révision demandée.
L'API QLDB renvoie la preuve sous forme de chaîne de la liste ordonnée des hachages de nœuds. Pour convertir cette chaîne en une liste de la représentation binaire des hachages de nœuds, vous pouvez utiliser un lecteur Ion de la bibliothèque Amazon Ion. Pour plus d'informations sur l'utilisation de la bibliothèque Ion, consultez le livre de recettes Amazon Ion
Dans cet exemple, vous utilisez IonReader
pour effectuer la conversion binaire.
String proofText = revisionResult.getProof().getIonText();
// Take the proof and convert it to a list of byte arrays
List<byte[]> internalHashes = new ArrayList<>();
IonReader reader = SYSTEM.newReader(proofText);
reader.next();
reader.stepIn();
while (reader.next() != null) {
internalHashes.add(reader.newBytes());
}
Étape 4 : Recalculer le résumé à partir de la révision
Utilisez la liste des hachages de la preuve pour recalculer le résumé, en commençant par le hachage de révision. Tant que le résumé précédemment enregistré est connu et fiable en dehors de QLDB, l'intégrité de la révision du document est prouvée si le hachage du résumé recalculé correspond au hachage du résumé enregistré.
// Calculate digest
byte[] calculatedDigest = internalHashes.stream().reduce(hash.getBytes(), BlockHashVerification::dot);
boolean verified = Arrays.equals(expectedDigest, calculatedDigest);
if (verified) {
System.out.printf("Successfully verified document revision for id '%s'!%n", metadataId);
} else {
System.out.printf("Document revision for id '%s' verification failed!%n", metadataId);
return;
}
Étape 5 : Demandez une preuve pour le bloc de journal
Ensuite, vous devez vérifier le bloc de journal qui contient la révision du document.
Utilisez l'adresse de blocage et l'adresse du pourboire figurant dans le résumé que vous avez enregistré à l'étape 1 pour soumettre une GetBlock
demande. Comme pour la GetRevision
demande de l'étape 2, vous devez à nouveau fournir l'adresse du pourboire figurant dans le résumé enregistré pour obtenir une preuve du blocage. Cette opération d'API renvoie un objet qui inclut le bloc et la preuve du bloc.
Pour plus d'informations sur la structure des blocs de journal et son contenu, consultezContenu du journal dans Amazon QLDB.
// Submit a request for the block
GetBlockRequest getBlockRequest = new GetBlockRequest()
.withName(ledgerName)
.withBlockAddress(new ValueHolder().withIonText(blockAddressText))
.withDigestTipAddress(digestResult.getDigestTipAddress());
// Get a result back
GetBlockResult getBlockResult = client.getBlock(getBlockRequest);
Ensuite, récupérez le hachage du bloc et la preuve à partir du résultat.
Dans cet exemple, vous l'utilisez IonLoader
pour charger l'objet du bloc dans un IonDatagram
conteneur.
String blockText = getBlockResult.getBlock().getIonText();
IonDatagram datagram = SYSTEM.getLoader().load(blockText);
ionStruct = (IonStruct)datagram.get(0);
final byte[] blockHash = ((IonBlob)ionStruct.get("blockHash")).getBytes();
Vous pouvez également IonLoader
charger la preuve dans unIonDatagram
.
proofText = getBlockResult.getProof().getIonText();
// Take the proof and create a list of hash binary data
datagram = SYSTEM.getLoader().load(proofText);
ListIterator<IonValue> listIter = ((IonList)datagram.iterator().next()).listIterator();
internalHashes.clear();
while (listIter.hasNext()) {
internalHashes.add(((IonBlob)listIter.next()).getBytes());
}
Étape 6 : Recalculer le condensé à partir du bloc
Utilisez la liste des hachages de la preuve pour recalculer le condensé, en commençant par le hachage du bloc. Tant que le condensé précédemment enregistré est connu et fiable en dehors de QLDB, l'intégrité du bloc est prouvée si le hachage du condensé recalculé correspond au hachage du condensé enregistré.
// Calculate digest
calculatedDigest = internalHashes.stream().reduce(blockHash, BlockHashVerification::dot);
verified = Arrays.equals(expectedDigest, calculatedDigest);
if (verified) {
System.out.printf("Block address '%s' successfully verified!%n", blockAddressText);
} else {
System.out.printf("Block address '%s' verification failed!%n", blockAddressText);
}
Les exemples de code précédents utilisent la dot
fonction suivante lors du recalcul du condensé. Cette fonction prend une entrée de deux hachages, les trie, les concatène, puis renvoie le hachage du tableau concaténé.
/**
* Takes two hashes, sorts them, concatenates them, and then returns the
* hash of the concatenated array.
*
* @param h1
* Byte array containing one of the hashes to compare.
* @param h2
* Byte array containing one of the hashes to compare.
* @return the concatenated array of hashes.
*/
public static byte[] dot(final byte[] h1, final byte[] h2) {
if (h1.length != HASH_LENGTH || h2.length != HASH_LENGTH) {
throw new IllegalArgumentException("Invalid hash.");
}
int byteEqual = 0;
for (int i = h1.length - 1; i >= 0; i--) {
byteEqual = Byte.compare(h1[i], h2[i]);
if (byteEqual != 0) {
break;
}
}
byte[] concatenated = new byte[h1.length + h2.length];
if (byteEqual < 0) {
System.arraycopy(h1, 0, concatenated, 0, h1.length);
System.arraycopy(h2, 0, concatenated, h1.length, h2.length);
} else {
System.arraycopy(h2, 0, concatenated, 0, h2.length);
System.arraycopy(h1, 0, concatenated, h2.length, h1.length);
}
MessageDigest messageDigest;
try {
messageDigest = MessageDigest.getInstance("SHA-256");
} catch (NoSuchAlgorithmException e) {
throw new IllegalStateException("SHA-256 message digest is unavailable", e);
}
messageDigest.update(concatenated);
return messageDigest.digest();
}
Exécutez l'exemple de code complet
Exécutez l'exemple de code complet comme suit pour effectuer toutes les étapes précédentes du début à la fin.
import com.amazon.ion.IonBlob;
import com.amazon.ion.IonDatagram;
import com.amazon.ion.IonList;
import com.amazon.ion.IonReader;
import com.amazon.ion.IonString;
import com.amazon.ion.IonStruct;
import com.amazon.ion.IonSystem;
import com.amazon.ion.IonValue;
import com.amazon.ion.system.IonSystemBuilder;
import com.amazonaws.services.qldb.AmazonQLDB;
import com.amazonaws.services.qldb.AmazonQLDBClientBuilder;
import com.amazonaws.services.qldb.model.GetBlockRequest;
import com.amazonaws.services.qldb.model.GetBlockResult;
import com.amazonaws.services.qldb.model.GetDigestRequest;
import com.amazonaws.services.qldb.model.GetDigestResult;
import com.amazonaws.services.qldb.model.GetRevisionRequest;
import com.amazonaws.services.qldb.model.GetRevisionResult;
import com.amazonaws.services.qldb.model.ValueHolder;
import java.security.MessageDigest;
import java.security.NoSuchAlgorithmException;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.List;
import java.util.ListIterator;
import software.amazon.awssdk.regions.Region;
import software.amazon.awssdk.services.qldbsession.QldbSessionClient;
import software.amazon.awssdk.services.qldbsession.QldbSessionClientBuilder;
import software.amazon.qldb.QldbDriver;
import software.amazon.qldb.Result;
public class BlockHashVerification {
private static final IonSystem SYSTEM = IonSystemBuilder.standard().build();
private static final QldbDriver driver = createQldbDriver();
private static final AmazonQLDB client = AmazonQLDBClientBuilder.standard().build();
private static final String region = "us-east-1";
private static final String ledgerName = "vehicle-registration";
private static final String tableName = "VehicleRegistration";
private static final String vin = "KM8SRDHF6EU074761";
private static final int HASH_LENGTH = 32;
/**
* Create a pooled driver for creating sessions.
*
* @return The pooled driver for creating sessions.
*/
public static QldbDriver createQldbDriver() {
QldbSessionClientBuilder sessionClientBuilder = QldbSessionClient.builder();
sessionClientBuilder.region(Region.of(region));
return QldbDriver.builder()
.ledger(ledgerName)
.sessionClientBuilder(sessionClientBuilder)
.build();
}
/**
* Takes two hashes, sorts them, concatenates them, and then returns the
* hash of the concatenated array.
*
* @param h1
* Byte array containing one of the hashes to compare.
* @param h2
* Byte array containing one of the hashes to compare.
* @return the concatenated array of hashes.
*/
public static byte[] dot(final byte[] h1, final byte[] h2) {
if (h1.length != HASH_LENGTH || h2.length != HASH_LENGTH) {
throw new IllegalArgumentException("Invalid hash.");
}
int byteEqual = 0;
for (int i = h1.length - 1; i >= 0; i--) {
byteEqual = Byte.compare(h1[i], h2[i]);
if (byteEqual != 0) {
break;
}
}
byte[] concatenated = new byte[h1.length + h2.length];
if (byteEqual < 0) {
System.arraycopy(h1, 0, concatenated, 0, h1.length);
System.arraycopy(h2, 0, concatenated, h1.length, h2.length);
} else {
System.arraycopy(h2, 0, concatenated, 0, h2.length);
System.arraycopy(h1, 0, concatenated, h2.length, h1.length);
}
MessageDigest messageDigest;
try {
messageDigest = MessageDigest.getInstance("SHA-256");
} catch (NoSuchAlgorithmException e) {
throw new IllegalStateException("SHA-256 message digest is unavailable", e);
}
messageDigest.update(concatenated);
return messageDigest.digest();
}
public static void main(String[] args) {
// Get a digest
GetDigestRequest digestRequest = new GetDigestRequest().withName(ledgerName);
GetDigestResult digestResult = client.getDigest(digestRequest);
java.nio.ByteBuffer digest = digestResult.getDigest();
// expectedDigest is the buffer we will use later to compare against our calculated digest
byte[] expectedDigest = new byte[digest.remaining()];
digest.get(expectedDigest);
// Retrieve info for the given vin's document revisions
Result result = driver.execute(txn -> {
final String query = String.format("SELECT blockAddress, hash, metadata.id FROM _ql_committed_%s WHERE data.VIN = '%s'", tableName, vin);
return txn.execute(query);
});
System.out.printf("Verifying document revisions for vin '%s' in table '%s' in ledger '%s'%n", vin, tableName, ledgerName);
for (IonValue ionValue : result) {
IonStruct ionStruct = (IonStruct)ionValue;
// Get the requested fields
IonValue blockAddress = ionStruct.get("blockAddress");
IonBlob hash = (IonBlob)ionStruct.get("hash");
String metadataId = ((IonString)ionStruct.get("id")).stringValue();
System.out.printf("Verifying document revision for id '%s'%n", metadataId);
String blockAddressText = blockAddress.toString();
// Submit a request for the revision
GetRevisionRequest revisionRequest = new GetRevisionRequest()
.withName(ledgerName)
.withBlockAddress(new ValueHolder().withIonText(blockAddressText))
.withDocumentId(metadataId)
.withDigestTipAddress(digestResult.getDigestTipAddress());
// Get a result back
GetRevisionResult revisionResult = client.getRevision(revisionRequest);
String proofText = revisionResult.getProof().getIonText();
// Take the proof and convert it to a list of byte arrays
List<byte[]> internalHashes = new ArrayList<>();
IonReader reader = SYSTEM.newReader(proofText);
reader.next();
reader.stepIn();
while (reader.next() != null) {
internalHashes.add(reader.newBytes());
}
// Calculate digest
byte[] calculatedDigest = internalHashes.stream().reduce(hash.getBytes(), BlockHashVerification::dot);
boolean verified = Arrays.equals(expectedDigest, calculatedDigest);
if (verified) {
System.out.printf("Successfully verified document revision for id '%s'!%n", metadataId);
} else {
System.out.printf("Document revision for id '%s' verification failed!%n", metadataId);
return;
}
// Submit a request for the block
GetBlockRequest getBlockRequest = new GetBlockRequest()
.withName(ledgerName)
.withBlockAddress(new ValueHolder().withIonText(blockAddressText))
.withDigestTipAddress(digestResult.getDigestTipAddress());
// Get a result back
GetBlockResult getBlockResult = client.getBlock(getBlockRequest);
String blockText = getBlockResult.getBlock().getIonText();
IonDatagram datagram = SYSTEM.getLoader().load(blockText);
ionStruct = (IonStruct)datagram.get(0);
final byte[] blockHash = ((IonBlob)ionStruct.get("blockHash")).getBytes();
proofText = getBlockResult.getProof().getIonText();
// Take the proof and create a list of hash binary data
datagram = SYSTEM.getLoader().load(proofText);
ListIterator<IonValue> listIter = ((IonList)datagram.iterator().next()).listIterator();
internalHashes.clear();
while (listIter.hasNext()) {
internalHashes.add(((IonBlob)listIter.next()).getBytes());
}
// Calculate digest
calculatedDigest = internalHashes.stream().reduce(blockHash, BlockHashVerification::dot);
verified = Arrays.equals(expectedDigest, calculatedDigest);
if (verified) {
System.out.printf("Block address '%s' successfully verified!%n", blockAddressText);
} else {
System.out.printf("Block address '%s' verification failed!%n", blockAddressText);
}
}
}
}