任意の文字列からパスワードを作成する
複雑なパスワードを作るときの支援アプリです。
任意の文字列から、数字・アルファベット・記号の文字種を使ったパスワードを作成します。長さは4~40文字までです。
入力文字列(ここでは合言葉と表現しておきます)は日本語も使えますので、例えばお気に入りの小説の一節からパスワードを作ることもできます。
空白文字(スペース、タブ文字、改行文字)は無視しますので、これが混じっていても生成されるパスワードは変化しません。
・css
・javascript
参考
- ・Seeding the random number generator in Javascript – Stack Overflow
- ・パスワードに使用可能な記号一覧 – 登記・供託オンライン申請システム
ソース
・html<div id="p1837">
<div class="v-flex in">
<div>合言葉</div>
<div class="h-flex">
<div class="f-grow v-flex">
<div><textarea class="words js-in w100" rows="4" spellcheck="false"></textarea></div>
<div class="seed ml-auto">seed: <span class="js-seed"></span></div></div>
<div class="v-flex">
<div><a href="#" class="btn js-clear">消去</a></div>
</div>
</div>
<div>パスワードの文字数</div>
<div class="h-flex">
<div class="f-grow"><input type="range" class="w100 js-range" min="4" max="40" value="8"></div>
<div class="num js-chars">8</div></div>
</div>
<div class="next-arrow">▼</div>
<div class="v-flex out">
<div>パスワード</div>
<div>
<div class="caption">0~9</div>
<div class="h-flex">
<div class="f-grow"><input type="text" class="gray js-out1" spellcheck="false" data-num="1" readonly></div>
<div><a href="#" class="btn js-copy" data-num="1">コピー</a></div>
</div>
</div>
<div>
<div class="caption">0~9, a~z</div>
<div class="h-flex">
<div class="f-grow"><input type="text" class="gray js-out2" spellcheck="false" data-num="2" readonly></div>
<div><a href="#" class="btn js-copy" data-num="2">コピー</a></div>
</div>
</div>
<div>
<div class="caption">0~9, a~z, A~Z</div>
<div class="h-flex">
<div class="f-grow"><input type="text" class="gray js-out3" spellcheck="false" data-num="3" readonly></div>
<div><a href="#" class="btn js-copy" data-num="3">コピー</a></div>
</div>
</div>
<div>
<div class="caption">0~9, a~z, A~Z, 記号</div>
<div class="h-flex">
<div class="f-grow"><input type="text" class="gray js-out4" spellcheck="false" data-num="4" readonly></div>
<div><a href="#" class="btn js-copy" data-num="4">コピー</a></div>
</div>
</div>
</div>.v-flex { display: flex; flex-direction: column; }
.h-flex { display: flex; flex-direction: row; }
.f-grow { flex-grow: 1; }
.w100 { width: 100%; }
.ml-auto { margin-left: auto; }
.words {
line-height: 1.25;
padding: 0.5em 0.5em;
tab-size: 4;
}
.caption {
color: #888;
font-size: 80%;
line-height: 1;
}
.copy {
font-size: 80%;
line-height: 1;
}
.in {
padding: 1em 2em;
border: 8px solid #DFD;
border-radius: 16px;
}
.out {
padding: 1em 2em;
background-color: #DFD;
border-radius: 16px;
}
.gray {
background-color: #EEE;
}
#p1837 .btn {
display: block;
padding: 0.333em 1em;
margin-left: 1em;
background-color: #DDD;
text-decoration: none;
border-radius: 5px;
white-space: nowrap;
}
.num {
margin-left: 2em;
}
.next-arrow {
text-align: center;
color: #0A9;
}
.seed {
color: #888;
font-size: 80%;
line-height: 1;
}jQuery(($) => {
let seed;
main();
function main() {
$('.js-in')
.on('keyup', updateSeedAndPasswords)
.change(updateSeedAndPasswords);
$('.js-range')
.on('input', updatePasswords) // on dragging thumb
.change(updatePasswords); // on changed
$('.js-copy').click((ev) => {
const num = $(ev.currentTarget).data('num');
const pw = $(`input[data-num="${num}"]`).val();
console.log(pw);
copyToClipboard(pw);
});
$('.js-clear').click(() => {
$('.js-in').val('');
clearAll();
});
clearAll();
}
// クリップボードへテキストをコピー
function copyToClipboard(s) {
navigator.clipboard.writeText(s).then(() => {
alert('コピーしました。');
});
}
// 合言葉から seed を計算&パスワード作成
function updateSeedAndPasswords() {
const s = getIn();
if (s == '') {
clearAll();
} else {
seed = cyrb128(s);
printSeed(seed);
updatePasswords();
}
}
// 合言葉の取得&不要文字の削除
function getIn() {
let s = $('.js-in').val();
s = s.replace(/[ \t\r\n\u3000]/g, ''); // 空白文字の削除
return s;
}
// 空欄にする
function clearAll() {
$('.js-seed').val('');
for (let i = 1; i <= 4; i++) {
$(`.js-out${i}`).val('');
}
}
// seed の表示
function printSeed(seed) {
const out = [];
for (const v of seed) {
out.push(('00000000' + v.toString(16)).slice(-8));
}
$('.js-seed').text(out.join(' '));
}
// パスワード作成&表示
function updatePasswords() {
const NUM = '0123456789'; // 10
const LOWER = 'abcdefghijklmnopqrstuvwxyz'; // 26
const UPPER = 'ABCDEFGHIJKLMNOPQRSTUVWXYZ'; // 26
const MARK = '`~!@#$%^&*()_+-={}[]\\|:;"\'<>,.?/'; // 32
const pwLength = getChars();
$('.js-chars').text(pwLength);
const retry = 1000;
let pw;
pw = makePasswordByFamily(seed, pwLength, [NUM], retry);
$('.js-out1').val(pw);
pw = makePasswordByFamily(seed, pwLength, [NUM, LOWER], retry);
$('.js-out2').val(pw);
pw = makePasswordByFamily(seed, pwLength, [NUM, LOWER, UPPER], retry);
$('.js-out3').val(pw);
pw = makePasswordByFamily(seed, pwLength, [NUM, LOWER, UPPER, MARK], retry);
$('.js-out4').val(pw);
}
// パスワード文字数を取得
function getChars() {
return $('.js-range').val() | 0;
}
// 文字種からパスワードを作成
function makePasswordByFamily(seed, pwLength, family, retry = 1) {
const f = family.join('');
for (skip = 0; skip < retry; skip++) {
pw = makePasswordByLetters(seed, pwLength, f, skip);
if (checkPassword(pw, family)) return pw;
}
return '';
}
// パスワードの作成
function makePasswordByLetters(seed, pwLength, letters, skip = 0) {
const rand = sfc32(...seed);
const ltLength = letters.length;
let pw = '';
for (let i = 0; i < skip; i++) rand();
for (let i = 0; i < pwLength; i++) {
const j = rand() % ltLength;
pw += letters[j];
}
return pw;
}
// 各文字種から最低1文字は含めるようにする。
function checkPassword(pw, family) {
for (const s of family) {
if (!containChar(pw, s)) return false;
}
return true;
}
// subject の中に、chars のうちのどれか1文字が存在するか?
function containChar(subject, chars) {
for (const ch of chars) {
if (subject.indexOf(ch) != -1) return true;
}
return false;
}
// 文字列から seed を作成
function cyrb128(s) {
let h1 = 1779033703,
h2 = 3144134277,
h3 = 1013904242,
h4 = 2773480762;
for (let i = 0; i < s.length; i++) {
const k = s.charCodeAt(i);
h1 = h2 ^ Math.imul(h1 ^ k, 597399067);
h2 = h3 ^ Math.imul(h2 ^ k, 2869860233);
h3 = h4 ^ Math.imul(h3 ^ k, 951274213);
h4 = h1 ^ Math.imul(h4 ^ k, 2716044179);
}
h1 = Math.imul(h3 ^ (h1 >>> 18), 597399067);
h2 = Math.imul(h4 ^ (h2 >>> 22), 2869860233);
h3 = Math.imul(h1 ^ (h3 >>> 17), 951274213);
h4 = Math.imul(h2 ^ (h4 >>> 19), 2716044179);
return [
(h1 ^ h2 ^ h3 ^ h4) >>> 0,
(h2 ^ h1) >>> 0,
(h3 ^ h1) >>> 0,
(h4 ^ h1) >>> 0
];
}
// 指定された seed 値の乱数関数を作成
function sfc32(a, b, c, d) {
return function() {
a >>>= 0;
b >>>= 0;
c >>>= 0;
d >>>= 0;
let t = (a + b) | 0;
a = b ^ b >>> 9;
b = c + (c << 3) | 0;
c = (c << 21 | c >>> 11);
d = d + 1 | 0;
t = t + d | 0;
c = c + t | 0;
return t >>> 0;
}
}
});