390 likes | 578 Views
Chapter 9 Writing a Provider. 데이타베이스 실험실 강 민 석 kangms@dblab.hannam.ac.kr. Contents. Getting Start Adding the ElGamal Classes ElGamal Generating Keys Signature Cipher. Provider. MessageDigest.getInstance(“MD5”). Program. Providers. 1. MD5 알고리즘을 구현한 instance 를 주세요.
E N D
Chapter 9Writing a Provider 데이타베이스 실험실 강 민 석 kangms@dblab.hannam.ac.kr
Contents • Getting Start • Adding the ElGamal Classes • ElGamal • Generating Keys • Signature • Cipher DB Lab.
Provider MessageDigest.getInstance(“MD5”) Program Providers 1.MD5알고리즘을 구현한 instance를 주세요 4. sun.security.provider. MD5 class입니다. 3.누가 MD5 message digest class를 가지고 있나? 6. 여기 있습니다. MessageDigest Security 2. MD5를 구현한 message digest 객체 요구 5. 여기 instance가 있습니다. DB Lab.
Getting Started(Algorithm Names and Implementations) • 알고리즘 이름을 클래스 이름으로 mapping DES/CBC/PKCS5Padding : oreilly.jonathan.crypto.CBCWrapper DES/CFB/NoPadding : oreilly.jonathan.crypto.CFBWrapper • Cryptographic provider • 여러 종류의 알고리즘을 포함 할 수 있음 • key pair generation, signatures, cipers,.. • 알고리즘 이름은 type and name으로 만들어짐 Cipher.DES/CBC/PKCS5Padding : oreilly.jonathan.crypto.CBCWrapper Cipher.DES/CFB/NoPadding : oreilly.jonathan.crypto.CFBWrapper DB Lab.
A Simple Provider • Subclass 생성 • CBCWrapper, CFBWrapper classes에 대한 mapping을 포함 하는 subclass 생성 • oreilly.jonathan.security.provider package oreilly.jonathan.crypto; import java.security.*; public class Provider extends java.security.Provider { public Provider() { super ("Jonathan", 1.2, "Jonathan's Cryptography Provider"); put("Cipher.DES/CBC/PKCS5Padding", "oreilly.jonathan.crypto.CBCWrapper"); put("Cipher.DES/CFB/Nopadding", "oreilly.jonathan.crypto.CFBWrapper"); } } Provider name을 받아들이는 getInstance() 메소드에서 이용됨 DB Lab.
An Algorithm by Any Other Name • 다른 알고리즘 name으로 같은 구현을 return • sun provider에서 다음은 같은 message digest 구현을 얻음 MessageDigest one = MessageDigest.getInstance("SHA-1"); MessageDigest two = MessageDigest.getInstance("SHA"); • Class가 8 bit CFB 모드이기 때문에 CFBWrapper와 연결하기 위한 “DES/CFB8/NoPadding”을 원할 경우 Put(“Alg.Alias.Cipher.DES/CFB8/NoPadding” , “DES/CFB/NoPadding”); • 우리의 provider에서 다음은 같은 결과를 초래 Cipher one = Cipher.getInstance("DES/CFB8/NoPadding"); Cipher two = Cipher.getInstance("DES/CFB/NoPadding"); DB Lab.
Installing the Provider ~/Lib/security/java.security파일 수정 security.provider.1 = sun.security.provider.Sun security.provider.2 = com.sun.crypto.provider.SunJCE security.provider.3 = oreilly.jonathan.crypto.Provider security.provider.4 = cryptix.security.Cryptix security.provider.5 = iaik.security.provider.IAIK Application에서 runtime시 Jonathan Provider를 추가하는 방법 java.security.Provider p = new oreilly.jonathan.security.Provider(); Security.addProvider(p); DB Lab.
Adding the ElGamal Classes • Jonathan provider의 class oreilly.jonathan.crypto.ElGamalKeyPairGenerator oreilly.jonathan.crypto.ElgamalSignature oreilly.jonathan.crypto.ElGamalCipher • Adding the Provider(ElGamal) Classes package oreilly.jonathan.crypto; import java.security.*; public class Provider extends java.security.Provider { public Provider( ) { super("Jonathan", 1.2, "Jonathan's Cryptography Provider"); put("KeyPairGenerator.ElGamal", "oreilly.jonathan.crypto.ElGamalKeyPairGenerator"); DB Lab.
Cont’d put("Cipher.ElGamal", "oreilly.jonathan.crypto.ElGamalCipher"); put("Signature.ElGamal", "oreilly.jonathan.crypto.ElGamalSignature"); put("Cipher.DES/CBC/PKCS5Padding", "oreilly.jonathan.crypto.CBCWrapper"); put("Cipher.DES/CFB/NoPadding", "oreilly.jonathan.crypto.CFBWrapper"); put("Alg.Alias.Cipher.DES/CFB8/NoPadding", "DES/CFB/NoPadding"); } } DB Lab.
Encryption Decryption cipher cipher ciphertext plaintext plaintext ElGamal 알고리즘의 이용 ElGamal 알고리즘 이용 DB Lab.
ElGamal(Key pair Generation) • Random prime p를 생성(modulus라 불림) • p size는 key size와 같다. • Ex. : 2048 bit key has a P that is 2048 bit • Random number g, x 선택 • g, x < p • x is private key • y = gx mod p 계산 • p, g, y are public key DB Lab.
Signature 생성 단계 • p - 1과 서로소인 난수 k를 선택 • k와 p-1은 1을 제외한 공통인수가 없음 • a와 b를 계산 • signature 확인 K는 공개 되서는 안됨 a = gk mod p b = ((m-xa) / k) mod (p-1) * m은 메시지이고 a,b는 signature yaab mod p = gm mod p를 검사 DB Lab.
Cont’d 예) p=11, g =2 , x = 8 라면 암호화(Cipher) 난수 k = 9를 선택, m = 5 a= gk mod p = 29 mod 11 = 6 b= yk m mod p = 39 * 5 mod 11 = 9 • y = gx mod p • = 28 mod 11 = 3 공개키 : y =3, g =2, p = 11 인증(signature) 복호화 난수 k = 9를 선택, m = 5라 할 때 a = gk mod p = 29 mod 11 = 6 b = ((m-xa) / k) mod (p-1) = ((5-8*6) / 9) mod 10 = 3 m = ( b / ax ) mod P 인증(signature) 확인 yaab mod 11 = gm mod p 3663 mod 11 = 9, 25 mod 11 = 9 DB Lab.
Cipher • p - 1과 서로소인 난수 k를 선택 • 암호문 a 와 b를 계산 • 복호(decrypt)를 위해 m = ( b /ax ) mod P 를 계산 a = gk mod p b = yk * m mod p * m 은 평문, 숫자 a와 b는 암호문(modulus p 보다 2배 큼) ( b /ax ) mod p = ((yk * m) / gkx ) mod p = ((gkx * m) / gkx ) mod p = m mod p DB Lab.
Generating Keys(key Classes) • ElGamal의 공개키(p, g) 생성 공개키 p, g, y 개인키 x package oreilly.jonathan.crypto; import java.math.BigInteger; // 모든 수학적인 계산을 수행함 import java.security.*; public class ElGamalKey implements Key { private BigInteger mP, mG; protected ElGamalKey (BigInteger g, BigInteger p) mG=g; mP=p; } protected BigInteger getG( ) { return mG ; } protected BigInteger getP( ) { return mP; } public String getAlgorithm( ) { return "ElGamal"; } public String getFormat( ) { return "NONE"; } public byte[] getEncoded( ) { return null; } } DB Lab.
Cont’d • 공개키 y를 포함하는 Public key class 생성 package oreilly.jonathan.crypto ; import java.math.BigInteger ; import java.security.* ; public class ElGamalPublicKey extends ElGamalKey implements PublicKey { private BigInteger mY; protected ElGamalPublicKey(BigInteger y, BigInteger g, BigInteger p) { super(g, p); mY=y; } protected BigInteger getY() return mY; } DB Lab.
Cont’d • ElGamalPrivateKey x 생성 packgae oreilly.jonathan.crypto; import java.math.BigInteger; import java.security.*; public class ElGamalPrivateKey extends ElGamalKey implements PrivateKey { private BigInteger mX ; protected ElGamalPrivateKey(BigInteger x, BigInteger g, BigInteger p) { super(g, p) ; mX= x ; } protected BigInteger getX() return mX; } DB Lab.
ElGamal Key Pair Generator package oreilly.jonathan.crypto; import java.math.BigInteger; import java.security.*; public class ElGamalKeyPairGenerator extends KeyPairGeneratorSpi { private int mStrength = 0; private SecureRandom mSecureRandom = null; // Strength is interpreted as the bit length of p. public void initialize(int strength, SecureRandom random) { mStrength=strength; mSecureRandom=random; } Strength와 SecureRandom의 측정으로 초기화됨 DB Lab.
Cont’d public KeyPair generateKeyPair( ) { if (mSecureRandom==null) { /* default 처리 */ mStrength=1024; mSecureRandom=new SecureRandom( ); } BigInteger p=new BigInteger (mStrength, 16, mSecureRandom); BigInteger g=new BigInteger (mStrength, -1, mSecureRandom); BigInteger x=new BigInteger (mStrength, -1, mSecureRandom); BigInteger y=g.modPow(x, p); // y 값 계산 ElGamalPublicKey publicKey = new ElGamalPublicKey(y, g, p); ElGamalPrivateKey privateKey = new ElGamalPrivateKey(x, g, p); return new KeyPair(publicKey, privateKey); } } 소수가 될 확률 : 1- 0.516 DB Lab.
Signature package oreilly.jonathan.crypto ; import java.io.ByteArrayOutputStream; import java.math.BigInteger; import java.sercurity.*; public class ElGamalSignature extends SignatureSpi { protected ElGamalKeymKey ; /* 나중에 서명 값을 생성 또는 확인할 key 저장 */ protected ByteArrayOutputStream mOut; /* data 저장 - 서명 값 생성 또는 확인 시 이용함 */ protected static BigInteger kOne = BigInteger.valueOf(1) ; ElGamal signature class를 구현하기 위하여signature의 SPI를 구현함 DB Lab.
Cont’d 서명의 initVerify() 호출 시 이 메소드가 호출됨 protected void engineInitVerify(PublicKey key) throws InvalidKeyException { if ( !(key instanceof ElGamalPublicKey)) throw new InvalidKeyException(”I didn'd get an ElGamalPublicKey.")"; mKey = (ElGamalKey)key; mOut = new ByteArrayOutputStream( ); } protected void engineInitSign(PrivateKey key) throws InvalidKeyException { if ( !(key instanceof ElGamaPrivateKey)) throw new InvalidKeyException("I didn't get an ElGamalPrivateKey."); mKey = (ElGamalKey)key; mOut = new ByteArrayOutputStream( ); } 서명의 initsign( ) 호출 시 이 메소드가 호출됨 DB Lab.
Cont’d //signature’s update( ) 메소드 호출시 호출됨 protected void engineUpdate(byte b) throws SignatureException { mOut.write(b) ; } protected void engineUpdate(byte[] b, int off, int len) throws SignatureException { mOut.write(b, off, len); } //Signature API에 있는 sign()와 동등한 SPI protected byte[] engineSign() throws SignatureException { BigInteger x = ((ElGamalPrivateKey)mKey).getX(); BigInteger g = mKey.getG(); BigInteger p = mKey.getP(); BigInteger pminusone = p.subtract(kOne); // p-1 값 계산 ByteArrayOutputStream에 input data 기록 DB Lab.
Cont’d BigInteger k ; // 랜덤한 k 값 선택 do { k = new BigInteger(p.bitLength() - 1, new SecureRamdom()); } while (k.gcd(pminusone).equals(kOne) == false); // ByteArrayOutputStream에 저장된 모든 byte를 BingInterger m으로 변환 BigInteger m = new BigInteger(1, mOut.toByteArray()); // 서명이 a, b의 계산에 의해 표현 BigInteger a = g.modPow(k, p); BigInteger top = m.subtract(x.multiply(a)).mod(pminusone); BigInteger b = top.multiply( k.modPow(kOne.negate( ) , pminusone)).mod(pminusone); DB Lab.
int modulusLength = (p.bitLength() + 7) /8; byte[] signature = new byte[modulusLength * 2]; byte[] aBytes = getBytes(a); int aLength = aBytes.length; byte[] bBytes = getBytes(b); int bLength = bBytes.length; System.arraycopy(aBytes, 0, signature, modulusLength - aLength, aLength); System.arraycopy(bBytes, 0, signature, modulusLength * 2 - bLength, bLength); return signature ; } protected boolean engineVerify(byte[] sigBytes) throws SignatureException { BigInteger y = ((ElGamalPublicKey).getY(); Biginteger g = mKey.getG(); BigInteger p = mKey.getP(); Modulus p의 두배 만큼 배열을 할당하고 a, b 값을 저장 공개키로부터 매개변수 추출 DB Lab.
Cont’d // a, b를 추출하기 위하여 BigInteger 생성 int modulusLength = (p.bitLength() + 7) / 8 ; byte[] aBytes = new byte[modulusLength] ; byte[] bBytes = new byte[modulusLength] ; System.arraycopy(sigBytes, 0, aBytes, 0, modulusLength) ; System.arraycopy(sigBytes, modulusLength, bBytes, 0, moduluslength) ; BigInteger a = new BigInteger(1, aBytes) ; BigInteger b = new BigInteger(1, bBytes) ; BigInteger first = y.modPow(a,p).multiply(a.modPow(b, p)).mod(p) ; // 서명의 정확성 검사 루틴 BigInterger m = new BigInteger(1, mOut.toByteArray()) ; BigInteger second = g.modPow(m,p) ; return first.equals(second) ; } DB Lab.
Cont’d BigInteger 값을 갖는 byte 배열 return protected byte[] getBytes(BigInteger big) { byte[] bigBytes = big.toByteArray( ); if ((big.bitLength( ) % 8) != 0) { return bigBytes ; } else { byte[] smallerBytes = new byte[big.bitLength() / 8] ; System.arraycopy(bigBytes, 1, smallerByters, 0, smallerByters.length) ; return smallerBytes ; } } /*ElGamalSignature는 parameter를 이용하지 않음. 하지만 이것을 정의하지 않으면 SignatureSpi의 subclass를 생성할 수 없음 */ protected void engineSetParameter ( String param, Object value) throws InvalidParameterException { } protected object engineGetParameter(String param) throws InvalidParameterException { return null ; } } DB Lab.
plaintext ElGamal Encryption Public Key ciphertext 암호화 Key(p) size의 2배 ElGamal Decryption 0 0 0 Public Key Decrypted plaintext Cipher DB Lab.
Cipher package oreilly.jonathan.crypto; import java.math.BigInteger; import java.security.* ; import java.security.spec.* ; import javax.crypto.* ; public class ElGamalCipher extends BlockCipher { protected int mState ; protected Key mKey ; protected SecureRandom mSecureRandom ; protected int mPlainBlockSize ; protected int mCihperBlockSize ; Java.crypto.CipherSpi를 확장한 class. oreilly.jonathan.crypto packag에 있음 초기화 될때 ElGamalCipher에게 전달됨 DB Lab.
Cont’d protected void engineSetMode(String mode) throws NoSuchAlgorithmException { throw new NoSuchAlgorithmException("ElGamalCipher supports no modes.") ; } // padding과 no mode를 지원하기 때문에 engineSetMode와 engineSetPadding는 호출 시 바로 예외 처리로 감 // protected void engineSetPadding(String padding) throws NoSuchPaddingException { throw new NoSuchPaddingException("ElGamalCipher supports no padding.") ; } protected int engineGetBlockSize( ) { // cipher의 상태를 근거로한 if ( mState == Cipher.DECRYPT_MODE ) //적당한 크기를 return return mCipherBlockSize ; else return mPlainBlockSize ; } DB Lab.
plaintext ElGamal Encryption Public Key ciphertext 암호화 Key(p) size의 2배 ElGamal Decryption 0 0 0 Public Key Decrypted plaintext ElGamalCipher에서의 Block handling DB Lab.
Cont’d protected int engineGetOutputSize(int inLen) { int inBlocks ; int outLength ; if (mState == Cipher.ENCRYPT_MODE) { inBlocks = ( inLen + getBufferedDataLength() + mPlainBlockSize - 1) / mPlainBlockSize ; outLength = inBlocks * mCipherBlocksize ; } else { inBlocks = (inLen + getBufferedDataLengrh() + mCipherBlockSize - 1) / mCipherBlockSize ; outLength = inBlocks * mPlainBlockSize ; } return outLength ; } protected byte[] engineGetIV( ) { return null ; } //ElGamal은 ECB mode에서만 작동 하기 때문에 null 값을 return Output buffer size의 계산 DB Lab.
Cont’d // block 크기를 계산하기 위하여 calculateBlockSize() 메소드 이용 protected void engineInit ( int opmode, Key key, SecureRandom random) throws InvalidkeyException { if ( opmode == Cipher.ENCRYPT_MODE ) if ( !(key instanceof ElGamalPublicKey)) throw new InvalidkeyException("I didn't get an ElGamalPublicKey."); else if (opmode == Cipher.DECRYPT_MODE) if ( !(key instanceof ElGamalPrivatekey)) throw new InvalidKeyException("I didn't get an ElGamalPrivatekey.") ; else throw new IllegalArgumentException("Bad mode: " + opmode); // key 값이 정확한지 검사 mState = opmode; mKey = key; mSecureRandom = random ; calculateBlockSizes(key) ; } // block 크기를 계산하기 DB Lab.
Cont’d protected void engineInti(int opmode, Key key, AlgorithmParameterSpec params, SecureRandom random) throws InvalidKeyException, InvalidAlgoritmParameterException { engineInit(opmode, key, random); //이전에 오버로드 된 버전만 호출 } protected void calculateBlockSizes(Key key) { int modulusLength = (( ElGamalKey)key).getP().bitLength() ; mPlainBlockSize = (modulusLength - 1) / 8 ; mCipherBlockSize = ((modulusLength + 7) / 8) * 2 ; } // 암호 초기화 이전에 평문과 암호문의 크기 계산 DB Lab.
Cont’d protected int engineTransformBlock(byte[] input, int inputOffset, int inputLength, byte[] output, int outputOffset ) throws ShortBufferException { If ( mState == Cipher.ENCRYPT_MODE) return encryptBlock(input, inputOffset, inputLength, output, outputOffset); else if (mState == Cipher.DECRYPT_MODE) return decryptBlock ( input, inputOffset, inputLength, output, outputOffset ) ; return 0 ; } // no padding을 지원하기 때문에 두 메소드는 같은 역할 protected int engineTransformBlockFinal(byte[] input, int inputOffset, int inputLength, byte[] output, int outputOffset ) throws ShortBufferException { If ( inputLength == 0 ) return 0 ; return engineTransformBlock(input, inputOffset, InputLength, output, outputOffset) ; } DB Lab.
Input byte로부터 message value 생성 Cont’d protected int encryptBlock ( byte[] in, int inOff, int inLen, byte[] out, int outOff) { byte[] messageBytes = new byte[mPlainBlockSize] ; int inputLength = Math.min(mPlainBlockSize, inLen) ; System.arraycopy(in, inOff, messageBytes, 0, inputLength) ; BigInteger m = new BigInteger(1, messageBytes) ; ElGamalPublicKey key = (ElGamalPublicKey)mKey ; BigInteger p = key.getP() ; BigInteger one = BigInteger.valueOf(1) ; BigInteger pminusone = p.subtract(one) ; BigInteger k ; // k 생성 do { k = new BigInteger ( p.bitLength() - 1, mSecureRandom) ; } while (k.gcd(pminusone).equals(one) == false) ; DB Lab.
Cont’d BigInteger a = key.getG().modPow(k, p); //a, b 계산 BigInteger b = key.getY().modPow(k, p).multiply(m).mod(p) ; byte[] aBytes = getBytes(a) ; // a, b를 output buffer에 복사 byte[] bBytes = getBytes(b) ; System.arraycopy(aBytes, 0, out, outOff + mCipherBlockSize / 2 - aBytes.length, aBytes.length) ; System.arraycopy(bBytes, 0, out, outOff + mCipherBlockSize - bBytes.length, bBytes.length); return mCipherBlockSize ; // encryptBlock()은 쓰여진 암호문 byte의 //수를 return } DB Lab.
Cont’d protected int decryptBlock(byte[] in, int inOff, int inLen, //복호처리byte[] out, int outOff ) { //pull out our key ElGamalPrivateKey key = (ElGamalPrivateKey)mKey ; BigInteger p = key.getP() ; // Extract a and b byte[] aBytes = new byte[mCipherBlockSize / 2] ; System.arraycopy(in, inOff, aBytes, 0, mCipherBlockSize / 2) ; byte[] bBytes = new byte[mCipherBlockSize / 2 ] ; System.arraycopy(in, inOff + mCipherBlockSize / 2, bBytes, 0, mCipherBlockSize /2) ; BigInteger a = new BigInteger(1, aBytes) ; BigInteger b = new BigInteger(1, bBytes) ; // 메시지 m 계산 BigInteger m = b.multiply(a.modPow(key.getX().negate(), p)).mod(p) ; DB Lab.
Cont’d byte[ ] messageBytes = getBytes(m) ; int gatedLength = Math.min(messageBytes.length, mPlainBlockSize) ; System.arraycopy(messageBytes, 0, out, outOff + mPlainBlockSize - gatedLength, gatedlength ) ; return mPlainBlockSize ; // 메시지 값의 byte를 output buffer에 복사 } // 복호된 byte의 수를 return protected byte[ ] getBytes( BigInteger big ) { byte[ ] bigBytes = big.toByteArray(); if (( big.bitLength() % 8 ) !=0 ) { return bigBytes ; } else { byte[ ] smallerBytes = new byte[big.bitLenth() / 8 ] ; System.arraycopy(bigBytes, 1, smallerBytes, 0, smallerBytes.length ) ; return smallerBytes ; } } } DB Lab.
추후 연구 • Padding 기법 구현 • 복호된 평문이 이전의 원문과 같아 지도록 • 예외처리를 향상 시켜라. • encryptBlcok( ) method • decryptBlock( ) method DB Lab.