[metrics-tasks/master] Add Java code to verify server descriptors (#2768).

commit bda584d6f61140c14a326ade1f5234e13246cbb7 Author: Karsten Loesing <karsten.loesing@gmx.net> Date: Fri Mar 30 13:16:01 2012 +0200 Add Java code to verify server descriptors (#2768). --- task-2768/.gitignore | 4 + task-2768/VerifyServerDescriptors.java | 140 ++++++++++++++++++++++++++++++++ task-2768/run.sh | 3 + 3 files changed, 147 insertions(+), 0 deletions(-) diff --git a/task-2768/.gitignore b/task-2768/.gitignore new file mode 100644 index 0000000..e813ffc --- /dev/null +++ b/task-2768/.gitignore @@ -0,0 +1,4 @@ +*.jar +*.class +in/ + diff --git a/task-2768/VerifyServerDescriptors.java b/task-2768/VerifyServerDescriptors.java new file mode 100644 index 0000000..978d4a3 --- /dev/null +++ b/task-2768/VerifyServerDescriptors.java @@ -0,0 +1,140 @@ +/* Copyright 2012 The Tor Project + * See LICENSE for licensing information */ +import java.io.ByteArrayOutputStream; +import java.io.File; +import java.io.StringReader; +import java.security.Security; +import java.security.interfaces.RSAPublicKey; +import java.util.Iterator; + +import org.apache.commons.codec.binary.Base64; +import org.apache.commons.codec.binary.Hex; +import org.bouncycastle.asn1.ASN1OutputStream; +import org.bouncycastle.crypto.digests.SHA1Digest; +import org.bouncycastle.crypto.encodings.PKCS1Encoding; +import org.bouncycastle.crypto.engines.RSAEngine; +import org.bouncycastle.crypto.params.RSAKeyParameters; +import org.bouncycastle.jce.provider.BouncyCastleProvider; +import org.bouncycastle.openssl.PEMReader; +import org.torproject.descriptor.Descriptor; +import org.torproject.descriptor.DescriptorFile; +import org.torproject.descriptor.DescriptorReader; +import org.torproject.descriptor.DescriptorSourceFactory; +import org.torproject.descriptor.ServerDescriptor; + +/* + * Verify server descriptors using the contained signing key. Verify that + * 1) the contained fingerprint is actually a hash of the signing key and + * 2) the router signature was created using the signing key. + * + * Usage: + * - Extract server descriptors to in/. + * - Clone metrics-lib, run `ant jar`, and copy descriptor.jar to this + * directory. + * - Download Apache Commons Codec jar file commons-codec-1.4.jar and put + * it in this directory. + * - Download BouncyCastle 1.47 jar files bcprov-jdk15on-147.jar and + * bcpkix-jdk15on-147.jar and put them in this directory. + * - Compile and run this class: + * $ javac -cp descriptor.jar:commons-codec-1.4.jar:bcprov-jdk15on-147.jar:bcpkix-jdk15on-147.jar VerifyServerDescriptors.java + * $ java -cp descriptor.jar:commons-codec-1.4.jar:bcprov-jdk15on-147.jar:bcpkix-jdk15on-147.jar:. VerifyServerDescriptors + */ +public class VerifyServerDescriptors { + public static void main(String[] args) throws Exception { + System.out.println("Verifying descriptors..."); + if (Security.getProvider("BC") == null) { + Security.addProvider(new BouncyCastleProvider()); + } + File inputDirectory = new File("in/"); + DescriptorReader reader = DescriptorSourceFactory + .createDescriptorReader(); + reader.addDirectory(inputDirectory); + Iterator<DescriptorFile> descriptorFiles = reader.readDescriptors(); + int processedDescriptors = 0, verifiedDescriptors = 0; + while (descriptorFiles.hasNext()) { + DescriptorFile descriptorFile = descriptorFiles.next(); + if (descriptorFile.getDescriptors() == null) { + continue; + } + for (Descriptor descriptor : descriptorFile.getDescriptors()) { + if (!(descriptor instanceof ServerDescriptor)) { + continue; + } + ServerDescriptor serverDescriptor = (ServerDescriptor) descriptor; + boolean isVerified = true; + + /* Verify that the contained fingerprint is a hash of the signing + * key. */ + String signingKeyString = serverDescriptor.getSigningKey(); + String fingerprintString = + serverDescriptor.getFingerprint().toLowerCase(); + PEMReader pemReader = new PEMReader(new StringReader( + signingKeyString)); + RSAPublicKey signingKey = (RSAPublicKey) pemReader.readObject(); + + ByteArrayOutputStream baos = new ByteArrayOutputStream(); + new ASN1OutputStream(baos).writeObject( + new org.bouncycastle.asn1.pkcs.RSAPublicKey( + signingKey.getModulus(), + signingKey.getPublicExponent()).toASN1Primitive()); + byte[] pkcs = baos.toByteArray(); + byte[] signingKeyHashBytes = new byte[20]; + SHA1Digest sha1 = new SHA1Digest(); + sha1.update(pkcs, 0, pkcs.length); + sha1.doFinal(signingKeyHashBytes, 0); + String signingKeyHashString = Hex.encodeHexString( + signingKeyHashBytes); + if (!signingKeyHashString.equals(fingerprintString)) { + System.out.println("In " + descriptorFile.getFile() + + ", server descriptor " + + serverDescriptor.getServerDescriptorDigest() + + ", the calculated signing key hash " + + signingKeyHashString + + " does not match the contained fingerprint " + + fingerprintString + "!"); + isVerified = false; + } + + /* Verify that the router signature was created using the signing + * key. */ + String serverDescriptorDigestString = serverDescriptor + .getServerDescriptorDigest().toLowerCase(); + String routerSignatureString = serverDescriptor + .getRouterSignature(); + byte[] routerSignature = Base64 + .decodeBase64(routerSignatureString.substring( + 0 + "-----BEGIN SIGNATURE-----\n".length(), + routerSignatureString.length() + - "-----END SIGNATURE-----\n".length()).replaceAll( + "\n", "")); + RSAKeyParameters rsakp = new RSAKeyParameters(false, + signingKey.getModulus(), signingKey.getPublicExponent()); + PKCS1Encoding pe = new PKCS1Encoding(new RSAEngine()); + pe.init(false, rsakp); + byte[] decryptedSignature = pe.processBlock(routerSignature, 0, + routerSignature.length); + String decryptedSignatureString = + Hex.encodeHexString(decryptedSignature); + if (!decryptedSignatureString.equals( + serverDescriptorDigestString)) { + System.out.println("In " + descriptorFile.getFile() + + ", server descriptor " + + serverDescriptor.getServerDescriptorDigest() + + ", the decrypted signature " + + decryptedSignatureString + + " does not match the descriptor digest " + + serverDescriptorDigestString + "!"); + isVerified = false; + } + + processedDescriptors++; + if (isVerified) { + verifiedDescriptors++; + } + } + } + System.out.println("Verified " + verifiedDescriptors + "/" + + processedDescriptors + " server descriptors."); + } +} + diff --git a/task-2768/run.sh b/task-2768/run.sh new file mode 100755 index 0000000..a819ac5 --- /dev/null +++ b/task-2768/run.sh @@ -0,0 +1,3 @@ +#!/bin/bash +javac -cp descriptor.jar:commons-codec-1.4.jar:bcprov-jdk15on-147.jar:bcpkix-jdk15on-147.jar VerifyServerDescriptors.java && java -cp descriptor.jar:commons-codec-1.4.jar:bcprov-jdk15on-147.jar:bcpkix-jdk15on-147.jar:. VerifyServerDescriptors +
participants (1)
-
karsten@torproject.org