한것
카이사르 암호화와
모노 알파베틱 암호화를
RSA와 함께 적용했다
(회원가입, 로그인, 비밀번호 변경)
내가 쓴 카이사르와 모노알파베틱 암호화
카이사르 암호(Caesar cipher)
카이사르 암호란 각 글자를 n만큼 shift시킨 간단한 치환암호 예 : 'abz'를 +1만큼 shift -> 'bca' 코드화 문자열의 각 문자를 유니코드화 -> 각 유니코드에 원하는 shift만큼 더해줌 -> 이 때 'z' 다음에는 '
fullfish.tistory.com
모노 알파베틱 암호화 (Monoalphabetic Cipher)
앞선 카이사르 암호화와 마찬가지로 치환암호 26자의 알파벳을 각기 다른 알파벳으로 치환시킴 코드화 알파벳이 오름차순된 ('abcd...') 문자열과 랜덤으로 뒤섞인 문자열을 준비해서 각 알파벳의
fullfish.tistory.com
흐름
예를들어 평문 'a'를
클라이언트단에서
-> 모노 알파베틱 암호화
-> 유니코드화
-> 카이사르 암호화
-> RSA 암호화
-> 서버 전송
-> 서버단에서 RSA복호화
-> 카이사르 복호화
-> 문자화
-> 모노 알파베틱 복호화
코드
// 클라이언트단 암호화 함수들
exports.monoAlphabeticEncrypt = str => {
const lowerAlphabet = 'abcdefghijklmnopqrstuvwxyz';
const lowerMonoAlphabet = process.env.REACT_APP_LOWERMONOALPHABET;
const upperAlphabet = 'ABCDEFGHIGKLMNOPQRSTUVWXYZ';
const upperMonoAlphabet = process.env.REACT_APP_UPPERMONOALPHABET;
let resultArr = [];
for (let i = 0; i < str.length; i++) {
if (/[a-z]/.test(str[i]))
resultArr.push(lowerMonoAlphabet[lowerAlphabet.indexOf(str[i])]);
else if (/[A-Z]/.test(str[i]))
resultArr.push(upperMonoAlphabet[upperAlphabet.indexOf(str[i])]);
else resultArr.push(str[i]);
}
return resultArr.join('');
};
exports.caesarEncrypt = unicode => {
let shift = process.env.REACT_APP_SHIFTNUMBER % 26;
let a = 0;
if (65 <= unicode && unicode <= 90) {
if (unicode + shift > 90) a = 26;
return unicode + shift - a;
} else if (97 <= unicode && unicode <= 122) {
if (unicode + shift > 122) a = 26;
return unicode + shift - a;
} else return unicode;
};
// 클라이언트단 회원가입
export const signUpApi = async (email, nickname, password) => {
const res = await signCustomApi.post('up', {
createKey: true,
nickname,
email,
});
let encrypted = [];
const e = BigInt(Number(JSON.parse(res.data.data.e)));
const N = BigInt(Number(JSON.parse(res.data.data.N)));
BigInt.prototype.toJSON = function () {
return this.toString();
};
password = caesar_monoAlphabet.monoAlphabeticEncrypt(password);
for (let i = 0; i < password.length; i++) {
let a = BigInt(
caesar_monoAlphabet.caesarEncrypt(password[i].charCodeAt(0)),
);
encrypted[i] = JSON.stringify(power(a, e, N));
}
const res2 = await signCustomApi.post('up', {
email,
nickname,
password: encrypted,
});
return res2;
};
// 서버단 복호화 함수들
exports.monoAlphabeticDecrypt = (str) => {
const lowerAlphabet = "abcdefghijklmnopqrstuvwxyz";
const lowerMonoAlphabet = process.env.LOWERMONOALPHABET;
const upperAlphabet = "ABCDEFGHIGKLMNOPQRSTUVWXYZ";
const upperMonoAlphabet = process.env.UPPERMONOALPHABET;
let resultArr = [];
for (let i = 0; i < str.length; i++) {
if (/[a-z]/.test(str[i])) resultArr.push(lowerAlphabet[lowerMonoAlphabet.indexOf(str[i])]);
else if (/[A-Z]/.test(str[i])) resultArr.push(upperAlphabet[upperMonoAlphabet.indexOf(str[i])]);
else resultArr.push(str[i]);
}
return resultArr.join("");
};
exports.caesarDecrypt = (unicode) => {
let shift = process.env.REACT_APP_SHIFTNUMBER % 26;
let a = 0;
if (65 <= unicode && unicode <= 90) {
if (unicode - shift < 65) a = 26;
return unicode - shift + a;
} else if (97 <= unicode && unicode <= 122) {
if (unicode - shift < 97) a = 26;
return unicode - shift + a;
} else return unicode;
};
// 서버단 회원가입 post 요청
up: {
post: async (req, res) => {
try {
const { email, nickname, createKey } = req.body;
let password = req.body.password;
if (!email || !nickname) {
await slack.slack("Signup Post 422");
return res.status(422).send({ message: "insufficient parameters supplied" });
}
const userInfo = await user.findOne({
where: {
email,
},
});
// 가입 안되어 있을 경우 키 생성
if (createKey === true) {
// 이미 가입되었을 경우
if (userInfo) {
await slack.slack("Signup Post 409");
return res.status(409).send({ message: "email already exists" });
}
let [e, N, d] = RSA.createKey();
const payload = {
email,
nickname,
password: "temp",
d,
e,
N,
};
await user.create(payload);
BigInt.prototype.toJSON = function () {
return this.toString();
};
e = JSON.stringify(e);
N = JSON.stringify(N);
return res.status(201).send({ data: { e: e, N: N } });
} else {
if (!email || !password || !nickname) {
await slack.slack("Signup Post 422");
return res.status(422).send({ message: "insufficient parameters supplied" });
}
let passwordBigIntArr = [];
console.time("복호화");
for (let i = 0; i < password.length; i++) {
passwordBigIntArr[i] = BigInt(Number(JSON.parse(password[i])));
}
let d = BigInt(userInfo.dataValues.d);
let N = BigInt(userInfo.dataValues.N);
const passwordDecryptedArr = passwordBigIntArr.map((ele) => {
return String.fromCharCode(caesar_monoAlphabet.caesarDecrypt(Number(power(ele, d, N))));
});
password = passwordDecryptedArr.join("");
console.timeEnd("복호화");
password = caesar_monoAlphabet.monoAlphabeticDecrypt(password);
// //? 방법 1 salt 생성 후 소금 치기
bcrypt.genSalt(13, async function (err, salt) {
bcrypt.hash(password, salt, async function (err, hash) {
userInfo.password = hash;
const result = await userInfo.save();
await slack.slack("User Post 201", `id : ${result.dataValues.id}`);
return res.status(201).send({ data: { id: result.dataValues.id } });
});
});
// //? 방법 2 salt 자동 생성
// bcrypt.hash(password, 13, async function (err, hash) {
// userInfo.password = hash;
// const result = await userInfo.save();
// await slack.slack("User Post 201", `id : ${result.dataValues.id}`);
// return res.status(201).send({ data: { id: result.dataValues.id } });
// });
// //? ---
}
} catch (err) {
await slack.slack("Signup Post 501");
res.status(501).send("Signup Post");
}
},
},
'Project > codestates-final-project' 카테고리의 다른 글
리펙토링 및 개선 - 7 / nodemailer를 이용한 비밀번호 재발급 (1) | 2022.06.08 |
---|---|
리펙토링 및 개선 - 6 / 환율 적용 (0) | 2022.06.07 |
리펙토링 및 개선 - 4 / RSA 적용 (0) | 2022.06.01 |
리펙토링 및 개선 - 3 / Bcrypt 적용 (0) | 2022.05.31 |
리펙토링 및 개선 - 2 / 잡다한 버그 해결 (0) | 2022.05.31 |