GPG data stream encryption and signing with Bouncycastle

I found it incredibly difficult to use BouncyCastle’s Java library to create data that GPG was actually able to decrypt. Even if compression is not the matter because you only have small tokens, it did not work without a Zip-DataGenerator in between (although there are plaintext variants of these Generators among the BC classes). GPG was complaining about not being able to decode character sequence “2D” or something.

You can save the string to a file if you need this, I had to create string data for passing it to a restful API.

protected final String publickeyFile; // the public key of the receiver, in armored format
protected final String privatekeyFile; // your private key ring file 
protected final String userId; // usually your email-address in your key ring
protected final String password; // password to your private key ring
protected final SecureRandom randomGenerator;

/**
 * Encrypt plaintext message using public key from publickeyFile.
 * 
 * @param message the message
 * @return the string
 */
private String encrypt( String message )
    throws PGPException, IOException, NoSuchProviderException
{
    // read public key file, this is an armored key file, no binary stream
    InputStream in =
        new ArmoredInputStream( Thread.currentThread().getContextClassLoader()
            .getResourceAsStream( publickeyFile ) );
    // find the PGP key in the file
    PGPPublicKey publicKey = findPublicGPGKey( in );
    Preconditions
        .checkNotNull( publicKey, Format.format( "Did not find the GPG key in keyfile ", publickeyFile ) );

    // Encode the string into bytes using utf-8
    byte[] utf8Bytes = message.getBytes( UTF8_ENCODING );

    ByteArrayOutputStream compressedOutput = new ByteArrayOutputStream();
    // compress bytes with zip
    PGPLiteralDataGenerator literalDataGenerator = new PGPLiteralDataGenerator();
    // the reason why we compress here is GPG not being able to decrypt our message input but if we do not compress.
    // I guess pkzip compression also encodes only to GPG-friendly characters.
    PGPCompressedDataGenerator compressedDataGenerator =
        new PGPCompressedDataGenerator( CompressionAlgorithmTags.ZIP );
    try
    {
        OutputStream literalDataOutput =
            literalDataGenerator.open( compressedOutput, PGPLiteralData.BINARY, FAKE_PGP_INPUT_FILENAME,
                utf8Bytes.length, new Date() );
        // update bytes in the stream
        literalDataOutput.write( utf8Bytes );
    }
    catch ( IOException e )
    {
        // catch but close the streams in finally
        throw e;
    }
    finally
    {
        compressedDataGenerator.close();
        Closeables.closeQuietly( compressedOutput );
    }

    // now we have zip-compressed bytes
    byte[] compressedBytes = compressedOutput.toByteArray();

    PGPEncryptedDataGenerator encryptedDataGenerator =
        new PGPEncryptedDataGenerator( PGPEncryptedData.CAST5, true, randomGenerator, BC_PGP_PROVIDER );
    // use public key to encrypt data
    encryptedDataGenerator.addMethod( publicKey );

    // literalDataOutput --> compressedOutput --> ArmoredOutputStream --> ByteArrayOutputStream
    ByteArrayOutputStream byteArrayOutputStream = new ByteArrayOutputStream();
    ArmoredOutputStream armoredOut = new ArmoredOutputStream( byteArrayOutputStream );
    OutputStream encryptedOutput = null;
    try
    {
        encryptedOutput = encryptedDataGenerator.open( armoredOut, compressedBytes.length );
        encryptedOutput.write( compressedBytes );
    }
    catch ( IOException e )
    {
        throw e;
    }
    catch ( PGPException e )
    {
        throw e;
    }
    finally
    {
        Closeables.closeQuietly( encryptedOutput );
        Closeables.closeQuietly( armoredOut );
    }
    String encrypted = new String( byteArrayOutputStream.toByteArray() );
    LOG.debug( Format.format( "Message: {} ", message ) );
    LOG.debug( Format.format( "Encrypted: {} ", encrypted ) );
    return encrypted;
}

/**
 * Find public gpg key in InputStream.
 * 
 * @param inputStream the input stream
 * @return the PGP public key
 */
