Errore CURL su invocazione php_cli

CURL è una libreria clienti per effettuare connessioni

Errore CURL da linea di comando

Qualsiasi comando che invoca il client PHP, ad esempio:

$ php -v

mi ritorna questo errore:

PHP Warning: PHP Startup: Unable to load dynamic library 'curl.so' (tried: /usr/lib/php/20190902/curl.so (/usr/lib/php/20190902/curl.so: undefined symbol: curl_mime_addpart, version CURL_OPENSSL_4), /usr/lib/php/20190902/curl.so.so (/usr/lib/php/20190902/curl.so.so: undefined symbol: curl_mime_addpart, version CURL_OPENSSL_4)) in Unknown on line 0

Per fixarlo ho eliminato questo link smbolico:

$ cd /usr/local/lib
$ ll
totale 14360
drwxr-xr-x  9 root root     4096 set 29 00:04 ./
drwxr-xr-x 19 root root     4096 set 24 15:33 ../
-rw-r--r--  1 root root  1006444 mag 23  2017 libcurl.a
-rwxr-xr-x  1 root root     1042 mag 23  2017 libcurl.la*
lrwxrwxrwx  1 root root       16 lug  4 16:26 libcurl.so.4 -> libcurl.so.4.4.0*
-rwxr-xr-x  1 root root   542272 mag 23  2017 libcurl.so.4.4.0*
-rw-r--r--  1 root root  4129514 gen 30  2018 libfilezilla.a
-rwxr-xr-x  1 root root      976 gen 30  2018 libfilezilla.la*
lrwxrwxrwx  1 root root       21 gen 30  2018 libfilezilla.so -> libfilezilla.so.0.0.0*
lrwxrwxrwx  1 root root       21 gen 30  2018 libfilezilla.so.0 -> libfilezilla.so.0.0.0*
-rwxr-xr-x  1 root root  1823960 gen 30  2018 libfilezilla.so.0.0.0*
-rwxr--r--  1 root root  3090092 mag 14  2016 libOpenBUGS.so*
-rwxr-xr-x  1 root root     1059 ott 27  2020 libopenconnect.la*
lrwxrwxrwx  1 root root       23 ott 27  2020 libopenconnect.so -> libopenconnect.so.5.6.0*
lrwxrwxrwx  1 root root       23 ott 27  2020 libopenconnect.so.5 -> libopenconnect.so.5.6.0*
-rwxr-xr-x  1 root root  1458464 ott 27  2020 libopenconnect.so.5.6.0*
-rw-r--r--  1 root root  1681976 mag 22  2017 libssh2.a
-rwxr-xr-x  1 root root      950 mag 22  2017 libssh2.la*
lrwxrwxrwx  1 root root       16 mag 22  2017 libssh2.so -> libssh2.so.1.0.1*
lrwxrwxrwx  1 root root       16 mag 22  2017 libssh2.so.1 -> libssh2.so.1.0.1*
-rwxr-xr-x  1 root root   893968 mag 22  2017 libssh2.so.1.0.1*
drwxr-xr-x  5 root root     4096 gen 10  2018 php/
-rwxr-xr-x  1 root root     1278 mar 28  2018 phpunit*
drwxr-xr-x  2 root root     4096 ott 27  2020 pkgconfig/
drwxrwsr-x  4 root staff    4096 set 28 23:55 python2.7/
drwxrwsr-x  3 root staff    4096 ott 21  2015 python3.4/
drwxrwsr-x  3 root staff    4096 ott 21  2015 python3.5/
drwxrwsr-x  3 root staff    4096 ago  6  2019 python3.6/
drwxr-xr-x  3 root root     4096 set 28 22:12 python3.9/

$ sudo unlink libcurl.so.4 

Il problema scompare

$ php -v
PHP 7.4.16 (cli) (built: Oct 26 2021 16:46:20) ( NTS )
Copyright (c) The PHP Group
Zend Engine v3.4.0, Copyright (c) Zend Technologies
with Zend OPcache v7.4.16, Copyright (c), by Zend Technologies

Attenzione: tra qualche tempo, si ripresenterà il problema e so che dovrò reintrodurre il link simbolico.

Incredibile!

Ma non ho tempo di studiare il perché, so che facendo flip-flop temporaneamente si risolve il problema.

Mi spiace ma è così.

Risorse web su CURL

Considerazioni su database NoSQL

Una veloce incursione nella sfera dei databse NoSQL.

NoSQL
NoSQL

NoSQL è un movimento che promuove sistemi software dove la persistenza dei dati è in generale caratterizzata dal fatto di non utilizzare il modello relazionale, di solito usato dalle basi di dati tradizionali (RDBMS).

L’espressione fa infatti riferimento al linguaggio SQL, che è il più comune linguaggio di interrogazione dei dati nei database relazionali, qui preso a simbolo dell’intero paradigma relazionale.

Questi archivi di dati il più delle volte non richiedono uno schema fisso (schemeless), evitano spesso le operazioni di giunzione (join) e puntano a scalare in modo orizzontale. Gli accademici e gli articoli si riferiscono a queste basi di dati come memorizzazione strutturata (structured storage).

