Created
March 26, 2015 15:29
-
-
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
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
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