private PGPPublicKey findPublicGPGKey( InputStream inputStream )
    throws IOException, PGPException
{
    // get all key rings in the input stream
    PGPPublicKeyRingCollection publicKeyRingCollection =
        new PGPPublicKeyRingCollection( PGPUtil.getDecoderStream( inputStream ) );
    LOG.debug( Format.format( "key ring size: {}", publicKeyRingCollection.size() ) );
    Iterator<PGPPublicKeyRing> keyRingIter = publicKeyRingCollection.getKeyRings();
    // iterate over keyrings
    while ( keyRingIter.hasNext() )
    {
        PGPPublicKeyRing keyRing = keyRingIter.next();
        Iterator<PGPPublicKey> keyIter = keyRing.getPublicKeys();
        // iterate over public keys in the key ring
        while ( keyIter.hasNext() )
        {
            PGPPublicKey tmpKey = keyIter.next();
            Preconditions.checkArgument( tmpKey != null );
            LOG.debug( Format.format( "Encryption key = {}, Master key = {}, UserIDs: {}",
                tmpKey.isEncryptionKey(), tmpKey.isMasterKey(), Iterators.toString( tmpKey.getUserIDs() ) ) );
            // we need a master encryption key
            if ( tmpKey.isEncryptionKey() && tmpKey.isMasterKey() )
            {
                return tmpKey;
            }
        }
    }
    throw new PGPException( "No public key found!" );
}

/**
 * Find private gpg key in InputStream.
 * 
 * @param inputStream the input stream
 * @param userId the user id
 * @return the PGP secret key
 */
private PGPSecretKey findPrivateGPGKey( InputStream inputStream, String userId )
    throws IOException, PGPException, NoSuchProviderException
{
    // iterate over every private key in the key ring
    PGPSecretKeyRingCollection secretKeyRings =
        new PGPSecretKeyRingCollection( PGPUtil.getDecoderStream( inputStream ) );
    // look for the key ring that is used to authenticate our reporting facilities
    Iterator<PGPSecretKeyRing> privateKeys = secretKeyRings.getKeyRings( userId );
    // iterate over every private key in the ring
    while ( privateKeys.hasNext() )
    {
        PGPSecretKeyRing secretKeyRing = privateKeys.next();
        PGPSecretKey tmpKey = secretKeyRing.getSecretKey();
        Preconditions.checkArgument( tmpKey != null );
        LOG.debug( Format.format( "Signing key = {}, Master key = {}, UserId = {}", tmpKey.isSigningKey(),
            tmpKey.isMasterKey(), userId ) );
        // we want the signing master key
        if ( tmpKey.isSigningKey() && tmpKey.isMasterKey() )
        {
            return tmpKey;
        }
    }
    throw new PGPException( "No private key found!" );
}

/**
 * Sign a plaintext message using our private PGP key file.
 * 
 * @param message the message
 * @return the string
 */