I creatori di MongoDB, il database non relazionale più popolare, individuano queste cause nell’affermarsi di database NoSQL:

  • Le richieste di maggiore produttività e tempi di commercializzazione più rapidi sono frenate da rigidi modelli di dati relazionali che non corrispondono al codice moderno e impongono complesse interdipendenze tra i team di progettazione.
  • Le organizzazioni non sono in grado di lavorare ed estrarre informazioni dai massicci aumenti dei nuovi dati strutturati, semistrutturati e polimorfici in rapida evoluzione generati dalle applicazioni odierne.
  • I vecchi (legacy) database monolitici e fragili inibiscono il passaggio estensivo a sistemi distribuiti e cloud computing che forniscono la resilienza e la scalabilità di cui le aziende hanno bisogno, rendendo più difficile soddisfare i nuovi requisiti normativi per la privacy dei dati.
  • I carichi di lavoro transazionali, analitici, di ricerca e mobili precedentemente separati stanno convergendo per creare applicazioni ricche e basate sui dati e sulle esperienze degli utilizzatori. Tuttavia, ogni carico di lavoro è stato tradizionalmente alimentato dal proprio database, creando silos di dati duplicati connessi con fragili pipeline ETL a cui si accede da diverse API di sviluppatori.

Processi ETL di esempio possono essere:

  • Migrazione dei dati da un’applicazione a un’altra
  • Replica dei dati per l’esecuzione di backup o analisi della ridondanza
  • Processi operativi quali il trasferimento di dati da un sistema CRM in un ODS (Operational Data Store) per l’ottimizzazione e l’arricchimento, per poi restituire i dati al sistema CRM
  • Inserimento dei dati in un Data Warehouse per l’assimilazione, l’ordinamento e la trasformazione in business intelligence
  • Migrazione delle applicazioni locali in infrastrutture Cloud, Cloud ibride o multi-Cloud
  • Sincronizzazione di sistemi chiave

Vengono formulate anche considerazioni critiche basate su alcune dimensioni che definiscono questi sistemi:

  • modello di dati;
  • modello di interrogazione;
  • consistenza e modello transazionale;
  • API;
  • dati mobili;
  • piattaforma dati e supporto commerciale,
  • forza della comunità e
  • libertà dal lock-in (abilità di non chiudersi dentro ad una soluzione che non cresce).

Modello di dati per database NoSQL

Il modo principale in cui i database non tabulari differiscono dai database relazionali è il modello di dati. Sebbene ci siano dozzine di database non tabulari, essi generalmente rientrano in tre categorie:

  • database di documenti,
  • database di grafi e
  • database chiave-valore o archivi a colonne larghe.

Modello documentale

Mentre i database relazionali archiviano i dati in righe e colonne, i database di documenti archiviano i dati in documenti utilizzando JavaScript Object Notation (JSON), un formato di scambio dati basato su testo popolare tra gli sviluppatori. I documenti forniscono un modo intuitivo e naturale per modellare i dati che è strettamente allineato con la programmazione orientata agli oggetti: ogni documento è effettivamente un oggetto che corrisponde agli oggetti con cui gli sviluppatori lavorano nel codice.

I documenti contengono uno o più campi e ogni campo contiene un valore digitato come una stringa, una data, un valore binario, decimale o una matrice. Anziché distribuire un record su più colonne e tabelle collegate a chiavi esterne, ogni record viene archiviato insieme ai dati associati (ovvero correlati) in un unico documento gerarchico.

Questo modello accelera la produttività degli sviluppatori, semplifica l’accesso ai dati e, in molti casi, elimina la necessità di costose operazioni di join e livelli di astrazione complessi come il mapping relazionale degli oggetti (ORM).

Lo schema di un database relazionale è definito da tabelle; in un database di documenti, la nozione di schema è dinamica: ogni documento può contenere campi diversi. Questa flessibilità può essere particolarmente utile per la modellazione dei dati in cui le strutture possono cambiare da un record all’altro, ad esempio dati polimorfici.

Inoltre, diventa più semplice l’evoluzione di un’applicazione durante il suo ciclo di vita, ad esempio se si devono aggiungere nuovi campi. Inoltre, alcuni database di documenti forniscono l’espressività delle query che gli sviluppatori si aspettano dai database relazionali.

In particolare, i dati possono essere interrogati in base a qualsiasi combinazione di campi in un documento, con ricchi indici secondari che forniscono percorsi di accesso efficienti per supportare quasi tutti i modelli di query. Alcuni database di documenti offrono anche la possibilità di applicare uno schema ai documenti.

Applicazioni

I database di documenti sono utili per un’ampia varietà di applicazioni grazie alla flessibilità del modello di dati, alla capacità di eseguire query su qualsiasi campo e alla mappatura naturale del modello di dati del documento agli oggetti nei moderni linguaggi di programmazione.

Esempi

MongoDB, Azure CosmosDB, Apache CouchDB

Nella sostanza, per visualizzare un database i tipo documentale, l’unico modo è stampare i file JSON contente gli archivi. Questo esempio è tratto da miaplatfrom.eu:

Modello documentale, documento 1 "AUTORI"
Modello documentale, documento 1 “AUTORI”
Modello documentale, documento 2 "LIBRI"
Modello documentale, documento 2 “LIBRI”

Modello a grafi

I database di grafi utilizzano strutture di grafi con nodi, bordi e proprietà per rappresentare i dati. In sostanza, i dati sono modellati come una rete di relazioni tra elementi specifici. Sebbene il modello a grafi possa essere controintuitivo, può essere utile per una classe specifica di query. Il suo principale vantaggio è che semplifica la modellazione e la navigazione delle relazioni tra le entità in un’applicazione.

Applicazioni

I database di grafi sono utili nei casi in cui l’attraversamento delle relazioni è fondamentale per l’applicazione, come la navigazione nelle connessioni di social network, nelle topologie di rete o nelle catene di approvvigionamento.

Esempi

Neo4j, Amazon Neptune

In sostanza i nodi sono tabelle normali dichiarate come node (AS NODE), le relazioni sono anch’esse tabelle (come le tabelle di join) dichiarate come edge (AS EDGE). Si chiamano anche tabelle di bordo, o tabelle perimetrali.

Il seguente esempio è tratto da Microsoft:

Database a grafo
Database NoSQL a grafo

