miércoles, noviembre 20, 2013

Alfviral 1.3.0.420 para Alfresco 4.2

He subido una nueva versión de Alfviral (Alfresco Virus Alert) adaptado para que funcione en la nueva versión 4.2 de Alfresco. Los cambios han sido en su mayoría sobre las acciones de usuario en Share.

Testeado en:
    Alfresco Community 4.2c
    Tomcat 7.0.47
    Java JDK/Jre 1.7.0_45



Se encuentra disponible para su descarga en: http://code.google.com/p/alfviral/downloads/detail?name=fegorsoft-alfviral-1.3.0.420.zip&can=2&q=#makechanges

viernes, noviembre 08, 2013

Alfresco Summit 2013 - Barcelona

Finalmente pude dar las dos "charlas relámpago" que tenía previstas y que tanto miedo me daban por tener que ser en inglés. Una sobre Alfviral para poder escanear documentos en busca de virus y software maligno y la otra (Alfresco Audio Transcriber) sobre la indexación de audio mediante la extracción y transcripción de palabras a texto. Ambos proyectos son pilotos funcionales y por tanto no eran charlas teóricas.

Sobre el Summit en general, muy bien, montado al siempre estilo Alfresco y aunque el primer día no estuve (Party and Pool) me consta que fue un gran comienzo. Vi a personas y sobre todo amigos que hacía tiempo no veía y sobre todo pude constatar que Alfresco como sistema de gestión documental está muy vivo y avanza, quizás, hasta demasiado rápido ;-)

Como anécdota deciros que en una ruleta que había con premios, sobre todo camisetas, bolígrafos y chapas, había dos regalos más grandes, un escáner y una mochila, pues bien, tiré y... ¡mochila! aquí os dejo una imagen :-)


Y aquí os dejo también las presentaciones.




Espero poder ir al del año que viene, sea donde sea...  ;-)

martes, septiembre 17, 2013

Extender Freemarker en Alfresco

El binomio Javascript (Rhino) + Freemarker se ha mostrado como uno de los mejores sistemas para realizar la parte controlador + visor en Alfresco. Su capacidad de acceso desde Javascript al API de Alfresco y la capacidad del motor de plantillas de Freemarker ha marcado el punto de evolución de los llamados Webscritps y ha terminado, por ahora, en un fantástico framework llamado Spring-Surf.

Si bien hay pocas cosas que no puedan hacerse con Freemarker, si podemos encontrar algunas excepciones en las que tengamos que extender su funcionalidad.

Un ejemplo de esto es para el traspaso de datos entre sistemas Alfresco + aplicaciones en los que dicha transferencia puede realizarse vía JSON, XML, etc.

JSON por otra parte se está revelando como un sistema más agil que el traspaso de información mediante XML pero también está limitado a los validadores que en un momento dado pueden echar para atrás una comunicación.

En este sentido, una forma de pasar los datos entre sistemas es usando una codificación ya algo antigua (de 1987), ideada en un principio para transferencias con el correo electrónico pero que es muy válida hoy día.

Extender Freemarker


Vamos a utilizar la siguiente técnica:

Creamos una clase que implemente la interface TemplateMethodModelEx y sobreescribimos el método exec creando nuestro algoritmo de transformación a sistema base64.

Un ejemplo podría ser el siguiente:

Fichero: Base64EncoderMethod.java

package com.fegor.alfresco.freemarker.utils;

import java.io.UnsupportedEncodingException;
import java.util.List;

import org.springframework.security.crypto.codec.Base64;

import freemarker.template.SimpleScalar;
import freemarker.template.TemplateMethodModelEx;
import freemarker.template.TemplateModelException;

/**
 * Codificaci?n en base64 de cadenas pasadas a FreeMarker
 *
 * @author Fernando
 *
 */
public class Base64EncoderMethod implements TemplateMethodModelEx {

    @SuppressWarnings("rawtypes")
    @Override
    public Object exec(List args) throws TemplateModelException {
        String result = null;
        SimpleScalar ss = new SimpleScalar(args.get(0).toString());
        String res = ss.getAsString();
        byte[] bytes = res.getBytes();
        byte[] base64 = Base64.encode(bytes);
        try {
            result = (new String(base64, "UTF-8"));
        } catch (UnsupportedEncodingException e) {
            e.printStackTrace();
        }
        return result;
    }
}


En este caso he usado la clase Base64 del paquete de SpringFramework, aquí hay que tener cuidado con el que utilizamos ya que algunas clases pueden variar como cortar las líneas en 76 caracteres añadiendo un retorno de carro que hará que los validadores (en el caso de JSON) no funcionen. También hay que tener en cuenta que esta librería no está incluida hasta la versión 4 de Alfresco por lo que si usamos una versión como por ejemplo la 3 tendremos que incluirla en nuestro paquete/módulo.

Si usamos una clase de alguna librería que nos devuelva el resultado de esta forma solo habrá que reemplazar la línea:

     result = (new String(base64, "UTF-8"));
por
     result = (new String(base64, "UTF-8")).replace("\n", "");

El siguiente paso será crear una plantilla de utilidad para crear una instancia de esta clase:

Fichero: utils.ftl

<#assign base64Encode = 'com.fegor.alfresco.freemarker.utils.Base64EncoderMethod'?new()>

Con esto tendríamos nuestra extensión, ahora necesitamos probarla, para ello creamos un Webscript que va a enviarnos la información del título y el contenido de un nodo codificando en base64 el contenido.

Creamos el descriptor...

Fichero: content_base64.get.desc.xml

<webscript>
    <shortname>content_base64</shortname>
    <description>Codificar content en base64</description>
    <url>/content_base64?uuid={uuid?}</url>
    <url>/content_base64.json?uuid={uuid?}</url>
    <url>/content_base64.xml?uuid={uuid?}</url>
    <authentication>user</authentication>
    <format default="json">extension</format>
</webscript>


Creamos el controlador...

Fichero: content_base64.get.js

var query_lucene = 'ID:"workspace://SpacesStore/' + args["uuid"] + '"';
var nodeRefs = search.luceneSearch(query_lucene);

model.nodeRef = "none";

if (nodeRefs.length != 0) {
    model.nodeRef = nodeRefs[0];   
}


Y solo resta crear las plantillas, en este caso crearemos dos, una para JSON y otra para XML...

Fichero: content_base64.get.json.ftl
<#import 'utils.ftl' as u>
 {
 "Node" : [ {
   <#if nodeRef = null>
        "Error": "nodeRef es null"
   <#else>
        "Título": "${item.properties['cm:title']}",
        "Contenido": "${u.base64Encode(item.properties['cm:content'].content)}"
   </#if>
   } ]
 }



