Skip to content

Instantly share code, notes, and snippets.

@bazi
Created March 26, 2015 15:29
Show Gist options
  • Save bazi/34b5f5de6e6f531f8479 to your computer and use it in GitHub Desktop.
Save bazi/34b5f5de6e6f531f8479 to your computer and use it in GitHub Desktop.
A try to implement MIME multipart/signed content type generator aka template engine in Ninja framework
package controllers.helper;
import java.io.ByteArrayInputStream;
import java.io.ByteArrayOutputStream;
import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStream;
import java.util.Base64;
import java.util.UUID;
import org.bouncycastle.openpgp.PGPException;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.subutai.hub.security.pgp.PGPUtils;
import com.sun.mail.util.CRLFOutputStream;
import ninja.Context;
import ninja.Result;
import ninja.template.TemplateEngine;
import ninja.utils.ResponseStreams;
import static java.nio.charset.StandardCharsets.UTF_8;
public class MultipartSignedTemplateEngine implements TemplateEngine
{
public static final String CONTENT_TYPE = "multipart/signed";
public static final String PROTOCOL = "application/pgp-signature";
public static final String MIC_ALGORITHM = "pgp-sha1";
private static final Logger LOGGER = LoggerFactory.getLogger(SignedBodyTemplateEngine.class );
private static final byte[] MIME_NEW_LINE = new byte[]
{
'\r', '\n'
};
@Override
public void invoke( Context context, Result result )
{
String boundary = UUID.randomUUID().toString();
result.contentType( makeContentTypeWithParameters( boundary ) );
ResponseStreams responseStreams = context.finalizeHeaders( result );
byte[] boundaryBytes = boundary.getBytes();
try ( OutputStream out = responseStreams.getOutputStream() )
{
byte[] dataPart = makeDataPart( result.getRenderable().toString().getBytes() );
byte[] signature = createSignature( dataPart );
writeBoundary( boundaryBytes, out, false );
out.write( dataPart );
writeBoundary( boundaryBytes, out, false );
out.write( "Content-Type: application/pgp-signature".getBytes() );
out.write( MIME_NEW_LINE );
out.write( MIME_NEW_LINE );
out.write( signature );
writeBoundary( boundaryBytes, out, true );
}
catch ( IOException ex )
{
LOGGER.error( "Failed to write {} data", getContentType(), ex );
}
catch ( PGPException ex )
{
LOGGER.error( "Failed to create signature", ex );
}
}
@Override
public String getSuffixOfTemplatingEngine()
{
return null;
}
@Override
public String getContentType()
{
return CONTENT_TYPE;
}
private String makeContentTypeWithParameters( String boundary )
{
StringBuilder sb = new StringBuilder();
sb.append( getContentType() ).append( "; " );
sb.append( "boundary=" ).append( boundary ).append( "; " );
sb.append( "micalg=" ).append( MIC_ALGORITHM ).append( "; " );
sb.append( "protocol=\"" ).append( PROTOCOL ).append( "\"" );
return sb.toString();
}
private byte[] makeDataPart( byte[] data ) throws IOException
{
String newLine = new String( MIME_NEW_LINE, UTF_8 );
StringBuilder sb = new StringBuilder();
sb.append( "Content-Type: text/plain; charset=" ).append( UTF_8.name() ).append( newLine );
sb.append( "Content-Transfer-Encoding: base64" ).append( newLine );
byte[] bytes1 = sb.toString().getBytes();
byte[] bytes2 = makeMimeEncodedData( data );
byte[] res = new byte[bytes1.length + bytes2.length];
System.arraycopy( bytes1, 0, res, 0, bytes1.length );
System.arraycopy( bytes2, 0, res, bytes1.length, bytes2.length );
return res;
}
private byte[] createSignature( byte[] data ) throws PGPException, IOException
{
ByteArrayOutputStream out = new ByteArrayOutputStream( 1024 );
try ( InputStream keyStream = getSignatureKeyStream() )
{
PGPUtils.createSignature( new ByteArrayInputStream( data ), keyStream, out, new char[0] );
}
return out.toByteArray();
}
private void writeBoundary( byte[] boundary, OutputStream os, boolean closing ) throws IOException
{
byte[] boundaryPrefix = new byte[]
{
'-', '-'
};
os.write( MIME_NEW_LINE );
os.write( boundaryPrefix );
os.write( boundary );
if ( closing )
{
os.write( boundaryPrefix );
}
os.write( MIME_NEW_LINE );
}
private byte[] makeMimeEncodedData( byte[] data ) throws IOException
{
ByteArrayOutputStream converted = new ByteArrayOutputStream();
try ( OutputStream out = new CRLFOutputStream( converted ) )
{
out.write( data );
}
return Base64.getMimeEncoder().encode( converted.toByteArray() );
}
private InputStream getSignatureKeyStream()
{
return ClassLoader.getSystemResourceAsStream( "sample.secret.gpg.key" );
}
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment