Skip to content

Commit 39f5fe2

Browse files
author
Bruno Vinícius
committed
Finished... init styles
1 parent bac0fa4 commit 39f5fe2

File tree

8 files changed

+1006
-24
lines changed

8 files changed

+1006
-24
lines changed

LICENSE

+674
Large diffs are not rendered by default.

package.json

+2-1
Original file line numberDiff line numberDiff line change
@@ -12,7 +12,8 @@
1212
"dependencies": {
1313
"gh-pages": "^1.2.0",
1414
"register-service-worker": "^1.0.0",
15-
"vue": "^2.5.17"
15+
"vue": "^2.5.17",
16+
"vue-snotify": "^3.2.1"
1617
},
1718
"devDependencies": {
1819
"@vue/cli-plugin-babel": "^3.0.0-rc.5",

public/index.html

+2-1
Original file line numberDiff line numberDiff line change
@@ -7,12 +7,13 @@
77
<link rel="icon" href="<%= BASE_URL %>favicon.ico">
88
<title>ssh-generate-js</title>
99
<link rel="stylesheet" href="https://stackpath.bootstrapcdn.com/bootstrap/4.1.3/css/bootstrap.min.css">
10+
<link rel="stylesheet" href="https://use.fontawesome.com/releases/v5.2.0/css/all.css">
1011
</head>
1112
<body>
1213
<noscript>
1314
<strong>We're sorry but ssh-generate-js doesn't work properly without JavaScript enabled. Please enable it to continue.</strong>
1415
</noscript>
15-
<div id="app"></div>
16+
<div id="app" />
1617
<!-- built files will be auto injected -->
1718
</body>
1819
</html>

src/App.vue

+2-1
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,7 @@
11
<template>
22
<div id="app">
33
<sshComponent />
4+
<vue-snotify />
45
</div>
56
</template>
67

@@ -15,7 +16,7 @@ export default {
1516
};
1617
</script>
1718

18-
<style lang="scss">
19+
<style lang="scss" scoped>
1920
#app {
2021
font-family: 'Avenir', Helvetica, Arial, sans-serif;
2122
-webkit-font-smoothing: antialiased;

src/assets/logo.png

-6.69 KB
Binary file not shown.

src/components/ssh-component.vue

+314-16
Original file line numberDiff line numberDiff line change
@@ -1,32 +1,330 @@
11
<template>
2-
<div class="hello">
3-
ssh component works!
2+
<div class="container">
3+
<div class="input-group mb-3 col-md-6 offset-md-3">
4+
<input id="name-ssh-generate" type="text" class="form-control" v-model="username" placeholder="SSH Key name" aria-label="Recipient's username" aria-describedby="button-addon2">
5+
<div class="input-group-append">
6+
<button :disabled="!username" @click='getKeys' class="btn btn-primary" type="button" id="button-addon2">Generate</button>
7+
</div>
8+
</div>
9+
<div v-if="sended" class="col-md-12">
10+
<div class="mb-5">
11+
<p>id_rsa</p>
12+
<textarea readonly id="generate-ssh-id-rsa" v-model="idRsa" />
13+
<button
14+
class="btn-icon btn-icon-transparent btn-small fas fa-paste"
15+
data-toggle="tooltip"
16+
@click="copyKey('generate-ssh-id-rsa')"
17+
data-placement="bottom"
18+
title="Copy id_rsa"
19+
/>
20+
<button
21+
class="btn-icon btn-icon-transparent btn-small fas fa-download"
22+
data-toggle="tooltip"
23+
@click="downloadKeyFile('id_rsa', idRsa)"
24+
data-placement="bottom"
25+
title="Download Key File"
26+
/>
27+
</div>
28+
<hr />
29+
30+
<div class="id-rsa-pub-div">
31+
<p>id_rsa.pub</p>
32+
<textarea readonly id="generate-ssh-id-rsa-pub" v-model="idRsaPub" />
33+
<button
34+
class="btn-icon fas fa-paste"
35+
data-toggle="tooltip"
36+
@click="copyKey('generate-ssh-id-rsa-pub')"
37+
data-placement="bottom"
38+
title="Copy id_rsa.pub"
39+
/>
40+
<button
41+
class="btn-icon fas fa-download"
42+
data-toggle="tooltip"
43+
@click="downloadKeyFile('id_rsa.pub', idRsaPub)"
44+
data-placement="bottom"
45+
title="Download Key File"
46+
/>
47+
</div>
48+
</div>
449
</div>
550
</template>
651

752
<script>
853
export default {
9-
name: 'HelloWorld',
54+
name: 'sshComponent',
1055
props: {
1156
msg: String
57+
},
58+
data() {
59+
return {
60+
sended: false,
61+
loading: false,
62+
email: undefined,
63+
idRsa: undefined,
64+
idRsaPub: undefined,
65+
username: undefined,
66+
isClicked: undefined,
67+
isDownloaded: false,
68+
isChecked: false,
69+
btnEnabled: undefined
70+
};
71+
},
72+
methods: {
73+
btnEnable() {
74+
document.getElementById('checkbox-if-downloaded').checked
75+
? (this.btnEnabled = true)
76+
: (this.btnEnabled = false);
77+
},
78+
copyKey(element) {
79+
document.getElementById(element).select();
80+
document.execCommand('copy');
81+
this.$snotify.success('The key was copied to you clipboard', '');
82+
},
83+
downloadKeyFile(name, content) {
84+
let atag = document.createElement('a');
85+
let file = new Blob([content]);
86+
atag.href = URL.createObjectURL(file);
87+
atag.download = name;
88+
atag.click();
89+
this.$snotify.success('Your download is starting...', '');
90+
},
91+
getKeys() {
92+
this.sended = true;
93+
const extractable = true;
94+
var name = 'nome da chave',
95+
alg = 'RSASSA-PKCS1-v1_5',
96+
size = 1024;
97+
generateKeyPair(alg, size, name)
98+
.then(keys => {
99+
// 'get id rsa' and 'id rsa pub'...
100+
this.idRsa = `-----BEGIN RSA PRIVATE KEY-----\n${
101+
keys[0]
102+
}-----END RSA PRIVATE KEY-----`;
103+
this.idRsaPub = keys[1].replace(
104+
'RSASSA-PKCS1-v1_5',
105+
`${this.username || 'name'}`
106+
);
107+
})
108+
.catch(() => {
109+
this.$snotify.error('', 'Error! Please, try again...');
110+
})
111+
.finally(() => {
112+
this.loading = false;
113+
});
114+
115+
function wrap(text, len) {
116+
const length = len || 72;
117+
let result = '';
118+
for (let i = 0; i < text.length; i += length) {
119+
result += text.slice(i, i + length);
120+
result += '\n';
121+
}
122+
return result;
123+
}
124+
125+
function rsaPrivateKey(key) {
126+
return `-----BEGIN RSA PRIVATE KEY-----\n${key}-----END RSA PRIVATE KEY-----`;
127+
}
128+
129+
function arrayBufferToBase64(buffer) {
130+
let binary = '';
131+
const bytes = new Uint8Array(buffer);
132+
const len = bytes.byteLength;
133+
for (let i = 0; i < len; i += 1) {
134+
binary += String.fromCharCode(bytes[i]);
135+
}
136+
return window.btoa(binary);
137+
}
138+
139+
function generateKeyPair(name) {
140+
return window.crypto.subtle
141+
.generateKey(
142+
{
143+
name: 'RSASSA-PKCS1-v1_5',
144+
modulusLength: 2048, // 1024, 2048, 4096
145+
publicExponent: new Uint8Array([0x01, 0x00, 0x01]),
146+
hash: { name: 'SHA-1' } // SHA-1, SHA-256, SHA-384, SHA-512
147+
},
148+
true,
149+
['sign', 'verify']
150+
)
151+
.then(key => {
152+
const privateKey = window.crypto.subtle
153+
.exportKey('jwk', key.privateKey)
154+
.then(encodePrivateKey)
155+
.then(wrap)
156+
.then(rsaPrivateKey());
157+
158+
const publicKey = window.crypto.subtle
159+
.exportKey('jwk', key.publicKey)
160+
.then(jwk => encodePublicKey(jwk, name));
161+
return Promise.all([privateKey, publicKey]);
162+
});
163+
}
164+
165+
function arrayToString(a) {
166+
return String.fromCharCode.apply(null, a);
167+
}
168+
169+
function stringToArray(s) {
170+
return s.split('').map(c => c.charCodeAt());
171+
}
172+
173+
function pemToArray(pem) {
174+
return stringToArray(window.atob(pem));
175+
}
176+
177+
function arrayToPem(a) {
178+
return window.btoa(a.map(c => String.fromCharCode(c)).join(''));
179+
}
180+
181+
function arrayToLen(a) {
182+
let result = 0;
183+
for (let i = 0; i < a.length; i += 1) {
184+
result = result * 256 + a[i];
185+
}
186+
return result;
187+
}
188+
189+
function integerToOctet(n) {
190+
const result = [];
191+
for (let i = n; i > 0; i >>= 8) {
192+
result.push(i & 0xff);
193+
}
194+
return result.reverse();
195+
}
196+
197+
function lenToArray(n) {
198+
const oct = integerToOctet(n);
199+
let i;
200+
for (i = oct.length; i < 4; i += 1) {
201+
oct.unshift(0);
202+
}
203+
return oct;
204+
}
205+
206+
function decodePublicKey(s) {
207+
const split = s.split(' ');
208+
const prefix = split[0];
209+
if (prefix !== 'ssh-rsa') {
210+
throw new Error(`Unknown prefix: ${prefix}`);
211+
}
212+
const buffer = pemToArray(split[1]);
213+
const nameLen = arrayToLen(buffer.splice(0, 4));
214+
const type = arrayToString(buffer.splice(0, nameLen));
215+
if (type !== 'ssh-rsa') {
216+
throw new Error(`Unknown key type: ${type}`);
217+
}
218+
const exponentLen = arrayToLen(buffer.splice(0, 4));
219+
const exponent = buffer.splice(0, exponentLen);
220+
const keyLen = arrayToLen(buffer.splice(0, 4));
221+
const key = buffer.splice(0, keyLen);
222+
return { type, exponent, key, name: split[2] };
223+
}
224+
225+
function checkHighestBit(v) {
226+
if (v[0] >> 7 === 1) {
227+
// addd leading zero if first bit is set
228+
v.unshift(0);
229+
}
230+
return v;
231+
}
232+
233+
function jwkToInternal(jwk) {
234+
return {
235+
type: 'ssh-rsa',
236+
exponent: checkHighestBit(stringToArray(base64urlDecode(jwk.e))),
237+
name: 'name',
238+
key: checkHighestBit(stringToArray(base64urlDecode(jwk.n)))
239+
};
240+
}
241+
242+
function encodePublicKey(jwk, name) {
243+
const k = jwkToInternal(jwk);
244+
k.name = name;
245+
const keyLenA = lenToArray(k.key.length);
246+
const exponentLenA = lenToArray(k.exponent.length);
247+
const typeLenA = lenToArray(k.type.length);
248+
const array = [].concat(
249+
typeLenA,
250+
stringToArray(k.type),
251+
exponentLenA,
252+
k.exponent,
253+
keyLenA,
254+
k.key
255+
);
256+
const encoding = arrayToPem(array);
257+
return `${k.type} ${encoding} ${k.name}`;
258+
}
259+
260+
function asnEncodeLen(n) {
261+
let result = [];
262+
if (n >> 7) {
263+
result = integerToOctet(n);
264+
result.unshift(0x80 + result.length);
265+
} else {
266+
result.push(n);
267+
}
268+
return result;
269+
}
270+
271+
function encodePrivateKey(jwk) {
272+
const order = ['n', 'e', 'd', 'p', 'q', 'dp', 'dq', 'qi'];
273+
const list = order.map(prop => {
274+
const v = checkHighestBit(stringToArray(base64urlDecode(jwk[prop])));
275+
const len = asnEncodeLen(v.length);
276+
return [0x02].concat(len, v); // int. tag is 0x02
277+
});
278+
let seq = [0x02, 0x01, 0x00]; // extra seq. for SSH
279+
seq = seq.concat(...list);
280+
const len = asnEncodeLen(seq.length);
281+
const a = [0x30].concat(len, seq); // seq. is 0x30
282+
return arrayToPem(a);
283+
}
284+
285+
function base64urlEncode(arg) {
286+
const step1 = window.btoa(arg); // Regular base64 encoder
287+
const step2 = step1.split('=')[0]; // Remove any trailing '='s
288+
const step3 = step2.replace(/\+/g, '-'); // 62nd char of encoding
289+
const step4 = step3.replace(/\//g, '_'); // 63rd char of encoding
290+
return step4;
291+
}
292+
293+
function base64urlDecode(s) {
294+
const step1 = s.replace(/-/g, '+'); // 62nd char of encoding
295+
const step2 = step1.replace(/_/g, '/'); // 63rd char of encoding
296+
let step3 = step2;
297+
switch (step2.length % 4) { // Pad with trailing '='s
298+
case 0: // No pad chars in this case
299+
break;
300+
case 2: // Two pad chars
301+
step3 += '==';
302+
break;
303+
case 3: // One pad char
304+
step3 += '=';
305+
break;
306+
default:
307+
throw new Error('Illegal base64url string!');
308+
}
309+
return window.atob(step3); // Regular base64 decoder
310+
}
311+
}
12312
}
13-
}
313+
};
14314
</script>
15315

16-
<!-- Add "scoped" attribute to limit CSS to this component only -->
17316
<style scoped lang="scss">
18-
h3 {
19-
margin: 40px 0 0;
20-
}
21-
ul {
22-
list-style-type: none;
23-
padding: 0;
317+
#generate-ssh-id-rsa,
318+
#generate-ssh-id-rsa-pub {
319+
width: 100%;
320+
height: 200px;
321+
text-align: center;
24322
}
25-
li {
26-
display: inline-block;
27-
margin: 0 10px;
323+
.btn-icon {
324+
background-color: transparent;
325+
border: none;
28326
}
29-
a {
30-
color: #42b983;
327+
hr {
328+
border-top: 1px dashed #ABABAB;
31329
}
32330
</style>

0 commit comments

Comments
 (0)