SMIME et Certificats x509
Le protocole X.509 permet de générer des fichiers chiffrés dont la taille est supérieure aux limites des algorithmes asymétrique type RSA.
En pratique on génère une paire de clés en RSA, on choisit une clé symétrique au hasard (RK) on la chiffre avec RSA. Ensuite on chiffre le gros fichier avec AES et la clé RK. smime.permet de "packager" la clé RZ chiffrée et le fichier chiffré.
Le destinataire possédant l'autre clé de la paire RSA déchiffre la clé RZ et s'en sert pour déchiffrer le gros fichier.
Pour être plus précis on va créer un certificat autosigné composé de :
- La clé privée
- Un certificat contenant le clé publique et les métadonnées (un CN et une auto certification)
- Dans ce certificat une clé publique.
Openssl sait faite tout ça en une commande.
Création des "bi clés" soit une clé privée et un certificat public
$ openssl req -x509 -nodes -days 600 -newkey rsa:2048 -keyout privatekey.pem -out public.crt -subj '/'
$ ls -1
privatekey.pem
public.crt
Le certificat aura une date d'expiration de 600 jours (moins de 2 ans) mais c'est un exemple
La clé privée
C'est un "package" qui, de l'extérieur, ressemble à ça
-----BEGIN PRIVATE KEY-----
MIIEvQIBADANBgkqhkiG9w0BAQEFAASCBKcwggSjAgEAAoIBAQDBpp/tHdDo/yku
+ukBRzTS6kB9esDqqbDhGKZHX8jnTwDt2YynHM0nqDloMohc/OabrhPgL4dsYEut
ondfGdlULVEoaxLk2QqCVaXAowfyl1KO5btmIuspzqPnUHlU5t9F+zJGZ7BF3A30
eXWZAq9WJSRwLk1z8+s4NpmEwRgISml4VGH+VkoL4RefnRI2XiSHoaz9HqtqUkgh
Ph61ct9SkQPcWv1SFKbNwoV1S3xypOJ1oycXCzuYUgGqmd65jgQCUoWWBgdtyCuy
KOigiy6QTwd1rRn7BFlBiJHPsTqGyclQXJsDdl6vriCkTtaF60DeWCSZzKO9u0OJ
mQHxGYzRAgMBAAECggEBAJq1ifMf7PUYVUipQFm7D6Kpb1hQJ6vSYfOTg9Rl29Wg
MnfTZCFmSyixpxtts6/GhF4/1rU2g5S74foYYBHkTtKHWd1WncOi+lhiIIgkN4dC
Cb0MIIlawFGoOx3kFusTf7EjHGlPEW7efW+hUagqkvXZuqC8/Tfz2SoRxNhgg9iq
ZkiaLSz6rudU+6vIUFLOQLyFEKko1TZpH0YBQUT6vaAQfOaBkcAxe9eRDOAdhdGv
ztp4AklTxJDoHbJSLtcKlnzk2+uD04lDYjD3Y8+FLaECgYEA57TEN9d8SR3WUMEn
RG1zpBdbo37OLzOPsA7+I3wlo/7rK7PaaLIW0bB8h5Hz1w1LMsu66DjXCRkrvsOf
mM9XJBaG4wSF5Xa7sjzD24MxNNRqnVVqJM2RzMCIOdJHx8JDjsLF5Kcc9D305NAd
BtwEilGD1tsWazTzpKfxor3AnPUCgYEA1fRqemamPC4Bh+s9vcV7F/8zsi1ZILyU
8C7NMPKUxU1XBbQBOEZczeb3mjxvcrTRhgPLKFnLX2FIB9JlBfRU972dtLke0BLx
H7x64reeZNbQySyr5jJ2H6rlAx+dfhQN7u/aGrbfJE6iQCEij3CrJIuAqU/mOtE1
AmEc9WEghu0CgYB7OtmMtW8lV9gOtNuUef2hzMZxCtTPp8WIpkClULaYJNCgOpQz
UFvwg8OHOqmLu0c+KborHWfIL/njCBmOZN98kvqELHil1GL45XZo/boi1cIqES5V
ins7gT4yOlLwu7HAcHhdJj+w6m6fbsuxRTyo77eFgpnGxK+755IMsoyJLQKBgDUW
GRlXwOVrsNuNpdshVcKFgskx8UxrHjVeF3GOrxUpdG8o06in5Lz+Eu+FEU4PyXEM
HetcJFb9J0cxj/ljP3Xc6XV+/UM5f/SU6pS4ZKe2mUVhPSG34ZFH8NzOimBPD4n6
n05Dk6hJjn6E+jNHDF/b8UeRWJm9cP6vI+VKfSydAoGAAqtioBttp/jKgan4T3J5
QKOwiepakwEfHiy8lFLBTU8hzLPXavXOjkx5UpuAGadbAtjCjKtvyUPLuTJHeyAb
SSBeyHvj8Uv7uyrxEyuy6CnfgtEPKrxIL6eNjYaP+U6JL3xBscT0woDp5JFOcDPz
T39TFy1noefT9sWGqO5UYFE=
-----END PRIVATE KEY-----
Pour comprendre ce qu'il y a dedans:
openssl rsa -in privatekey.pem -text -noout
Qui nous donne les détails:
RSA Private-Key: (2048 bit, 2 primes)
modulus:
00:c1:a6:9f:ed:1d:d0:e8:ff:29:2e:fa:e9:01:47:
............. ICI PLEIN DE LIGNES ...........
40:de:58:24:99:cc:a3:bd:bb:43:89:99:01:f1:19:
8c:d1
publicExponent: 65537 (0x10001)
privateExponent:
00:9a:b5:89:f3:1f:ec:f5:18:55:48:a9:40:59:bb:
............. ICI PLEIN DE LIGNES ...........
2d:a1
prime1:
00:e7:b4:c4:37:d7:7c:49:1d:d6:50:c1:27:44:6d:
............. ICI PLEIN DE LIGNES ...........
f3:a4:a7:f1:a2:bd:c0:9c:f5
prime2:
00:d5:f4:6a:7a:66:a6:3c:2e:01:87:eb:3d:bd:c5:
............. ICI PLEIN DE LIGNES ...........
a2:40:21:22:8f:70:ab:24:8b:80:a9:4f:e6:3a:d1:
35:02:61:1c:f5:61:20:86:ed
exponent1:
7b:3a:d9:8c:b5:6f:25:57:d8:0e:b4:db:94:79:fd:
............. ICI PLEIN DE LIGNES ...........
bb:e7:92:0c:b2:8c:89:2d
exponent2:
35:16:19:19:57:c0:e5:6b:b0:db:8d:a5:db:21:55:
............. ICI PLEIN DE LIGNES ...........
fe:af:23:e5:4a:7d:2c:9d
coefficient:
02:ab:62:a0:1b:6d:a7:f8:ca:81:a9:f8:4f:72:79:
............. ICI PLEIN DE LIGNES ...........
f6:c5:86:a8:ee:54:60:51
Je suis as un expert x509... je sais pas ce que ca veut dire. Dans la clé privée il y a aussi la clé publique :
openssl rsa -in privatekey.pem -pubout
La clé publique est plus courte:
-----BEGIN PUBLIC KEY-----
MIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEAwaaf7R3Q6P8pLvrpAUc0
0upAfXrA6qmw4RimR1/I508A7dmMpxzNJ6g5aDKIXPzmm64T4C+HbGBLraJ3XxnZ
VC1RKGsS5NkKglWlwKMH8pdSjuW7ZiLrKc6j51B5VObfRfsyRmewRdwN9Hl1mQKv
ViUkcC5Nc/PrODaZhMEYCEppeFRh/lZKC+EXn50SNl4kh6Gs/R6ralJIIT4etXLf
UpED3Fr9UhSmzcKFdUt8cqTidaMnFws7mFIBqpneuY4EAlKFlgYHbcgrsijooIsu
kE8Hda0Z+wRZQYiRz7E6hsnJUFybA3Zer64gpE7WhetA3lgkmcyjvbtDiZkB8RmM
0QIDAQAB
-----END PUBLIC KEY-----
Certificat
Encore un "package" PEM :
-----BEGIN CERTIFICATE-----
MIIC4TCCAcmgAwIBAgIUY0MAqFLTdBfXJUcpSOrktft/ZlQwDQYJKoZIhvcNAQEL
BQAwADAeFw0yNTA4MDUxMjEyMjNaFw0yNzAzMjgxMjEyMjNaMAAwggEiMA0GCSqG
SIb3DQEBAQUAA4IBDwAwggEKAoIBAQDBpp/tHdDo/yku+ukBRzTS6kB9esDqqbDh
GKZHX8jnTwDt2YynHM0nqDloMohc/OabrhPgL4dsYEutondfGdlULVEoaxLk2QqC
VaXAowfyl1KO5btmIuspzqPnUHlU5t9F+zJGZ7BF3A30eXWZAq9WJSRwLk1z8+s4
NpmEwRgISml4VGH+VkoL4RefnRI2XiSHoaz9HqtqUkghPh61ct9SkQPcWv1SFKbN
woV1S3xypOJ1oycXCzuYUgGqmd65jgQCUoWWBgdtyCuyKOigiy6QTwd1rRn7BFlB
iJHPsTqGyclQXJsDdl6vriCkTtaF60DeWCSZzKO9u0OJmQHxGYzRAgMBAAGjUzBR
MB0GA1UdDgQWBBRFTqFH8NpaMWMDJhXMkPZifIgTLzAfBgNVHSMEGDAWgBRFTqFH
8NpaMWMDJhXMkPZifIgTLzAPBgNVHRMBAf8EBTADAQH/MA0GCSqGSIb3DQEBCwUA
A4IBAQAH2CXH+Lxj6EjTdXbfVcLhP6xful1GMYoHUWah41r8RO6HlxvY32IoyTYC
J9sZL3qRLHkDsRYDkGFjtEj4JdglIecx/KNqo+0JWW7gcmiFCRG5ygtfhYmGaAqx
PHWmUQpDfOO7BftZbQB+hqcFRhxrDnY7Lv2a6hQZUWsJF59nl9Drafe+VNLWcSLP
WIRNc8wRCxjEW3qXRdSk0TYbVnlayC+9z+cvpCg/5imk4UaUfuiN+KLxMZHD3KwC
K4tG2KcA4m7S9XmHgO5LnzwB/kK//VXXWRwSLV7O+t41nZfJLEKoW1Ws4OB02KtC
ovoJ767G3Nwubqra7lPWAsPildAF
-----END CERTIFICATE-----
On le regarde ce qu'il y a dedans par :
openssl x509 -inform pem -noout -text -in public.crt
Qui nous dit plein de choses:
Certificate:
Data:
Version: 3 (0x2)
Serial Number:
63:43:00:a8:52:d3:74:17:d7:25:47:29:48:ea:e4:b5:fb:7f:66:54
Signature Algorithm: sha256WithRSAEncryption
Issuer:
Validity
Not Before: Aug 5 12:12:23 2025 GMT
Not After : Mar 28 12:12:23 2027 GMT
Subject:
Subject Public Key Info:
Public Key Algorithm: rsaEncryption
RSA Public-Key: (2048 bit)
Modulus:
00:c1:a6:9f:ed:1d:d0:e8:ff:29:2e:fa:e9:01:47:
34:d2:ea:40:7d:7a:c0:ea:a9:b0:e1:18:a6:47:5f:
c8:e7:4f:00:ed:d9:8c:a7:1c:cd:27:a8:39:68:32:
88:5c:fc:e6:9b:ae:13:e0:2f:87:6c:60:4b:ad:a2:
77:5f:19:d9:54:2d:51:28:6b:12:e4:d9:0a:82:55:
a5:c0:a3:07:f2:97:52:8e:e5:bb:66:22:eb:29:ce:
a3:e7:50:79:54:e6:df:45:fb:32:46:67:b0:45:dc:
0d:f4:79:75:99:02:af:56:25:24:70:2e:4d:73:f3:
eb:38:36:99:84:c1:18:08:4a:69:78:54:61:fe:56:
4a:0b:e1:17:9f:9d:12:36:5e:24:87:a1:ac:fd:1e:
ab:6a:52:48:21:3e:1e:b5:72:df:52:91:03:dc:5a:
fd:52:14:a6:cd:c2:85:75:4b:7c:72:a4:e2:75:a3:
27:17:0b:3b:98:52:01:aa:99:de:b9:8e:04:02:52:
85:96:06:07:6d:c8:2b:b2:28:e8:a0:8b:2e:90:4f:
07:75:ad:19:fb:04:59:41:88:91:cf:b1:3a:86:c9:
c9:50:5c:9b:03:76:5e:af:ae:20:a4:4e:d6:85:eb:
40:de:58:24:99:cc:a3:bd:bb:43:89:99:01:f1:19:
8c:d1
Exponent: 65537 (0x10001)
X509v3 extensions:
X509v3 Subject Key Identifier:
45:4E:A1:47:F0:DA:5A:31:63:03:26:15:CC:90:F6:62:7C:88:13:2F
X509v3 Authority Key Identifier:
keyid:45:4E:A1:47:F0:DA:5A:31:63:03:26:15:CC:90:F6:62:7C:88:13:2F
X509v3 Basic Constraints: critical
CA:TRUE
Signature Algorithm: sha256WithRSAEncryption
07:d8:25:c7:f8:bc:63:e8:48:d3:75:76:df:55:c2:e1:3f:ac:
5f:ba:5d:46:31:8a:07:51:66:a1:e3:5a:fc:44:ee:87:97:1b:
d8:df:62:28:c9:36:02:27:db:19:2f:7a:91:2c:79:03:b1:16:
03:90:61:63:b4:48:f8:25:d8:25:21:e7:31:fc:a3:6a:a3:ed:
09:59:6e:e0:72:68:85:09:11:b9:ca:0b:5f:85:89:86:68:0a:
b1:3c:75:a6:51:0a:43:7c:e3:bb:05:fb:59:6d:00:7e:86:a7:
05:46:1c:6b:0e:76:3b:2e:fd:9a:ea:14:19:51:6b:09:17:9f:
67:97:d0:eb:69:f7:be:54:d2:d6:71:22:cf:58:84:4d:73:cc:
11:0b:18:c4:5b:7a:97:45:d4:a4:d1:36:1b:56:79:5a:c8:2f:
bd:cf:e7:2f:a4:28:3f:e6:29:a4:e1:46:94:7e:e8:8d:f8:a2:
f1:31:91:c3:dc:ac:02:2b:8b:46:d8:a7:00:e2:6e:d2:f5:79:
87:80:ee:4b:9f:3c:01:fe:42:bf:fd:55:d7:59:1c:12:2d:5e:
ce:fa:de:35:9d:97:c9:2c:42:a8:5b:55:ac:e0:e0:74:d8:ab:
42:a2:fa:09:ef:ae:c6:dc:dc:2e:6e:aa:da:ee:53:d6:02:c3:
e2:95:d0:05
On peut simplement extraire la clé publique :
openssl x509 -in public.crt -pubkey -noout
-----BEGIN PUBLIC KEY-----
MIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEAwaaf7R3Q6P8pLvrpAUc0
0upAfXrA6qmw4RimR1/I508A7dmMpxzNJ6g5aDKIXPzmm64T4C+HbGBLraJ3XxnZ
VC1RKGsS5NkKglWlwKMH8pdSjuW7ZiLrKc6j51B5VObfRfsyRmewRdwN9Hl1mQKv
ViUkcC5Nc/PrODaZhMEYCEppeFRh/lZKC+EXn50SNl4kh6Gs/R6ralJIIT4etXLf
UpED3Fr9UhSmzcKFdUt8cqTidaMnFws7mFIBqpneuY4EAlKFlgYHbcgrsijooIsu
kE8Hda0Z+wRZQYiRz7E6hsnJUFybA3Zer64gpE7WhetA3lgkmcyjvbtDiZkB8RmM
0QIDAQAB
-----END PUBLIC KEY-----
Qui est bien la même que celle dans le fichier de la clé privée.
openssl rsa -in privatekey.pem -pubout
et
openssl x509 -in public.crt -pubkey -noout donne la même chose.
On envoie don notre certificat au client. Si il était issue d'une "vrai" root CA il pourrait le vérifier.
Chiffrement
C'est donc le partenaire auquel on a envoyé le certificat qui va chiffrer le fichier qu'il désire nous envoyer.
openssl smime -encrypt -aes256 -in largefile.txt -binary -outform DEM -out largefile.txt.cry public.crt
Le fichier largefile.txt.cry qui est binaire. En base 64 ca donnerait :
MIIEkAYJKoZIhvcNAQcDoIIEgTCCBH0CAQAxggE0MIIBMAIBADAYMAACFGNDAKhS03QX1yVHKUjq
5LX7f2ZUMA0GCSqGSIb3DQEBAQUABIIBALI0lpVY4g9yotpItgjx6QUkQ/z4pMOIzTl9soxSBB7S
xCi9TvdOP0q8BFo02QV7O87kVCvtno/ZUri0Uvpw7a0w2ZAi2DOVauuUXf/zCmKjtQqotWmS2FN4
TMDX/+FOsEfYJsJFtzxcqam5N28tivKKDbLoKv0eOl2yDf/hUgqXyFVeakAPP4tGh4+9nEcjCc8s
U44mGce2DU5IzIHaJNjMZI0TX9xTtbwzprtmY9K7jT7j6UFLdetmSWYvYiPLMLOemITcoF+TtsVa
YRGASn6QpJV+5ZzzUtwmKQAkEUE0YxcQ7eyss3qE14oRZ+DZCAM5Yfd+97sa0cPsSQDo9/MwggM+
BgkqhkiG9w0BBwEwHQYJYIZIAWUDBAEqBBBqQA7V2lnCuC3ttLN9X6elgIIDEOnxshX83L9AbaWW
mbmp779WjL/5OjXxlqV6lD6MThZYjJdHtqlcyOzgNlVC+PSaiyMXt3UV8v+h3q30vgP0HPYI7zoW
bcseOhfg8srnGEBjHZ+OYk9TKTs4PJpNL0mBspw7YGtSOqYWMDcT9xb48lVB4MbfnF5TKrfPf1TN
1SSxVgZFtCchUDHkJKifYsaE+YBb4HPUwdKPsxzgK5ldPVDg3S1WRjkjxVsVOxhsLjBTTKZn5EIo
oNwVvVY1LQZ25pJ5xxOc8myTmQPlYv5GuXb8jpq0FityyHZX09BfrzkB8m6uiTClyowdXe0hwx0y
79ryjDPIVrVvPu//kDnZW+jHID8zHwlKAaT/6SKt+3wgjNFnnmFNm0P9A8xFsU6Gt23gGjLv+IRy
/kZqx7/K9l6fcn7Ut1GgtEcJYKmMN/0wq/wxaI7ojrcHIBksdJnYS1XVy54dnCXrFs8CVGIiT/8P
qW5BvuAy+UX3vDexLl9SmnYQrqYCl1E8k8iVwDduE4A92R0NUdsIytJWdgCVD9lbfeZUkSAkHVBl
9X46nAP0rcXcH4it/HmT1D8ehcYyhWLRMuqFCEI0ZfQ+hAF1WhTPYQ3UD5h9VKEY+9BBPWl0oBtQ
RcaizypI67hxoC5zUGqKgXlalwGhfs6cAhvQ8OSSLXQzHzKTUxERTv3Heg2zEUlatwhOncfreb0+
M3TVTZW0T2o2rAoi9WMV50VhrCe8g5/6SYhRVQckVxRuWsgcNn1Yb4IvnDiC/+xOtG9epVxz0A5a
blCFKbZ6ZffRrZNa7j+bmXg2gX/ZZkgZeRawV9RyoJMPQ/94RVjD5BLDFJSWB2f25ry+liyK7TFk
v40xzJR29WfS1FlVooUN0ppsW/SY43R5ka36+nn+09p3gorhSTvynWV+0Ersf0VI01wTcOUfU8WW
ppr6esp2vDZYcbU55TsqnNn3SBQmJGvynM1e57DLzuuU8+UjJbHk0WoF54utTc1EgcGHKj/x4BKq
dFbBiCwp2mLK0yd5yP3gpdiBsTM9p26gsmUb+pD9y6M=
En javascript
On peut aussi chiffrer en javascript!
async function pemToArrayBuffer(pem) {
// Remove header, footer, and line breaks
const b64 = pem
.replace(/-----BEGIN PUBLIC KEY-----/g, '')
.replace(/-----END PUBLIC KEY-----/g, '')
.replace(/\s+/g, '');
const raw = atob(b64);
const bytes = new Uint8Array(raw.length);
for (let i = 0; i < raw.length; i++) bytes[i] = raw.charCodeAt(i);
return bytes.buffer;
}
async function importRsaPublicKey(pem) {
const spki = await pemToArrayBuffer(pem);
return crypto.subtle.importKey(
'spki',
spki,
{
name: 'RSA-OAEP',
hash: 'SHA-256',
},
false,
['encrypt']
);
}
async function encryptMessage(publicKeyPem, message) {
const key = await importRsaPublicKey(publicKeyPem);
const data = new TextEncoder().encode(message);
const ciphertext = await crypto.subtle.encrypt(
{ name: 'RSA-OAEP' },
key,
data
);
// Convert to base64 for display/transport
const bytes = new Uint8Array(ciphertext);
let bin = '';
for (let i = 0; i < bytes.length; i++) bin += String.fromCharCode(bytes[i]);
return btoa(bin);
}
// On la charge en dur mais on pourait chercher une URL par exemple
const publicKeyPem ='-----BEGIN PUBLIC KEY-----\n
MIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEAwaaf7R3Q6P8pLvrpAUc0\n\
0upAfXrA6qmw4RimR1/I508A7dmMpxzNJ6g5aDKIXPzmm64T4C+HbGBLraJ3XxnZ\n\
VC1RKGsS5NkKglWlwKMH8pdSjuW7ZiLrKc6j51B5VObfRfsyRmewRdwN9Hl1mQKv\n\
ViUkcC5Nc/PrODaZhMEYCEppeFRh/lZKC+EXn50SNl4kh6Gs/R6ralJIIT4etXLf\n\
UpED3Fr9UhSmzcKFdUt8cqTidaMnFws7mFIBqpneuY4EAlKFlgYHbcgrsijooIsu\n\
kE8Hda0Z+wRZQYiRz7E6hsnJUFybA3Zer64gpE7WhetA3lgkmcyjvbtDiZkB8RmM\n\
0QIDAQAB\n\
-----END PUBLIC KEY-----';
(async () => {
const message = 'message to encrypt';
const encryptedBase64 = await encryptMessage(publicKeyPem, message);
console.log('Encrypted (base64):', encryptedBase64);
// On logue dans la console mais on pourait l'envoyer au serveur
})();
Attention ca sort le fichier en base64 pas en binaire!
Déchiffrage
Notre partenaire va nous envoyer le fichier. On le déchiffre par:
openssl smime -decrypt -in largefile.txt.cry -binary -inform DEM -inkey privatekey.pem # Résultat sur la console
openssl smime -decrypt -in largefile.txt.cry -binary -inform DEM -inkey privatekey.pem -out mylargefile.txt
Pour déchiffrer le texte chiffré par le javascript avec la clé publique la syntaxe est un peu diffrérente :
# On decode le base64
base64 -d chiffre.base64 > chiffre.cry
# On déchiffre le fichier avec la clé privée
openssl pkeyutl -decrypt -pkeyopt rsa_padding_mode:oaep -pkeyopt rsa_oaep_md:sha256 -in chiffre.cry -inkey privatekey.pem
Mais ca fait la même chose.
Exercice
Utiliser le fichier largefile.txt.cryci-dessus à partir du dump base64 et créer le fichier d'origine! La clé privée est donnée en haut.