Database chiave-valore e modelli a colonna larga

Dal punto di vista del modello dati, i database chiave-valore sono il tipo più elementare di database non tabulare. Ogni elemento nel database viene memorizzato come nome di attributo o chiave, insieme al suo valore. Il valore, tuttavia, è completamente opaco per il sistema: i dati possono essere interrogati solo dalla chiave. Questo modello può essere utile per rappresentare dati polimorfici e non strutturati, perché il database non impone uno schema impostato tra coppie chiave-valore.

Gli archivi a colonne larghe, o archivi di famiglie di colonne, utilizzano una mappa ordinata sparsa, distribuita e multidimensionale per archiviare i dati. Ogni record può variare nel numero di colonne memorizzate. Le colonne possono essere raggruppate in famiglie di colonne o distribuite su più famiglie. I dati vengono recuperati dalla chiave primaria per famiglia di colonne.

Applicazioni

I database a chiave-valore e gli archivi a colonne larghe sono utili per un set specializzato di applicazioni che eseguono query sui dati utilizzando un singolo valore di chiave. L’attrattiva di questi sistemi è la loro prestazione e scalabilità, che possono essere altamente ottimizzate grazie alla semplicità dei modelli di accesso ai dati e all’opacità dei dati stessi.

Esempi

Redis, Amazon DynamoDB (key-value); Apache HBase, Apache Cassandra (wide-column)

Per esempio il seguente database chiave-valore è tratto AmazonWS:

Key-Value database AmazonWS
Key-Value database NoSQL AmazonWS

Punti chiave

  • I modelli di dati chiave-valore e a colonna ampia sono opachi nel sistema: è possibile eseguire query solo sulla chiave primaria (ad esempio non si può cercare nel testo).
  • Il modello dati documentale ha la più ampia applicabilità.
  • Il modello a colonne larghe fornisce un accesso più granulare ai dati rispetto al modello chiave-valore, ma è meno flessibile rispetto al modello documento.
  • Il modello dati documentale è il più naturale e produttivo perché mappa direttamente gli oggetti nei linguaggi OO.

Bibliografia

Installazione GDAL

gdal logo
gdal logo

GDAL (Geospatial Data Abstraction Library) è una libreria di traduzione per formati di dati geospaziali raster e vettoriali rilasciata con una licenza Open Source in stile X/MIT dalla Open Source Geospatial Foundation. Come libreria, presenta un singolo modello di dati astratti raster e un singolo modello di dati astratti vettoriali all’applicazione chiamante per tutti i formati supportati. Inoltre viene fornito con una varietà di utili utilità della riga di comando per la traduzione e l’elaborazione dei dati.

PPA

A differenza di quanto riportato in diversi “how to”, in un forum ho trovato un suggerimento che mi segnalava che per la Hirsute Hippo di Ubuntu (21.04) non era necessario aggiugere la PPA di unbuntugis. L’avevo fatto in precedenza e incorrevo nell’errore

E: Il repository "http://ppa.launchpad.net/ubuntugis/ppa/ubuntu hirsute Release" non ha un file Release.

La spiegazione è che dalla versione 20 di Ubuntu, il repostiory di qgis è già incluso nel core. Quindi ho applicato i soli comandi di installazione:

$ sudo apt -y install gdal-bin python3-gdal
$ gdalinfo --version
GDAL 3.2.2, released 2021/03/05

Risorse internet su GDAL

Ubuntu – errore architettura in update

Ubuntu 21.04 Hirsute Hippo
Ubuntu 21.04 Hirsute Hippo

Installando QGIS ho questo errore:

N: Acquisizione del file "main/binary-i386/Packages" saltata in quanto il repository "https://qgis.org/ubuntu hirsute InRelease" non supporta l'architettura "i386"

Soluzione

Modificare così il file /etc/apt/sources.list.d/archive_uri-https_qgis_org_ubuntu-hirsute.list

deb [arch=amd64] https://qgis.org/ubuntu hirsute main

(aggiungere la parte in rosso).

L’update termina ora senza errori.

Ovviamente non è un errore specifico di QGIS, ma capita sempre se cerchiamo di scaricare pacchetti non corrispondenti all’architettura del processore.

GIMP – disegnare un’ellisse

Molto basic, ma uso poco Gimp e me le lo dimentico sempre:

Con lo strumento di selezione, selezionare l’area che si vuole (ad esempio un’ellisse).

Poi si può fare in due modi:

  1. Trasformare il bordo della selezione in linea: Menu Modifica > Delinea Selezione (verrà usato il colore di foreground già impostato; si può modificare lo spessore della linea dall’interno del dialog)
  2. Trasformare una selezione in tracciato: Strumenti > Tracciati (a questo punto può fare tracciato complicato quanto si vuole). Poi cliccare su Menu Modifica > Delinea Tracciato.

Risorse esterne

Manuale GIMP

Perché il cielo è blu? Feynman spiega lo scattering di Rayleigh

Blue Sky
Blue Sky

Il colore del cielo sereno in pieno giorno sufficentemente lontano dal disco solare è azzurro.

Questo colore è dovuto al fenomeno dello scattering (diffusione) di Rayleigh, cioè della radiazione emessa dalle molecole che compongono l’aria in seguito ad eccitazione della luce solare.

Dallo studio risulta che vengono diffuse tutte le frequenze ma le onde elettromagnetiche di frequenza più elevata (verso il blu) vengono diffuse molto di più, dando la dominante di azzurro.

Il link alla lezione contenuta nelle Feynman Lectures on Physics.

Non so voi, ma questa cosa mi meraviglia ogni volta.

Protetto: PKCS#11

Questo contenuto è protetto da password. Per visualizzarlo inserisci la password qui sotto.