Fichero: content_base64.get.xml.ftl

<#import 'utils.ftl' as u>
<Node>
    <#if nodeRef = "none">
        <Error>No se ha encontrado el nodo</Error>
    <#else>      
        <Título>${nodeRef.properties['cm:title']}</Título>
        <Contenido>${u.base64Encode(nodeRef.properties['cm:content'].content)}</Contenido>
    </#if>       
</Node>



Con esto obtenemos las propiedades que necesitamos codificadas para que los caracteres que puedan contener no afecten en el protocolo ni en el sistema que se van a utilizar. Esto es muy importante cuando hablamos de  las normas ENI (Esquema Nacional de Interoperabilidad) e incluso para algunos casos del ENS (Esquema Nacional de Seguridad) ya que podemos usar este sistema también para transferir información encriptada usando algún algoritmo como RSA.

jueves, abril 25, 2013

Alfresco, CMIS y Python

El estandar CMIS (Content Management Interoperability Services) es un estandar para la gestion de contenidos aprobada por OASIS.

http://en.wikipedia.org/wiki/Content_Management_Interoperability_Services

Esto nos permite tener una capa de llamadas estandar para multitud de productos de gestion de contenidos que incluyan CMIS y por tanto también acceder desde multitud de lenguajes y no solo desde Java.

Este es el caso de cmislib de Jeff Potts para Python.

La installación es muy sencilla, necesitamos un interprete Python (versión
2.7) además del módulo cmislib y setuptools para instalar el primero y
Alfresco 4.

Podemos usar la línea de comandos de Python para hacer las siguientes pruebas:

Importamos la librería:

from cmislib.model import CmisClient, Repository

Creamos el cliente configurando el end-point y las credenciales:

client = CmisClient('http://localhost:8080/alfresco/s/cmis', 'admin', 'admin')

Recuperamos el servicio de repositorio por defecto:

repo = client.getDefaultRepository()
repo.getRepositoryId()

Recuperamos información del repositorio:

info = repo.getRepositoryInfo()
for k,v in info.items():
    print "%s:%s" % (k,v)

Deberia devolvernos algo así:

cmisSpecificationTitle:Version 1.0 OASIS Standard
aclCapability:

cmisVersionSupported:1.0
principalAnonymous:guest
principalAnyone:GROUP_EVERYONE
repositoryDescription:None
changesOnType:cmis:document
changesIncomplete:true
productVersion:4.0.0 (4003)
rootFolderId:workspace://SpacesStore/b92b669b-4f2a-42ab-a9d8-1451b1596e80
repositoryId:6891ed19-41e3-4160-8c77-65de35c5a428
repositoryName:Main Repository
vendorName:Alfresco
productName:Alfresco Repository (Community)

Bien, ya lo tenemos, ahora creamos...

... una carpeta:

rootFolder = repo.rootFolder
newFolder = rootFolder.createFolder('Carpeta nueva')
newFolder.id

... un contenido:

f = open('Mi fichero', 'r')
newDoc = newFolder.createDocument('New Document', contentFile=f)

... y listamos las propiedades:

for k,v in newDoc.properties.items()
    print '%s = %s' % (k,v)

Personalmente me parece una idea muy buena y más con el lenguaje más popular
del momento ;-)

Más información en:

https://code.google.com/p/cmislib/
http://chemistry.apache.org/python/cmislib.html
https://pypi.python.org/pypi/setuptools#files
http://www.python.org/
https://www.oasis-open.org/committees/tc_home.php?wg_abbrev=cmis
http://cmis.alfresco.com/

jueves, febrero 07, 2013

Transformar ficheros RAW en Alfresco

Como ya sabemos, Alfresco tiene muchas posibilidades de manipulación y transformación de ficheros/documentos. Una de las cosas más flexibles es el uso de transformadores que puden ser de dos tipos básicamente:
  1. Llamadas directas a algún comando o utilidad del sistema
  2. Desarrollar una clase Java que realice la transformación
Aquí vamos a ver la primera para utilizar la transformación de fotografías tomadas en formato RAW (o bruto como generalmente se llama) a TIFF y a JPEG. Actualmente hago fotos con Nikon y Olympus y me interesa pasar el formato NEF (por poner el de Nikon) a TIFF y a JPEG. ImageMagick puede hacerlo pero me ha dado problemas tanto en el resultado (no se pueden ver) como en la propia conversión por lo que voy a usar otra utilidad que existe para Linux, Mac OS X y Windows.
Esta utilidad se llama dcraw y podemos bajarla de http://www.cybercom.net/~dcoffin/dcraw/ para el sistema operativo que tengamos. En este caso voy a utilizar la que hay para Windows compilada con MingW desde la dirección http://www.rawness.es/dcraw/?lang=es
Una vez instalada o descomprimida en el directorio que nos guste obtendremos el programa ejecutable dcraw.exe
Bien, ahora a configurar el transformador...
Toda la configuración se basa en un bean llamado RuntimeExecutableContentTransformer para versiones anteriore a la 3.2 de Alfresco o RuntimeExecutableContentTransformeWorker para las siguientes. A su vez este bean contiene las siguientes propiedades principales:
  • transformCommand: Define el comando y parámetros usando variables como ${source} y ${target}
  • checkCommand: Chequea si la sintaxis es correcta y si ocurre un error también se puede definir con errorCodes. Cuando no está disponible el método getReliability devuelve 0:0 este método se usa para utilizar el transformador más eficiente en caso de que haya varios definidos que conviertan de la misma fuente al mismo destino.
  • explicitTransformations establece el tipo MIME fuente y destino de forma explícita. 
