Package org.platformlayer.auth.crypto

Source Code of org.platformlayer.auth.crypto.SecretStore

package org.platformlayer.auth.crypto;

import java.io.ByteArrayInputStream;
import java.io.ByteArrayOutputStream;
import java.io.Closeable;
import java.io.DataInputStream;
import java.io.DataOutputStream;
import java.io.EOFException;
import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStream;
import java.security.PrivateKey;
import java.security.PublicKey;
import java.security.cert.X509Certificate;
import java.util.Arrays;

import javax.crypto.SecretKey;

import org.platformlayer.auth.ProjectInfo;
import org.platformlayer.auth.UserEntity;
import org.platformlayer.crypto.CryptoUtils;
import org.platformlayer.crypto.RsaUtils;
import org.platformlayer.model.ProjectAuthorization;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

import com.fathomdb.crypto.CryptoKey;
import com.fathomdb.crypto.FathomdbCrypto;
import com.fathomdb.crypto.OpenSshUtils;
import com.fathomdb.hash.Md5Hash;
import com.fathomdb.io.IoUtils;
import com.google.common.io.ByteStreams;

public class SecretStore {
  private static final Logger log = LoggerFactory.getLogger(SecretStore.class);

  @Deprecated
  public static final byte ASYMETRIC_SYSTEM_KEY = 1;

  public static final byte USER_KEY = 2;
  public static final byte USER_PASSWORD = 3;
  public static final byte TOKEN = 4;
  public static final byte ASYMETRIC_USER_KEY = 5;
  public static final byte PROJECT_KEY = 6;
  public static final byte ASYMETRIC_PROJECT_KEY = 7;

  public static final byte ASYMETRIC_KEY = 8;

  byte[] encoded;

  public SecretStore(byte[] encoded) {
    this.encoded = encoded;
  }

  public Writer buildWriter() {
    return new Writer(new ByteArrayOutputStream());
  }

  public void appendContents(Writer writer) throws IOException {
    writer.dos.flush();
    writer.os.flush();

    ByteArrayOutputStream os = (ByteArrayOutputStream) writer.os;
    byte[] added = os.toByteArray();

    this.encoded = CryptoUtils.concat(encoded, added);
  }

  public byte[] getEncoded() {
    return encoded;
  }

  public static class Reader implements Closeable {
    final DataInputStream in;

    public Reader(InputStream is) {
      this.in = new DataInputStream(is);
    }

    @Override
    public void close() throws IOException {
      in.close();
    }

    public SecretKey read(SecretStoreVisitor visitor) throws IOException {
      while (true) {
        byte type;

        try {
          type = in.readByte();
        } catch (EOFException e) {
          break;
        }

        switch (type) {
        case ASYMETRIC_SYSTEM_KEY: {
          int keyId = readEncoded(in);
          byte[] data = readArray(in);
          visitor.visitAsymetricSystemKey(keyId, data);
          break;
        }

        case ASYMETRIC_KEY: {
          byte[] publicKeySignature = readArray(in);
          byte[] data = readArray(in);
          visitor.visitGenericAsymetricKey(publicKeySignature, data);
          break;
        }

        case ASYMETRIC_USER_KEY: {
          int userId = readEncoded(in);
          byte[] data = readArray(in);
          visitor.visitAsymetricUserKey(userId, data);
          break;
        }

        case USER_KEY: {
          int userId = readEncoded(in);
          byte[] data = readArray(in);
          visitor.visitUserKey(userId, data);
          break;
        }

        case PROJECT_KEY: {
          int projectId = readEncoded(in);
          byte[] data = readArray(in);
          visitor.visitProjectKey(projectId, data);
          break;
        }

        case USER_PASSWORD: {
          // int userId = readEncoded(in);
          byte[] salt = readArray(in);
          byte[] data = readArray(in);

          visitor.visitPassword(salt, data);
          break;
        }

        case TOKEN: {
          int tokenId = readEncoded(in);
          byte[] data = readArray(in);
          // Ignore
          // visitor.visitToken(tokenId, data);
          break;
        }

        default: {
          log.warn("Unexpected format: " + ((int) type));
          throw new IllegalArgumentException("Unexpected format");
        }
        }
      }

      return null;
    }

  }

  public static class Writer {
    final DataOutputStream dos;
    final OutputStream os;

    public Writer(OutputStream os) {
      this.os = os;
      this.dos = new DataOutputStream(os);
    }

    public void close() throws IOException {
      dos.close();
    }

    public void writeLockedByProjectKey(byte[] plaintext, int projectId, CryptoKey projectSecret)
        throws IOException {
      dos.writeByte(PROJECT_KEY);
      byte[] encrypted = FathomdbCrypto.encrypt(projectSecret, plaintext);
      writeEncoded(dos, projectId);
      writeArray(dos, encrypted);
    }