Firma digitale di file PDF con iText

Questo esercizio è tratto da

Capitolo 1 – Utilizzo delle funzioni di crittografia

La prima classe da scrivere calcola firme di file.

In inglese la firma elettronica si chiama anche digest, come Reader’s Digest, la rivista americana di estrema destra che significa “Il digesto del Lettore” o, meglio, “Il riassunto del Lettore”. DeWitt Wallace, il fondatore, voleva fare una rivista che pubblicasse riassunti di articoli di altre riviste.

Digest quindi come riassunto, sintesi: è una funzione di hash crittografico che mappa messaggi lunghi in messaggi corti a lunghezza fissa facendo in modo da evitare collisioni (la probabilità che due file diversi generino lo stesso digest è molto piccola) e in modo tale da rendere impossibile (sarebbe più onesto dire “molto difficile”) risalire al contenuto di un file dalla sua firma.

I package che vedremo supportano diversi tipi di digest.

Calcolo della firma di una stringa

Realizziamo questa cosa con la classe java C1_01_DigestDefault:

package com.itextpdf.samples.signatures.chapter01;

import java.io.File;
import java.io.UnsupportedEncodingException;
import java.math.BigInteger;
import java.security.GeneralSecurityException;
import java.security.MessageDigest;
import java.util.Arrays;

public class C1_01_DigestDefault {
    public static final String DEST = "./target/test/resources/signatures/chapter01/";

    public static final String EXPECTED_OUTPUT = "Digest using MD5: 16\n" +
            "Digest: 5f4dcc3b5aa765d61d8327deb882cf99\n" +
            "Is the password 'password'? true\n" +
            "Is the password 'secret'? false\n" +
            "Digest using SHA-1: 20\n" +
            "Digest: 5baa61e4c9b93f3f0682250b6cf8331b7ee68fd8\n" +
            "Is the password 'password'? true\n" +
            "Is the password 'secret'? false\n" +
            "Digest using SHA-224: 28\n" +
            "Digest: d63dc919e201d7bc4c825630d2cf25fdc93d4b2f0d46706d29038d01\n" +
            "Is the password 'password'? true\n" +
            "Is the password 'secret'? false\n" +
            "Digest using SHA-256: 32\n" +
            "Digest: 5e884898da28047151d0e56f8dc6292773603d0d6aabbdd62a11ef721d1542d8\n" +
            "Is the password 'password'? true\n" +
            "Is the password 'secret'? false\n" +
            "Digest using SHA-384: 48\n" +
            "Digest: a8b64babd0aca91a59bdbb7761b421d4f2bb38280d3a75ba0f21f2bebc4558" +
            "3d446c598660c94ce680c47d19c30783a7\n" +
            "Is the password 'password'? true\n" +
            "Is the password 'secret'? false\n" +
            "Digest using SHA-512: 64\n" +
            "Digest: b109f3bbbc244eb82441917ed06d618b9008dd09b3befd1b5e07394c706a8bb980b" +
            "1d7785e5976ec049b46df5f1326af5a2ea6d103fd07c95385ffab0cacbc86\n" +
            "Is the password 'password'? true\n" +
            "Is the password 'secret'? false\n" +
            "RIPEMD128 MessageDigest not available\n" +
            "RIPEMD160 MessageDigest not available\n" +
            "RIPEMD256 MessageDigest not available\n";

    protected byte[] digest;
    protected MessageDigest messageDigest;

    protected C1_01_DigestDefault(String password, String algorithm, String provider) throws GeneralSecurityException,
            UnsupportedEncodingException {
        if (provider == null) {
            messageDigest = MessageDigest.getInstance(algorithm);
        } else {
            messageDigest = MessageDigest.getInstance(algorithm, provider);
        }
        digest = messageDigest.digest(password.getBytes("UTF-8"));
    }

    public static C1_01_DigestDefault getInstance(String password, String algorithm) throws GeneralSecurityException,
            UnsupportedEncodingException {
        return new C1_01_DigestDefault(password, algorithm, null);
    }

    public static void main(String[] args) {
        File file = new File(DEST);
        file.mkdirs();

        testAll();
    }

    public static void testAll() {
        showTest("MD5");
        showTest("SHA-1");
        showTest("SHA-224");
        showTest("SHA-256");
        showTest("SHA-384");
        showTest("SHA-512");
        showTest("RIPEMD128");
        showTest("RIPEMD160");
        showTest("RIPEMD256");
    }

    public static void showTest(String algorithm) {
        try {
            C1_01_DigestDefault app = getInstance("password", algorithm);
            System.out.println("Digest using " + algorithm + ": " + app.getDigestSize());
            System.out.println("Digest: " + app.getDigestAsHexString());
            System.out.println("Is the password 'password'? " + app.checkPassword("password"));
            System.out.println("Is the password 'secret'? " + app.checkPassword("secret"));
        } catch (Exception exc) {
            System.out.println(exc.getMessage());
        }
    }

    public int getDigestSize() {
        return digest.length;
    }

    public String getDigestAsHexString() {
        return new BigInteger(1, digest).toString(16);
    }

    /* This method checks if the digest of the password is equal
     * to the digest of the text line which is passed as argument
     */
    public boolean checkPassword(String password) {
        return Arrays.equals(digest, messageDigest.digest(password.getBytes()));
    }
}