private String sign( String message )
    throws SignatureException, NoSuchAlgorithmException, PGPException, NoSuchProviderException, IOException
{
    InputStream in = Thread.currentThread().getContextClassLoader().getResourceAsStream( privatekeyFile );
    PGPSecretKey secretKey = findPrivateGPGKey( in, userId );
    PGPPrivateKey privateKey = secretKey.extractPrivateKey( password.toCharArray(), BC_PGP_PROVIDER );

    PGPSignatureGenerator signature =
        new PGPSignatureGenerator( secretKey.getPublicKey().getAlgorithm(), PGPUtil.SHA1, BC_PGP_PROVIDER );
    signature.initSign( PGPSignature.BINARY_DOCUMENT, privateKey );

    Iterator<String> userIds = secretKey.getPublicKey().getUserIDs();
    // use the first userId
    if ( userIds.hasNext() )
    {
        PGPSignatureSubpacketGenerator subpacketGenerator = new PGPSignatureSubpacketGenerator();
        subpacketGenerator.setSignerUserID( false, userIds.next() );
        signature.setHashedSubpackets( subpacketGenerator.generate() );
    }
    else
    {
        throw new ReportingException( "Did not find userId" );
    }

    byte[] utf8Bytes = message.getBytes( UTF8_ENCODING );

    ByteArrayOutputStream byteArrayOutputStream = new ByteArrayOutputStream();
    ArmoredOutputStream armoredOut = new ArmoredOutputStream( byteArrayOutputStream );

    PGPCompressedDataGenerator compressedDataGenerator = new PGPCompressedDataGenerator( PGPCompressedData.ZLIB );
    BCPGOutputStream bcOutputStream = new BCPGOutputStream( compressedDataGenerator.open( armoredOut ) );

    signature.generateOnePassVersion( false ).encode( bcOutputStream );

    // literalDataOutput --> bcOutputStream--> compressedOutput --> ArmoredOutputStream --> ByteArrayOutputStream
    // &&
    // signatureOutput --> bcOutputStream--> compressedOutput --> ArmoredOutputStream --> ByteArrayOutputStream
    PGPLiteralDataGenerator literalDataGenerator = new PGPLiteralDataGenerator();
    try
    {
        OutputStream literalDataOutput =
            literalDataGenerator.open( bcOutputStream, PGPLiteralData.BINARY, FAKE_PGP_INPUT_FILENAME,
                utf8Bytes.length, new Date() );

        // update bytes in the streams
        literalDataOutput.write( utf8Bytes );
        signature.update( utf8Bytes );

        // close generators and update signature
        literalDataGenerator.close();
        signature.generate().encode( bcOutputStream );
        compressedDataGenerator.close();
    }
    catch ( IOException e )
    {
        throw e;
    }
    catch ( PGPException e )
    {
        throw e;
    }
    finally
    {
        Closeables.closeQuietly( byteArrayOutputStream );
        Closeables.closeQuietly( armoredOut );
    }
    // }
    String signed = new String( byteArrayOutputStream.toByteArray() );
    LOG.debug( Format.format( "Message: {} ", message ) );
    LOG.debug( Format.format( "Signature: {} ", signed ) );
    return signed;
}

Apache httpcomponents-core NIO SSL example broken

The example snippet of a SSL enabled Apache http-components-core-based Webserver did not work out for me. It crashed after every Request with the following message:

Connection closed: [null]
I/O error: I/O dispatch worker terminated abnormally
Shutdown

Printing the StackTrace revealed the following:

org.apache.http.nio.reactor.IOReactorException: I/O dispatch worker terminated abnormally
at org.apache.http.impl.nio.reactor.AbstractMultiworkerIOReactor .execute(AbstractMultiworkerIOReactor.java:324)
at com.demandware.tenantssl.NHttpSSLServer.main(SslPaymentServer.java:176)
Caused by: java.lang.UnsupportedOperationException: Entity template does not implement getContent()
at org.apache.http.entity.EntityTemplate.getContent(EntityTemplate.java:57)
at org.apache.http.nio.entity.NHttpEntityWrapper.(NHttpEntityWrapper.java:56)
at org.apache.http.nio.protocol.AsyncNHttpServiceHandler .sendResponse(AsyncNHttpServiceHandler.java:495)
at org.apache.http.nio.protocol.AsyncNHttpServiceHandler .responseReady(AsyncNHttpServiceHandler.java:362)
at org.apache.http.nio.protocol.BufferingHttpServiceHandler .responseReady(BufferingHttpServiceHandler.java:135)
at org.apache.http.impl.nio.DefaultNHttpServerConnection .produceOutput(DefaultNHttpServerConnection.java:221)
at org.apache.http.impl.nio.ssl.SSLServerIOEventDispatch .outputReady(SSLServerIOEventDispatch.java:252)
at org.apache.http.impl.nio.reactor.BaseIOReactor .writable(BaseIOReactor.java:185)
at org.apache.http.impl.nio.reactor.AbstractIOReactor .processEvent(AbstractIOReactor.java:338)
at org.apache.http.impl.nio.reactor.AbstractIOReactor .processEvents(AbstractIOReactor.java:315)
at org.apache.http.impl.nio.reactor.AbstractIOReactor .execute(AbstractIOReactor.java:275)
at org.apache.http.impl.nio.reactor.BaseIOReactor .execute(BaseIOReactor.java:104)
at org.apache.http.impl.nio.reactor.AbstractMultiworkerIOReactor$Worker .run(AbstractMultiworkerIOReactor.java:542)
at java.lang.Thread.run(Thread.java:662)

