yannstatic/static/2024/04/16/GnuPG_Yubikey_5.html

2624 lines
219 KiB
HTML
Raw Normal View History

2024-10-31 20:18:37 +01:00
<!DOCTYPE html><html lang="fr">
<head><meta charset="utf-8">
<meta http-equiv="X-UA-Compatible" content="IE=edge">
<meta name="viewport" content="width=device-width, initial-scale=1, user-scalable=no"><title>OpenPGP Yubikey 5 - YannStatic</title>
<meta name="description" content="Comment configurer votre YubiKey avec OpenPGP après avoir généré la clé privée puis déplacer les sous-clés vers le YubiKey">
<link rel="canonical" href="https://static.rnmkcy.eu/2024/04/16/GnuPG_Yubikey_5.html"><link rel="alternate" type="application/rss+xml" title="YannStatic" href="/feed.xml">
<!-- - include head/favicon.html - -->
<link rel="shortcut icon" type="image/png" href="/assets/favicon/favicon.png"><link rel="stylesheet" href="/assets/css/main.css"><link rel="stylesheet" href="https://use.fontawesome.com/releases/v5.0.13/css/all.css" ><!-- start custom head snippets --><link rel="stylesheet" href="/assets/css/expand.css">
<!-- end custom head snippets --><script>(function() {
window.isArray = function(val) {
return Object.prototype.toString.call(val) === '[object Array]';
};
window.isString = function(val) {
return typeof val === 'string';
};
window.hasEvent = function(event) {
return 'on'.concat(event) in window.document;
};
window.isOverallScroller = function(node) {
return node === document.documentElement || node === document.body || node === window;
};
window.isFormElement = function(node) {
var tagName = node.tagName;
return tagName === 'INPUT' || tagName === 'SELECT' || tagName === 'TEXTAREA';
};
window.pageLoad = (function () {
var loaded = false, cbs = [];
window.addEventListener('load', function () {
var i;
loaded = true;
if (cbs.length > 0) {
for (i = 0; i < cbs.length; i++) {
cbs[i]();
}
}
});
return {
then: function(cb) {
cb && (loaded ? cb() : (cbs.push(cb)));
}
};
})();
})();
(function() {
window.throttle = function(func, wait) {
var args, result, thisArg, timeoutId, lastCalled = 0;
function trailingCall() {
lastCalled = new Date;
timeoutId = null;
result = func.apply(thisArg, args);
}
return function() {
var now = new Date,
remaining = wait - (now - lastCalled);
args = arguments;
thisArg = this;
if (remaining <= 0) {
clearTimeout(timeoutId);
timeoutId = null;
lastCalled = now;
result = func.apply(thisArg, args);
} else if (!timeoutId) {
timeoutId = setTimeout(trailingCall, remaining);
}
return result;
};
};
})();
(function() {
var Set = (function() {
var add = function(item) {
var i, data = this._data;
for (i = 0; i < data.length; i++) {
if (data[i] === item) {
return;
}
}
this.size ++;
data.push(item);
return data;
};
var Set = function(data) {
this.size = 0;
this._data = [];
var i;
if (data.length > 0) {
for (i = 0; i < data.length; i++) {
add.call(this, data[i]);
}
}
};
Set.prototype.add = add;
Set.prototype.get = function(index) { return this._data[index]; };
Set.prototype.has = function(item) {
var i, data = this._data;
for (i = 0; i < data.length; i++) {
if (this.get(i) === item) {
return true;
}
}
return false;
};
Set.prototype.is = function(map) {
if (map._data.length !== this._data.length) { return false; }
var i, j, flag, tData = this._data, mData = map._data;
for (i = 0; i < tData.length; i++) {
for (flag = false, j = 0; j < mData.length; j++) {
if (tData[i] === mData[j]) {
flag = true;
break;
}
}
if (!flag) { return false; }
}
return true;
};
Set.prototype.values = function() {
return this._data;
};
return Set;
})();
window.Lazyload = (function(doc) {
var queue = {js: [], css: []}, sources = {js: {}, css: {}}, context = this;
var createNode = function(name, attrs) {
var node = doc.createElement(name), attr;
for (attr in attrs) {
if (attrs.hasOwnProperty(attr)) {
node.setAttribute(attr, attrs[attr]);
}
}
return node;
};
var end = function(type, url) {
var s, q, qi, cbs, i, j, cur, val, flag;
if (type === 'js' || type ==='css') {
s = sources[type], q = queue[type];
s[url] = true;
for (i = 0; i < q.length; i++) {
cur = q[i];
if (cur.urls.has(url)) {
qi = cur, val = qi.urls.values();
qi && (cbs = qi.callbacks);
for (flag = true, j = 0; j < val.length; j++) {
cur = val[j];
if (!s[cur]) {
flag = false;
}
}
if (flag && cbs && cbs.length > 0) {
for (j = 0; j < cbs.length; j++) {
cbs[j].call(context);
}
qi.load = true;
}
}
}
}
};
var load = function(type, urls, callback) {
var s, q, qi, node, i, cur,
_urls = typeof urls === 'string' ? new Set([urls]) : new Set(urls), val, url;
if (type === 'js' || type ==='css') {
s = sources[type], q = queue[type];
for (i = 0; i < q.length; i++) {
cur = q[i];
if (_urls.is(cur.urls)) {
qi = cur;
break;
}
}
val = _urls.values();
if (qi) {
callback && (qi.load || qi.callbacks.push(callback));
callback && (qi.load && callback());
} else {
q.push({
urls: _urls,
callbacks: callback ? [callback] : [],
load: false
});
for (i = 0; i < val.length; i++) {
node = null, url = val[i];
if (s[url] === undefined) {
(type === 'js' ) && (node = createNode('script', { src: url }));
(type === 'css') && (node = createNode('link', { rel: 'stylesheet', href: url }));
if (node) {
node.onload = (function(type, url) {
return function() {
end(type, url);
};
})(type, url);
(doc.head || doc.body).appendChild(node);
s[url] = false;
}
}
}
}
}
};
return {
js: function(url, callback) {
load('js', url, callback);
},
css: function(url, callback) {
load('css', url, callback);
}
};
})(this.document);
})();
</script><script>
(function() {
var TEXT_VARIABLES = {
version: '2.2.6',
sources: {
font_awesome: 'https://use.fontawesome.com/releases/v5.0.13/css/all.css',
jquery: '/assets/js/jquery.min.js',
leancloud_js_sdk: '//cdn.jsdelivr.net/npm/leancloud-storage@3.13.2/dist/av-min.js',
chart: 'https://cdn.bootcss.com/Chart.js/2.7.2/Chart.bundle.min.js',
gitalk: {
js: 'https://cdn.bootcss.com/gitalk/1.2.2/gitalk.min.js',
css: 'https://cdn.bootcss.com/gitalk/1.2.2/gitalk.min.css'
},
valine: 'https://unpkg.com/valine/dist/Valine.min.js'
},
site: {
toc: {
selectors: 'h1,h2,h3'
}
},
paths: {
search_js: '/assets/search.js'
}
};
window.TEXT_VARIABLES = TEXT_VARIABLES;
})();
</script>
</head>
<body>
<div class="root" data-is-touch="false">
<div class="layout--page js-page-root"><!----><div class="page__main js-page-main page__viewport hide-footer has-aside has-aside cell cell--auto">
<div class="page__main-inner"><div class="page__header d-print-none"><header class="header"><div class="main">
<div class="header__title">
<div class="header__brand"><svg id="svg" version="1.1" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" width="400" height="478.9473684210526" viewBox="0, 0, 400,478.9473684210526"><g id="svgg"><path id="path0" d="M308.400 56.805 C 306.970 56.966,303.280 57.385,300.200 57.738 C 290.906 58.803,278.299 59.676,269.200 59.887 L 260.600 60.085 259.400 61.171 C 258.010 62.428,256.198 63.600,255.645 63.600 C 255.070 63.600,252.887 65.897,252.598 66.806 C 252.460 67.243,252.206 67.600,252.034 67.600 C 251.397 67.600,247.206 71.509,247.202 72.107 C 247.201 72.275,246.390 73.190,245.400 74.138 C 243.961 75.517,243.598 76.137,243.592 77.231 C 243.579 79.293,241.785 83.966,240.470 85.364 C 239.176 86.740,238.522 88.365,237.991 91.521 C 237.631 93.665,236.114 97.200,235.554 97.200 C 234.938 97.200,232.737 102.354,232.450 104.472 C 232.158 106.625,230.879 109.226,229.535 110.400 C 228.933 110.926,228.171 113.162,226.434 119.500 C 226.178 120.435,225.795 121.200,225.584 121.200 C 225.373 121.200,225.200 121.476,225.200 121.813 C 225.200 122.149,224.885 122.541,224.500 122.683 C 223.606 123.013,223.214 123.593,223.204 124.600 C 223.183 126.555,220.763 132.911,219.410 134.562 C 218.443 135.742,217.876 136.956,217.599 138.440 C 217.041 141.424,215.177 146.434,214.532 146.681 C 214.240 146.794,214.000 147.055,214.000 147.261 C 214.000 147.467,213.550 148.086,213.000 148.636 C 212.450 149.186,212.000 149.893,212.000 150.208 C 212.000 151.386,208.441 154.450,207.597 153.998 C 206.319 153.315,204.913 150.379,204.633 147.811 C 204.365 145.357,202.848 142.147,201.759 141.729 C 200.967 141.425,199.200 137.451,199.200 135.974 C 199.200 134.629,198.435 133.224,196.660 131.311 C 195.363 129.913,194.572 128.123,193.870 125.000 C 193.623 123.900,193.236 122.793,193.010 122.540 C 190.863 120.133,190.147 118.880,188.978 115.481 C 188.100 112.928,187.151 111.003,186.254 109.955 C 185.358 108.908,184.518 107.204,183.847 105.073 C 183.280 103.273,182.497 101.329,182.108 100.753 C 181.719 100.177,180.904 98.997,180.298 98.131 C 179.693 97.265,178.939 95.576,178.624 94.378 C 178.041 92.159,177.125 90.326,175.023 87.168 C 174.375 86.196,173.619 84.539,173.342 83.486 C 172.800 81.429,171.529 79.567,170.131 78.785 C 169.654 78.517,168.697 77.511,168.006 76.549 C 167.316 75.587,166.594 74.800,166.402 74.800 C 166.210 74.800,164.869 73.633,163.421 72.206 C 160.103 68.936,161.107 69.109,146.550 69.301 C 133.437 69.474,128.581 70.162,126.618 72.124 C 126.248 72.495,125.462 72.904,124.872 73.033 C 124.282 73.163,123.088 73.536,122.219 73.863 C 121.349 74.191,119.028 74.638,117.061 74.858 C 113.514 75.254,109.970 76.350,108.782 77.419 C 107.652 78.436,100.146 80.400,97.388 80.400 C 95.775 80.400,93.167 81.360,91.200 82.679 C 90.430 83.195,89.113 83.804,88.274 84.031 C 85.875 84.681,78.799 90.910,74.400 96.243 L 73.400 97.456 73.455 106.028 C 73.526 117.055,74.527 121.238,77.820 124.263 C 78.919 125.273,80.400 127.902,80.400 128.842 C 80.400 129.202,81.075 130.256,81.900 131.186 C 83.563 133.059,85.497 136.346,86.039 138.216 C 86.233 138.886,87.203 140.207,88.196 141.153 C 89.188 142.098,90.000 143.104,90.000 143.388 C 90.000 144.337,92.129 148.594,92.869 149.123 C 93.271 149.410,93.600 149.831,93.600 150.059 C 93.600 150.286,93.932 150.771,94.337 151.136 C 94.743 151.501,95.598 153.004,96.237 154.475 C 96.877 155.947,97.760 157.351,98.200 157.596 C 98.640 157.841,99.900 159.943,101.000 162.267 C 102.207 164.817,103.327 166.644,103.825 166.876 C 104.278 167.087,105.065 168.101,105.573 169.130 C 107.658 173.348,108.097 174.093,110.006 176.647 C 111.103 178.114,112.000 179.725,112.000 180.227 C 112.000 181.048,113.425 183.163,114.678 184.200 C 115.295 184.711,117.396 188.733,117.720 190.022 C 117.855 190.562,118.603 191.633,119.381 192.402 C 120.160 193.171,121.496 195.258,122.351 197.039 C 123.206 198.820,124.167 200.378,124.487 200.501 C 124.807 200.624,125.953 202.496,127.034 204.662 C 128.114 206.828,129.676 209.299,130.505 210.153 C 131.333 211.007,132.124 212.177,132.262 212.753 C 132.618 214.239,134.291 217.048,136.288 219.5
" href="/">YannStatic</a></div><!--<button class="button button--secondary button--circle search-button js-search-toggle"><i class="fas fa-search"></i></button>--><!-- <li><button class="button button--secondary button--circle search-button js-search-toggle"><i class="fas fa-search"></i></button></li> -->
<!-- Champ de recherche -->
<div id="searchbox" class="search search--dark" style="visibility: visible">
<div class="main">
<div class="search__header"></div>
<div class="search-bar">
<div class="search-box js-search-box">
<div class="search-box__icon-search"><i class="fas fa-search"></i></div>
<input id="search-input" type="text" />
<!-- <div class="search-box__icon-clear js-icon-clear">
<a><i class="fas fa-times"></i></a>
</div> -->
</div>
</div>
</div>
</div>
<!-- Script pointing to search-script.js -->
<script>/*!
* Simple-Jekyll-Search
* Copyright 2015-2020, Christian Fei
* Licensed under the MIT License.
*/
(function(){
'use strict'
var _$Templater_7 = {
compile: compile,
setOptions: setOptions
}
const options = {}
options.pattern = /\{(.*?)\}/g
options.template = ''
options.middleware = function () {}
function setOptions (_options) {
options.pattern = _options.pattern || options.pattern
options.template = _options.template || options.template
if (typeof _options.middleware === 'function') {
options.middleware = _options.middleware
}
}
function compile (data) {
return options.template.replace(options.pattern, function (match, prop) {
const value = options.middleware(prop, data[prop], options.template)
if (typeof value !== 'undefined') {
return value
}
return data[prop] || match
})
}
'use strict';
function fuzzysearch (needle, haystack) {
var tlen = haystack.length;
var qlen = needle.length;
if (qlen > tlen) {
return false;
}
if (qlen === tlen) {
return needle === haystack;
}
outer: for (var i = 0, j = 0; i < qlen; i++) {
var nch = needle.charCodeAt(i);
while (j < tlen) {
if (haystack.charCodeAt(j++) === nch) {
continue outer;
}
}
return false;
}
return true;
}
var _$fuzzysearch_1 = fuzzysearch;
'use strict'
/* removed: const _$fuzzysearch_1 = require('fuzzysearch') */;
var _$FuzzySearchStrategy_5 = new FuzzySearchStrategy()
function FuzzySearchStrategy () {
this.matches = function (string, crit) {
return _$fuzzysearch_1(crit.toLowerCase(), string.toLowerCase())
}
}
'use strict'
var _$LiteralSearchStrategy_6 = new LiteralSearchStrategy()
function LiteralSearchStrategy () {
this.matches = function (str, crit) {
if (!str) return false
str = str.trim().toLowerCase()
crit = crit.trim().toLowerCase()
return crit.split(' ').filter(function (word) {
return str.indexOf(word) >= 0
}).length === crit.split(' ').length
}
}
'use strict'
var _$Repository_4 = {
put: put,
clear: clear,
search: search,
setOptions: __setOptions_4
}
/* removed: const _$FuzzySearchStrategy_5 = require('./SearchStrategies/FuzzySearchStrategy') */;
/* removed: const _$LiteralSearchStrategy_6 = require('./SearchStrategies/LiteralSearchStrategy') */;
function NoSort () {
return 0
}
const data = []
let opt = {}
opt.fuzzy = false
opt.limit = 10
opt.searchStrategy = opt.fuzzy ? _$FuzzySearchStrategy_5 : _$LiteralSearchStrategy_6
opt.sort = NoSort
opt.exclude = []
function put (data) {
if (isObject(data)) {
return addObject(data)
}
if (isArray(data)) {
return addArray(data)
}
return undefined
}
function clear () {
data.length = 0
return data
}
function isObject (obj) {
return Boolean(obj) && Object.prototype.toString.call(obj) === '[object Object]'
}
function isArray (obj) {
return Boolean(obj) && Object.prototype.toString.call(obj) === '[object Array]'
}
function addObject (_data) {
data.push(_data)
return data
}
function addArray (_data) {
const added = []
clear()
for (let i = 0, len = _data.length; i < len; i++) {
if (isObject(_data[i])) {
added.push(addObject(_data[i]))
}
}
return added
}
function search (crit) {
if (!crit) {
return []
}
return findMatches(data, crit, opt.searchStrategy, opt).sort(opt.sort)
}
function __setOptions_4 (_opt) {
opt = _opt || {}
opt.fuzzy = _opt.fuzzy || false
opt.limit = _opt.limit || 10
opt.searchStrategy = _opt.fuzzy ? _$FuzzySearchStrategy_5 : _$LiteralSearchStrategy_6
opt.sort = _opt.sort || NoSort
opt.exclude = _opt.exclude || []
}
function findMatches (data, crit, strategy, opt) {
const matches = []
for (let i = 0; i < data.length && matches.length < opt.limit; i++) {
const match = findMatchesInObject(data[i], crit, strategy, opt)
if (match) {
matches.push(match)
}
}
return matches
}
function findMatchesInObject (obj, crit, strategy, opt) {
for (const key in obj) {
if (!isExcluded(obj[key], opt.exclude) && strategy.matches(obj[key], crit)) {
return obj
}
}
}
function isExcluded (term, excludedTerms) {
for (let i = 0, len = excludedTerms.length; i < len; i++) {
const excludedTerm = excludedTerms[i]
if (new RegExp(excludedTerm).test(term)) {
return true
}
}
return false
}
/* globals ActiveXObject:false */
'use strict'
var _$JSONLoader_2 = {
load: load
}
function load (location, callback) {
const xhr = getXHR()
xhr.open('GET', location, true)
xhr.onreadystatechange = createStateChangeListener(xhr, callback)
xhr.send()
}
function createStateChangeListener (xhr, callback) {
return function () {
if (xhr.readyState === 4 && xhr.status === 200) {
try {
callback(null, JSON.parse(xhr.responseText))
} catch (err) {
callback(err, null)
}
}
}
}
function getXHR () {
return window.XMLHttpRequest ? new window.XMLHttpRequest() : new ActiveXObject('Microsoft.XMLHTTP')
}
'use strict'
var _$OptionsValidator_3 = function OptionsValidator (params) {
if (!validateParams(params)) {
throw new Error('-- OptionsValidator: required options missing')
}
if (!(this instanceof OptionsValidator)) {
return new OptionsValidator(params)
}
const requiredOptions = params.required
this.getRequiredOptions = function () {
return requiredOptions
}
this.validate = function (parameters) {
const errors = []
requiredOptions.forEach(function (requiredOptionName) {
if (typeof parameters[requiredOptionName] === 'undefined') {
errors.push(requiredOptionName)
}
})
return errors
}
function validateParams (params) {
if (!params) {
return false
}
return typeof params.required !== 'undefined' && params.required instanceof Array
}
}
'use strict'
var _$utils_9 = {
merge: merge,
isJSON: isJSON
}
function merge (defaultParams, mergeParams) {
const mergedOptions = {}
for (const option in defaultParams) {
mergedOptions[option] = defaultParams[option]
if (typeof mergeParams[option] !== 'undefined') {
mergedOptions[option] = mergeParams[option]
}
}
return mergedOptions
}
function isJSON (json) {
try {
if (json instanceof Object && JSON.parse(JSON.stringify(json))) {
return true
}
return false
} catch (err) {
return false
}
}
var _$src_8 = {};
(function (window) {
'use strict'
let options = {
searchInput: null,
resultsContainer: null,
json: [],
success: Function.prototype,
searchResultTemplate: '<li><a href="{url}" title="{desc}">{title}</a></li>',
templateMiddleware: Function.prototype,
sortMiddleware: function () {
return 0
},
noResultsText: 'No results found',
limit: 10,
fuzzy: false,
debounceTime: null,
exclude: []
}
let debounceTimerHandle
const debounce = function (func, delayMillis) {
if (delayMillis) {
clearTimeout(debounceTimerHandle)
debounceTimerHandle = setTimeout(func, delayMillis)
} else {
func.call()
}
}
const requiredOptions = ['searchInput', 'resultsContainer', 'json']
/* removed: const _$Templater_7 = require('./Templater') */;
/* removed: const _$Repository_4 = require('./Repository') */;
/* removed: const _$JSONLoader_2 = require('./JSONLoader') */;
const optionsValidator = _$OptionsValidator_3({
required: requiredOptions
})
/* removed: const _$utils_9 = require('./utils') */;
window.SimpleJekyllSearch = function (_options) {
const errors = optionsValidator.validate(_options)
if (errors.length > 0) {
throwError('You must specify the following required options: ' + requiredOptions)
}
options = _$utils_9.merge(options, _options)
_$Templater_7.setOptions({
template: options.searchResultTemplate,
middleware: options.templateMiddleware
})
_$Repository_4.setOptions({
fuzzy: options.fuzzy,
limit: options.limit,
sort: options.sortMiddleware,
exclude: options.exclude
})
if (_$utils_9.isJSON(options.json)) {
initWithJSON(options.json)
} else {
initWithURL(options.json)
}
const rv = {
search: search
}
typeof options.success === 'function' && options.success.call(rv)
return rv
}
function initWithJSON (json) {
_$Repository_4.put(json)
registerInput()
}
function initWithURL (url) {
_$JSONLoader_2.load(url, function (err, json) {
if (err) {
throwError('failed to get JSON (' + url + ')')
}
initWithJSON(json)
})
}
function emptyResultsContainer () {
options.resultsContainer.innerHTML = ''
}
function appendToResultsContainer (text) {
options.resultsContainer.innerHTML += text
}
function registerInput () {
options.searchInput.addEventListener('input', function (e) {
if (isWhitelistedKey(e.which)) {
emptyResultsContainer()
debounce(function () { search(e.target.value) }, options.debounceTime)
}
})
}
function search (query) {
if (isValidQuery(query)) {
emptyResultsContainer()
render(_$Repository_4.search(query), query)
}
}
function render (results, query) {
const len = results.length
if (len === 0) {
return appendToResultsContainer(options.noResultsText)
}
for (let i = 0; i < len; i++) {
results[i].query = query
appendToResultsContainer(_$Templater_7.compile(results[i]))
}
}
function isValidQuery (query) {
return query && query.length > 0
}
function isWhitelistedKey (key) {
return [13, 16, 20, 37, 38, 39, 40, 91].indexOf(key) === -1
}
function throwError (message) {
throw new Error('SimpleJekyllSearch --- ' + message)
}
})(window)
}());
</script>
<!-- Configuration -->
<script>
SimpleJekyllSearch({
searchInput: document.getElementById('search-input'),
resultsContainer: document.getElementById('results-container'),
json: '/search.json',
//searchResultTemplate: '<li><a href="https://static.rnmkcy.eu{url}">{date}&nbsp;{title}</a></li>'
searchResultTemplate: '<li><a href="{url}">{date}&nbsp;{title}</a></li>'
})
</script>
<!-- Fin déclaration champ de recherche --></div><nav class="navigation">
<ul><li class="navigation__item"><a href="/archive.html">Etiquettes</a></li><li class="navigation__item"><a href="/htmldoc.html">Documents</a></li><li class="navigation__item"><a href="/liens_ttrss.html">Liens</a></li><li class="navigation__item"><a href="/aide-jekyll-text-theme.html">Aide</a></li></ul>
</nav></div>
</header>
</div><div class="page__content"><div class ="main"><div class="grid grid--reverse">
<div class="col-main cell cell--auto"><!-- start custom main top snippet --><div id="results-container" class="search-result js-search-result"></div><!-- end custom main top snippet -->
<article itemscope itemtype="http://schema.org/Article"><div class="article__header"><header><h1 style="color:Tomato;">OpenPGP Yubikey 5</h1></header></div><meta itemprop="headline" content="OpenPGP Yubikey 5"><div class="article__info clearfix"><ul class="left-col menu"><li>
<a class="button button--secondary button--pill button--sm"
href="/archive.html?tag=chiffrement">chiffrement</a>
</li></ul><ul class="right-col menu"><li>
<i class="far fa-calendar-alt"></i>&nbsp;<span title="Création" style="color:#FF00FF">16&nbsp;avr.&nbsp;&nbsp;2024</span>
<span title="Modification" style="color:#00FF7F">17&nbsp;avr.&nbsp;&nbsp;2024</span></li></ul></div><meta itemprop="datePublished" content="2024-04-17T00:00:00+02:00">
<meta itemprop="keywords" content="chiffrement"><div class="js-article-content">
<div class="layout--article"><!-- start custom article top snippet -->
<style>
#myBtn {
display: none;
position: fixed;
bottom: 10px;
right: 10px;
z-index: 99;
font-size: 12px;
font-weight: bold;
border: none;
outline: none;
background-color: white;
color: black;
cursor: pointer;
padding: 5px;
border-radius: 4px;
}
#myBtn:hover {
background-color: #555;
}
</style>
<button onclick="topFunction()" id="myBtn" title="Haut de page">&#8679;</button>
<script>
//Get the button
var mybutton = document.getElementById("myBtn");
// When the user scrolls down 20px from the top of the document, show the button
window.onscroll = function() {scrollFunction()};
function scrollFunction() {
if (document.body.scrollTop > 20 || document.documentElement.scrollTop > 20) {
mybutton.style.display = "block";
} else {
mybutton.style.display = "none";
}
}
// When the user clicks on the button, scroll to the top of the document
function topFunction() {
document.body.scrollTop = 0;
document.documentElement.scrollTop = 0;
}
</script>
<!-- end custom article top snippet -->
<div class="article__content" itemprop="articleBody"><details>
<summary><b>Afficher/cacher Sommaire</b></summary>
<!-- affichage sommaire -->
<div class="toc-aside js-toc-root"></div>
</details><p><em>Comment configurer votre YubiKey avec OpenPGP après avoir généré la clé privée puis déplacer les sous-clés vers le YubiKey</em></p>
<p><img src="/images/gnupg-logo.png" alt="GnuPG" /> <img src="/images/yubikey5nfc.png" alt="Yubikey 5" height="150" /></p>
<ul>
<li><a href="#openpgp">OpenPGP</a>
<ul>
<li><a href="#clé-gnupg-principale">Clé GnuPG principale</a></li>
<li><a href="#création-des-sous-clés">Création des sous-clés</a></li>
<li><a href="#sauvegarde-restauration">Sauvegarde Restauration</a></li>
<li><a href="#exporter-clé-sur-serveur">Exporter clé sur serveur</a></li>
</ul>
</li>
<li><a href="#openpgp-yubikey-5">OpenPGP Yubikey 5</a>
<ul>
<li><a href="#prérequis-archlinux">Prérequis archlinux</a></li>
<li><a href="#retour-paramétrage-usine-de-pgp">Retour paramétrage usine de pgp</a></li>
<li><a href="#configuration-yubikey">Configuration yubikey</a></li>
<li><a href="#exporter-les-clefs-vers-la-yubikey">Exporter les clefs vers la Yubikey</a></li>
</ul>
</li>
</ul>
<h2 id="openpgp">OpenPGP</h2>
<ul>
<li><a href="https://blog.eleven-labs.com/fr/openpgp-paire-clef-presque-parfaite-partie-1/">OpenPGP - Une paire de clés presque parfaite</a></li>
</ul>
<p>Lors de la création dune clé OpenPGP dans son mode de base, gpg va créer une paire de clés qui permet de signer et de certifier. Pour augmenter la sécurité de notre clé, nous allons utiliser une particularité dOpenPGP : les sous-clés.<br />
OpenPGP permet de créer des sous-clés avec un usage spécifique : signer, chiffrer et authentifier. Un autre avantage à lutilisation des sous-clés est quen cas de perte ou vol des clés secrètes des sous-clés, il suffira de révoquer la sous-clé sans avoir à révoquer la clé principale (celle qui permet de certifier dautres clés).</p>
<p>Commençons par créer la clé principale, celle qui va détenir notre identité. Puis ensuite, créons des sous-clés pour signer, chiffrer et authentifier.</p>
<h3 id="clé-gnupg-principale">Clé GnuPG principale</h3>
<div class="item">
<div class="item__image">
<img class="image image--sm" src="/images/subkeys.png" />
</div>
<div class="item__content">
<div class="item__header">
<h4>Clé GPG</h4>
</div>
<div class="item__description">
<ul>
<li>C (Certifier) : Permet de certifier les clés publiques des autres utilisateurs.</li>
<li>S (Signer) : Permet de signer les messages électroniques.</li>
<li>A (Authenticate) : Permet dauthentifier lutilisateur.</li>
<li>E (Encrypt) : Permet de chiffrer les messages électroniques.</li>
</ul>
</div>
</div>
</div>
<p>Ces permissions peuvent être cumulées sur une même clé ou réparties sur plusieurs sous-clés. Ainsi, il est possible de créer une clé principale avec la permission C et des sous-clés avec les permissions S, E et A (pour séparer les usages).<br />
Une sous-clé (subkey) est une clé secondaire à une clé principale. Comme dit plus haut, celle-ci possède ses propres permissions et sa propre date dexpiration. La clé principale est la clé qui certifie ou révoque les sous-clés.</p>
<p>Générer la clé GPG</p>
<div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code>gpg --expert --full-gen-key
</code></pre></div></div>
<details>
<summary><b>Etendre Réduire</b></summary>
<figure class="highlight"><pre><code class="language-bash" data-lang="bash">
gpg <span class="o">(</span>GnuPG<span class="o">)</span> 2.2.40<span class="p">;</span> Copyright <span class="o">(</span>C<span class="o">)</span> 2022 g10 Code GmbH
This is free software: you are free to change and redistribute it.
There is NO WARRANTY, to the extent permitted by law.
Sélectionnez le <span class="nb">type </span>de clef désiré :
<span class="o">(</span>1<span class="o">)</span> RSA et RSA <span class="o">(</span>par défaut<span class="o">)</span>
<span class="o">(</span>2<span class="o">)</span> DSA et Elgamal
<span class="o">(</span>3<span class="o">)</span> DSA <span class="o">(</span>signature seule<span class="o">)</span>
<span class="o">(</span>4<span class="o">)</span> RSA <span class="o">(</span>signature seule<span class="o">)</span>
<span class="o">(</span>7<span class="o">)</span> DSA <span class="o">(</span>indiquez vous-même les capacités<span class="o">)</span>
<span class="o">(</span>8<span class="o">)</span> RSA <span class="o">(</span>indiquez vous-même les capacités<span class="o">)</span>
<span class="o">(</span>9<span class="o">)</span> ECC et ECC
<span class="o">(</span>10<span class="o">)</span> ECC <span class="o">(</span>signature seule<span class="o">)</span>
<span class="o">(</span>11<span class="o">)</span> ECC <span class="o">(</span>indiquez vous-même les capacités<span class="o">)</span>
<span class="o">(</span>13<span class="o">)</span> Clef existante
<span class="o">(</span>14<span class="o">)</span> Existing key from card
Quel est votre choix ? 11
Actions possibles pour une clef ECDSA/EdDSA : Signer Certifier Authentifier
Actions actuellement permises : Signer Certifier
<span class="o">(</span>S<span class="o">)</span> Inverser la capacité de signature
<span class="o">(</span>A<span class="o">)</span> Inverser la capacité d<span class="s1">'authentification
(Q) Terminé
Quel est votre choix ? s
Actions possibles pour une clef ECDSA/EdDSA : Signer Certifier Authentifier
Actions actuellement permises : Certifier
(S) Inverser la capacité de signature
(A) Inverser la capacité d'</span>authentification
<span class="o">(</span>Q<span class="o">)</span> Terminé
Quel est votre choix ? q
Sélectionnez le <span class="nb">type </span>de courbe elliptique désiré :
<span class="o">(</span>1<span class="o">)</span> Curve 25519
<span class="o">(</span>3<span class="o">)</span> NIST P-256
<span class="o">(</span>4<span class="o">)</span> NIST P-384
<span class="o">(</span>5<span class="o">)</span> NIST P-521
<span class="o">(</span>6<span class="o">)</span> Brainpool P-256
<span class="o">(</span>7<span class="o">)</span> Brainpool P-384
<span class="o">(</span>8<span class="o">)</span> Brainpool P-512
<span class="o">(</span>9<span class="o">)</span> secp256k1
Quel est votre choix ? 1
Veuillez indiquer le temps pendant lequel cette clef devrait être valable.
0 <span class="o">=</span> la clef n<span class="s1">'expire pas
&lt;n&gt; = la clef expire dans n jours
&lt;n&gt;w = la clef expire dans n semaines
&lt;n&gt;m = la clef expire dans n mois
&lt;n&gt;y = la clef expire dans n ans
Pendant combien de temps la clef est-elle valable ? (0) 10y
La clef expire le ven. 14 avril 2034 12:42:11 CEST
Est-ce correct ? (o/N) o
GnuPG doit construire une identité pour identifier la clef.
Nom réel : Yannick MEUNIER
Adresse électronique : yann@rnmkcy.eu
Commentaire :
Vous avez sélectionné cette identité :
« Yannick MEUNIER &lt;yann@rnmkcy.eu&gt; »
Changer le (N)om, le (C)ommentaire, l'</span><span class="o">(</span>A<span class="o">)</span>dresse électronique
ou <span class="o">(</span>O<span class="o">)</span>ui/<span class="o">(</span>Q<span class="o">)</span>uitter ? O
De nombreux octets aléatoires doivent être générés. Vous devriez faire
autre chose <span class="o">(</span>taper au clavier, déplacer la souris, utiliser les disques<span class="o">)</span>
pendant la génération de nombres premiers <span class="p">;</span> cela donne au générateur de
nombres aléatoires une meilleure chance d<span class="s1">'obtenir suffisamment d'</span>entropie.</code></pre></figure>
</details>
<p>Résultat</p>
<div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code>gpg: /home/leno/.gnupg/trustdb.gpg : base de confiance créée
gpg: répertoire « /home/leno/.gnupg/openpgp-revocs.d » créé
gpg: revocation certificate stored as '/home/leno/.gnupg/openpgp-revocs.d/9CA99CA33CFE67290528AFF456F69B4479ED2CFD.rev'
les clefs publique et secrète ont été créées et signées.
pub ed25519 2024-04-16 [C] [expire : 2034-04-14]
9CA99CA33CFE67290528AFF456F69B4479ED2CFD
uid Yannick MEUNIER &lt;yann@rnmkcy.eu&gt;
</code></pre></div></div>
<h3 id="création-des-sous-clés">Création des sous-clés</h3>
<p>il est important den avoir une dédiée à chaque tâche :</p>
<ul>
<li>Authentification (A)</li>
<li>Signature (S)</li>
<li>Chiffrement (E)</li>
</ul>
<p>Créer des clés secondaires</p>
<div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code>gpg --edit-key --expert yann@rnmkcy.eu
</code></pre></div></div>
<div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code>gpg (GnuPG) 2.2.40; Copyright (C) 2022 g10 Code GmbH
This is free software: you are free to change and redistribute it.
There is NO WARRANTY, to the extent permitted by law.
La clef secrète est disponible.
sec ed25519/56F69B4479ED2CFD
créé : 2024-04-16 expire : 2034-04-14 utilisation : C
confiance : ultime validité : ultime
[ ultime ] (1). Yannick MEUNIER &lt;yann@rnmkcy.eu&gt;
</code></pre></div></div>
<p>La commande addkey sera exécutée 3 fois</p>
<details>
<summary><b>Etendre Réduire</b></summary>
<figure class="highlight"><pre><code class="language-bash" data-lang="bash">
gpg&gt; addkey
Sélectionnez le <span class="nb">type </span>de clef désiré :
<span class="o">(</span>3<span class="o">)</span> DSA <span class="o">(</span>signature seule<span class="o">)</span>
<span class="o">(</span>4<span class="o">)</span> RSA <span class="o">(</span>signature seule<span class="o">)</span>
<span class="o">(</span>5<span class="o">)</span> Elgamal <span class="o">(</span>chiffrement seul<span class="o">)</span>
<span class="o">(</span>6<span class="o">)</span> RSA <span class="o">(</span>chiffrement seul<span class="o">)</span>
<span class="o">(</span>7<span class="o">)</span> DSA <span class="o">(</span>indiquez vous-même les capacités<span class="o">)</span>
<span class="o">(</span>8<span class="o">)</span> RSA <span class="o">(</span>indiquez vous-même les capacités<span class="o">)</span>
<span class="o">(</span>10<span class="o">)</span> ECC <span class="o">(</span>signature seule<span class="o">)</span>
<span class="o">(</span>11<span class="o">)</span> ECC <span class="o">(</span>indiquez vous-même les capacités<span class="o">)</span>
<span class="o">(</span>12<span class="o">)</span> ECC <span class="o">(</span>chiffrement seul<span class="o">)</span>
<span class="o">(</span>13<span class="o">)</span> Clef existante
<span class="o">(</span>14<span class="o">)</span> Existing key from card
Quel est votre choix ? 11
Actions possibles pour une clef ECDSA/EdDSA : Signer Authentifier
Actions actuellement permises : Signer
<span class="o">(</span>S<span class="o">)</span> Inverser la capacité de signature
<span class="o">(</span>A<span class="o">)</span> Inverser la capacité d<span class="s1">'authentification
(Q) Terminé
Quel est votre choix ? Q
Sélectionnez le type de courbe elliptique désiré :
(1) Curve 25519
(3) NIST P-256
(4) NIST P-384
(5) NIST P-521
(6) Brainpool P-256
(7) Brainpool P-384
(8) Brainpool P-512
(9) secp256k1
Quel est votre choix ? 1
Veuillez indiquer le temps pendant lequel cette clef devrait être valable.
0 = la clef n'</span>expire pas
&lt;n&gt; <span class="o">=</span> la clef expire dans n jours
&lt;n&gt;w <span class="o">=</span> la clef expire dans n semaines
&lt;n&gt;m <span class="o">=</span> la clef expire dans n mois
&lt;n&gt;y <span class="o">=</span> la clef expire dans n ans
Pendant combien de temps la clef est-elle valable ? <span class="o">(</span>0<span class="o">)</span> 5y
La clef expire le dim. 15 avril 2029 13:44:51 CEST
Est-ce correct ? <span class="o">(</span>o/N<span class="o">)</span> o
Faut-il vraiment la créer ? <span class="o">(</span>o/N<span class="o">)</span> o
De nombreux octets aléatoires doivent être générés. Vous devriez faire
autre chose <span class="o">(</span>taper au clavier, déplacer la souris, utiliser les disques<span class="o">)</span>
pendant la génération de nombres premiers <span class="p">;</span> cela donne au générateur de
nombres aléatoires une meilleure chance d<span class="s1">'obtenir suffisamment d'</span>entropie.
sec ed25519/56F69B4479ED2CFD
créé : 2024-04-16 expire : 2034-04-14 utilisation : C
confiance : ultime validité : ultime
ssb ed25519/1654EE1DCB6A4B92
créé : 2024-04-16 expire : 2029-04-15 utilisation : S
<span class="o">[</span> ultime <span class="o">]</span> <span class="o">(</span>1<span class="o">)</span><span class="nb">.</span> Yannick MEUNIER &lt;yann@rnmkcy.eu&gt;
gpg&gt; addkey
Sélectionnez le <span class="nb">type </span>de clef désiré :
<span class="o">(</span>3<span class="o">)</span> DSA <span class="o">(</span>signature seule<span class="o">)</span>
<span class="o">(</span>4<span class="o">)</span> RSA <span class="o">(</span>signature seule<span class="o">)</span>
<span class="o">(</span>5<span class="o">)</span> Elgamal <span class="o">(</span>chiffrement seul<span class="o">)</span>
<span class="o">(</span>6<span class="o">)</span> RSA <span class="o">(</span>chiffrement seul<span class="o">)</span>
<span class="o">(</span>7<span class="o">)</span> DSA <span class="o">(</span>indiquez vous-même les capacités<span class="o">)</span>
<span class="o">(</span>8<span class="o">)</span> RSA <span class="o">(</span>indiquez vous-même les capacités<span class="o">)</span>
<span class="o">(</span>10<span class="o">)</span> ECC <span class="o">(</span>signature seule<span class="o">)</span>
<span class="o">(</span>11<span class="o">)</span> ECC <span class="o">(</span>indiquez vous-même les capacités<span class="o">)</span>
<span class="o">(</span>12<span class="o">)</span> ECC <span class="o">(</span>chiffrement seul<span class="o">)</span>
<span class="o">(</span>13<span class="o">)</span> Clef existante
<span class="o">(</span>14<span class="o">)</span> Existing key from card
Quel est votre choix ? 11
Actions possibles pour une clef ECDSA/EdDSA : Signer Authentifier
Actions actuellement permises : Signer
<span class="o">(</span>S<span class="o">)</span> Inverser la capacité de signature
<span class="o">(</span>A<span class="o">)</span> Inverser la capacité d<span class="s1">'authentification
(Q) Terminé
Quel est votre choix ? A
Actions possibles pour une clef ECDSA/EdDSA : Signer Authentifier
Actions actuellement permises : Signer Authentifier
(S) Inverser la capacité de signature
(A) Inverser la capacité d'</span>authentification
<span class="o">(</span>Q<span class="o">)</span> Terminé
Quel est votre choix ? S
Actions possibles pour une clef ECDSA/EdDSA : Signer Authentifier
Actions actuellement permises : Authentifier
<span class="o">(</span>S<span class="o">)</span> Inverser la capacité de signature
<span class="o">(</span>A<span class="o">)</span> Inverser la capacité d<span class="s1">'authentification
(Q) Terminé
Quel est votre choix ? Q
Sélectionnez le type de courbe elliptique désiré :
(1) Curve 25519
(3) NIST P-256
(4) NIST P-384
(5) NIST P-521
(6) Brainpool P-256
(7) Brainpool P-384
(8) Brainpool P-512
(9) secp256k1
Quel est votre choix ? 1
Veuillez indiquer le temps pendant lequel cette clef devrait être valable.
0 = la clef n'</span>expire pas
&lt;n&gt; <span class="o">=</span> la clef expire dans n jours
&lt;n&gt;w <span class="o">=</span> la clef expire dans n semaines
&lt;n&gt;m <span class="o">=</span> la clef expire dans n mois
&lt;n&gt;y <span class="o">=</span> la clef expire dans n ans
Pendant combien de temps la clef est-elle valable ? <span class="o">(</span>0<span class="o">)</span> 5y
La clef expire le dim. 15 avril 2029 13:47:50 CEST
Est-ce correct ? <span class="o">(</span>o/N<span class="o">)</span> o
Faut-il vraiment la créer ? <span class="o">(</span>o/N<span class="o">)</span> o
De nombreux octets aléatoires doivent être générés. Vous devriez faire
autre chose <span class="o">(</span>taper au clavier, déplacer la souris, utiliser les disques<span class="o">)</span>
pendant la génération de nombres premiers <span class="p">;</span> cela donne au générateur de
nombres aléatoires une meilleure chance d<span class="s1">'obtenir suffisamment d'</span>entropie.
sec ed25519/56F69B4479ED2CFD
créé : 2024-04-16 expire : 2034-04-14 utilisation : C
confiance : ultime validité : ultime
ssb ed25519/1654EE1DCB6A4B92
créé : 2024-04-16 expire : 2029-04-15 utilisation : S
ssb ed25519/61386614B5A2EB55
créé : 2024-04-16 expire : 2029-04-15 utilisation : A
<span class="o">[</span> ultime <span class="o">]</span> <span class="o">(</span>1<span class="o">)</span><span class="nb">.</span> Yannick MEUNIER &lt;yann@rnmkcy.eu&gt;
gpg&gt; addkey
Sélectionnez le <span class="nb">type </span>de clef désiré :
<span class="o">(</span>3<span class="o">)</span> DSA <span class="o">(</span>signature seule<span class="o">)</span>
<span class="o">(</span>4<span class="o">)</span> RSA <span class="o">(</span>signature seule<span class="o">)</span>
<span class="o">(</span>5<span class="o">)</span> Elgamal <span class="o">(</span>chiffrement seul<span class="o">)</span>
<span class="o">(</span>6<span class="o">)</span> RSA <span class="o">(</span>chiffrement seul<span class="o">)</span>
<span class="o">(</span>7<span class="o">)</span> DSA <span class="o">(</span>indiquez vous-même les capacités<span class="o">)</span>
<span class="o">(</span>8<span class="o">)</span> RSA <span class="o">(</span>indiquez vous-même les capacités<span class="o">)</span>
<span class="o">(</span>10<span class="o">)</span> ECC <span class="o">(</span>signature seule<span class="o">)</span>
<span class="o">(</span>11<span class="o">)</span> ECC <span class="o">(</span>indiquez vous-même les capacités<span class="o">)</span>
<span class="o">(</span>12<span class="o">)</span> ECC <span class="o">(</span>chiffrement seul<span class="o">)</span>
<span class="o">(</span>13<span class="o">)</span> Clef existante
<span class="o">(</span>14<span class="o">)</span> Existing key from card
Quel est votre choix ? 12
Sélectionnez le <span class="nb">type </span>de courbe elliptique désiré :
<span class="o">(</span>1<span class="o">)</span> Curve 25519
<span class="o">(</span>3<span class="o">)</span> NIST P-256
<span class="o">(</span>4<span class="o">)</span> NIST P-384
<span class="o">(</span>5<span class="o">)</span> NIST P-521
<span class="o">(</span>6<span class="o">)</span> Brainpool P-256
<span class="o">(</span>7<span class="o">)</span> Brainpool P-384
<span class="o">(</span>8<span class="o">)</span> Brainpool P-512
<span class="o">(</span>9<span class="o">)</span> secp256k1
Quel est votre choix ? 1
Veuillez indiquer le temps pendant lequel cette clef devrait être valable.
0 <span class="o">=</span> la clef n<span class="s1">'expire pas
&lt;n&gt; = la clef expire dans n jours
&lt;n&gt;w = la clef expire dans n semaines
&lt;n&gt;m = la clef expire dans n mois
&lt;n&gt;y = la clef expire dans n ans
Pendant combien de temps la clef est-elle valable ? (0) 5y
La clef expire le dim. 15 avril 2029 13:49:58 CEST
Est-ce correct ? (o/N) o
Faut-il vraiment la créer ? (o/N) o
De nombreux octets aléatoires doivent être générés. Vous devriez faire
autre chose (taper au clavier, déplacer la souris, utiliser les disques)
pendant la génération de nombres premiers ; cela donne au générateur de
nombres aléatoires une meilleure chance d'</span>obtenir suffisamment d<span class="s1">'entropie.
sec ed25519/56F69B4479ED2CFD
créé : 2024-04-16 expire : 2034-04-14 utilisation : C
confiance : ultime validité : ultime
ssb ed25519/1654EE1DCB6A4B92
créé : 2024-04-16 expire : 2029-04-15 utilisation : S
ssb ed25519/61386614B5A2EB55
créé : 2024-04-16 expire : 2029-04-15 utilisation : A
ssb cv25519/F2C20511596953B4
créé : 2024-04-16 expire : 2029-04-15 utilisation : E
[ ultime ] (1). Yannick MEUNIER &lt;yann@rnmkcy.eu&gt;
gpg&gt; save</span></code></pre></figure>
</details>
<p>Accorder la confiance</p>
<div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code>gpg --edit-key yann@rnmkcy.eu
</code></pre></div></div>
<details>
<summary><b>Etendre Réduire</b></summary>
<figure class="highlight"><pre><code class="language-bash" data-lang="bash">
gpg <span class="o">(</span>GnuPG<span class="o">)</span> 2.2.40<span class="p">;</span> Copyright <span class="o">(</span>C<span class="o">)</span> 2022 g10 Code GmbH
This is free software: you are free to change and redistribute it.
There is NO WARRANTY, to the extent permitted by law.
La clef secrète est disponible.
sec ed25519/56F69B4479ED2CFD
créé : 2024-04-16 expire : 2034-04-14 utilisation : C
confiance : inconnu validité : inconnu
ssb ed25519/1654EE1DCB6A4B92
créé : 2024-04-16 expire : 2029-04-15 utilisation : S
ssb ed25519/61386614B5A2EB55
créé : 2024-04-16 expire : 2029-04-15 utilisation : A
ssb cv25519/F2C20511596953B4
créé : 2024-04-16 expire : 2029-04-15 utilisation : E
<span class="o">[</span> inconnue] <span class="o">(</span>1<span class="o">)</span><span class="nb">.</span> Yannick MEUNIER &lt;yann@rnmkcy.eu&gt;
gpg&gt; trust
sec ed25519/56F69B4479ED2CFD
créé : 2024-04-16 expire : 2034-04-14 utilisation : C
confiance : inconnu validité : inconnu
ssb ed25519/1654EE1DCB6A4B92
créé : 2024-04-16 expire : 2029-04-15 utilisation : S
ssb ed25519/61386614B5A2EB55
créé : 2024-04-16 expire : 2029-04-15 utilisation : A
ssb cv25519/F2C20511596953B4
créé : 2024-04-16 expire : 2029-04-15 utilisation : E
<span class="o">[</span> inconnue] <span class="o">(</span>1<span class="o">)</span><span class="nb">.</span> Yannick MEUNIER &lt;yann@rnmkcy.eu&gt;
Décidez maintenant de la confiance que vous portez en cet utilisateur pour
vérifier les clefs des autres utilisateurs <span class="o">(</span>en regardant les passeports, en
vérifiant les empreintes depuis diverses sources, etc.<span class="o">)</span>
1 <span class="o">=</span> je ne sais pas ou n<span class="s1">'ai pas d'</span>avis
2 <span class="o">=</span> je ne fais PAS confiance
3 <span class="o">=</span> je fais très légèrement confiance
4 <span class="o">=</span> je fais entièrement confiance
5 <span class="o">=</span> j<span class="s1">'attribue une confiance ultime
m = retour au menu principal
Quelle est votre décision ? 5
Voulez-vous vraiment attribuer une confiance ultime à cette clef ? (o/N) o
sec ed25519/56F69B4479ED2CFD
créé : 2024-04-16 expire : 2034-04-14 utilisation : C
confiance : ultime validité : inconnu
ssb ed25519/1654EE1DCB6A4B92
créé : 2024-04-16 expire : 2029-04-15 utilisation : S
ssb ed25519/61386614B5A2EB55
créé : 2024-04-16 expire : 2029-04-15 utilisation : A
ssb cv25519/F2C20511596953B4
créé : 2024-04-16 expire : 2029-04-15 utilisation : E
[ inconnue] (1). Yannick MEUNIER &lt;yann@rnmkcy.eu&gt;
Veuillez remarquer que la validité affichée pour la clef n'</span>est pas
forcément correcte avant d<span class="s1">'avoir relancé le programme.
gpg&gt; quit</span></code></pre></figure>
</details>
<h3 id="sauvegarde-restauration">Sauvegarde Restauration</h3>
<p>Sauvegarder les clés GnuPG dans un dossier <code class="language-plaintext highlighter-rouge">/sharenfs/rnmkcy/.gnupg/</code></p>
<div class="language-bash highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="c"># Clé publique</span>
gpg <span class="nt">--export</span> <span class="nt">--armor</span> yann@rnmkcy.eu <span class="o">&gt;</span> /sharenfs/rnmkcy/.gnupg/yann_rnmkcy_eu_public.key
<span class="c"># Clé privée principale et les clés privées des subkeys</span>
<span class="c"># Il faut saisir la passphrase</span>
gpg <span class="nt">--export-secret-keys</span> <span class="nt">--armor</span> yann@rnmkcy.eu <span class="o">&gt;</span> /sharenfs/rnmkcy/.gnupg/yann_rnmkcy_eu_private.key
gpg <span class="nt">--export-secret-subkeys</span> <span class="nt">--armor</span> yann@rnmkcy.eu <span class="o">&gt;</span> /sharenfs/rnmkcy/.gnupg/yann_rnmkcy_eu_private-subkeys.key
</code></pre></div></div>
<p>Pour vérifier la sauvegarde, on va supprimer les clés du trousseau</p>
<div class="language-bash highlighter-rouge"><div class="highlight"><pre class="highlight"><code>gpg <span class="nt">--delete-secret-keys</span> yann@rnmkcy.eu
gpg <span class="nt">--delete-keys</span> yann@rnmkcy.eu
</code></pre></div></div>
<p>Les commandes <code class="language-plaintext highlighter-rouge">gpg --list-keys</code> et <code class="language-plaintext highlighter-rouge">gpg --list-secret-keys</code> ne devraient plus rien retourner.</p>
<p>Réimporter les clés sauvegardées</p>
<div class="language-bash highlighter-rouge"><div class="highlight"><pre class="highlight"><code>gpg <span class="nt">--import</span> /sharenfs/rnmkcy/.gnupg/yann_rnmkcy_eu_private.key
</code></pre></div></div>
<p>Résultat</p>
<div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code>gpg: clef 56F69B4479ED2CFD : clef publique « Yannick MEUNIER &lt;yann@rnmkcy.eu&gt; » importée
gpg: clef 56F69B4479ED2CFD : clef secrète importée
gpg: Quantité totale traitée : 1
gpg: importées : 1
gpg: clefs secrètes lues : 1
gpg: clefs secrètes importées : 1
</code></pre></div></div>
<p>Liste des clés</p>
<div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code>leno@rnmkcy:~$ gpg --list-keys
gpg: vérification de la base de confiance
gpg: marginals needed: 3 completes needed: 1 trust model: pgp
gpg: profondeur : 0 valables : 1 signées : 0
confiance : 0 i., 0 n.d., 0 j., 0 m., 0 t., 1 u.
gpg: la prochaine vérification de la base de confiance aura lieu le 2034-04-14
/home/leno/.gnupg/pubring.kbx
-----------------------------
pub ed25519 2024-04-16 [C] [expire : 2034-04-14]
9CA99CA33CFE67290528AFF456F69B4479ED2CFD
uid [ ultime ] Yannick MEUNIER &lt;yann@rnmkcy.eu&gt;
sub ed25519 2024-04-16 [S] [expire : 2029-04-15]
sub ed25519 2024-04-16 [A] [expire : 2029-04-15]
sub cv25519 2024-04-16 [E] [expire : 2029-04-15]
leno@rnmkcy:~$ gpg --list-secret-keys
/home/leno/.gnupg/pubring.kbx
-----------------------------
sec ed25519 2024-04-16 [C] [expire : 2034-04-14]
9CA99CA33CFE67290528AFF456F69B4479ED2CFD
uid [ ultime ] Yannick MEUNIER &lt;yann@rnmkcy.eu&gt;
ssb ed25519 2024-04-16 [S] [expire : 2029-04-15]
ssb ed25519 2024-04-16 [A] [expire : 2029-04-15]
ssb cv25519 2024-04-16 [E] [expire : 2029-04-15]
</code></pre></div></div>
<h3 id="exporter-clé-sur-serveur">Exporter clé sur serveur</h3>
<p>Linterface Web de keys.openpgp.org vous permet de :</p>
<ul>
<li><a href="https://keys.openpgp.org/">Chercher</a> des clés manuellement, par empreinte ou par adresse courriel.</li>
<li><a href="https://keys.openpgp.org/upload">Téléverser</a> des clés manuellement et de les confirmer une fois téléversées.</li>
<li><a href="https://keys.openpgp.org/manage">Gérer</a> vos clés et supprimer les identités publiées.</li>
</ul>
<p><img src="/images/pgp00.png" alt="" /><br />
<img src="/images/pgp01.png" alt="" /><br />
<img src="/images/pgp02.png" alt="" /><br />
<img src="/images/pgp03.png" alt="" /><br />
<img src="/images/pgp04.png" alt="" /></p>
<h2 id="openpgp-yubikey-5">OpenPGP Yubikey 5</h2>
<ul>
<li><a href="https://ludovicrousseau.blogspot.com/2021/08/pcsc-lite-configuration-using.html">pcsc-lite: configuration using /etc/default/pcscd</a></li>
<li><a href="https://support.yubico.com/hc/en-us/articles/360013757959-Resetting-Your-YubiKey-5-Series-to-Factory-Defaults">Resetting Your YubiKey 5 Series to Factory Defaults</a></li>
<li><a href="https://blog.eleven-labs.com/fr/openpgp-clef-secrete-yubikey-partie-2/">OpenPGP - Exporter les clefs secrètes sur une Yubikey</a></li>
</ul>
<h3 id="prérequis-archlinux">Prérequis archlinux</h3>
<p>Installez ccid et opensc à partir des référentiels officiels</p>
<div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code>sudo pacman -S ccid opensc
</code></pre></div></div>
<p>Créer un fichier</p>
<div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code>echo "disable-ccid" &gt; ~/.gnupg/scdaemon.conf
</code></pre></div></div>
<p>Activer et démarrer le service pcscd.service</p>
<div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code>sudo systemctl enable pcscd.service --now
</code></pre></div></div>
<h3 id="retour-paramétrage-usine-de-pgp">Retour paramétrage usine de pgp</h3>
<p>Retour paramétrage usine de la partie pgp, installer: <code class="language-plaintext highlighter-rouge">yay -S yubikey-manager</code><br />
Exécuter : <code class="language-plaintext highlighter-rouge">ykman openpgp reset</code></p>
<div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code>WARNING! This will delete all stored OpenPGP keys and data and restore factory settings. Proceed? [y/N]: y
Resetting OpenPGP data, don't remove the YubiKey...
Success! All data has been cleared and default PINs are set.
PIN: 123456
Reset code: NOT SET
Admin PIN: 12345678
</code></pre></div></div>
<p>Satus <strong>Yubikeys 5</strong></p>
<div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code>gpg --card-status
</code></pre></div></div>
<p>Etat</p>
<div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code>Reader ...........: 1050:0407:X:0
Application ID ...: D2760001240100000006245545860000
Application type .: OpenPGP
Version ..........: 3.4
Manufacturer .....: Yubico
Serial number ....: 24554586
Name of cardholder: [non positionné]
Language prefs ...: [non positionné]
Salutation .......:
URL of public key : [non positionné]
Login data .......: [non positionné]
Signature PIN ....: non forcé
Key attributes ...: rsa2048 rsa2048 rsa2048
Max. PIN lengths .: 127 127 127
PIN retry counter : 3 0 3
Signature counter : 0
KDF setting ......: off
UIF setting ......: Sign=off Decrypt=off Auth=off
Signature key ....: [none]
Encryption key....: [none]
Authentication key: [none]
General key info..: [none]
</code></pre></div></div>
<p>La carte est vierge, il ny aucune information personnelle. Il est recommandé de compléter les informations au cas où une personne retrouve cette clef.</p>
<h3 id="configuration-yubikey">Configuration yubikey</h3>
<p>Éditons la carte et passons en mode admin. Vous pouvez entrer help pour avoir la liste des commandes disponibles.</p>
<div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code>gpg --card-edit
gpg/carte&gt; admin
Les commandes d'administration sont permises
</code></pre></div></div>
<p>Nous allons tout dabord changer le code PIN dadministration de la clef et le code PIN utilisateur. Par défaut, le code PIN de ladministrateur est 12345678 et 123456 pour le code PIN utilisateur.</p>
<p>Le PIN administrateur est requis pour quelques opérations sur la carte (lexport de clef par exemple), et pour débloquer quand un code PIN a été entré 3 fois par erreur.</p>
<p>Entrer passwd pour les changer. Commençons par le PIN administrateur et ensuite le PIN utilisateur.</p>
<div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code>gpg/carte&gt; passwd
gpg: carte OpenPGP nº D2760001240100000006245545860000 détectée
1 - change PIN
2 - unblock PIN
3 - change Admin PIN
4 - set the Reset Code
Q - quit
Quel est votre choix ? 3
PIN changed.
1 - change PIN
2 - unblock PIN
3 - change Admin PIN
4 - set the Reset Code
Q - quit
Quel est votre choix ? 1
PIN changed.
1 - change PIN
2 - unblock PIN
3 - change Admin PIN
4 - set the Reset Code
Q - quit
Quel est votre choix ? q
gpg/carte&gt;
</code></pre></div></div>
<p>Ensuite, entrer les informations pour personnaliser votre clef :</p>
<div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code>gpg/carte&gt; name
Nom du détenteur de la carte : MEUNIER
Prénom du détenteur de la carte : Yannick
gpg/carte&gt; url
URL pour récupérer la clef publique : https://keys.openpgp.org/search?q=yann@rnmkcy.eu
gpg/carte&gt; quit
</code></pre></div></div>
<p>La clef est a présent configurée. Nous pouvons exporter les clefs privées des sous-clefs dans la carte à puce.</p>
<h3 id="exporter-les-clefs-vers-la-yubikey">Exporter les clefs vers la Yubikey</h3>
<p>Lobjectif est de déplacer les clefs secrètes des sous-clefs dans la Yubikey. Pour cela, nous allons séléctionner chaque sous-clef une par une avec la commande key n et la déplacer dans la carte avec keytocard. À la fin, il ny aura plus aucun secret dans le trousseau de clef gpg.</p>
<p>Éditons la clef.</p>
<div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code>gpg --expert --edit-key yann@rnmkcy.eu
</code></pre></div></div>
<div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code>La clef secrète est disponible.
gpg: vérification de la base de confiance
gpg: marginals needed: 3 completes needed: 1 trust model: pgp
gpg: profondeur : 0 valables : 1 signées : 0
confiance : 0 i., 0 n.d., 0 j., 0 m., 0 t., 1 u.
gpg: la prochaine vérification de la base de confiance aura lieu le 2034-04-14
sec ed25519/56F69B4479ED2CFD
créé : 2024-04-16 expire : 2034-04-14 utilisation : C
confiance : ultime validité : ultime
ssb ed25519/1654EE1DCB6A4B92
créé : 2024-04-16 expire : 2029-04-15 utilisation : S
ssb ed25519/61386614B5A2EB55
créé : 2024-04-16 expire : 2029-04-15 utilisation : A
ssb cv25519/F2C20511596953B4
créé : 2024-04-16 expire : 2029-04-15 utilisation : E
[ ultime ] (1). Yannick MEUNIER &lt;yann@rnmkcy.eu&gt;
gpg&gt;
</code></pre></div></div>
<p>Ensuite, nous sélectionnons les clés une à une pour les importer sur la Yubikey.</p>
<details>
<summary><b>Etendre Réduire</b></summary>
<figure class="highlight"><pre><code class="language-text" data-lang="text">
gpg&gt; key 1
sec ed25519/56F69B4479ED2CFD
créé : 2024-04-16 expire : 2034-04-14 utilisation : C
confiance : ultime validité : ultime
ssb* ed25519/1654EE1DCB6A4B92
créé : 2024-04-16 expire : 2029-04-15 utilisation : S
ssb ed25519/61386614B5A2EB55
créé : 2024-04-16 expire : 2029-04-15 utilisation : A
ssb cv25519/F2C20511596953B4
créé : 2024-04-16 expire : 2029-04-15 utilisation : E
[ ultime ] (1). Yannick MEUNIER &lt;yann@rnmkcy.eu&gt;
---
Le petit astérisque devant l'empreinte de la clef indique qu'elle est sélectionnée.
Entrer keytocard pour l'exporter vers la Yubikey.
---
gpg&gt; keytocard
Veuillez sélectionner l'endroit où stocker la clef :
(1) Clef de signature
(3) Clef d'authentification
Quel est votre choix ? 1
sec ed25519/56F69B4479ED2CFD
créé : 2024-04-16 expire : 2034-04-14 utilisation : C
confiance : ultime validité : ultime
ssb* ed25519/1654EE1DCB6A4B92
créé : 2024-04-16 expire : 2029-04-15 utilisation : S
ssb ed25519/61386614B5A2EB55
créé : 2024-04-16 expire : 2029-04-15 utilisation : A
ssb cv25519/F2C20511596953B4
créé : 2024-04-16 expire : 2029-04-15 utilisation : E
[ ultime ] (1). Yannick MEUNIER &lt;yann@rnmkcy.eu&gt;
Note: the local copy of the secret key will only be deleted with "save".
---
gpg va vous demander le mot de passe de la clef secrète de chiffrement et ensuite le code pin d'administration de la clef Yubikey. Une fois le code pin d'administration de la Yubikey entré, la clef secrète de chiffrement est bien dans la Yubikey. Nous pourrons le vérifier juste après avoir déplacé les deux autres clef.
Séléctionnons la clef de signature. Il faut désélectionner la première clef et séléctionner la seconde.
---
gpg&gt; key 1
gpg&gt; key 2
sec ed25519/56F69B4479ED2CFD
créé : 2024-04-16 expire : 2034-04-14 utilisation : C
confiance : ultime validité : ultime
ssb ed25519/1654EE1DCB6A4B92
créé : 2024-04-16 expire : 2029-04-15 utilisation : S
ssb* ed25519/61386614B5A2EB55
créé : 2024-04-16 expire : 2029-04-15 utilisation : A
ssb cv25519/F2C20511596953B4
créé : 2024-04-16 expire : 2029-04-15 utilisation : E
[ ultime ] (1). Yannick MEUNIER &lt;yann@rnmkcy.eu&gt;
gpg&gt; keytocard
Veuillez sélectionner l'endroit où stocker la clef :
(3) Clef d'authentification
Quel est votre choix ? 3
sec ed25519/56F69B4479ED2CFD
créé : 2024-04-16 expire : 2034-04-14 utilisation : C
confiance : ultime validité : ultime
ssb ed25519/1654EE1DCB6A4B92
créé : 2024-04-16 expire : 2029-04-15 utilisation : S
ssb* ed25519/61386614B5A2EB55
créé : 2024-04-16 expire : 2029-04-15 utilisation : A
ssb cv25519/F2C20511596953B4
créé : 2024-04-16 expire : 2029-04-15 utilisation : E
[ ultime ] (1). Yannick MEUNIER &lt;yann@rnmkcy.eu&gt;
Note: the local copy of the secret key will only be deleted with "save".
gpg&gt; key 2
gpg&gt; key 3
sec ed25519/56F69B4479ED2CFD
créé : 2024-04-16 expire : 2034-04-14 utilisation : C
confiance : ultime validité : ultime
ssb ed25519/1654EE1DCB6A4B92
créé : 2024-04-16 expire : 2029-04-15 utilisation : S
ssb ed25519/61386614B5A2EB55
créé : 2024-04-16 expire : 2029-04-15 utilisation : A
ssb* cv25519/F2C20511596953B4
créé : 2024-04-16 expire : 2029-04-15 utilisation : E
[ ultime ] (1). Yannick MEUNIER &lt;yann@rnmkcy.eu&gt;
gpg&gt; keytocard
Veuillez sélectionner l'endroit où stocker la clef :
(2) Clef de chiffrement
Quel est votre choix ? 2
sec ed25519/56F69B4479ED2CFD
créé : 2024-04-16 expire : 2034-04-14 utilisation : C
confiance : ultime validité : ultime
ssb ed25519/1654EE1DCB6A4B92
créé : 2024-04-16 expire : 2029-04-15 utilisation : S
ssb ed25519/61386614B5A2EB55
créé : 2024-04-16 expire : 2029-04-15 utilisation : A
ssb* cv25519/F2C20511596953B4
créé : 2024-04-16 expire : 2029-04-15 utilisation : E
[ ultime ] (1). Yannick MEUNIER &lt;yann@rnmkcy.eu&gt;
Note: the local copy of the secret key will only be deleted with "save".
---
Nous avons terminé. Taper save
---
gpg&gt; save</code></pre></figure>
</details>
<p>Vérifions que nous navons plus aucune clef secrète dans notre trousseau de clefs gpg</p>
<div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code>gpg --list-secret-keys
</code></pre></div></div>
<div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code>/home/yann/.gnupg/pubring.kbx
-----------------------------
sec ed25519 2024-04-16 [C] [expire : 2034-04-14]
9CA99CA33CFE67290528AFF456F69B4479ED2CFD
uid [ ultime ] Yannick MEUNIER &lt;yann@rnmkcy.eu&gt;
ssb&gt; ed25519 2024-04-16 [S] [expire : 2029-04-15]
ssb&gt; ed25519 2024-04-16 [A] [expire : 2029-04-15]
ssb&gt; cv25519 2024-04-16 [E] [expire : 2029-04-15]
</code></pre></div></div>
<p>Le chevron <code class="language-plaintext highlighter-rouge">&gt;</code> devant <code class="language-plaintext highlighter-rouge">ssb</code> indique que la clef secrète nexiste pas pour cette clef.</p>
<p>Vérifions que ces clefs secrètes sont bien dans la Yubikey.</p>
<div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code>gpg --card-status
</code></pre></div></div>
<div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code>Reader ...........: 1050:0407:X:0
Application ID ...: D2760001240100000006245545860000
Application type .: OpenPGP
Version ..........: 3.4
Manufacturer .....: Yubico
Serial number ....: 24554586
Name of cardholder: Yannick MEUNIER
Language prefs ...: [non positionné]
Salutation .......:
URL of public key : https://keys.openpgp.org/search?q=yann@rnmkcy.eu
Login data .......: [non positionné]
Signature PIN ....: non forcé
Key attributes ...: ed25519 cv25519 ed25519
Max. PIN lengths .: 127 127 127
PIN retry counter : 3 0 3
Signature counter : 0
KDF setting ......: off
UIF setting ......: Sign=off Decrypt=off Auth=off
Signature key ....: AD71 F453 30D5 2031 4CD5 A984 1654 EE1D CB6A 4B92
created ....: 2024-04-16 11:44:25
Encryption key....: 4CB4 96B1 B13F DC8F DEC6 38B0 F2C2 0511 5969 53B4
created ....: 2024-04-16 11:49:31
Authentication key: FB14 DDA4 0D0F EDDB A1BF B1A9 6138 6614 B5A2 EB55
created ....: 2024-04-16 11:46:53
General key info..: sub ed25519/1654EE1DCB6A4B92 2024-04-16 Yannick MEUNIER &lt;yann@rnmkcy.eu&gt;
sec ed25519/56F69B4479ED2CFD créé : 2024-04-16 expire : 2034-04-14
ssb&gt; ed25519/1654EE1DCB6A4B92 créé : 2024-04-16 expire : 2029-04-15
nº de carte : 0006 24554586
ssb&gt; ed25519/61386614B5A2EB55 créé : 2024-04-16 expire : 2029-04-15
nº de carte : 0006 24554586
ssb&gt; cv25519/F2C20511596953B4 créé : 2024-04-16 expire : 2029-04-15
nº de carte : 0006 24554586
</code></pre></div></div>
<p>Nous retrouvons bien les informations personnelles dans la première partie. Ensuite, il y a les informations sur les clefs présentes dans la Yubikey.</p>
<p>Nous voyons quil y a le chevron &gt; devant ssb. Comme vu plus haut, cela indique labsence de la clef secrète dans le trousseau de clefs. Mais juste en dessous, il y a une ligne supplémentaire qui permet de dire à gpg où trouver la clef secrète. Ici, nous avons le numéro de série de la Yubikey card-no: 0006 24554586.<br />
Ce numéro de série est également imprimé sur la clef physiquement. Si vous avez plusieurs Yubikey, il sera facile de retrouver celle que vous cherchez.</p>
</div>
<div class="d-print-none"><footer class="article__footer"><meta itemprop="dateModified" content="2024-04-16T00:00:00+02:00"><!-- start custom article footer snippet -->
<!-- end custom article footer snippet -->
<!--
<div align="right"><a type="application/rss+xml" href="/feed.xml" title="S'abonner"><i class="fa fa-rss fa-2x"></i></a>
&emsp;</div>
-->
</footer>
<div class="article__section-navigator clearfix"><div class="previous"><span>PRÉCÉDENT</span><a href="/2024/03/24/Installation_Simplifiee_Jekyll_(generateur_de_site_statique)_sur_Linux.html">Installation Simplifiée Jekyll (générateur de site statique) sur Linux</a></div><div class="next"><span>SUIVANT</span><a href="/2024/04/27/HomeGallery.html">HomeGallery</a></div></div></div>
</div>
<script>(function() {
var SOURCES = window.TEXT_VARIABLES.sources;
window.Lazyload.js(SOURCES.jquery, function() {
$(function() {
var $this ,$scroll;
var $articleContent = $('.js-article-content');
var hasSidebar = $('.js-page-root').hasClass('layout--page--sidebar');
var scroll = hasSidebar ? '.js-page-main' : 'html, body';
$scroll = $(scroll);
$articleContent.find('.highlight').each(function() {
$this = $(this);
$this.attr('data-lang', $this.find('code').attr('data-lang'));
});
$articleContent.find('h1[id], h2[id], h3[id], h4[id], h5[id], h6[id]').each(function() {
$this = $(this);
$this.append($('<a class="anchor d-print-none" aria-hidden="true"></a>').html('<i class="fas fa-anchor"></i>'));
});
$articleContent.on('click', '.anchor', function() {
$scroll.scrollToAnchor('#' + $(this).parent().attr('id'), 400);
});
});
});
})();
</script>
</div><section class="page__comments d-print-none"></section></article><!-- start custom main bottom snippet -->
<!-- end custom main bottom snippet -->
</div>
</div></div></div></div>
</div><script>(function() {
var SOURCES = window.TEXT_VARIABLES.sources;
window.Lazyload.js(SOURCES.jquery, function() {
var $body = $('body'), $window = $(window);
var $pageRoot = $('.js-page-root'), $pageMain = $('.js-page-main');
var activeCount = 0;
function modal(options) {
var $root = this, visible, onChange, hideWhenWindowScroll = false;
var scrollTop;
function setOptions(options) {
var _options = options || {};
visible = _options.initialVisible === undefined ? false : show;
onChange = _options.onChange;
hideWhenWindowScroll = _options.hideWhenWindowScroll;
}
function init() {
setState(visible);
}
function setState(isShow) {
if (isShow === visible) {
return;
}
visible = isShow;
if (visible) {
activeCount++;
scrollTop = $(window).scrollTop() || $pageMain.scrollTop();
$root.addClass('modal--show');
$pageMain.scrollTop(scrollTop);
activeCount === 1 && ($pageRoot.addClass('show-modal'), $body.addClass('of-hidden'));
hideWhenWindowScroll && window.hasEvent('touchstart') && $window.on('scroll', hide);
$window.on('keyup', handleKeyup);
} else {
activeCount > 0 && activeCount--;
$root.removeClass('modal--show');
$window.scrollTop(scrollTop);
activeCount === 0 && ($pageRoot.removeClass('show-modal'), $body.removeClass('of-hidden'));
hideWhenWindowScroll && window.hasEvent('touchstart') && $window.off('scroll', hide);
$window.off('keyup', handleKeyup);
}
onChange && onChange(visible);
}
function show() {
setState(true);
}
function hide() {
setState(false);
}
function handleKeyup(e) {
// Char Code: 27 ESC
if (e.which === 27) {
hide();
}
}
setOptions(options);
init();
return {
show: show,
hide: hide,
$el: $root
};
}
$.fn.modal = modal;
});
})();
</script><div class="modal modal--overflow page__search-modal d-print-none js-page-search-modal"><script>
(function () {
var SOURCES = window.TEXT_VARIABLES.sources;
window.Lazyload.js(SOURCES.jquery, function() {
// search panel
var search = (window.search || (window.search = {}));
var useDefaultSearchBox = window.useDefaultSearchBox === undefined ?
true : window.useDefaultSearchBox ;
var $searchModal = $('.js-page-search-modal');
var $searchToggle = $('.js-search-toggle');
var searchModal = $searchModal.modal({ onChange: handleModalChange, hideWhenWindowScroll: true });
var modalVisible = false;
search.searchModal = searchModal;
var $searchBox = null;
var $searchInput = null;
var $searchClear = null;
function getModalVisible() {
return modalVisible;
}
search.getModalVisible = getModalVisible;
function handleModalChange(visible) {
modalVisible = visible;
if (visible) {
search.onShow && search.onShow();
useDefaultSearchBox && $searchInput[0] && $searchInput[0].focus();
} else {
search.onShow && search.onHide();
useDefaultSearchBox && $searchInput[0] && $searchInput[0].blur();
setTimeout(function() {
useDefaultSearchBox && ($searchInput.val(''), $searchBox.removeClass('not-empty'));
search.clear && search.clear();
window.pageAsideAffix && window.pageAsideAffix.refresh();
}, 400);
}
}
$searchToggle.on('click', function() {
modalVisible ? searchModal.hide() : searchModal.show();
});
// Char Code: 83 S, 191 /
$(window).on('keyup', function(e) {
if (!modalVisible && !window.isFormElement(e.target || e.srcElement) && (e.which === 83 || e.which === 191)) {
modalVisible || searchModal.show();
}
});
if (useDefaultSearchBox) {
$searchBox = $('.js-search-box');
$searchInput = $searchBox.children('input');
$searchClear = $searchBox.children('.js-icon-clear');
search.getSearchInput = function() {
return $searchInput.get(0);
};
search.getVal = function() {
return $searchInput.val();
};
search.setVal = function(val) {
$searchInput.val(val);
};
$searchInput.on('focus', function() {
$(this).addClass('focus');
});
$searchInput.on('blur', function() {
$(this).removeClass('focus');
});
$searchInput.on('input', window.throttle(function() {
var val = $(this).val();
if (val === '' || typeof val !== 'string') {
search.clear && search.clear();
} else {
$searchBox.addClass('not-empty');
search.onInputNotEmpty && search.onInputNotEmpty(val);
}
}, 400));
$searchClear.on('click', function() {
$searchInput.val(''); $searchBox.removeClass('not-empty');
search.clear && search.clear();
});
}
});
})();
</script><div class="search search--dark">
<div class="main">
<div class="search__header">Recherche</div>
<div class="search-bar">
<div class="search-box js-search-box">
<div class="search-box__icon-search"><i class="fas fa-search"></i></div>
<input id="search-input" type="text" />
<div class="search-box__icon-clear js-icon-clear">
<a><i class="fas fa-times"></i></a>
</div>
</div>
<button class="button button--theme-dark button--pill search__cancel js-search-toggle">
Annuler</button>
</div>
<div id="results-container" class="search-result js-search-result"></div>
</div>
</div>
<!-- Script pointing to search-script.js -->
<script>/*!
* Simple-Jekyll-Search
* Copyright 2015-2020, Christian Fei
* Licensed under the MIT License.
*/
(function(){
'use strict'
var _$Templater_7 = {
compile: compile,
setOptions: setOptions
}
const options = {}
options.pattern = /\{(.*?)\}/g
options.template = ''
options.middleware = function () {}
function setOptions (_options) {
options.pattern = _options.pattern || options.pattern
options.template = _options.template || options.template
if (typeof _options.middleware === 'function') {
options.middleware = _options.middleware
}
}
function compile (data) {
return options.template.replace(options.pattern, function (match, prop) {
const value = options.middleware(prop, data[prop], options.template)
if (typeof value !== 'undefined') {
return value
}
return data[prop] || match
})
}
'use strict';
function fuzzysearch (needle, haystack) {
var tlen = haystack.length;
var qlen = needle.length;
if (qlen > tlen) {
return false;
}
if (qlen === tlen) {
return needle === haystack;
}
outer: for (var i = 0, j = 0; i < qlen; i++) {
var nch = needle.charCodeAt(i);
while (j < tlen) {
if (haystack.charCodeAt(j++) === nch) {
continue outer;
}
}
return false;
}
return true;
}
var _$fuzzysearch_1 = fuzzysearch;
'use strict'
/* removed: const _$fuzzysearch_1 = require('fuzzysearch') */;
var _$FuzzySearchStrategy_5 = new FuzzySearchStrategy()
function FuzzySearchStrategy () {
this.matches = function (string, crit) {
return _$fuzzysearch_1(crit.toLowerCase(), string.toLowerCase())
}
}
'use strict'
var _$LiteralSearchStrategy_6 = new LiteralSearchStrategy()
function LiteralSearchStrategy () {
this.matches = function (str, crit) {
if (!str) return false
str = str.trim().toLowerCase()
crit = crit.trim().toLowerCase()
return crit.split(' ').filter(function (word) {
return str.indexOf(word) >= 0
}).length === crit.split(' ').length
}
}
'use strict'
var _$Repository_4 = {
put: put,
clear: clear,
search: search,
setOptions: __setOptions_4
}
/* removed: const _$FuzzySearchStrategy_5 = require('./SearchStrategies/FuzzySearchStrategy') */;
/* removed: const _$LiteralSearchStrategy_6 = require('./SearchStrategies/LiteralSearchStrategy') */;
function NoSort () {
return 0
}
const data = []
let opt = {}
opt.fuzzy = false
opt.limit = 10
opt.searchStrategy = opt.fuzzy ? _$FuzzySearchStrategy_5 : _$LiteralSearchStrategy_6
opt.sort = NoSort
opt.exclude = []
function put (data) {
if (isObject(data)) {
return addObject(data)
}
if (isArray(data)) {
return addArray(data)
}
return undefined
}
function clear () {
data.length = 0
return data
}
function isObject (obj) {
return Boolean(obj) && Object.prototype.toString.call(obj) === '[object Object]'
}
function isArray (obj) {
return Boolean(obj) && Object.prototype.toString.call(obj) === '[object Array]'
}
function addObject (_data) {
data.push(_data)
return data
}
function addArray (_data) {
const added = []
clear()
for (let i = 0, len = _data.length; i < len; i++) {
if (isObject(_data[i])) {
added.push(addObject(_data[i]))
}
}
return added
}
function search (crit) {
if (!crit) {
return []
}
return findMatches(data, crit, opt.searchStrategy, opt).sort(opt.sort)
}
function __setOptions_4 (_opt) {
opt = _opt || {}
opt.fuzzy = _opt.fuzzy || false
opt.limit = _opt.limit || 10
opt.searchStrategy = _opt.fuzzy ? _$FuzzySearchStrategy_5 : _$LiteralSearchStrategy_6
opt.sort = _opt.sort || NoSort
opt.exclude = _opt.exclude || []
}
function findMatches (data, crit, strategy, opt) {
const matches = []
for (let i = 0; i < data.length && matches.length < opt.limit; i++) {
const match = findMatchesInObject(data[i], crit, strategy, opt)
if (match) {
matches.push(match)
}
}
return matches
}
function findMatchesInObject (obj, crit, strategy, opt) {
for (const key in obj) {
if (!isExcluded(obj[key], opt.exclude) && strategy.matches(obj[key], crit)) {
return obj
}
}
}
function isExcluded (term, excludedTerms) {
for (let i = 0, len = excludedTerms.length; i < len; i++) {
const excludedTerm = excludedTerms[i]
if (new RegExp(excludedTerm).test(term)) {
return true
}
}
return false
}
/* globals ActiveXObject:false */
'use strict'
var _$JSONLoader_2 = {
load: load
}
function load (location, callback) {
const xhr = getXHR()
xhr.open('GET', location, true)
xhr.onreadystatechange = createStateChangeListener(xhr, callback)
xhr.send()
}
function createStateChangeListener (xhr, callback) {
return function () {
if (xhr.readyState === 4 && xhr.status === 200) {
try {
callback(null, JSON.parse(xhr.responseText))
} catch (err) {
callback(err, null)
}
}
}
}
function getXHR () {
return window.XMLHttpRequest ? new window.XMLHttpRequest() : new ActiveXObject('Microsoft.XMLHTTP')
}
'use strict'
var _$OptionsValidator_3 = function OptionsValidator (params) {
if (!validateParams(params)) {
throw new Error('-- OptionsValidator: required options missing')
}
if (!(this instanceof OptionsValidator)) {
return new OptionsValidator(params)
}
const requiredOptions = params.required
this.getRequiredOptions = function () {
return requiredOptions
}
this.validate = function (parameters) {
const errors = []
requiredOptions.forEach(function (requiredOptionName) {
if (typeof parameters[requiredOptionName] === 'undefined') {
errors.push(requiredOptionName)
}
})
return errors
}
function validateParams (params) {
if (!params) {
return false
}
return typeof params.required !== 'undefined' && params.required instanceof Array
}
}
'use strict'
var _$utils_9 = {
merge: merge,
isJSON: isJSON
}
function merge (defaultParams, mergeParams) {
const mergedOptions = {}
for (const option in defaultParams) {
mergedOptions[option] = defaultParams[option]
if (typeof mergeParams[option] !== 'undefined') {
mergedOptions[option] = mergeParams[option]
}
}
return mergedOptions
}
function isJSON (json) {
try {
if (json instanceof Object && JSON.parse(JSON.stringify(json))) {
return true
}
return false
} catch (err) {
return false
}
}
var _$src_8 = {};
(function (window) {
'use strict'
let options = {
searchInput: null,
resultsContainer: null,
json: [],
success: Function.prototype,
searchResultTemplate: '<li><a href="{url}" title="{desc}">{title}</a></li>',
templateMiddleware: Function.prototype,
sortMiddleware: function () {
return 0
},
noResultsText: 'No results found',
limit: 10,
fuzzy: false,
debounceTime: null,
exclude: []
}
let debounceTimerHandle
const debounce = function (func, delayMillis) {
if (delayMillis) {
clearTimeout(debounceTimerHandle)
debounceTimerHandle = setTimeout(func, delayMillis)
} else {
func.call()
}
}
const requiredOptions = ['searchInput', 'resultsContainer', 'json']
/* removed: const _$Templater_7 = require('./Templater') */;
/* removed: const _$Repository_4 = require('./Repository') */;
/* removed: const _$JSONLoader_2 = require('./JSONLoader') */;
const optionsValidator = _$OptionsValidator_3({
required: requiredOptions
})
/* removed: const _$utils_9 = require('./utils') */;
window.SimpleJekyllSearch = function (_options) {
const errors = optionsValidator.validate(_options)
if (errors.length > 0) {
throwError('You must specify the following required options: ' + requiredOptions)
}
options = _$utils_9.merge(options, _options)
_$Templater_7.setOptions({
template: options.searchResultTemplate,
middleware: options.templateMiddleware
})
_$Repository_4.setOptions({
fuzzy: options.fuzzy,
limit: options.limit,
sort: options.sortMiddleware,
exclude: options.exclude
})
if (_$utils_9.isJSON(options.json)) {
initWithJSON(options.json)
} else {
initWithURL(options.json)
}
const rv = {
search: search
}
typeof options.success === 'function' && options.success.call(rv)
return rv
}
function initWithJSON (json) {
_$Repository_4.put(json)
registerInput()
}
function initWithURL (url) {
_$JSONLoader_2.load(url, function (err, json) {
if (err) {
throwError('failed to get JSON (' + url + ')')
}
initWithJSON(json)
})
}
function emptyResultsContainer () {
options.resultsContainer.innerHTML = ''
}
function appendToResultsContainer (text) {
options.resultsContainer.innerHTML += text
}
function registerInput () {
options.searchInput.addEventListener('input', function (e) {
if (isWhitelistedKey(e.which)) {
emptyResultsContainer()
debounce(function () { search(e.target.value) }, options.debounceTime)
}
})
}
function search (query) {
if (isValidQuery(query)) {
emptyResultsContainer()
render(_$Repository_4.search(query), query)
}
}
function render (results, query) {
const len = results.length
if (len === 0) {
return appendToResultsContainer(options.noResultsText)
}
for (let i = 0; i < len; i++) {
results[i].query = query
appendToResultsContainer(_$Templater_7.compile(results[i]))
}
}
function isValidQuery (query) {
return query && query.length > 0
}
function isWhitelistedKey (key) {
return [13, 16, 20, 37, 38, 39, 40, 91].indexOf(key) === -1
}
function throwError (message) {
throw new Error('SimpleJekyllSearch --- ' + message)
}
})(window)
}());
</script>
<!-- Configuration -->
<script>
SimpleJekyllSearch({
searchInput: document.getElementById('search-input'),
resultsContainer: document.getElementById('results-container'),
noResultsText: '<p>Aucun résultat!</p>',
json: '/search.json',
searchResultTemplate: '<li><a href="{url}">{date}&nbsp;{title}</a>&nbsp;(Création {create})</li>'
})
</script>
</div></div>
<script>(function() {
var SOURCES = window.TEXT_VARIABLES.sources;
window.Lazyload.js(SOURCES.jquery, function() {
function scrollToAnchor(anchor, duration, callback) {
var $root = this;
$root.animate({ scrollTop: $(anchor).position().top }, duration, function() {
window.history.replaceState(null, '', window.location.href.split('#')[0] + anchor);
callback && callback();
});
}
$.fn.scrollToAnchor = scrollToAnchor;
});
})();
(function() {
var SOURCES = window.TEXT_VARIABLES.sources;
window.Lazyload.js(SOURCES.jquery, function() {
function affix(options) {
var $root = this, $window = $(window), $scrollTarget, $scroll,
offsetBottom = 0, scrollTarget = window, scroll = window.document, disabled = false, isOverallScroller = true,
rootTop, rootLeft, rootHeight, scrollBottom, rootBottomTop,
hasInit = false, curState;
function setOptions(options) {
var _options = options || {};
_options.offsetBottom && (offsetBottom = _options.offsetBottom);
_options.scrollTarget && (scrollTarget = _options.scrollTarget);
_options.scroll && (scroll = _options.scroll);
_options.disabled !== undefined && (disabled = _options.disabled);
$scrollTarget = $(scrollTarget);
isOverallScroller = window.isOverallScroller($scrollTarget[0]);
$scroll = $(scroll);
}
function preCalc() {
top();
rootHeight = $root.outerHeight();
rootTop = $root.offset().top + (isOverallScroller ? 0 : $scrollTarget.scrollTop());
rootLeft = $root.offset().left;
}
function calc(needPreCalc) {
needPreCalc && preCalc();
scrollBottom = $scroll.outerHeight() - offsetBottom - rootHeight;
rootBottomTop = scrollBottom - rootTop;
}
function top() {
if (curState !== 'top') {
$root.removeClass('fixed').css({
left: 0,
top: 0
});
curState = 'top';
}
}
function fixed() {
if (curState !== 'fixed') {
$root.addClass('fixed').css({
left: rootLeft + 'px',
top: 0
});
curState = 'fixed';
}
}
function bottom() {
if (curState !== 'bottom') {
$root.removeClass('fixed').css({
left: 0,
top: rootBottomTop + 'px'
});
curState = 'bottom';
}
}
function setState() {
var scrollTop = $scrollTarget.scrollTop();
if (scrollTop >= rootTop && scrollTop <= scrollBottom) {
fixed();
} else if (scrollTop < rootTop) {
top();
} else {
bottom();
}
}
function init() {
if(!hasInit) {
var interval, timeout;
calc(true); setState();
// run calc every 100 millisecond
interval = setInterval(function() {
calc();
}, 100);
timeout = setTimeout(function() {
clearInterval(interval);
}, 45000);
window.pageLoad.then(function() {
setTimeout(function() {
clearInterval(interval);
clearTimeout(timeout);
}, 3000);
});
$scrollTarget.on('scroll', function() {
disabled || setState();
});
$window.on('resize', function() {
disabled || (calc(true), setState());
});
hasInit = true;
}
}
setOptions(options);
if (!disabled) {
init();
}
$window.on('resize', window.throttle(function() {
init();
}, 200));
return {
setOptions: setOptions,
refresh: function() {
calc(true, { animation: false }); setState();
}
};
}
$.fn.affix = affix;
});
})();
(function() {
var SOURCES = window.TEXT_VARIABLES.sources;
window.Lazyload.js(SOURCES.jquery, function() {
function toc(options) {
var $root = this, $window = $(window), $scrollTarget, $scroller, $tocUl = $('<ul class="toc toc--ellipsis"></ul>'), $tocLi, $headings, $activeLast, $activeCur,
selectors = 'h1,h2,h3', container = 'body', scrollTarget = window, scroller = 'html, body', disabled = false,
headingsPos, scrolling = false, hasRendered = false, hasInit = false;
function setOptions(options) {
var _options = options || {};
_options.selectors && (selectors = _options.selectors);
_options.container && (container = _options.container);
_options.scrollTarget && (scrollTarget = _options.scrollTarget);
_options.scroller && (scroller = _options.scroller);
_options.disabled !== undefined && (disabled = _options.disabled);
$headings = $(container).find(selectors).filter('[id]');
$scrollTarget = $(scrollTarget);
$scroller = $(scroller);
}
function calc() {
headingsPos = [];
$headings.each(function() {
headingsPos.push(Math.floor($(this).position().top));
});
}
function setState(element, disabled) {
var scrollTop = $scrollTarget.scrollTop(), i;
if (disabled || !headingsPos || headingsPos.length < 1) { return; }
if (element) {
$activeCur = element;
} else {
for (i = 0; i < headingsPos.length; i++) {
if (scrollTop >= headingsPos[i]) {
$activeCur = $tocLi.eq(i);
} else {
$activeCur || ($activeCur = $tocLi.eq(i));
break;
}
}
}
$activeLast && $activeLast.removeClass('active');
($activeLast = $activeCur).addClass('active');
}
function render() {
if(!hasRendered) {
$root.append($tocUl);
$headings.each(function() {
var $this = $(this);
$tocUl.append($('<li></li>').addClass('toc-' + $this.prop('tagName').toLowerCase())
.append($('<a></a>').text($this.text()).attr('href', '#' + $this.prop('id'))));
});
$tocLi = $tocUl.children('li');
$tocUl.on('click', 'a', function(e) {
e.preventDefault();
var $this = $(this);
scrolling = true;
setState($this.parent());
$scroller.scrollToAnchor($this.attr('href'), 400, function() {
scrolling = false;
});
});
}
hasRendered = true;
}
function init() {
var interval, timeout;
if(!hasInit) {
render(); calc(); setState(null, scrolling);
// run calc every 100 millisecond
interval = setInterval(function() {
calc();
}, 100);
timeout = setTimeout(function() {
clearInterval(interval);
}, 45000);
window.pageLoad.then(function() {
setTimeout(function() {
clearInterval(interval);
clearTimeout(timeout);
}, 3000);
});
$scrollTarget.on('scroll', function() {
disabled || setState(null, scrolling);
});
$window.on('resize', window.throttle(function() {
if (!disabled) {
render(); calc(); setState(null, scrolling);
}
}, 100));
}
hasInit = true;
}
setOptions(options);
if (!disabled) {
init();
}
$window.on('resize', window.throttle(function() {
init();
}, 200));
return {
setOptions: setOptions
};
}
$.fn.toc = toc;
});
})();
/*(function () {
})();*/
</script><script>
/* toc must before affix, since affix need to konw toc' height. */(function() {
var SOURCES = window.TEXT_VARIABLES.sources;
var TOC_SELECTOR = window.TEXT_VARIABLES.site.toc.selectors;
window.Lazyload.js(SOURCES.jquery, function() {
var $window = $(window);
var $articleContent = $('.js-article-content');
var $tocRoot = $('.js-toc-root'), $col2 = $('.js-col-aside');
var toc;
var tocDisabled = false;
var hasSidebar = $('.js-page-root').hasClass('layout--page--sidebar');
var hasToc = $articleContent.find(TOC_SELECTOR).length > 0;
function disabled() {
return $col2.css('display') === 'none' || !hasToc;
}
tocDisabled = disabled();
toc = $tocRoot.toc({
selectors: TOC_SELECTOR,
container: $articleContent,
scrollTarget: hasSidebar ? '.js-page-main' : null,
scroller: hasSidebar ? '.js-page-main' : null,
disabled: tocDisabled
});
$window.on('resize', window.throttle(function() {
tocDisabled = disabled();
toc && toc.setOptions({
disabled: tocDisabled
});
}, 100));
});
})();
(function() {
var SOURCES = window.TEXT_VARIABLES.sources;
window.Lazyload.js(SOURCES.jquery, function() {
var $window = $(window), $pageFooter = $('.js-page-footer');
var $pageAside = $('.js-page-aside');
var affix;
var tocDisabled = false;
var hasSidebar = $('.js-page-root').hasClass('layout--page--sidebar');
affix = $pageAside.affix({
offsetBottom: $pageFooter.outerHeight(),
scrollTarget: hasSidebar ? '.js-page-main' : null,
scroller: hasSidebar ? '.js-page-main' : null,
scroll: hasSidebar ? $('.js-page-main').children() : null,
disabled: tocDisabled
});
$window.on('resize', window.throttle(function() {
affix && affix.setOptions({
disabled: tocDisabled
});
}, 100));
window.pageAsideAffix = affix;
});
})();
</script><!---->
</div>
<script>(function () {
var $root = document.getElementsByClassName('root')[0];
if (window.hasEvent('touchstart')) {
$root.dataset.isTouch = true;
document.addEventListener('touchstart', function(){}, false);
}
})();
</script>
</body>
</html>