Creamos el fichero nef-tiff-jpeg-transform-context.xml:
<?xml version='1.0' encoding='UTF-8'?>
<!DOCTYPE beans PUBLIC '-//SPRING//DTD BEAN//EN' 'http://www.springframework.org/dtd/spring-beans.dtd'>
<beans>
    <bean id="transformer.worker.NEFtoTIFF" class="org.alfresco.repo.content.transform.RuntimeExecutableContentTransformerWorker">
        <property name="checkCommand">
            <bean class="org.alfresco.util.exec.RuntimeExec">
                <property name="commandMap">
                    <map>
                        <entry key=".*">
                            <value>${dcraw.exe} -i ${dcraw.home}/test.nef</value>
                        </entry>
                    </map>
                </property>
                <property name="errorCodes">
                    <value>1</value>
                </property>
            </bean>
        </property>
        <property name="transformCommand">
            <bean class="org.alfresco.util.exec.RuntimeExec">
                <property name="commandMap">
                    <map>
                        <entry key="Linux.*">                   
                            <value>${dcraw.exe} -T '${source}' '${target}'</value>
                        </entry>
                        <entry key="Mac OS X">
                            <value>${dcraw.exe} -T '${source}' '${target}'</value>
                        </entry>                   
                        <entry key="Windows.*">               
                            <value>${dcraw.exe} -T "${source}" "${target}"</value>
                        </entry>
                    </map>
                </property>
                <property name="waitForCompletion">
                    <value>true</value>
                </property>
            </bean>
        </property>
        <property name="explicitTransformations">
            <list>
                <bean class="org.alfresco.repo.content.transform.ExplictTransformationDetails" >
                    <property name="sourceMimetype"><value>image/x-nikon-nef</value></property>
                    <property name="targetMimetype"><value>image/tiff</value></property>
                </bean>
            </list>
        </property>
        <property name="mimetypeService">
            <ref bean="mimetypeService"/>
        </property>
    </bean>
    <bean id="transformer.NEFtoTIFF" class="org.alfresco.repo.content.transform.ProxyContentTransformer" parent="baseContentTransformer">
        <property name="worker">
            <ref bean="transformer.worker.NEFtoTIFF"/>
        </property>
    </bean>
    <bean id="transformer.complex.Nef.Jpg"
        class="org.alfresco.repo.content.transform.ComplexContentTransformer"
        parent="baseContentTransformer" >
        <property name="transformers">
            <list>
                <ref bean="transformer.NEFtoTIFF" />
                <ref bean="transformer.ImageMagick" />
            </list>
        </property>
        <property name="intermediateMimetypes">
            <list>
                <value>image/tiff</value>
            </list>
        </property>
    </bean>
</beans>

Como vemos hay además otro bean definido de la clase ComplexContentTransformer, este sirve para realizar transformaciones a partir de otras, es decir, si no podemos transformar una imagen del formato NEF al formato JPEG pero si podemos realizar la transformación de NEF a TIFF y de TIFF a JPEG podemos configurarlo a través de este bean en el que hay que poner los transformadores y el mimetype intermedio de la transformación.

 Con esto ya podemos transformar imágenes tomadas directamente en RAW (NEF en el caso de Nikon) a TIFF y a si vez y mediante ComplexContentTransformer directamente a JPEG.
Ahora con dcraw.exe...
Esta utilidad de línea de comandos al igual que convert.exe de ImageMagick tiene la siguiente sintaxis:
C:\Users\fernando.gonzalez\workspace\Common\MingW-release-orig-x32>dcraw.exe
Raw photo decoder "dcraw" v9.06
by Dave Coffin, dcoffin a cybercom o net
Usage:  dcraw.exe [OPTION]... [FILE]...
-v        Print verbose messages
-c        Write image data to standard output
-e        Extract embedded thumbnail image
-i        Identify files without decoding them
-i -v     Identify files and show metadata
-z        Change file dates to camera timestamp
-w        Use camera white balance, if possible
-a        Average the whole image for white balance
-A <x y w h> Average a grey box for white balance
-r <r g b g> Set custom white balance
+M/-M     Use/don't use an embedded color matrix
-C <r b>  Correct chromatic aberration
-P <file> Fix the dead pixels listed in this file
-K <file> Subtract dark frame (16-bit raw PGM)
-k <num>  Set the darkness level
-S <num>  Set the saturation level
-n <num>  Set threshold for wavelet denoising
-H [0-9]  Highlight mode (0=clip, 1=unclip, 2=blend, 3+=rebuild)
-t [0-7]  Flip image (0=none, 3=180, 5=90CCW, 6=90CW)
-o [0-5]  Output colorspace (raw,sRGB,Adobe,Wide,ProPhoto,XYZ)
-d        Document mode (no color, no interpolation)
-D        Document mode without scaling (totally raw)
-j        Don't stretch or rotate raw pixels
-W        Don't automatically brighten the image
-b <num>  Adjust brightness (default = 1.0)
-g <p ts> Set custom gamma curve (default = 2.222 4.5)
-q [0-3]  Set the interpolation quality
-h        Half-size color image (twice as fast as "-q 0")
-f        Interpolate RGGB as four colors
-m <num>  Apply a 3x3 median filter to R-G and B-G
-s [0..N-1] Select one raw image or "all" from each file
-6        Write 16-bit instead of 8-bit
-4        Linear 16-bit, same as "-6 -W -g 1 1"
-T        Write TIFF instead of PPM

Podemos observar tres cosas:
  1. Podemos usar el modificador -i para chequear la utilidad, solo hay que usar una imagen y llamarla por ejemplo test.nef
  2. Debemos usar el modificador -T para pasar las imágenes a TIFF en lugar de PPM que es lo que hace por defecto
  3. Tenemos un problema, el resultado de la transformación es siempre el mismo fichero pero con la extensión cambiada, es decir, no podemos obligar a guardar el resultado en otro fichero (bueno, si, con el modificador -c que saca la salida por consola y capturandola con una redirección ">" pero esto no funciona bien)
Como Alfresco utiliza en la transformación dos variables ${source} y ${target} y ambos son distintos (genera los nombres de estos ficheros distintas) nos encontramos con el problema de que no podemos llamarlo directamente por lo que hay que crear un script que llame correctamente al ejecutable con los modificadores adecuados.
Este script en formato BAT/CMD de Windows y llamado dcraw.cmd es el siguiente:
@ECHO OFF
set path_dcraw=%0
SET opts=%1
SET source=%2
SET target=%3
%path_dcraw:~,-3%exe %opts% %source%
IF "%opts%"=="-i" GOTO END
MOVE %source:~,-4%tiff" %target%
:END

Seguidamente vamos a crear las variables en el fichero de propiedades, en este caso utilizo el mismo alfresco-global.properties:
dcraw.home=C:/Users/fernando.gonzalez/workspace/Common/MingW-release-orig-x32
dcraw.exe=${dcraw.home}/dcraw.cmd

Y por último debemos declarar los tipos MIME (mimetypes) en el fichero mimetypes-extension-map.xml que está en <extensionRoot>/alfresco/extension/mimetype:

<alfresco-config area="mimetype-map">
    <config evaluator="string-compare" condition="Mimetype Map">
        <mimetypes>
            <mimetype mimetype="image/x-nikon-nef" display="Nikon Raw Image">
                <extension>nef</extension>
            </mimetype>
            </mimetype>
        </mimetypes>
    </config>
</alfresco-config>

 Listo, ya solo nos falta hacer las reglas necesarias para convertir NEF a TIFF o a JPEG directamente en nuesto estudio fotográfico y tener almacenadas y ordenadas todas nuestras fotografías. Además, podemos usar Alfresco de previsualizador de imágenes RAW aunque no tengamos nigún programa para ello como podemos ver en la siguiente imagen:
Para depurar podemos activar las siguientes líneas en Log4J (log4j.properties o custom-log4j.propeties en el extension):
log4j.logger.org.alfresco.repo.content.transform.TransformerDebug=debug
log4j.logger.org.alfresco.util.exec.RuntimeExec=debug
log4j.logger.org.alfresco.repo.content.transform.ContentTransformerRegistry=debug

domingo, diciembre 16, 2012

Cifrado de contenido en Alfresco

En muchas ocasiones es necesario el cifrado del contenido en Alfresco, en este sentido ya Alfresco en la versión 4.0 puede cifrar propiedades (http://wiki.alfresco.com/wiki/Data_Encryption) y también hay un módulo para utilizar el cifrado (http://addons.alfresco.com/addons/alfresco-encryption-module).

En este caso vamos a realizar dos acciones que cifren y descifren el contenido de un documento (propiedad content) usando el algoritmo de cifrado simétrico AES. Estos sirven para la versión 3.4 de Alfresco y siguientes.

El código también se encuentra en: http://code.google.com/p/alfcrypto

Algunas cosas importantes primero: Este software es una versión alpha o beta o como queráis llamarla pero sobre todo es un código hecho de forma más o menos rápida y por tanto no hay garantía ninguna de funcionamiento, se ha probado solo con algunos documentos MS-Word y PDF. Además, ya he detectado un problema, cuando se descifra el tipo MIME en el que se guarda la copia desencriptada es plain/text (no es que no funcione, si descargais el documento se puede abrir/editar, etc) por lo que hay que incluir en el modelo de datos una propiedad que guarde el valor original y lo restaure posteriormente (ya lo haré cuando consiga algo de tiempo). Se ha realizado solamente con carácter educativo y por lo tanto no lo recomiendo para su uso en sistemas de producción sin realizar antes algunas modificaciones y pruebas.

Otro apunte más, como bien comenta Toni de la Fuente (blyx.com), existen algunas restricciones derivadas del cifrado, la primera es en la previsualización, evidentemente no existe cuando los documentos están cifrados y no tiene sentido descifrar para previsualizar ya que rompería la seguridad. Tampoco es posible la indexación full-text ya que no es posible al igual que en contenidos de imágenes (JPG, GIF, etc.), es más, es una buena idea cambiar el tipo MIME a algún tipo que Alfresco no indexe y cuando sea descifrado reponer el tipo MIME original. El uso de este sistema sería para documentos que son necesariamente obligados a ser cifrados y solo se tenga acceso a ellos por personal especial (Recursos Humanos, I+D+I, datos con carácter especial de protección de datos, etc.) que una vez descifrados (en otra ubicación a la original principalmente) sean descargados y borrados (la copia descifrada) vaciando la papelera e incluso modificando la configuración para que no sean guardados en esta. Además recomendaría que o bien el cifrado, o el descifrado se aloje en otras unidades de disco diferentes usando para ello Content Store Selector (en este último caso solo para las versiones Enterprise).

Con todo esto, comencemos:

Lo primero que hay que hacer es construir la clase de cifrado que en este caso se llamará crypto.java:

/*
 * alfcrypto is free software: you can redistribute it and/or modify
 * it under the terms of the GNU Lesser General Public License as published by
 * the Free Software Foundation, either version 3 of the License, or
 * (at your option) any later version.
 *
 * alfcrypto is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 * GNU Lesser General Public License for more details.
 *
 * You should have received a copy of the GNU Lesser General Public License
 * along with Alfresco. If not, see <http://www.gnu.org/licenses/>.
 */
package com.fegor.alfresco.security.crypto;

import java.io.IOException;
import java.io.UnsupportedEncodingException;
import java.security.AlgorithmParameters;
import java.security.InvalidAlgorithmParameterException;
import java.security.InvalidKeyException;
import java.security.NoSuchAlgorithmException;
import java.security.SecureRandom;
import java.security.spec.InvalidKeySpecException;
import java.security.spec.InvalidParameterSpecException;
import java.security.spec.KeySpec;

import org.apache.log4j.Logger;

import javax.crypto.BadPaddingException;
import javax.crypto.Cipher;
import javax.crypto.IllegalBlockSizeException;
import javax.crypto.NoSuchPaddingException;
import javax.crypto.SecretKey;
import javax.crypto.SecretKeyFactory;
import javax.crypto.ShortBufferException;
import javax.crypto.spec.IvParameterSpec;
import javax.crypto.spec.PBEKeySpec;
import javax.crypto.spec.SecretKeySpec;

import org.apache.commons.codec.DecoderException;
import org.apache.commons.codec.binary.Hex;

public class Crypto {
    private final Logger logger = Logger.getLogger(Crypto.class);

    String password = null;
    public final static int SALT_LEN = 8;
    byte[] vector_init = null;
    byte[] salt_pos = null;

    byte[] input;
    byte[] output;

    Cipher eCipher = null;
    Cipher deCipher = null;

    private final int KEYLEN_BITS = 128;
    private final int ITERATIONS = 65536;

    /**
     * Constructor
     */
    public Crypto() {
    }

    /**
     * Encryption configuration
     *
     * @throws NoSuchAlgorithmException
     * @throws InvalidKeySpecException
     * @throws NoSuchPaddingException
     * @throws InvalidParameterSpecException
     * @throws IllegalBlockSizeException
     * @throws BadPaddingException
     * @throws UnsupportedEncodingException
     * @throws InvalidKeyException
     */
    public void configEncrypt() throws NoSuchAlgorithmException,
            InvalidKeySpecException, NoSuchPaddingException,
            InvalidParameterSpecException, IllegalBlockSizeException,
            BadPaddingException, UnsupportedEncodingException,
            InvalidKeyException {
        SecretKeyFactory factory = null;
        SecretKey tmp = null;

        salt_pos = new byte[SALT_LEN];
        SecureRandom rnd = new SecureRandom();
        rnd.nextBytes(salt_pos);

        if (logger.isDebugEnabled())
            logger.debug(this.getClass().getName() + ": [salt: "
                    + (new String(Hex.encodeHex(salt_pos))) + "]");

        factory = SecretKeyFactory.getInstance("PBKDF2WithHmacSHA1");

        /*
         * http://www.javamex.com/tutorials/cryptography/unrestricted_policy_files
         * .shtml
         */
        KeySpec spec = new PBEKeySpec(password.toCharArray(), salt_pos,
                ITERATIONS, KEYLEN_BITS);
        tmp = factory.generateSecret(spec);
        SecretKey secret = new SecretKeySpec(tmp.getEncoded(), "AES");

        eCipher = Cipher.getInstance("AES/CBC/PKCS5Padding");
        eCipher.init(Cipher.ENCRYPT_MODE, secret);
        AlgorithmParameters params = eCipher.getParameters();

        vector_init = params.getParameterSpec(IvParameterSpec.class).getIV();

        if (logger.isDebugEnabled())
            logger.debug(this.getClass().getName() + ": [vector ini: "
                    + (new String(Hex.encodeHex(vector_init))) + "]");
    }

    /**
     * Decryption configuration
     *
     * @param initvec
     * @param salt
     * @throws NoSuchAlgorithmException
     * @throws InvalidKeySpecException
     * @throws NoSuchPaddingException
     * @throws InvalidKeyException
     * @throws InvalidAlgorithmParameterException
     * @throws DecoderException
     */
    public void configDecrypt(String initvec, String salt)
            throws NoSuchAlgorithmException, InvalidKeySpecException,
            NoSuchPaddingException, InvalidKeyException,
            InvalidAlgorithmParameterException, DecoderException {
        SecretKeyFactory factory = null;
        SecretKey tmp = null;
        SecretKey secret = null;

        salt_pos = Hex.decodeHex(salt.toCharArray());

        if (logger.isDebugEnabled())
            logger.debug(this.getClass().getName() + ": [salt: "
                    + (new String(Hex.encodeHex(salt_pos))) + "]");

        vector_init = Hex.decodeHex(initvec.toCharArray());
        if (logger.isDebugEnabled())
            logger.debug(this.getClass().getName() + ": [vector ini: "
                    + (new String(Hex.encodeHex(vector_init))) + "]");

        /*
         * http://www.javamex.com/tutorials/cryptography/unrestricted_policy_files
         * .shtml
         */
        factory = SecretKeyFactory.getInstance("PBKDF2WithHmacSHA1");
        KeySpec spec = new PBEKeySpec(password.toCharArray(), salt_pos,
                ITERATIONS, KEYLEN_BITS);

        tmp = factory.generateSecret(spec);
        secret = new SecretKeySpec(tmp.getEncoded(), "AES");

        deCipher = Cipher.getInstance("AES/CBC/PKCS5Padding");
        deCipher.init(Cipher.DECRYPT_MODE, secret, new IvParameterSpec(
                vector_init));
    }

    /**
     * Cipher input
     *
     * @param input
     *            - the cleartext file to be encrypted
     * @param output
     *            - the encrypted data file
     * @throws IOException
     * @throws IllegalBlockSizeException
     * @throws BadPaddingException
     * @throws ShortBufferException
     */
    public void Cipher() throws IOException, IllegalBlockSizeException,
            BadPaddingException, ShortBufferException {
        try {
            this.output = eCipher.doFinal(this.input);
        } catch (IllegalBlockSizeException e) {
            e.printStackTrace();
        } catch (BadPaddingException e) {
            e.printStackTrace();
        }
    }

    /**
     * Decipher input
     *
     * @param input
     *            - the cleartext file to be encrypted
     * @param output
     *            - the encrypted data file
     * @throws IOException
     * @throws IllegalBlockSizeException
     * @throws BadPaddingException
     * @throws ShortBufferException
     */
    public void Decipher() throws IOException, IllegalBlockSizeException,
            BadPaddingException, ShortBufferException {
        try {
            this.output = deCipher.doFinal(this.input);
        } catch (IllegalBlockSizeException e) {
            e.printStackTrace();
        } catch (BadPaddingException e) {
            e.printStackTrace();
        }
    }

    /*
     * Methods setter and getter
     */
    public void setInput(byte[] input) {
        this.input = input;
    }

    public void setPassword(String password) {
        this.password = password;
    }

    public String getSalt() {
        return (new String(Hex.encodeHex(salt_pos)));
    }

    public String getVectorInit() {
        return (new String(Hex.encodeHex(vector_init)));
    }

    public byte[] getOutput() {
        return this.output;
    }
}


Como se observa es una clase normal con los métodos para configurar el cifrado y descifrado y la llamada para realizar las acciones correspondientes.

Utilizaremos dos aspectos para saber que documentos están cifrados y cuales han sido descifrados, el modelo de datos a utilizar será el siguiente:

<?xml version="1.0" encoding="UTF-8"?>
<model name="acr:alfcryptoModel" xmlns="http://www.alfresco.org/model/dictionary/1.0">
    <!-- Optional meta-data about the model -->
    <description>Alfresco Crypto Model</description>
    <author>Fernando González Ruano (twitter://fegorama)</author>
    <version>1.0</version>
    <imports>
        <import uri="http://www.alfresco.org/model/dictionary/1.0"
            prefix="d" />
        <import uri="http://www.alfresco.org/model/content/1.0" prefix="cm" />
    </imports>
    <namespaces>
        <namespace uri="http://www.fegorsoft.com/model/alfcrypto/1.0"
            prefix="acr" />
    </namespaces>
    <aspects>
        <aspect name="acr:ciphered">
            <title>Ciphered</title>
            <properties>
                <property name="acr:salt">
                    <type>d:text</type>
                    <mandatory>false</mandatory>
                    <index enabled="true">
                        <atomic>false</atomic>
                        <stored>false</stored>
                        <tokenised>false</tokenised>
                    </index>
                </property>
                <property name="acr:vector_ini">
                    <type>d:text</type>
                    <mandatory>false</mandatory>
                    <index enabled="true">
                        <atomic>false</atomic>
                        <stored>false</stored>
                        <tokenised>false</tokenised>
                    </index>
                </property>               
            </properties>
        </aspect>
        <aspect name="acr:deciphered">
            <title>Deciphered</title>
        </aspect>
    </aspects>
</model>


Para llamar a esta clase se necesitan dos acciones de Alfresco, estas serán CipherContent.java y DecipherContent.java:

Fichero: CipherContent.java
/*
 * alfcrypto is free software: you can redistribute it and/or modify
 * it under the terms of the GNU Lesser General Public License as published by
 * the Free Software Foundation, either version 3 of the License, or
 * (at your option) any later version.
 *
 * alfcrypto is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 * GNU Lesser General Public License for more details.
 *
 * You should have received a copy of the GNU Lesser General Public License
 * along with Alfresco. If not, see <http://www.gnu.org/licenses/>.
 */
package com.fegor.alfresco.action;

import java.io.IOException;
import java.io.Serializable;
import java.io.UnsupportedEncodingException;
import java.security.InvalidKeyException;
import java.security.NoSuchAlgorithmException;
import java.security.spec.InvalidKeySpecException;
import java.security.spec.InvalidParameterSpecException;
import java.util.HashMap;
import java.util.List;

import javax.crypto.BadPaddingException;
import javax.crypto.IllegalBlockSizeException;
import javax.crypto.NoSuchPaddingException;
import javax.crypto.ShortBufferException;

import org.alfresco.model.ContentModel;
import org.alfresco.repo.action.executer.ActionExecuterAbstractBase;
import org.alfresco.service.cmr.action.Action;
import org.alfresco.service.cmr.action.ParameterDefinition;
import org.alfresco.service.cmr.repository.ContentIOException;
import org.alfresco.service.cmr.repository.ContentReader;
import org.alfresco.service.cmr.repository.ContentService;
import org.alfresco.service.cmr.repository.ContentWriter;
import org.alfresco.service.cmr.repository.NodeRef;
import org.alfresco.service.cmr.repository.NodeService;
import org.alfresco.service.namespace.QName;
import org.apache.commons.io.IOUtils;
import org.apache.log4j.Logger;

import com.fegor.alfresco.model.AlfCryptoModel;
import com.fegor.alfresco.security.crypto.Crypto;
import com.google.gdata.util.common.util.Base64;

/**
 * CryptoRepo Action
 *
 * @author fegor
 *
 */
public class CipherContent extends ActionExecuterAbstractBase {

    private final Logger logger = Logger.getLogger(CipherContent.class);

    /*
     * Services
     */
    private ContentService contentService;
    private NodeService nodeService;

    private String password;
    //
    // TODO Poder usar más algoritmos que AES
    //
    // private String algorithm;

    private String salt;
    private String vector_init;

    /*
     * (non-Javadoc)
     *
     * @see
     * org.alfresco.repo.action.executer.ActionExecuterAbstractBase#executeImpl
     * (org.alfresco.service.cmr.action.Action,
     * org.alfresco.service.cmr.repository.NodeRef)
     */
    @Override
    protected void executeImpl(Action action, NodeRef actionedUponNodeRef) {
        if (!nodeService.hasAspect(actionedUponNodeRef,
                AlfCryptoModel.ASPECT_CIPHERED)) {
            if (logger.isDebugEnabled()) {
                logger.debug(this.getClass().getName() + ": [Action for: "
                        + actionedUponNodeRef + " is ciphering...]");
            }
            if (actionedUponNodeRef != null)
                try {
                    this.cryptoFileCipher(actionedUponNodeRef);
                } catch (ContentIOException e) {
                    e.printStackTrace();
                } catch (IOException e) {
                    e.printStackTrace();
                }
        }
    }

    /*
     * (non-Javadoc)
     *
     * @see org.alfresco.repo.action.ParameterizedItemAbstractBase#
     * addParameterDefinitions(java.util.List)
     */
    @Override
    protected void addParameterDefinitions(List<ParameterDefinition> arg0) {
    }

    /**
     * Crypto file for nodeRef
     *
     * @param nodeRef
     * @throws IOException
     * @throws ContentIOException
     */
    private void cryptoFileCipher(NodeRef nodeRef) throws ContentIOException,
            IOException {
        ContentReader contentReader = this.contentService.getReader(nodeRef,
                ContentModel.PROP_CONTENT);
        ContentWriter contentWriter = this.contentService.getWriter(nodeRef,
                ContentModel.PROP_CONTENT, true);

        if (contentReader != null) {
            Crypto crypto = new Crypto();
            crypto.setPassword(this.password);
            byte[] crb = IOUtils.toByteArray(contentReader
                    .getContentInputStream());

            try {
                crypto.configEncrypt();
            } catch (InvalidKeyException e) {
                e.printStackTrace();
            } catch (NoSuchAlgorithmException e) {
                e.printStackTrace();
            } catch (InvalidKeySpecException e) {
                e.printStackTrace();
            } catch (NoSuchPaddingException e) {
                e.printStackTrace();
            } catch (InvalidParameterSpecException e) {
                e.printStackTrace();
            } catch (IllegalBlockSizeException e) {
                e.printStackTrace();
            } catch (BadPaddingException e) {
                e.printStackTrace();
            } catch (UnsupportedEncodingException e) {
                e.printStackTrace();
            }

            crypto.setInput(crb);
            try {
                crypto.Cipher();
            } catch (IllegalBlockSizeException e) {
                e.printStackTrace();
            } catch (BadPaddingException e) {
                e.printStackTrace();
            } catch (ShortBufferException e) {
                e.printStackTrace();
            } catch (IOException e) {
                e.printStackTrace();
            }

            contentWriter.putContent(Base64.encode(crypto.getOutput()));

            this.salt = crypto.getSalt();
            this.vector_init = crypto.getVectorInit();

            this.removeAspect(nodeRef);
            this.addAspect(nodeRef);

        } else {
            if (logger.isDebugEnabled())
                logger.debug(this.getClass().getName()
                        + ": [contentReader is null]");
        }
    }

    /**
     * Remove aspect Deciphered
     *
     * @param nodeRef
     */
    private void removeAspect(NodeRef nodeRef) {
        if (nodeService.hasAspect(nodeRef, AlfCryptoModel.ASPECT_DECIPHERED)) {
            nodeService.removeAspect(nodeRef, AlfCryptoModel.ASPECT_DECIPHERED);
        }
    }

    /**
     * Add aspect Ciphered
     *
     * @param nodeRef
     */
    private void addAspect(NodeRef nodeRef) {
        HashMap<QName, Serializable> properties = new HashMap<QName, Serializable>(
                1, 1.0f);
        properties.put(AlfCryptoModel.PROP_SALT, this.salt);
        properties.put(AlfCryptoModel.PROP_VECTOR_INIT, this.vector_init);
        if (!nodeService.hasAspect(nodeRef, AlfCryptoModel.ASPECT_CIPHERED)) {
            nodeService.addAspect(nodeRef, AlfCryptoModel.ASPECT_CIPHERED,
                    properties);
        }
    }

    /**
     * @param contentService
     */
    public void setContentService(ContentService contentService) {
        this.contentService = contentService;
    }

    /**
     * @param nodeService
     */
    public void setNodeService(NodeService nodeService) {
        this.nodeService = nodeService;
    }

    /**
     * @param password
     */
    public void setPassword(String password) {
        this.password = password;
    }

    /**
     * @param algorithm
     */
    //
    // TODO Poder usar más algoritmos que AES
    //
    // public void setAlgorithm(String algorithm) {
    // this.algorithm = algorithm;
    // }
}


Fichero: DecipherContent.java
/*
 * alfcrypto is free software: you can redistribute it and/or modify
 * it under the terms of the GNU Lesser General Public License as published by
 * the Free Software Foundation, either version 3 of the License, or
 * (at your option) any later version.
 *
 * alfcrypto is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 * GNU Lesser General Public License for more details.
 *
 * You should have received a copy of the GNU Lesser General Public License
 * along with Alfresco. If not, see <http://www.gnu.org/licenses/>.
 */
package com.fegor.alfresco.action;

import java.io.ByteArrayInputStream;
import java.io.IOException;
import java.io.InputStream;
import java.security.InvalidAlgorithmParameterException;
import java.security.InvalidKeyException;
import java.security.NoSuchAlgorithmException;
import java.security.spec.InvalidKeySpecException;
import java.util.List;

import javax.crypto.BadPaddingException;
import javax.crypto.IllegalBlockSizeException;
import javax.crypto.NoSuchPaddingException;
import javax.crypto.ShortBufferException;

import org.alfresco.model.ContentModel;
import org.alfresco.repo.action.executer.ActionExecuterAbstractBase;
import org.alfresco.service.cmr.action.Action;
import org.alfresco.service.cmr.action.ParameterDefinition;
import org.alfresco.service.cmr.repository.ContentReader;
import org.alfresco.service.cmr.repository.ContentService;
import org.alfresco.service.cmr.repository.ContentWriter;
import org.alfresco.service.cmr.repository.NodeRef;
import org.alfresco.service.cmr.repository.NodeService;
import org.apache.commons.codec.DecoderException;
import org.apache.log4j.Logger;

import com.fegor.alfresco.model.AlfCryptoModel;
import com.fegor.alfresco.security.crypto.Crypto;
import com.google.gdata.util.common.util.Base64;
import com.google.gdata.util.common.util.Base64DecoderException;

/**
 * DecryptoRepo Action
 *
 * @author fegor
 *
 */
public class DecipherContent extends ActionExecuterAbstractBase {

    private final Logger logger = Logger.getLogger(DecipherContent.class);

    /*
     * Services
     */
    private ContentService contentService;
    private NodeService nodeService;

    private String password;
    //
    // TODO Poder usar más algoritmos que AES
    //
    // private String algorithm;

    private String salt;
    private String vector_init;

    /*
     * (non-Javadoc)
     *
     * @see
     * org.alfresco.repo.action.executer.ActionExecuterAbstractBase#executeImpl
     * (org.alfresco.service.cmr.action.Action,
     * org.alfresco.service.cmr.repository.NodeRef)
     */
    @Override
    protected void executeImpl(Action action, NodeRef actionedUponNodeRef) {
        if (nodeService.hasAspect(actionedUponNodeRef,
                AlfCryptoModel.ASPECT_CIPHERED)) {
            if (logger.isDebugEnabled()) {
                logger.debug(this.getClass().getName() + ": [Action for: "
                        + actionedUponNodeRef + " is deciphering...]");
            }
            if (actionedUponNodeRef != null)
                try {
                    this.cryptoFileDecipher(actionedUponNodeRef);
                } catch (InvalidAlgorithmParameterException e) {
                    e.printStackTrace();
                } catch (DecoderException e) {
                    e.printStackTrace();
                }
        }
    }

    /*
     * (non-Javadoc)
     *
     * @see org.alfresco.repo.action.ParameterizedItemAbstractBase#
     * addParameterDefinitions(java.util.List)
     */
    @Override
    protected void addParameterDefinitions(List<ParameterDefinition> arg0) {
    }

    /**
     * Scan file for nodeRef
     *
     * @param nodeRef
     * @throws DecoderException
     * @throws InvalidAlgorithmParameterException
     */
    private void cryptoFileDecipher(NodeRef nodeRef)
            throws InvalidAlgorithmParameterException, DecoderException {
        ContentReader contentReader = this.contentService.getReader(nodeRef,
                ContentModel.PROP_CONTENT);
        ContentWriter contentWriter = this.contentService.getWriter(nodeRef,
                ContentModel.PROP_CONTENT, true);

        if (contentReader != null) {

            byte[] crb = contentReader.getContentString().getBytes();
            Crypto crypto = new Crypto();
            crypto.setPassword(this.password);
            this.salt = (String) nodeService.getProperty(nodeRef,
                    AlfCryptoModel.PROP_SALT);
            this.vector_init = (String) nodeService.getProperty(nodeRef,
                    AlfCryptoModel.PROP_VECTOR_INIT);

            try {
                crypto.configDecrypt(this.vector_init, this.salt);
            } catch (InvalidKeyException e) {
                e.printStackTrace();
            } catch (NoSuchAlgorithmException e) {
                e.printStackTrace();
            } catch (InvalidKeySpecException e) {
                e.printStackTrace();
            } catch (NoSuchPaddingException e) {
                e.printStackTrace();
            }

            try {
                crypto.setInput(Base64.decode(crb));
            } catch (Base64DecoderException e1) {
                e1.printStackTrace();
            }

            try {
                crypto.Decipher();
            } catch (IllegalBlockSizeException e) {
                e.printStackTrace();
            } catch (BadPaddingException e) {
                e.printStackTrace();
            } catch (ShortBufferException e) {
                e.printStackTrace();
            } catch (IOException e) {
                e.printStackTrace();
            }
            contentWriter.setMimetype("text/plain");
            contentWriter.putContent((InputStream) (new ByteArrayInputStream(
                    crypto.getOutput())));

            this.removeAspect(nodeRef);
            this.addAspect(nodeRef);

        } else {
            if (logger.isDebugEnabled())
                logger.debug(this.getClass().getName()
                        + ": [contentReader is null]");
        }
    }

    /**
     * Remove aspect Ciphered
     *
     * @param nodeRef
     */
    private void removeAspect(NodeRef nodeRef) {
        if (nodeService.hasAspect(nodeRef, AlfCryptoModel.ASPECT_CIPHERED)) {
            nodeService.removeAspect(nodeRef, AlfCryptoModel.ASPECT_CIPHERED);
        }
    }

    /**
     * Add aspect Deciphered
     *
     * @param nodeRef
     */
    private void addAspect(NodeRef nodeRef) {
        if (!nodeService.hasAspect(nodeRef, AlfCryptoModel.ASPECT_CIPHERED)) {
            nodeService
                    .addAspect(nodeRef, AlfCryptoModel.ASPECT_CIPHERED, null);
        }
    }

    /**
     * @param contentService
     */
    public void setContentService(ContentService contentService) {
        this.contentService = contentService;
    }

    /**
     * @param nodeService
     */
    public void setNodeService(NodeService nodeService) {
        this.nodeService = nodeService;
    }

    /**
     * @param password
     */
    public void setPassword(String password) {
        this.password = password;
    }

    /**
     * @param algorithm
     */
    //
    // TODO Poder usar más algoritmos que AES
    //
    // public void setAlgorithm(String algorithm) {
    // this.algorithm = algorithm;
    // }
}


El siguiente paso es configurar y registrar las acciones en Alfresco:

Fichero: actions-context.xml
<?xml version='1.0' encoding='UTF-8'?>
<!DOCTYPE beans PUBLIC '-//SPRING//DTD BEAN//EN' 'http://www.springframework.org/dtd/spring-beans.dtd'>
<beans>
     <bean class="org.springframework.beans.factory.config.PropertyPlaceholderConfigurer">
        <property name="ignoreUnresolvablePlaceholders">
            <value>true</value>
        </property>
        <property name="locations">
            <list>
                <value>classpath:alfresco/extension/alfcrypto.properties</value>
            </list>
        </property>
    </bean>
   
    <bean id="alfcrypto.cipher.action" class="com.fegor.alfresco.action.CipherContent"
        parent="action-executer">
        <property name="contentService">
            <ref bean="contentService" />
        </property>
        <property name="nodeService">
            <ref bean="nodeService" />
        </property>
        <!-- TODO Poder usar más algoritmos que AES -->
        <!--
        <property name="algorithm">
            <value>${alfviral.algorithm}</value>
        </property>
         -->
        <property name="password">
            <value>${alfviral.password}</value>
        </property>
    </bean>

    <bean id="alfcrypto.decipher.action" class="com.fegor.alfresco.action.DecipherContent"
        parent="action-executer">
        <property name="contentService">
            <ref bean="contentService" />
        </property>
        <property name="nodeService">
            <ref bean="nodeService" />
        </property>
        <!-- TODO Poder usar más algoritmos que AES -->
        <!--
        <property name="algorithm">
            <value>${alfviral.algorithm}</value>
        </property>
         -->
        <property name="password">
            <value>${alfviral.password}</value>
        </property>
    </bean>   
</beans>


Fichero: model-context.xml
<?xml version='1.0' encoding='UTF-8'?>
<!DOCTYPE beans PUBLIC '-//SPRING//DTD BEAN//EN' 'http://www.springframework.org/dtd/spring-beans.dtd'>

<beans>
    <!-- Modelos -->   
    <bean id="alfcrypto.dictionaryBootstrap" parent="dictionaryModelBootstrap" depends-on="dictionaryBootstrap">
        <property name="models">
            <list>
                <value>alfresco/module/alfcrypto/model/alfcryptoModel.xml</value>
            </list>
        </property>
    </bean> 
</beans>


Fichero: webclient-context.xml
<?xml version='1.0' encoding='UTF-8'?>
<!DOCTYPE beans PUBLIC '-//SPRING//DTD BEAN//EN' 'http://www.springframework.org/dtd/spring-beans.dtd'>

<beans>
    <bean id="alfcrypto.resourceBundle" parent="actionResourceBundles">
        <property name="resourceBundles">
            <list>
                <value>alfresco.module.alfcrypto.messages.alfcrypto</value>
            </list>
        </property>
    </bean>
  
    <bean id="alfcrypto.webclient.configBootstrap" class="org.alfresco.web.config.WebClientConfigBootstrap"
        init-method="init">
        <property name="configs">
            <list>
                <value>classpath:alfresco/module/alfcrypto/ui/web-client-config-custom.xml</value>
            </list>
        </property>
    </bean>
</beans>


Fichero: web-client-config-custom.xml
<alfresco-config>

   <config evaluator="aspect-name" condition="acr:ciphered">
      <property-sheet>
         <show-property name="acr:salt" display-label-id="alfcrypto.label.salt" read-only="true"/>
         <show-property name="acr:vector_init" display-label-id="alfcrypto.label.vector_init" read-only="true"/>
      </property-sheet>
   </config>
  
   <config evaluator="aspect-name" condition="acr:deciphered">
   
   </config>
  
   <config evaluator="string-compare" condition="Action Wizards">
      <aspects>
         <aspect name="acr:ciphered" display-label-id="alfcrypto.label.ciphered"/>
         <aspect name="acr:deciphered" display-label-id="alfcrypto.label.deciphered"/>
         </aspects>
   </config>
  
</alfresco-config>


Fichero: alfcrypto.properties (messages)
alfcrypto.cipher.action.title=Cifrar
alfcrypto.cipher.action.description=Cifrado del contenido

alfcrypto.decipher.action.title=Descifrar
alfcrypto.decipher.action.description=Descifrado del contenido

alfcrypto.label.ciphered=Cifrado
alfcrypto.label.deciphered=Descifrado


Y por último construir el fichero de configuración:

Fichero: alfcrypto.properties
# La elección de algoritmo (alfcrypto.algorithm) no está implementado todavía
alfcrypto.algorithm=AES
alfcrypto.password=estoesunaclavesecreta


A partir de aquí podemos utilizar el sistema para cifrar y descifrar, para ello podemos usar reglas para que el contenido en una carpeta sea cifrado, crear un workflow para descifrar el contenido de los documentos cifrados, etc.

Solo cifra el contenido, no las propiedades de los documentos.

Más sobre este tema:
http://es.wikipedia.org/wiki/Advanced_Encryption_Standard
http://en.wikipedia.org/wiki/Advanced_Encryption_Standard
http://wiki.alfresco.com/wiki/Data_Encryption
http://addons.alfresco.com/addons/alfresco-encryption-module