As inline subclassed EntityTemplate does not implement getContent(), you just have to add this tiny snippet in the two inline definitions of EntityTemplate for a minimal working SSL-server:

EntityTemplate body = new EntityTemplate(new ContentProducer()
{

	public void writeTo(final OutputStream outstream) throws IOException
	{
		OutputStreamWriter writer = new OutputStreamWriter(outstream, "UTF-8");
		writer.write("<html><body><h1>");
		writer.write("File ");
		writer.write(file.getPath());
		writer.write(" not found");
		writer.write("</h1></body></html>");
		writer.flush();
	}
}) {
	@Override
	public InputStream getContent() {
		return new InputStream() {

			@Override
			public int read() throws IOException
			{
				return 0;
			}

		};
	}
};

Hadoop MultipleOutputCollector MRunit Testcase

Code-Snippet for a test case with MRUnit and Mockito to test a Hadoop Reducer with MultipleOutputs. MRUnit ReduceDriver unfortunately does not return result data when using multiple Ouptuts.

private HashMap<String, MockOutputCollector> mockOutputCollectors = new HashMap<String, MockOutputCollector>();

@Test
public void test() {
	driver.withInput(new Text(INPUT_KEY, INPUT).run();
	List<Type> resultList = new ArrayList<Type>();

	// This has to be done that complicated way as the above driver.run() only returns an empty list
	// if run with ReduceDriver. For some reason it works with MapDriver, though...
	for (String collectorDirectory : mockOutputCollectors.keySet()) {
		MockOutputCollector<NullWritable, Object> mockOutputCollector = mockOutputCollectors
				.get(collectorDirectory);
		for (Pair<NullWritable, Object> pair : mockOutputCollector.getOutputs()) {
			if (pair.getSecond() instanceof MultipleOutputType1) {
				MultipleOutputType1 multipleOutputType = (MultipleOutputType1) pair.getSecond();
				logger.debug("MultipleOutputType1: "
						+ multipleOutputType.toString());
			}
			if  (pair.getSecond() instanceof MultipleOutputType2) {
				[...]
			}

		}
	}
}

@Before
public void setup() {
	for (Directories directory : Directories.values()) {
		mockOutputCollectors.put(directory.getDirectoryName(),
				new MockOutputCollector());
		when(
				multipleOutputs.getCollector(
						eq(directory.getDirectoryName()),
						argThat(new IsMockReporter()))).thenReturn(
				mockOutputCollectors.get(directory.getDirectoryName()));
	}
	mockOutputCollectors.put(
			OUTPUT_DIRECTORY_NAME,
			new MockOutputCollector());
	
	when(
			multipleOutputs.getCollector(
					eq(OUTPUT_DIRECTORY_NAME),
					argThat(new IsMockReporter()))).thenReturn(
				mockOutputCollectors.get(Constants.OUTPUT_DIRECTORY_NAME));
}

K3b-Plugin für Shuffle-unfähige CD-Player

Mein DVD-Player spielt zwar MP3-DVDs ab, kennt aber dummerweise keinen Random-Modus, spielt also alle Titel hintereinanderweg Album für Album durch. Als Gegenmaßnahme habe ich ein Plugin für K3b geschrieben, dass allen Dateien im Wurzelverzeichnis (mehr oder weniger) zufällige Namen gibt. Die Dateien heißen dann “titleXXXXX.ext”, die X werden durch Ziffern ersetzt, die Erweiterung wird beibehalten. Es basiert auf den K3b-Plugins audioprojectcddb und audiometainforenamer von Sebastian Trueg. Es arbeitet nur auf  neu erstellten Datenprojekten, nicht auf importieren Sessions.
k3bdataprojectrandomizerplugin

Core4400

Hardware-mäßig kein Exot, dafür aber Software-mäßig. Core2Duo 8400, 4 GB Crucial Ballistix RAM und eine Asus ATI Radeon 1950 Pro. Bei diesem System war mir die Lautstärke wichtig, ein flüsterleises und kühles System mit Bequiet-Netzteil für die Lüftersteuerung. Die Graka hat eine gigantische Heatpipe-Konstruktion und ist nicht hörbar. Der Skythe Samurai kühlt den Prozessor auf 40 Grad bei Vollast und ebenfalls flüsterleise. Am längsten hat die Auswahl des Mainboards gedauert, am Ende ist es dann ein Gigabyte GA P35C-DS3R geworden.Aber die Software!! Naja. Kein LFS mehr, ich habe mittlerweile Gentoo für mich entdeckt. Ein erstaunliches Build-System, wirklich. Hat mich überzeugt. Das macht doch tatsächlich genau, was ich will.. Das erste System überhaupt! Und wenn nicht dann kann man immernoch im gut dokumentierten Innereien herumsägen un basteln. Wirklich toll.

Die Besonderheit hierbei ist das Boot-Setup. Linux ist in einem Linux-Software-RAID 5 installiert, über insgesamt 3 SATA-Platten. Meine Daten-Laufwerke sollen auch unter Windows verfügbar sein und sind daher mit NTFS formatiert. Jedoch wollte ich auch hier nicht auf eine gewisse Datensicherheit verzichten, daher sind die beiden Datenpartitionen mit dem Windows-eigenen Software-RAID gespiegelt. Problem dabei ist, dass diese Windows-RAID-Laufwerke nur funktionieren, wenn sie auf einer Platte mit LDM angelegt werden können (dynamischer Datenträger unter Windows). Dummerweise liegen auch die beiden Partitionen des Linux-RAID5-Arrays auf den LDM-Platten. Diese Partitionstabelle erkennt der Autoassembler für die Linux-RAIDs beim Booten aber nicht. Dazu muss eine Initial-Ramdisk angelegt werden, die das Array zusammenbastelt. Eine Beispielramdisk gibts hier (initramfs.tar). Das entsprechende Initscript /sbin/init sieht folgendermaßen aus:

#!/bin/sh
export PATH=/bin:/sbin
umask 0077
mount -t proc proc /proc
mount -t sysfs sysfs /sys
mount -t tmpfs tmpfs /dev
busybox –install -s
mdev -s
echo /sbin/mdev > /proc/sys/kernel/hotplug
mknod /dev/md0 b 9 0
ln -s /dev/console /dev/tty
mdadm –assemble /dev/md0 /dev/sda11 /dev/sdb3 /dev/sdc3
mount /dev/md0 /new-root
echo > /proc/sys/kernel/hotplug
umount -l /proc /sys /dev
exec switch_root /new-root /sbin/init
exec /bin/sh

Unter Linux sind die LDM-Laufwerke nach folgendem Eintrag in /etc/mdadm.conf verfügbar:

DEVICE /dev/sdb1 /dev/sdc1
ARRAY /dev/md1 level=raid1 num-devices=2 UUID=867e99c4:e8616584:39ad41ad:XXXXXX
DEVICE /dev/sdb2 /dev/sdc2
ARRAY /dev/md2 level=raid1 num-devices=2 UUID=81183f55:72559ad3:3f16c699:XXXXXX

Mit folgendem Script kann man das ganze dann scharf schalten:

mdadm –assemble /dev/md1 /dev/sdb1
mdadm –run /dev/md1
mdadm –add /dev/md1 /dev/sdc1
mdadm –assemble /dev/md2 /dev/sdb2
mdadm –run /dev/md2
mdadm –add /dev/md2 /dev/sdc2

Danach wird das Array neu synchronisiert, das dauert ne Weile, aber es kann schon gemountet werden.

Powerbook Lombard

… mit 400 MHz G3 und 256 MB RAM. Hier läuft Gentoo Linux mit Unterstützung für (fast) alle Hardware. Sogar XV-Ausgabe und die recht beschränkten DRI-Fähigkeiten des Mach64 werden unterstützt – leider aber mit starken Farbfehlern. Liegt warhscheinlich an der Kombination alter X-Server und komisches Kernel-Modul für zu neuen Kernel. Nun ja. Xorg nochmal neu bauen, dann gehts vielleicht. Aber das wird wohl eher auf Gentoo hinauslaufen. Denn mittleiweile habe ich nicht mehr die Zeit, überhaupt ein LFS auf auktuellem Stand zu halten. Geschweige denn 4 oder 5.
Was nicht geht ist die Hardware-Beschleunigung für MPEG-Decodierung. Denn in diesem Powerbook ist eine DXR3-Hollywood-änhliche PCI-Karte eingebaut(!). Leider gibt es bis jetzt keinen Treiber dafür, nur einen Stub.

Motorola Powerstack

PC-Harware, aber PowerPC-Prozessor, 300 MHz PowerPC 604e. Leider habe ich nie ein PowerPC-Grafik-BIOS auf meine Matrox Millenium oder meine verschiedenen ATI-Karten bekommen. Damit würde nämlich auch der X-Server halbwegs erträglich sein auf der Kiste, im Gegensatz zur originalen Trident-Karte. Die hat einfach zu wenig RAM zum arbeiten und ist zu langsam zum Filme gucken. Ich habe auch mit einer Voodoo2-Karte herumprobiert, aber der framebuffer dafür hat nicht so richtig funktioniert. Schade, denn Soundkarte und Netzwerk waren bereits integriert, und der SCSI-Controller war schon recht schnell für damalige Verhältnisse und sogar der IDE-Chip wurde erkannt und unterstützt. Nur war auf dem Board keine Pfostenleiste aufgelötet. Und für alle, die es probieren wollen: Offensichtlich sind die Dinger auch nicht verdrahtet. Selbst wenn man eine auflötet, erkennt der Kernel kein IDE-Gerät…
Hier habe ich ein bisschen gecheatet, ich habe – hoho! – das fertige LFS auf die Powerstack gebügelt. Allerdings habe ich den X-Server mit allen möglichen patches mehrmals neukompiliert, aber ohne ein PPC-BIOS auf einer PPC-Kiste kein X…

Powerbook 1400

… mit prähistorischem Nubus, 48 MB RAM und übertaktetem Prozessor mit schlagmichtot-160 MHz? Ich muss nochmal nachschauen…Eigentlich hatte sich ein Kumpel von mir diese Kiste ersteigert. Jedoch scheiterte sein Vorhaben FreeBSD zu installieren an der fehlenden Nubus-Unterstützung. Und der Linux Nubus-Port kam gerade erst ins Rollen. Nach einigem Basteln mit dem Debian-Boot-Kernel, den obige Webseite zur Verfügung stellt, kann man sogar Debian installieren. Dann haben ich das damals topaktuelle LFS 5.0-Buch genommen und ein komplettes System mit X-Server aufgesetzt. Der hat sogar nach einigen Einstellereien funktioniert. Aber leider war damit erstmal Ende der Show. XFCE konnte man guten Gewissens laufen lassen, aber viel mehr auch nicht. Dann hat aber ein findiger Kerl einen Treiber für den TREX PCMCIA-Controller geschrieben – und plötzlich hatte ich Netzwerk auf der Kiste! Es funktionieren eine 3com-Karte von Pollin (Lötarbeit!), eine SMC-Karte und eine Orinocco-WLAN-Karte. Eine Prism54-Karte und eine Atheros-Karte gingen glaube ich auch. Es geht nicht: Sound, CD-ROM, Irda. Das /etc-Verzeichnis der Kiste liefere ich noch nach.

XT-Notebook mit NEC V20 Prozessor – einem Intel 8088-Klon

Das ELKS-Projekt hat ein Linux für 16-Bit Hardware wie etwa diesen Prozessor geschrieben, nur konnten sie ELKS nie mit einem NEC V20 testen, weil sie einfach keinen hatten. Nun ja, man kann sagen, dass sie gute Arbeit geleistet haben…
Aus dem letzten Satz kann man unschwer erkennen, dass ich gerne merkwürdige Hardware mit der Installation von Linux quäle. Bei letzterem Notebook war das nicht schwer – man muss nur ein Floppy-Image herunterladen und auf eine 720KB-Diskette packen und ca. 20 min warten. Dann hat man eine funktionierende Konsole…Bei den anderen Rechnern gestaltete sich die Sache nicht ganz so einfach – zumindest wenn man nicht nur einfach Debian installieren will. Zugegeben – ich wollte es mir nicht einfach machen. Aber nach meiner ersten LFS Installation habe ich Blut geleckt und diese wunderbar individualistische Spielart von Linux auf ein paar meiner Exoten gebannt.