L’output del meodo main() di questa classe è il seguente

 Digest using MD5: 16
 Digest: 5f4dcc3b5aa765d61d8327deb882cf99
 Is the password 'password'? true
 Is the password 'secret'? false
 Digest using SHA-1: 20
 Digest: 5baa61e4c9b93f3f0682250b6cf8331b7ee68fd8
 Is the password 'password'? true
 Is the password 'secret'? false
 Digest using SHA-224: 28
 Digest: d63dc919e201d7bc4c825630d2cf25fdc93d4b2f0d46706d29038d01
 Is the password 'password'? true
 Is the password 'secret'? false
 Digest using SHA-256: 32
 Digest: 5e884898da28047151d0e56f8dc6292773603d0d6aabbdd62a11ef721d1542d8
 Is the password 'password'? true
 Is the password 'secret'? false
 Digest using SHA-384: 48
 Digest: a8b64babd0aca91a59bdbb7761b421d4f2bb38280d3a75ba0f21f2bebc45583d446c598660c94ce680c47d19c30783a7
 Is the password 'password'? true
 Is the password 'secret'? false
 Digest using SHA-512: 64
 Digest: b109f3bbbc244eb82441917ed06d618b9008dd09b3befd1b5e07394c706a8bb980b1d7785e5976ec049b46df5f1326af5a2ea6d103fd07c95385ffab0cacbc86
 Is the password 'password'? true
 Is the password 'secret'? false
 RIPEMD128 MessageDigest not available
 RIPEMD160 MessageDigest not available
 RIPEMD256 MessageDigest not available
 Process finished with exit code 0

Stesso calcolo ma utilizzando la libreria di crittografia Bouncy Castle

Per poter utilizzare questa libreria occorre cercare nel JAR finder questo package: bcprov-jdk15on-169.jar

Io, utilizzando come jvm quella disponbile nel JDK1.8 (/usr/lib/jvm/jdk1.8.0_111/), sistemo gli script delle librerie sotto

/usr/lib/jvm/jdk1.8.0_111/jre/lib/ext

una volta sistemato lì, l’IDE sarà in grado di trovarlo, una volta che si è specificata la directory nella configurazione (Ctrl+Alt+Maius+S – Tab “Dependencies”, aggiunta nuova dependency (plsante +) – oppure Alt+Ins).

