java – signature with private .key encrypted password protected file

The tax administration of my country (México) -Sistema de Administración Tributaria, SAT- gaves us a pair of private and public key for us to authenticate. These are in the form of a file with the .key extension and a file with the .cer extension, respectively. They’re in DER format and the private key is in PKCS8 while the certificate is of type X.509 according to this post.
You can convert your .key file to the PEM format using openssl

openssl pkcs8 -inform DER -in acde970801.key -out
acde970801.pem

We wanted to use this keys, to sign and verify signed documents, in their original format since those are what the end user have. I searched how to do this and found different suggestions. I first tried this post from stackoverflow How to decrypt a private key in Java (without BC openssl)

public PrivateKey decryptKey(byte[] pkcs8Data, char[] password) throws Exception {
    PBEKeySpec pbeSpec = new PBEKeySpec(password);
    EncryptedPrivateKeyInfo pkinfo = new EncryptedPrivateKeyInfo(pkcs8Data);
    SecretKeyFactory skf = SecretKeyFactory.getInstance(pkinfo.getAlgName());
    Key secret = skf.generateSecret(pbeSpec);
    PKCS8EncodedKeySpec keySpec = pkinfo.getKeySpec(secret);
    KeyFactory kf = KeyFactory.getInstance("RSA");
    return kf.generatePrivate(keySpec);
}

but it didn’t work, it threw the following exception

Caused by: java.io.IOException: ObjectIdentifier() — data isn’t an object ID (tag = 48)

The funny thing is that I converted the .key file to the PEM format with the above openssl command and then I tried the answer provided in Read RSA private key of format PKCS1 in JAVA and it worked, I just had to make a few changes

private static PrivateKey readPrivateKeyPEM(String filename) throws Exception {
        PEMParser pemParser = new PEMParser(new FileReader(filename));
        JcaPEMKeyConverter converter = new JcaPEMKeyConverter().setProvider("BC");
        PrivateKeyInfo privateKeyInfo = (PrivateKeyInfo) pemParser.readObject();
        PrivateKey privateKey = converter.getPrivateKey(privateKeyInfo);
        return privateKey;
    }

unfortunately this failed to comply our initial requirement of using the original .key file, also note here that the password is no longer required since we decrypted de key while doing the conversion/export to the PEM format.

Finally I found the solution in How read a PKCS8 encrypted Private key which is also encoded in DER with bouncycastle?.

First, we need to import the required libraries (Bouncy Castle). Note that jdk15on means from jdk 1.5 to 1.8 (java 8).


        
            org.bouncycastle
            bcprov-jdk15on
            1.60
        
        
            org.bouncycastle
            bcpkix-jdk15on
            1.60
        
    

and then we can use the classes to get our private key and do the signing and verification. Our final code is something like this

import org.bouncycastle.asn1.ASN1Sequence;
import org.bouncycastle.asn1.pkcs.EncryptedPrivateKeyInfo;
import org.bouncycastle.asn1.pkcs.PrivateKeyInfo;
import org.bouncycastle.jce.provider.BouncyCastleProvider;
import org.bouncycastle.openssl.PEMParser;
import org.bouncycastle.openssl.jcajce.JcaPEMKeyConverter;
import org.bouncycastle.openssl.jcajce.JceOpenSSLPKCS8DecryptorProviderBuilder;
import org.bouncycastle.operator.InputDecryptorProvider;
import org.bouncycastle.pkcs.PKCS8EncryptedPrivateKeyInfo;
import org.bouncycastle.util.encoders.Base64;

import java.io.File;
import java.io.FileInputStream;
import java.io.FileReader;
import java.nio.file.Files;
import java.security.PrivateKey;
import java.security.Security;
import java.security.Signature;
import java.security.cert.CertificateFactory;
import java.security.cert.X509Certificate;

