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 – change ngb-datepicker date format

Screenshot from 2018-01-11 16-16-39

When you generate an entity with jhipster entity and add a LocalDate the UI generated uses ng-bootstrap Datepicker. To change the format you need to add an implementation of the class NgbDateParserFormatter. To change the language you need to provide an implementation of the class NgbDatepickerI18n (as shown in the examples).

First create the class files, i.e.

/webapp/src/main/webapp/app/blocks/config/ng-bootstrap.date-parser-formatter.ts

import { Injectable } from '@angular/core';
import { NgbDateParserFormatter, NgbDateStruct } from '@ng-bootstrap/ng-bootstrap';

function padNumber(value: number) {
    if (isNumber(value)) {
        return `0${value}`.slice(-2);
    } else {
        return "";
    }
}

function isNumber(value: any): boolean {
    return !isNaN(toInteger(value));
}

function toInteger(value: any): number {
    return parseInt(`${value}`, 10);
}


@Injectable()
export class NgbDateParserFormatterEsMX extends NgbDateParserFormatter {
    parse(value: string): NgbDateStruct {
        if (value) {
            const dateParts = value.trim().split('/');
            if (dateParts.length === 1 && isNumber(dateParts[0])) {
                return {year: toInteger(dateParts[0]), month: null, day: null};
            } else if (dateParts.length === 2 && isNumber(dateParts[0]) && isNumber(dateParts[1])) {
                return {year: toInteger(dateParts[1]), month: toInteger(dateParts[0]), day: null};
            } else if (dateParts.length === 3 && isNumber(dateParts[0]) && isNumber(dateParts[1]) && isNumber(dateParts[2])) {
                return {year: toInteger(dateParts[2]), month: toInteger(dateParts[1]), day: toInteger(dateParts[0])};
            }
        }
        return null;
    }

    format(date: NgbDateStruct): string {
        let stringDate: string = "";
        if(date) {
            stringDate += isNumber(date.day) ? padNumber(date.day) + "/" : "";
            stringDate += isNumber(date.month) ? padNumber(date.month) + "/" : "";
            stringDate += date.year;
        }
        return stringDate;
    }
}

<project>/direccion-juridica/webapp/src/main/webapp/app/blocks/config/ng-bootstrap-datepicker-i18n.ts

import {Component, Injectable} from '@angular/core';
import {NgbDatepickerI18n} from '@ng-bootstrap/ng-bootstrap';

const I18N_VALUES = {
    'es-MX': {
        weekdays: ['Lun', 'Mar', 'Mie', 'Jue', 'Vie', 'Sab', 'Dom'],
        months: ['Ene', 'Feb', 'Mar', 'Abr', 'May', 'Jun', 'Jul', 'Ago', 'Sep', 'Oct', 'Nov', 'Dic'],
    }
    // other languages you would support
};

// Define a service holding the language. You probably already have one if your app is i18ned. Or you could also
// use the Angular LOCALE_ID value
@Injectable()
export class I18n {
    language = 'es-MX';
}

// Define custom service providing the months and weekdays translations
@Injectable()
export class CustomDatepickerI18n extends NgbDatepickerI18n {

    constructor(private _i18n:I18n) {
        super();
    }

    getWeekdayShortName(weekday:number):string {
        return I18N_VALUES[this._i18n.language].weekdays[weekday - 1];
    }

    getMonthShortName(month:number):string {
        return I18N_VALUES[this._i18n.language].months[month - 1];
    }

    getMonthFullName(month:number):string {
        return this.getMonthShortName(month);
    }
}

finally define the provider in <project>/direccion-juridica/webapp/src/main/webapp/app/app.module.ts

import ...

...
    providers: [
        ProfileService,
        customHttpProvider(),
        PaginationConfig,
        UserRouteAccessService,
        I18n,
        {provide: NgbDatepickerI18n, useClass: CustomDatepickerI18n},
        {provide: NgbDateParserFormatter, useClass: NgbDateParserFormatterEsMX}
    ],
    bootstrap: [ JhiMainComponent ]
})
...

to change the displayed format of dates in tables you can replace in files date:'mediumDate' with date:'dd/MM/yyyy' and date:'medium' with date:'dd/MM/yyyy HH:mm' for example.

for ZonedDateTime it uses a datetime-local input which tries to use browser configuration -if supported-
Due to, and I’m quoting this from Mozilla

Date/time inputs sound convenient at first glance; they provide an easy UI 
for choosing dates and times, and they normalize the data format sent to the 
server, regardless of the user's locale. However, there are issues with 
 because of the **limited browser support.**

So I would recommend breaking DateTimes into a Datepicker and a Timepicker from ng-bootstrap and configuring as above.

sources:
https://stackoverflow.com/questions/40664523/angular2-ngbdatepicker-how-to-format-date-in-inputfield

https://github.com/ng-bootstrap/ng-bootstrap/issues/754#issuecomment-247767027

AngularJS – Ejecutar una función de javascript en una plantilla (template)

Screenshot from 2014-04-16 12:11:30

Para dar formato a una fecha, y muchas cosas más, existe una librería llamada Moment.js. Para utilizar sus métodos desde una plantilla encontré este post que nos explica cómo crear un filtro para lograrlo.

El filtro quedaría de la siguiente manera

angular.module('myModule').
  filter('fromNow', function() {
    return function(dateString) {
      return moment(dateString).fromNow()
    }
  })

y el template

{{ reply.createdDate | fromNow }}

Funciona muy bien, pero tendríamos que crear un filtro para cada función de la librería que quisieramos utilizar. La ventaja es que la implementación del filtro queda independiente de la vista (template).

Otra opción sería poner el objeto moment disponible en la vista de la siguiente manera

angular.module('myModule', []).
controller('myController', ['$scope', function($scope){
 $scope.moment = moment
}])

y en la plantilla

{{ moment(reply.createdDate).fromNow() }}

Fuentes:

http://www.34m0.com/2012/07/angularjs-user-friendly-date-display.html
http://stackoverflow.com/questions/12466661/using-helper-methods-while-templating-with-angular-js
http://momentjs.com/

BIRT – Formato de fecha desde un script


Tengo un encabezado estándar que uso en la mayoría de reportes, el cual he compartido a través de una librería. El detalle es que el título para cada reporte es diferente, así que en la plantilla coloqué etiquetas cuyo valor depende del parámetro que se les pase. Es común también que el título contenga el período que comprende el reporte. El formato de fecha que comunmente utilizamos es “dd/MM/yyyy” p. ej. 14/06/2011. Para dar este formato a las fechas -que recibo como párametros- utilizo el siguiente código en el evento “initialize” del reporte:

importPackage(Packages.java.text);
sdf = new SimpleDateFormat(“dd/MM/yyyy”);
params[“Titulo”]=”Reporte del “+sdf.format(params[“FechaInicio”])+” al “+sdf.format(params[“FechaFin”]);

Fuentes:
Eclipse Forums

GWT – Darle formato a una fecha


En Java, cuando quiero darle formato a una fecha o “parsear” una cadena para convertirla en un objeto Date, utilizo la clase SimpleDateFormat de la siguiente forma

SimpleDateFormat fmtDate=new SimpleDateFormat(“dd/MM/yyyy”);
String fecha=fmtDate.format(new Date());

Esto lo podemos hacer sin problemas en el servidor 😉 pero, que pasa cuando lo intentamos en el Cliente. Bueno, pues tenemos que utilizar la clase DateTimeFormat de la siguiente manera

DateTimeFormat fmtDate=DateTimeFormat.getFormat(“dd/MM/yyyy”);
String fecha=fmtDate.format(new Date());