    public void writeLockedByUserKey(byte[] plaintext, int userId, CryptoKey userSecret) throws IOException {
      dos.writeByte(USER_KEY);
      byte[] encrypted = FathomdbCrypto.encrypt(userSecret, plaintext);
      writeEncoded(dos, userId);
      writeArray(dos, encrypted);
    }

    public void writeUserPassword(byte[] plaintext, String password) throws IOException {
      dos.writeByte(USER_PASSWORD);

      byte[] salt = CryptoUtils.generateSecureRandom(16);
      CryptoKey derivedKey = FathomdbCrypto.deriveKey(salt, password);

      byte[] encrypted = FathomdbCrypto.encrypt(derivedKey, plaintext);
      // writeEncoded(dos, userId);
      writeArray(dos, salt);
      writeArray(dos, encrypted);
    }

    public void writeLockedByToken(byte[] plaintext, int tokenId, byte[] tokenSecret) throws IOException {
      dos.writeByte(TOKEN);
      writeEncoded(dos, tokenId);

      byte[] encrypted = xorByteArrays(plaintext, tokenSecret);
      writeArray(dos, encrypted);
    }

    public void writeAsymetricSystemKey(byte[] plaintext, int backend, PublicKey publicKey) throws IOException {
      dos.writeByte(ASYMETRIC_SYSTEM_KEY);
      byte[] encrypted = RsaUtils.encrypt(publicKey, plaintext);
      writeEncoded(dos, backend);
      writeArray(dos, encrypted);
    }

    public void writeAsymetricUserKey(byte[] plaintext, int userId, PublicKey publicKey) throws IOException {
      dos.writeByte(ASYMETRIC_USER_KEY);
      byte[] encrypted = RsaUtils.encrypt(publicKey, plaintext);
      writeEncoded(dos, userId);
      writeArray(dos, encrypted);
    }

    public void writeGenericAsymetricKey(byte[] plaintext, PublicKey publicKey) throws IOException {
      dos.writeByte(ASYMETRIC_KEY);
      byte[] encrypted = RsaUtils.encrypt(publicKey, plaintext);
      Md5Hash signature = OpenSshUtils.getSignature(publicKey);
      writeArray(dos, signature.toByteArray());
      writeArray(dos, encrypted);
    }

    public void writeAsymetricProjectKey(byte[] plaintext, int projectId, PublicKey publicKey) throws IOException {
      dos.writeByte(ASYMETRIC_PROJECT_KEY);
      byte[] encrypted = RsaUtils.encrypt(publicKey, plaintext);
      writeEncoded(dos, projectId);
      writeArray(dos, encrypted);
    }

  }

  public static byte[] xorByteArrays(byte[] a, byte[] b) {
    if (a.length != b.length) {
      throw new IllegalArgumentException();
    }
    int len = a.length;
    byte[] ret = new byte[len];
    for (int i = 0; i < len; i++) {
      ret[i] = a[i];
      ret[i] ^= b[i];
    }
    return ret;
  }

  private static byte[] readArray(DataInputStream dis) throws IOException {
    int length = readEncoded(dis);
    byte[] array = new byte[length];
    ByteStreams.readFully(dis, array, 0, length);
    return array;
  }

  private static void writeArray(DataOutputStream dos, byte[] data) throws IOException {
    writeEncoded(dos, data.length);
    dos.write(data);
  }

  private static int readEncoded(DataInputStream dis) throws IOException {
    int v = 0;
    int shift = 0;

    do {
      byte k = dis.readByte();

      v |= (k & 0x7f) << shift;
      if ((k & 0x80) == 0) {
        return v;
      }

      shift += 7;
    } while (true);
  }

  private static void writeEncoded(DataOutputStream dos, int v) throws IOException {
    do {
      byte k = (byte) (v & 0x7f);
      v >>= 7;

      if (v != 0) {
        k |= 0x80;
      }
      dos.write(k);
    } while (v != 0);
  }

  public static void read(byte[] data, SecretStoreVisitor visitor) throws IOException {
    SecretStore.Reader reader = null;
    try {
      reader = new SecretStore.Reader(new ByteArrayInputStream(data));
      reader.read(visitor);
    } finally {
      IoUtils.safeClose(reader);
    }

  }

  public CryptoKey getSecretFromUser(final UserEntity user) {
    SecretStoreDecoder visitor = new SecretStoreDecoder() {
      @Override
      public void visitUserKey(int userId, byte[] data) {
        if (userId == user.getId()) {
          setSecretKey(decryptSymetricKey(user.getUserSecret(), data));
        }
      }

      @Override
      public void visitAsymetricUserKey(int userId, byte[] data) {
        if (userId == user.getId()) {
          PrivateKey privateKey = user.getPrivateKey();
          setSecretKey(decryptAsymetricKey(privateKey, data));
        }
      }
    };
    try {
      read(encoded, visitor);
    } catch (IOException e) {
      throw new IllegalArgumentException("Secret data is corrupted", e);
    }
    return visitor.getSecretKey();
  }