public class Main {
    private static PrivateKey readPrivateKeyPEM(String filename) throws Exception {
        PEMParser pemParser = new PEMParser(new FileReader(filename));
        JcaPEMKeyConverter converter = new JcaPEMKeyConverter().setProvider("BC");
        PrivateKeyInfo privateKeyInfo = (PrivateKeyInfo) pemParser.readObject();
        PrivateKey privateKey = converter.getPrivateKey(privateKeyInfo);
        return privateKey;
    }

    private static PrivateKey readPrivateKeyDER(String filename, String password) throws Exception {
        ASN1Sequence asn1Sequence = ASN1Sequence.getInstance(Files.readAllBytes(new File(filename).toPath()));
        PKCS8EncryptedPrivateKeyInfo pkcs8EncryptedPrivateKeyInfo = new PKCS8EncryptedPrivateKeyInfo(EncryptedPrivateKeyInfo.getInstance(asn1Sequence));
        JcaPEMKeyConverter converter = new JcaPEMKeyConverter();
        InputDecryptorProvider decryptorProvider = new JceOpenSSLPKCS8DecryptorProviderBuilder().build(password.toCharArray());
        PrivateKeyInfo privateKeyInfo = pkcs8EncryptedPrivateKeyInfo.decryptPrivateKeyInfo(decryptorProvider);
        PrivateKey privateKey = converter.getPrivateKey(privateKeyInfo);
        return privateKey;
    }


    private static X509Certificate readCertificate(String filename) throws Exception {
        CertificateFactory certificateFactory = CertificateFactory.getInstance("X.509");
        return (X509Certificate) certificateFactory.generateCertificate(new FileInputStream(filename));
    }

    public static void main(String[] args) throws Exception {
        Security.addProvider(new BouncyCastleProvider());
        //PrivateKey privateKey = readPrivateKeyPEM("/tmp/aecc820430qu5.pem");
        PrivateKey privateKey = readPrivateKeyDER("/tmp/aecc820430qu5.key", "s3cr3t");
        X509Certificate cert = readCertificate("/tmp/aecc820430qu5.cer");
        System.out.println("Private key algorithm: " + privateKey.getAlgorithm());
        System.out.println("Private key format: " + privateKey.getFormat());

        File file = new File("/tmp/acuerdo.odt");
        byte[] data = Files.readAllBytes(file.toPath());

        Signature signature = Signature.getInstance("SHA1WithRSA");
        signature.initSign(privateKey);
        signature.update(data);
        byte[] sign = signature.sign();
        System.out.println("Signature: " + new String(Base64.encode(sign)));

        signature.initVerify(cert);
        //signature.update(Files.readAllBytes(new File("/tmp/acuerdo-a.odt").toPath()));
        signature.update(data);
        System.out.println("Firma verificada: " + signature.verify(sign));
    }
}

great!

update(jan 23, 2019): although it worked in a clean empty project, in my current project it was throwing the exception

Caused by: org.bouncycastle.operator.OperatorCreationException: 1.2.840.113549.1.5.13 not available: PBKDF2with8BIT SecretKeyFactory not available
at org.bouncycastle.openssl.jcajce.JceOpenSSLPKCS8DecryptorProviderBuilder$1.get(Unknown Source)

I just had to add the provider in the readPrivateKeyDER method

private static PrivateKey readPrivateKeyDER(String filename, String password) throws Exception {
        Security.addProvider(new BouncyCastleProvider());
        ASN1Sequence asn1Sequence = ASN1Sequence.getInstance(Files.readAllBytes(new File(filename).toPath()));
        PKCS8EncryptedPrivateKeyInfo pkcs8EncryptedPrivateKeyInfo = new PKCS8EncryptedPrivateKeyInfo(EncryptedPrivateKeyInfo.getInstance(asn1Sequence));
        JcaPEMKeyConverter converter = new JcaPEMKeyConverter();
        InputDecryptorProvider decryptorProvider = new JceOpenSSLPKCS8DecryptorProviderBuilder().build(password.toCharArray());
        PrivateKeyInfo privateKeyInfo = pkcs8EncryptedPrivateKeyInfo.decryptPrivateKeyInfo(decryptorProvider);
        PrivateKey privateKey = converter.getPrivateKey(privateKeyInfo);
        return privateKey;
    }