La libreria Bouncy Castle (“A lightweight cryptography API for Java and C#”) fornisce delle API generali per la crittografia.

package com.itextpdf.samples.signatures.chapter01;

import java.io.File;
import java.io.UnsupportedEncodingException;
import java.math.BigInteger;
import java.security.GeneralSecurityException;
import java.security.MessageDigest;
import java.security.Provider;
import java.util.Arrays;

import org.bouncycastle.jce.provider.BouncyCastleProvider;

public class C1_02_DigestBC {
    public static final String DEST = "./target/test/resources/signatures/chapter01/";

    public static final BouncyCastleProvider PROVIDER = new BouncyCastleProvider();

    public static final String EXPECTED_OUTPUT = "Digest using MD5: 16\n" +
            "Digest: 5f4dcc3b5aa765d61d8327deb882cf99\n" +
            "Is the password 'password'? true\n" +
            "Is the password 'secret'? false\n" +
            "Digest using SHA-1: 20\n" +
            "Digest: 5baa61e4c9b93f3f0682250b6cf8331b7ee68fd8\n" +
            "Is the password 'password'? true\n" +
            "Is the password 'secret'? false\n" +
            "Digest using SHA-224: 28\n" +
            "Digest: d63dc919e201d7bc4c825630d2cf25fdc93d4b2f0d46706d29038d01\n" +
            "Is the password 'password'? true\n" +
            "Is the password 'secret'? false\n" +
            "Digest using SHA-256: 32\n" +
            "Digest: 5e884898da28047151d0e56f8dc6292773603d0d6aabbdd62a11ef721d1542d8\n" +
            "Is the password 'password'? true\n" +
            "Is the password 'secret'? false\n" +
            "Digest using SHA-384: 48\n" +
            "Digest: a8b64babd0aca91a59bdbb7761b421d4f2bb38280d3a75ba0f21f2bebc45583d446c" +
            "598660c94ce680c47d19c30783a7\n" +
            "Is the password 'password'? true\n" +
            "Is the password 'secret'? false\n" +
            "Digest using SHA-512: 64\n" +
            "Digest: b109f3bbbc244eb82441917ed06d618b9008dd09b3befd1b5e07394c706a8bb980b1d7" +
            "785e5976ec049b46df5f1326af5a2ea6d103fd07c95385ffab0cacbc86\n" +
            "Is the password 'password'? true\n" +
            "Is the password 'secret'? false\n" +
            "Digest using RIPEMD128: 16\n" +
            "Digest: c9c6d316d6dc4d952a789fd4b8858ed7\n" +
            "Is the password 'password'? true\n" +
            "Is the password 'secret'? false\n" +
            "Digest using RIPEMD160: 20\n" +
            "Digest: 2c08e8f5884750a7b99f6f2f342fc638db25ff31\n" +
            "Is the password 'password'? true\n" +
            "Is the password 'secret'? false\n" +
            "Digest using RIPEMD256: 32\n" +
            "Digest: f94cf96c79103c3ccad10d308c02a1db73b986e2c48962e96ecd305e0b80ef1b\n" +
            "Is the password 'password'? true\n" +
            "Is the password 'secret'? false\n";

    protected byte[] digest;
    protected MessageDigest messageDigest;

    protected C1_02_DigestBC(String password, String algorithm, Provider provider) throws GeneralSecurityException,
            UnsupportedEncodingException {
        if (provider == null) {
            messageDigest = MessageDigest.getInstance(algorithm);
        } else {

            // BouncyCastle provider can be initialized in another way
            // by using Security.addProvider(Provider provider) method
            messageDigest = MessageDigest.getInstance(algorithm, provider);
        }
        digest = messageDigest.digest(password.getBytes("UTF-8"));
    }

    public static C1_02_DigestBC getInstance(String password, String algorithm) throws GeneralSecurityException,
            UnsupportedEncodingException {
        return new C1_02_DigestBC(password, algorithm, PROVIDER);
    }

    public static void main(String[] args) throws Exception {
        File file = new File(DEST);
        file.mkdirs();

        testAll();
    }

    public static void testAll() throws Exception {
        showTest("MD5");
        showTest("SHA-1");
        showTest("SHA-224");
        showTest("SHA-256");
        showTest("SHA-384");
        showTest("SHA-512");
        showTest("RIPEMD128");
        showTest("RIPEMD160");
        showTest("RIPEMD256");

    }

    public static void showTest(String algorithm) throws Exception {
        C1_02_DigestBC app = getInstance("password", algorithm);
        System.out.println("Digest using " + algorithm + ": " + app.getDigestSize());
        System.out.println("Digest: " + app.getDigestAsHexString());
        System.out.println("Is the password 'password'? " + app.checkPassword("password"));
        System.out.println("Is the password 'secret'? " + app.checkPassword("secret"));
    }

    public int getDigestSize() {
        return digest.length;
    }

    public String getDigestAsHexString() {
        return new BigInteger(1, digest).toString(16);
    }

    /* This method checks if the digest of the password is equal
     * to the digest of the text line which is passed as argument
     */
    public boolean checkPassword(String password) {
        return Arrays.equals(digest, messageDigest.digest(password.getBytes()));
    }
}

A differenza del default, qui al costruttore passiamo anche l’oggetto BouncyCastleProvider istanza della classe omonima, e lo passiamo esplicitamente al metodo che crea l’oggetto Digest (MessageDigest).

Anche stavolta l’output è quello atteso

Digest using MD5: 16
 Digest: 5f4dcc3b5aa765d61d8327deb882cf99
 Is the password 'password'? true
 Is the password 'secret'? false
 Digest using SHA-1: 20
 Digest: 5baa61e4c9b93f3f0682250b6cf8331b7ee68fd8
 Is the password 'password'? true
 Is the password 'secret'? false
 Digest using SHA-224: 28
 Digest: d63dc919e201d7bc4c825630d2cf25fdc93d4b2f0d46706d29038d01
 Is the password 'password'? true
 Is the password 'secret'? false
 Digest using SHA-256: 32
 Digest: 5e884898da28047151d0e56f8dc6292773603d0d6aabbdd62a11ef721d1542d8
 Is the password 'password'? true
 Is the password 'secret'? false
 Digest using SHA-384: 48
 Digest: a8b64babd0aca91a59bdbb7761b421d4f2bb38280d3a75ba0f21f2bebc45583d446c598660c94ce680c47d19c30783a7
 Is the password 'password'? true
 Is the password 'secret'? false
 Digest using SHA-512: 64
 Digest: b109f3bbbc244eb82441917ed06d618b9008dd09b3befd1b5e07394c706a8bb980b1d7785e5976ec049b46df5f1326af5a2ea6d103fd07c95385ffab0cacbc86
 Is the password 'password'? true
 Is the password 'secret'? false
 Digest using RIPEMD128: 16
 Digest: c9c6d316d6dc4d952a789fd4b8858ed7
 Is the password 'password'? true
 Is the password 'secret'? false
 Digest using RIPEMD160: 20
 Digest: 2c08e8f5884750a7b99f6f2f342fc638db25ff31
 Is the password 'password'? true
 Is the password 'secret'? false
 Digest using RIPEMD256: 32
 Digest: f94cf96c79103c3ccad10d308c02a1db73b986e2c48962e96ecd305e0b80ef1b
 Is the password 'password'? true
 Is the password 'secret'? false
 Process finished with exit code 0

Classe di encryption/decription

package com.itextpdf.samples.signatures.chapter01;

import javax.crypto.Cipher;
import java.io.File;
import java.io.FileInputStream;
import java.io.IOException;
import java.math.BigInteger;
import java.security.GeneralSecurityException;
import java.security.Key;
import java.security.KeyStore;
import java.security.KeyStoreException;
import java.security.cert.X509Certificate;

public class C1_03_EncryptDecrypt {
    public static final String DEST = "./target/test/resources/signatures/chapter01/";

    protected static final String KEYSTORE = "./src/test/resources/encryption/ks";

    protected static final String PASSWORD = "password";
    protected KeyStore ks;

    public static void main(String[] args) throws Exception {
        File file = new File(DEST);
        file.mkdirs();

        encryptDecrypt();
    }

    public static void encryptDecrypt() throws GeneralSecurityException, IOException {
        C1_03_EncryptDecrypt app = new C1_03_EncryptDecrypt();
        app.initKeyStore(KEYSTORE, PASSWORD);
        Key publicKey = app.getPublicKey("demo");
        Key privateKey = app.getPrivateKey("demo", "password");

        // Encrypt the message with the public key and then decrypt it with the private key
        System.out.println("Let's encrypt 'secret message' with a public key");
        byte[] encrypted = app.encrypt(publicKey, "secret message");
        System.out.println("Encrypted message: " + app.getDigestAsHexString(encrypted));
        System.out.println("Let's decrypt it with the corresponding private key");
        String decrypted = app.decrypt(privateKey, encrypted);
        System.out.println(decrypted);

        // Encrypt the message with the private key and then decrypt it with the public key
        System.out.println("You can also encrypt the message with a private key");
        encrypted = app.encrypt(privateKey, "secret message");
        System.out.println("Encrypted message: " + app.getDigestAsHexString(encrypted));
        System.out.println("Now you need the public key to decrypt it");
        decrypted = app.decrypt(publicKey, encrypted);
        System.out.println(decrypted);
    }

    private void initKeyStore(String keystore, String ks_pass) throws GeneralSecurityException, IOException {
        ks = KeyStore.getInstance(KeyStore.getDefaultType());
        ks.load(new FileInputStream(keystore), ks_pass.toCharArray());
    }

    private String getDigestAsHexString(byte[] digest) {
        return new BigInteger(1, digest).toString(16);
    }

    private X509Certificate getCertificate(String alias) throws KeyStoreException {
        return (X509Certificate) ks.getCertificate(alias);
    }

    private Key getPublicKey(String alias) throws GeneralSecurityException, IOException {
        return getCertificate(alias).getPublicKey();
    }

    private Key getPrivateKey(String alias, String pk_pass) throws GeneralSecurityException, IOException {
        return ks.getKey(alias, pk_pass.toCharArray());
    }

    // This method encrypts the message (using RSA algorithm) with the key, got as the 1st argument
    public byte[] encrypt(Key key, String message) throws GeneralSecurityException {
        Cipher cipher = Cipher.getInstance("RSA");
        cipher.init(Cipher.ENCRYPT_MODE, key);
        byte[] cipherData = cipher.doFinal(message.getBytes());
        return cipherData;
    }

    // This method decrypts the message (using RSA algorithm) with the key, got as the 1st argument
    public String decrypt(Key key, byte[] message) throws GeneralSecurityException {
        Cipher cipher = Cipher.getInstance("RSA");
        cipher.init(Cipher.DECRYPT_MODE, key);
        byte[] cipherData = cipher.doFinal(message);
        return new String(cipherData);
    }
}

Il cui output è

Let's encrypt 'secret message' with a public key
 Encrypted message: 18d8b419247c13816563897d0f3cef7ff917be86e7a20528133b7a95377c1690f2326e4cbc9c001e6cb877c5b3645c5f395b4cd48ade4a87651052d09810e4b423aa973c69853c4e0efde238d9b4c038ecd1a53e81b19ce44248ee67056d7d74edb381b29bfc5a7cbad67a147e06e02d7ff65f4fa52718ee7f3265a48c4c146bc0ae98762d57e6b82a77bee31ea1749a47a32c2936df7914d361853c17c46b8064300865c3f763e6a87a1d42594bf254e880b6181946890199e7ffbe26f3f59ce74627594eca7c3a590a4eaa31efeb7d95b2366dd2bafe921c5497bde7e827fa87bec87a26f887434b751555189e3e244f44ea6d63b725709fe8c68ed98c98d8
 Let's decrypt it with the corresponding private key
 secret message
 You can also encrypt the message with a private key
 Encrypted message: 1ecff10fc820eea4647060890ead7669f0f845cb0c42ea8ede8a962d9848dc30bd3ba34113f858264cca1ea195dd30beeff15dae1100c68a75790c0fbba8739791f01a236174e7e1593336def60793713a95c8e4ea53cf1527d73fea52468a11974d8786de7945e3f5e7a0e3f27e6488997811fc6781c4909d27fb0224340a2e40e4e49e5023a5cc9f8531e1ee2d5b763049dac0197b0b252564d1e556a2b5de2c2026c95f94f9de5ea63d95c231c9611eb92736df617474af78c86963d3381738de84d10bf594b07d10edee2aab752c43501cc7356d4cbd0826de2c461828dba4f47bae564defc80b849c27b0dfb1cbbdffabdeb94d71ccff26810986ae2c22
 Now you need the public key to decrypt it
 secret message
 Process finished with exit code 0

Abbiamo quindi cifrato e dicifrato un semlice messaggio “secret message”.

Alla fine, firmiamo il pdf

Posizioniamo il documento da firmare (HelloWorld.pdf) nella directory

./src/test/resources/pdfs/

e lo firmiamo in 4 modi diversi utiizzando gli algoritmi di firma SHA256 (standrd CMS e CADES), SHA512 (CMS) e RIPEMD160 (CADES).

package com.itextpdf.samples.signatures.chapter02;

import com.itextpdf.kernel.geom.Rectangle;
import com.itextpdf.kernel.pdf.PdfReader;
import com.itextpdf.kernel.pdf.StampingProperties;
import com.itextpdf.signatures.*;
import org.bouncycastle.jce.provider.BouncyCastleProvider;

import java.io.File;
import java.io.FileInputStream;
import java.io.FileOutputStream;
import java.io.IOException;
import java.security.GeneralSecurityException;
import java.security.KeyStore;
import java.security.PrivateKey;
import java.security.Security;
import java.security.cert.Certificate;

public class C2_01_SignHelloWorld {
    public static final String DEST = "./target/signatures/chapter02/";

    public static final String KEYSTORE = "./src/test/resources/encryption/ks";
    public static final String SRC = "./src/test/resources/pdfs/HelloWorld.pdf";

    public static final char[] PASSWORD = "password".toCharArray();

    public static final String[] RESULT_FILES = new String[] {
            "hello_signed1.pdf",
            "hello_signed2.pdf",
            "hello_signed3.pdf",
            "hello_signed4.pdf"
    };

    public void sign(String src, String dest, Certificate[] chain, PrivateKey pk, String digestAlgorithm,
                     String provider, PdfSigner.CryptoStandard signatureType, String reason, String location)
            throws GeneralSecurityException, IOException {
        PdfReader reader = new PdfReader(src);
        PdfSigner signer = new PdfSigner(reader, new FileOutputStream(dest), new StampingProperties());

        // Create the signature appearance
        Rectangle rect = new Rectangle(36, 648, 200, 100);
        PdfSignatureAppearance appearance = signer.getSignatureAppearance();
        appearance
                .setReason(reason)
                .setLocation(location)

                // Specify if the appearance before field is signed will be used
                // as a background for the signed field. The "false" value is the default value.
                .setReuseAppearance(false)
                .setPageRect(rect)
                .setPageNumber(1);
        signer.setFieldName("sig");

        IExternalSignature pks = new PrivateKeySignature(pk, digestAlgorithm, provider);
        IExternalDigest digest = new BouncyCastleDigest();

        // Sign the document using the detached mode, CMS or CAdES equivalent.
        signer.signDetached(digest, pks, chain, null, null, null, 0, signatureType);
    }

    public static void main(String[] args) throws GeneralSecurityException, IOException {
        File file = new File(DEST);
        file.mkdirs();

        BouncyCastleProvider provider = new BouncyCastleProvider();
        Security.addProvider(provider);
        KeyStore ks = KeyStore.getInstance(KeyStore.getDefaultType());
        ks.load(new FileInputStream(KEYSTORE), PASSWORD);
        String alias = ks.aliases().nextElement();
        PrivateKey pk = (PrivateKey) ks.getKey(alias, PASSWORD);
        Certificate[] chain = ks.getCertificateChain(alias);

        C2_01_SignHelloWorld app = new C2_01_SignHelloWorld();
        app.sign(SRC, DEST + RESULT_FILES[0], chain, pk, DigestAlgorithms.SHA256, provider.getName(),
                PdfSigner.CryptoStandard.CMS, "Prova 1", "Venezia");
        app.sign(SRC, DEST + RESULT_FILES[1], chain, pk, DigestAlgorithms.SHA512, provider.getName(),
                PdfSigner.CryptoStandard.CMS, "Prova 2", "Venezia");
        app.sign(SRC, DEST + RESULT_FILES[2], chain, pk, DigestAlgorithms.SHA256, provider.getName(),
                PdfSigner.CryptoStandard.CADES, "Prova 3", "Venezia");
        app.sign(SRC, DEST + RESULT_FILES[3], chain, pk, DigestAlgorithms.RIPEMD160, provider.getName(),
                PdfSigner.CryptoStandard.CADES, "Prova 4", "Venezia");
    }
}

I file che otteniamo in output sono questi:

Python,MySQL e date

Python logo
Python logo

Sto scrivendo un programma Python che deve inserire in una tabella di un db MySQL alcuni campi letti da un file; alcuni di questi sono di tipo data e sono formattati con il formato “%d-%m-%Y” (per esempio: “25/12/2020”) per cui nel programma devo rovesciare giorno e anno per presentarli a MySQL perché interpret la stringa come una data.

Orbene la seguente riga intesa a svolgere questo compito, non funziona (ometto la definizione della connessione e del cursore):

query = "select str_to_date(substring(%s,1,25), '%d-%m-%Y') data" 
prm=list()
prm.append('16-04-2021')
print(prm)
cur.execute(query, prm)
for row in cur.fetchall():
    print("------------> ",row[0])

Quello che capita è questo:

Traceback (most recent call last):
   File "./mail.py", line 46, in 
     cur.execute(query, prm)
   File "/usr/lib/python3/dist-packages/MySQLdb/cursors.py", line 206, in execute
     raise ProgrammingError(str(m))
 MySQLdb._exceptions.ProgrammingError: not enough arguments for format string

Dov’è che Python si incasina?

Ora, il casino deriva dalla notazione % che si usa sia per i parametri della query SQL che per il formato della data.

Pensavo che il parser ci arrivasse da solo visto che la stringa di formato è racchiusa da virgolette, e invece no. Ingenuo che sono, dopo tanti anni non ho ancora imparato!

Per risolvere questo problema occorre applicare un carattere di escape alla stringa di formato, nel modo seguente

query = "select str_to_date(substring(%s,1,25), '%%d-%%m-%%Y') data" 

Nota il doppio %%.

Facendo così il programma termina correttamente

------------>  2021-04-16

In realtà il formato della data è un po’ più complicato di così, ma l’ho semplificato per circoscrivere il problema.

Risorse web

VPN con Forticlient 6.2.0

vpn
VPN

Un cliente cambia l’infrastruttura di rete privata virtuale e adotta Fortinet.

Finora si era adottata openvpn.

Di seguito trascrivo le istruzioni per installare il client in Ubuntu 20.04.2 LTS focal.

Howto VPN per Ubuntu 18.04 LTS e 20.04 LTS

Scaricare la chiave GPG

wget https://repo.fortinet.com/repo/6.4/ubuntu/DEB-GPG-KEY

Aggiungerla al repository:

sudo apt-key add DEB-GPG-KEY

Aggiungere questa riga in /etc/apt/sources.list

deb [arch=amd64] https://repo.fortinet.com/repo/6.4/ubuntu/ /bionic multiverse

Aggiorna package lists

sudo apt-get update

Installazione FortiClient

sudo apt install forticlient

Procedura 2

La versione scaricata utilizzando sources.list non era compatibile al 100%, sono dunque andato a scricare direttamente il file deb. Prima ho però rimosso la versione appena installata:

sudo apt-get remove forticlient

e poi ho scaricato la versione deb direttamente:

wget https://filestore.fortinet.com/forticlient/downloads/FortiClientFullVPNInstaller_6.4.0.0851.deb

Poi si può procedere ad installarlo

sudo apt install ./FortiClientFullVPNInstaller_6.4.0.0851.deb

A questo punto

$ forticlient
 Starting FortiTray
 Platform detected: ubuntu

Appare la schermata di configurazione della VPN in cui si scrivono l’host del tunnel, la porta, il nome che si vuole dare alla connessione

Impostazioni VPN Fortinet
Impostazioni VPN Fortinet

Ci si connette fornendo username e password:

Client VPN Fortinet
Client VPN Fortinet

e la connessione (se non ci sono altri problemi) viene stabilita:

VPN connessa
VPN connessa

That’s all folks!

Risorse web