  public CryptoKey getSecretFromPassword(int userId, final String password) {
    SecretStoreDecoder visitor = new SecretStoreDecoder() {
      @Override
      public void visitPassword(byte[] salt, byte[] data) {
        CryptoKey secretKey = FathomdbCrypto.deriveKey(salt, password);
        setSecretKey(decryptSymetricKey(secretKey, data));
      }
    };
    try {
      read(encoded, visitor);
    } catch (IOException e) {
      throw new IllegalArgumentException("Secret data is corrupted", e);
    }
    return visitor.getSecretKey();
  }

  public CryptoKey getSecretFromProject(final ProjectInfo project) {
    return getSecretFromProject(project.getId(), project.getProjectSecret());
  }

  public CryptoKey getSecretFromProject(final ProjectAuthorization project) {
    return getSecretFromProject(project.getId(), project.getProjectSecret());
  }

  public CryptoKey getSecretFromProject(final int projectId, final CryptoKey projectSecret) {
    SecretStoreDecoder visitor = new SecretStoreDecoder() {
      @Override
      public void visitProjectKey(int itemProjectId, byte[] itemSecret) {
        if (projectId == itemProjectId) {
          setSecretKey(decryptSymetricKey(projectSecret, itemSecret));
        }
      }
    };
    try {
      read(encoded, visitor);
    } catch (IOException e) {
      throw new IllegalArgumentException("Secret data is corrupted", e);
    }
    return visitor.getSecretKey();
  }

  // public CryptoKey getSecretFromToken(final int tokenId, final byte[] tokenSecret) {
  // SecretStoreDecoder visitor = new SecretStoreDecoder() {
  // @Override
  // public void visitToken(int itemTokenId, byte[] itemData) {
  // if (itemTokenId == tokenId) {
  // // We want this to be reversible; so we XOR it.
  // // Both keys are random, so this is secure
  // if (tokenSecret.length == itemData.length) {
  // byte[] key = SecretStore.xorByteArrays(itemData, tokenSecret);
  // setSecretKey(FathomdbCrypto.deserializeKey(key));
  // }
  // }
  // }
  // };
  //
  // try {
  // read(encoded, visitor);
  // } catch (IOException e) {
  // throw new IllegalArgumentException("Secret data is corrupted", e);
  // }
  // return visitor.getSecretKey();
  // }

  // static class TokenSecretFinder extends SecretStoreVisitor {
  // final int tokenId;
  // final CryptoKey userSecret;
  //
  // public TokenSecretFinder(int tokenId, CryptoKey userSecret) {
  // this.tokenId = tokenId;
  // this.userSecret = userSecret;
  // }
  //
  // public byte[] tokenSecret;
  //
  // @Override
  // public void visitToken(int tokenId, byte[] data) {
  // if (tokenId == this.tokenId) {
  // byte[] userSecretBytes = FathomdbCrypto.serialize(userSecret);
  // this.tokenSecret = SecretStore.xorByteArrays(userSecretBytes, data);
  // }
  // }
  // }

  // public byte[] getTokenSecretWithUserSecret(int tokenId, CryptoKey userSecret) {
  // TokenSecretFinder visitor = new TokenSecretFinder(tokenId, userSecret);
  //
  // try {
  // read(encoded, visitor);
  // } catch (IOException e) {
  // throw new IllegalArgumentException("Secret data is corrupted", e);
  // }
  // return visitor.tokenSecret;
  // }

  public byte[] findChallengeForCertificate(X509Certificate[] certificateChain) {
    if (certificateChain.length == 0) {
      throw new IllegalArgumentException();
    }

    X509Certificate certificate = certificateChain[0];
    Md5Hash signature = OpenSshUtils.getSignature(certificate.getPublicKey());
    final byte[] signatureBytes = signature.toByteArray();

    SecretStoreDecoder visitor = new SecretStoreDecoder() {
      @Override
      public void visitGenericAsymetricKey(byte[] publicKeySignature, byte[] data) {
        if (Arrays.equals(publicKeySignature, signatureBytes)) {
          result = data;
        }
      }
    };

    try {
      read(encoded, visitor);
    } catch (IOException e) {
      throw new IllegalArgumentException("Secret data is corrupted", e);
    }

    return (byte[]) visitor.result;
  }

}
TOP

Related Classes of org.platformlayer.auth.crypto.SecretStore

TOP
Copyright © 2018 www.massapi.com. All rights reserved.
All source code are property of their respective owners. Java is a trademark of Sun Microsystems, Inc and owned by ORACLE Inc. Contact coftware#gmail.com.