Java tutorial
/* * Copyright (c) 2010 SimpleServer authors (see CONTRIBUTORS) * * Permission is hereby granted, free of charge, to any person obtaining a copy * of this software and associated documentation files (the "Software"), to deal * in the Software without restriction, including without limitation the rights * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell * copies of the Software, and to permit persons to whom the Software is * furnished to do so, subject to the following conditions: * The above copyright notice and this permission notice shall be included in * all copies or substantial portions of the Software. * * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN * THE SOFTWARE. */ package simpleserver.stream; import java.io.InputStream; import java.io.OutputStream; import java.io.UnsupportedEncodingException; import java.math.BigInteger; import java.security.GeneralSecurityException; import java.security.Key; import java.security.KeyFactory; import java.security.KeyPair; import java.security.KeyPairGenerator; import java.security.MessageDigest; import java.security.NoSuchAlgorithmException; import java.security.PrivateKey; import java.security.PublicKey; import java.security.spec.X509EncodedKeySpec; import java.util.Arrays; import javax.crypto.Cipher; import javax.crypto.KeyGenerator; import javax.crypto.SecretKey; import javax.crypto.spec.SecretKeySpec; import org.bouncycastle.crypto.BufferedBlockCipher; import org.bouncycastle.crypto.engines.AESFastEngine; import org.bouncycastle.crypto.io.CipherInputStream; import org.bouncycastle.crypto.io.CipherOutputStream; import org.bouncycastle.crypto.modes.CFBBlockCipher; import org.bouncycastle.crypto.params.KeyParameter; import org.bouncycastle.crypto.params.ParametersWithIV; public abstract class Encryption { protected SecretKey sharedKey; protected byte[] challengeToken; public static class ServerEncryption extends Encryption { private PublicKey publicKey; public ServerEncryption() { try { sharedKey = generateSharedKey(); } catch (NoSuchAlgorithmException e) { e.printStackTrace(); } } public byte[] getEncryptedSharedKey() { try { return encrypt(publicKey, sharedKey.getEncoded(), 1); } catch (Exception e) { e.printStackTrace(); } return null; } public void setPublicKey(byte[] keyBytes) { try { publicKey = getPublicKey(keyBytes); } catch (Exception e) { e.printStackTrace(); } } public byte[] encryptChallengeToken() { try { return encrypt(publicKey, challengeToken, 1); } catch (GeneralSecurityException e) { e.printStackTrace(); } return null; } } public static class ClientEncryption extends Encryption { private static KeyPair keyPair; public static void generateKeyPair() throws NoSuchAlgorithmException { KeyPairGenerator keyGenerator = KeyPairGenerator.getInstance("RSA"); keyGenerator.initialize(1024); keyPair = keyGenerator.generateKeyPair(); } public void setEncryptedSharedKey(byte[] encryptedSharedKey) { try { sharedKey = decryptSharedKey(encryptedSharedKey, keyPair.getPrivate()); } catch (GeneralSecurityException e) { e.printStackTrace(); } } public String getLoginHash(String name) throws NoSuchAlgorithmException, UnsupportedEncodingException { return new BigInteger(loginHash(name, keyPair.getPublic(), sharedKey)).toString(16); } public byte[] getPublicKey() { return keyPair.getPublic().getEncoded(); } public boolean checkChallengeToken(byte[] challengeTokenResponse) { try { return Arrays.equals(encrypt(keyPair.getPrivate(), challengeTokenResponse, 2), challengeToken); } catch (GeneralSecurityException e) { return false; } } } public BufferedBlockCipher getStreamCipher(boolean out) { BufferedBlockCipher cipher = new BufferedBlockCipher(new CFBBlockCipher(new AESFastEngine(), 8)); cipher.init(out, new ParametersWithIV(new KeyParameter(sharedKey.getEncoded()), sharedKey.getEncoded(), 0, 16)); return cipher; } public OutputStream encryptedOutputStream(OutputStream stream) { try { return new CipherOutputStream(stream, getStreamCipher(true)); } catch (Exception e) { e.printStackTrace(); } return null; } public InputStream encryptedInputStream(InputStream stream) { try { return new CipherInputStream(stream, getStreamCipher(false)); } catch (Exception e) { e.printStackTrace(); } return null; } public void setChallengeToken(byte[] challangeToken) { challengeToken = challangeToken; } private static PublicKey getPublicKey(byte[] keyBytes) throws GeneralSecurityException { return KeyFactory.getInstance("RSA").generatePublic(new X509EncodedKeySpec(keyBytes)); } private static SecretKey generateSharedKey() throws NoSuchAlgorithmException { KeyGenerator keyGenerator = KeyGenerator.getInstance("AES"); keyGenerator.init(128); return keyGenerator.generateKey(); } private static byte[] loginHash(String name, PublicKey publicKey, SecretKey sharedKey) throws NoSuchAlgorithmException, UnsupportedEncodingException { return getHash("SHA-1", new byte[][] { name.getBytes("ISO_8859_1"), sharedKey.getEncoded(), publicKey.getEncoded() }); } private static byte[] getHash(String algrithm, byte[][] data) throws NoSuchAlgorithmException { MessageDigest digest = MessageDigest.getInstance(algrithm); for (byte[] bytes : data) { digest.update(bytes); } return digest.digest(); } private static Cipher getCipher(String transformation, Key key, int mode) throws GeneralSecurityException { Cipher cipher; cipher = Cipher.getInstance(transformation); cipher.init(mode, key); return cipher; } private static byte[] encrypt(Key key, byte[] data, int opmode) throws GeneralSecurityException { return getCipher(key.getAlgorithm(), key, opmode).doFinal(data); } private static SecretKey decryptSharedKey(byte[] encryptedKey, PrivateKey privateKey) throws GeneralSecurityException { return new SecretKeySpec(encrypt(privateKey, encryptedKey, 2), "RC4"); } }