Project/codestates-final-project

리펙토링 및 개선 - 5 / 카이사르, 모노알파베틱 암호화 적용

fullfish 2022. 6. 6. 20:34

한것

카이사르 암호화와

모노 알파베틱 암호화를

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");
      }
    },
  },