Anuncios

jhipster angular primeNG file upload with entity as application/json

I was uploading some files along an entity in a jhipster generated app with the primeNG file upload component, but I was appending each field value in a new form data request parameter.

CursoResource.java

    @PostMapping("/cursos/archivos")
    @Timed
    public ResponseEntity<Curso> createCurso(@RequestPart(value = "titulo") String titulo, 
                                             @RequestPart(value = "descripcion") String descripcion,
                                             @RequestPart("archivos[]")MultipartFile[] archivos
    ) {
...
Curso curso = new Curso();
curso.setTitulo(titulo);
curso.setDescripcion(descripcion);
curso = this.cursoService.save(curso);
...
}

and in the client

curso-update.component.html

<p-fileUpload id="field_archivos" name="archivos[]" url="api/cursos/archivos" multiple="multiple"
                                  accept=".pdf,.doc,.docx,.xls,.xlsx"
                                  [maxFileSize]="3*1024*1024"                                  
                    (onBeforeSend)="onBeforeSend($event)">
                    </p-fileUpload>

curso-update.component.ts

    private onBeforeSend(event) {
        const token = this.localStorage.retrieve('authenticationToken') || this.sessionStorage.retrieve('authenticationToken');
        if (!!token) {
            event.xhr.setRequestHeader('Authorization', `Bearer  ${token}`);
        }
        event.formData.append('titulo', this.curso.titulo);
        event.formData.append('descripcion', this.curso.descripcion);
    }

but we can send the Entity serialized as JSON in a Blob inside our FormData using the correct Content-Type 'application/json'. With this we can include nested objects, add fields to our Entity -and we won't have to change the Resource Controller method signature with every new field-

curso-update.component.ts

    private onBeforeSend(event) {
        const token = this.localStorage.retrieve('authenticationToken') || this.sessionStorage.retrieve('authenticationToken');
        if (!!token) {
            event.xhr.setRequestHeader('Authorization', `Bearer  ${token}`);
        }
        event.formData.append('curso', new Blob([JSON.stringify(this.curso)], { type: 'application/json' }));
    }

change the Resource Controller accordingly

CursoResource.java

    @PostMapping("/cursos/archivos")
    @Timed
    public ResponseEntity createCurso(@Valid @RequestPart(value = "curso") Curso curso, 
                                             @RequestPart("archivos[]")MultipartFile[] archivos) {
...
Curso result = this.cursoService.save(curso);
...
}

primeng file upload angular i18n internationalization

primeNG doesn’t support i18n out of the box. But its components, like file upload, provide properties or templates to fit your needs.

In the case of file upload it has Label properties which we can use to translate

chooseLabel Label of the choose button.
uploadLabel Label of the upload button.
cancelLabel Label of the cancel button.

We have an app generated by jhipster which support internationalization through language files and directives. First we have to add the label translations in their respective files, in example

i18n/es/curso.json

{
    "cursosApp": {
        "curso" : {
            ...
            "upload": {
                "chooseLabel": "Seleccionar archivo"
            }
        }
    }
}

i18n/en/curso.json

{
    "cursosApp": {
        "curso" : {
            ...
            "upload": {
                "chooseLabel": "Choose file"
            }
        }
    }
}

and in your html

curso-update-component.html

<p-fileUpload id=...
[chooseLabel]="'cursosApp.curso.upload.chooseLabel' | translate">
</p-fileUpload>

notice the single quotes around labels

That’s it, now you can enable i18n in your primeNG components.

If you want to translate messages from your components, you could use the TranslateService as explained in this answer from stackoverflow.

sources:
https://forum.primefaces.org/viewtopic.php?t=51332
View story at Medium.com
https://github.com/jhipster/ng-jhipster/blob/master/src/language/jhi-translate.directive.ts
https://stackoverflow.com/questions/49513167/primeng-jhipster-insert-jhitranslation-in-growl-messages

