暗号化 - Sizuha/devdog GitHub Wiki

暗号化の豆知識

DES

  • AESの前の暗号化アルゴリズムで、8 bytes ブロック単位で暗号化。
  • 暗号化後のByte数もは8の倍数になる。
  • AESよりセキュリティ的に弱いので、3回重ねるTriple DES(3DES、DESede)で使う
  • 暗号鍵:24 bytes

AES128

  • AESがDESの発展型なので、基本的にDESと同じ
  • 128bit(16 bytes)ブロック単位で暗号化するので、暗号化後のByte数も16の倍数になる。
  • 暗号鍵:16 bytes

CBC/ECBモード

  • ECB:各ブロックを同じ鍵を使って独立して暗号化
  • CBC:各ブロックの暗号化に前のブロックの暗号文を使用するため、初期化ベクトル(IV)というものが必要(※ ECBはセキュリティ的にあまりお勧めしない)

IV(Initialization Vecor)について

  • 普通は、同じ原文を同じ暗号鍵で暗号化すると、同じ結果が出る。
  • IV:最初に入れるデータブロックで、同じ原文で同じ鍵でもIVが変わると結果も変わる。 (暗号化後のBytes数には影響なし)
  • IVは暗号化するブロックの長さ同じBytes数にすること。  例)AES128 => IV: 128 bit (16 bytes)

Padding

  • ブロック単位で暗号化する為、原文の長さもブロックのByte数の倍数にしないといけない。  例)AES128の場合: ”1234567890123456” (16 bytes) -> OK、 "1234567890" (10 bytes) -> NG
  • Paddingは原文に足りないByte分を埋める
  • PKCS5/PKCS7 などがある

AES

import static org.junit.Assert.*;

import org.junit.Test;

import java.nio.charset.StandardCharsets;

import javax.crypto.Cipher;
import javax.crypto.spec.IvParameterSpec;
import javax.crypto.spec.SecretKeySpec;

public class CryptoUtilTest {

    // 暗号化
    public static byte[] encryptAES(byte[] plainBytes, byte[] key, byte[] iv) {
        try {
            final Cipher cipher = Cipher.getInstance("AES/CBC/PKCS5Padding");
            final IvParameterSpec ivSpec = new IvParameterSpec(iv);
            final SecretKeySpec keySpec = new SecretKeySpec(key, "AES");

            cipher.init(Cipher.ENCRYPT_MODE, keySpec, ivSpec);
            return cipher.doFinal(plainBytes);
        } catch (Exception e) {
            return null;
        }
    }

    // 復号化
    public static byte[] decryptAES(byte[] encryptedBytes, byte[] key, byte[] iv) {
        try {
            final Cipher cipher = Cipher.getInstance("AES/CBC/PKCS5Padding");
            final IvParameterSpec ivSpec = new IvParameterSpec(iv);
            final SecretKeySpec keySpec = new SecretKeySpec(key, "AES");

            cipher.init(Cipher.DECRYPT_MODE, keySpec, ivSpec);
            return cipher.doFinal(encryptedBytes);
        } catch (Exception e) {
            return null;
        }
    }

    @Test
    public void testAES() {
        // 原文
        final String orgText = "AES-SAMPLE";

        // 暗号鍵
        final byte[] key = "1234567890123456".getBytes(StandardCharsets.UTF_8); // 16 bytes
        // 初期化ベクトル
        final byte[] iv = "1234567890abcdef".getBytes(StandardCharsets.UTF_8); // 16 bytes

        final byte[] bytes = orgText.getBytes(StandardCharsets.UTF_8);
        final byte[] encoded = encryptAES(bytes, key, iv);

        final byte[] decoded = decryptAES(encoded, key, iv);
        final String decodedText = new String(decoded, StandardCharsets.UTF_8);

        assertEquals(orgText, decodedText);
    }

}

RSA

  • 暗号化の鍵と復号化の鍵が別
  • 大きな数の素因数分解の計算が難しいことを利用
    例)4261 x 8521 = 36307981
    素数2つの掛け算は簡単、しかし、36307981を素因数分解するのは難度が高い
    ※参考:https://it-trend.jp/encryption/article/64-0056

暗号鍵と暗号化前後Byte数

Key Size(bit) 暗号化できるBytes 暗号化後のBytes
512 22 64
1024 86 128
2048 214 256

※Android/Javaで確認した結果

鍵のペアを生成

公開鍵と秘密鍵のペアを生成して使う

final KeyPairGenerator kpg;
try {
    kpg = KeyPairGenerator.getInstance("RSA");
} catch (NoSuchAlgorithmException e) {
    return null;
}

kpg.initialize(1024); // 鍵の大きさ(bits)
final KeyPair kp = kpg.genKeyPair(); // 鍵のペアを生成
final PublicKey pubKey = kp.getPublic(); // 公開鍵
final PrivateKey priKey = kp.getPrivate(); // 秘密鍵

final byte[] pubKeyBytes = pubKey.getEncoded(); // 公開鍵のByte配列

暗号化

// Byte配列からPublicKey作成
public static RSAPublicKey createRsaPublicKey(@NonNull byte[] pubKeyBytes) {
    final X509EncodedKeySpec spec = new X509EncodedKeySpec(pubKeyBytes);
    try {
        final KeyFactory kf = KeyFactory.getInstance("RSA");
        return (RSAPublicKey) kf.generatePublic(spec);
    } catch (NoSuchAlgorithmException | InvalidKeySpecException e) {
        return null;
    }
}

// 公開鍵で暗号化
public static byte[] encryptRSA(@NonNull byte[] plainBytes, @NonNull PublicKey rsaPublicKey) {
    try {
        final Cipher cipher = Cipher.getInstance("RSA/ECB/OAEPWithSHA1AndMGF1Padding");
        cipher.init(Cipher.ENCRYPT_MODE, rsaPublicKey);
        return cipher.doFinal(plainBytes);
    } catch (Exception e) {
        return null;
    }
}

復号化

public static byte[] decryptRSA(@NonNull byte[] encryptedBytes, @NonNull PrivateKey privateKey) {
    try {
        final Cipher cipher = Cipher.getInstance("RSA/ECB/OAEPWithSHA1AndMGF1Padding");
        cipher.init(Cipher.DECRYPT_MODE, privateKey);
        return cipher.doFinal(encryptedBytes);
    } catch (Exception e) {
        return null;
    }
}

SHA1

import java.security.MessageDigest;
import java.security.NoSuchAlgorithmException;

public class Sha1Hex {
    public String makeSHA1Hash(String input) throws NoSuchAlgorithmException {
            MessageDigest md = MessageDigest.getInstance("SHA1");
            md.reset();
            byte[] buffer = input.getBytes();
            md.update(buffer);
            byte[] digest = md.digest();

            String hexStr = "";
            for (int i = 0; i < digest.length; i++) {
                hexStr +=  Integer.toString( ( digest[i] & 0xff ) + 0x100, 16).substring( 1 );
            }
            return hexStr;
    }
}
⚠️ **GitHub.com Fallback** ⚠️