Netbeans – Enable symbolic links in Payara (GlassFish) for your application

Screenshot from 2017-11-09 13-08-37

To enable symbolic links in Payara you just have to go to the admin console (http://localhost:4848 or inside netbeans open it in the Services window | Servers | Payara right click it and select ‘open admin console’) and click Configuration | server-config | Virtual Servers | server, then under Additional Properties, click Add Property and enter allowLinking as name and true as value as shown in the image above.

Symbolic links do not work with war deployments, they only work with exploded wars (if you add a symbolic link inside a war, the files from the target dir will be copied inside the war -compressed file-)
The good news is that Netbeans runs your app as an exploded war (folder). The folder path is usually {project}/build/web, to find out the folder which it uses for your app just open the Payara admin console and go to the Applications section and click your app, then look for the attribute ‘Location’
Go to that location and create whatever symbolic link your app needs, for example

cd {project}/build/web
ln -s /usr/share/docs docs

then you can access all the resources inside that folder like so

http://localhost:8080/project/docs

In production, you’ll have to deploy your app as an exploded war and have the same symbolic link created in the server

sources:
http://www.stpe.se/2008/07/enable-symbolic-links-in-glassfish/

Linux – Copiar un archivo demasiado grande a una memoria usb (fat32)

El tamaño máximo de un archivo que puedes almacenar en una USB (fat32) es de 2Gb (o 4Gb si estas utilizando LFS).

Si quieres copiar un archivo con un tamaño mayor, en linux, puedes hacerlo mediante el comando split, por ejemplo

split -b 2048 image.iso

que nos producirá una serie de archivos

image.isoaa
image.isoab
image.isoac
...

Después para juntarlos utilizamos el comando cat

cat image.iso* > ~/image.iso

El comando anterior no nos muestra un avance del progreso. Para ver el progreso podemos utilizar el comando pv, para instalarlo ejecuta sudo apt-get install pv y lo utilizas de la siguiente manera

cat image.iso* | pv -s $(du -cb image.iso* | grep total | awk '{print $1}') > ~/image.iso

Fuentes:
http://unix.stackexchange.com/questions/24630/whats-the-best-way-to-join-files-again-after-splitting-them
http://www.catonmat.net/blog/unix-utilities-pipe-viewer/
http://en.wikipedia.org/wiki/File_Allocation_Table#FAT32
http://unix.stackexchange.com/questions/41550/find-the-total-size-of-certain-files-within-a-directory-branch

Java – Convertir una página específica de un TIFF multipágina a JPG

Para la digitalización de documentos utilizamos escáneres que producen archivos TIFF, estos permiten almacenar varias imágenes en el mismo archivo. Estas imágenes se guardan en una base de datos para su posterior consulta.

Para poder desplegar estas imágenes en una aplicación web, necesitamos convertirlas a un formato estándar para la web (JPG, PNG ó GIF). Esto se puede lograr utilizando la librería image magick. Para usarla en Java existen 2 opciones: JMagick e im4java. Decidí utilizar im4java debido a los riesgos de usar JNI en una aplicación web (http://im4java.sourceforge.net/docs/faq.html)

Lo primero es agregar la dependencia de im4java a nuestro proyecto, archivo pom.xml si usas maven o agregar el jar a tu build path.

   <dependency>
      <groupId>org.im4java</groupId>
      <artifactId>im4java</artifactId>
      <version>1.4.0</version>
   </dependency>

Utilizamos un arreglo de bytes para cargar la imágen desde la base de datos y lo pasamos como un stream a la librería im4java para seleccionar la página que deseamos convertir al formato jpg.

    public byte[] getPagina(Integer pagina) throws Exception{
        byte[] imgPagina = null;
        byte[] imagen = loadImageFromDatabase();
        try{
        IMOperation op = new IMOperation();
        op.addImage(String.format("-[%d]", pagina - 1)); // read from stdin and specify page number with 1-based index
        op.addImage("jpg:-");               // write to stdout in jpg-format
        ByteArrayInputStream fis = new ByteArrayInputStream(imagen);
        ByteArrayOutputStream fos = new ByteArrayOutputStream();
        // Pipe pipe = new Pipe(fis,fos);
        Pipe pipeIn  = new Pipe(fis,null);
        Pipe pipeOut = new Pipe(null,fos);
        // set up command
        ConvertCmd convert = new ConvertCmd();
        convert.setInputProvider(pipeIn);
        convert.setOutputConsumer(pipeOut);
        convert.run(op);
        imgPagina = fos.toByteArray();
        }catch(Exception e){
            throw new Exception(
                    "Error al obtener la imágen de la página " + pagina, e);
        }
        return imgPagina;
    }

este método lo podemos utilizar en un servicio web/servlet para servir la imágen a través de una url

    @GET
    @Path("documento/{id}/imagen/pagina/{numero}")
    @Produces("image/jpeg")
    public Response getPaginaPromocion(@PathParam("id")Integer id, @PathParam("numero")Integer numero){
        Response response = null;
        try{
            Documento doc = Documento.get(id);
            response = Response.ok(doc.getPagina(numero), "image/jpeg").build();
        }catch(Exception e){
            String error = String.format("Error al obtener la página %d del documento %s", numero, id);
            log.error(error, e);
            response = Response.serverError().entity(String.format("{\"response\": {\"status\": -1, \"data\": \"%s\"}}", error)).build();
        }
        return response;
    }

Fuentes:

http://www.imagemagick.org/discourse-server/viewtopic.php?f=1&t=16615#p61020
http://stackoverflow.com/questions/4809314/imagemagick-is-converting-only-the-first-page-of-the-pdf
http://im4java.sourceforge.net/docs/dev-guide.html#piping
http://stackoverflow.com/questions/2091454/byte-to-inputstream-or-outputstream
http://im4java.sourceforge.net/api/

Linux – Comprimir un archivo PDF

Para el registro de errores y tareas utilizamos bugzilla. Cuando me asignan una tarea mediante un oficio, adjunto el documento digitalizado. La mayoría de veces no tengo problemas, pero el día de ayer me arrojó un error debido al tamaño del archivo PDF.

Buscando una forma de reducir el tamaño del PDF (digitalizado mediante CamScanner con una iPad) me encontré este post. La forma que me pareció más sencilla y efectiva fue mediante ghostscript.

Primero asegurate de tener instalado ghostscript

sudo apt-get install ghostscript

Simplemente crea un archivo llamado shrinkpdf.sh en tu carpeta ~/bin o cualquiera que se encuentre en el path con el siguiente contenido.

    #!/bin/sh
    dpi=72
    if [ $# -lt 2 ]
    then
       echo "usage: $0 input output [dpi]"
       exit 1
    fi
    if [ $# -gt 2 ]
    then
       dpi=$3
    fi
    echo "Shrinking document..."
    gs  -q -dNOPAUSE -dBATCH -dSAFER \
        -sDEVICE=pdfwrite \
        -dCompatibilityLevel=1.3 \
        -dPDFSETTINGS=/screen \
        -dEmbedAllFonts=true \
        -dSubsetFonts=true \
        -dColorImageDownsampleType=/Bicubic \
        -dColorImageResolution=$dpi \
        -dGrayImageDownsampleType=/Bicubic \
        -dGrayImageResolution=$dpi \
        -dMonoImageDownsampleType=/Bicubic \
        -dMonoImageResolution=$dpi \
        -sOutputFile=$2 \
         $1

hazlo ejecutable con el comando

chmod a+x ~/bin/shrinkpdf.sh

finalmente utilizalo para comprimir cualquier archivo PDF de la siguiente forma

shrinkpdf.sh ~/input.pdf ~/output.pdf 150

El último parámetro le indica la resolución que va a utilizar. 150 dpi en el ejemplo, el cual produce una buena calidad de las imágenes. Puedes omitir este parámetro y por default utilizará una resolución de 72 dpi que produce archivos mucho más pequeños pero pierde calidad.