package io.fathom.cloud.io;
import java.io.ByteArrayInputStream;
import java.io.Closeable;
import java.io.IOException;
import java.io.InputStream;
import java.math.BigInteger;
import java.security.KeyFactory;
import java.security.NoSuchAlgorithmException;
import java.security.PublicKey;
import java.security.spec.InvalidKeySpecException;
import java.security.spec.RSAPublicKeySpec;
import com.google.common.io.ByteStreams;
import com.google.common.io.Closeables;
public class Asn {
static class AsnInputStream implements Closeable {
final InputStream is;
static final int MAX_BUFFER_SIZE = 32768;
public AsnInputStream(InputStream is) {
super();
this.is = is;
}
@Override
public void close() throws IOException {
is.close();
}
int readUint8() throws IOException {
int v = is.read();
if (v == -1) {
throw new IOException("EOF");
}
return v;
}
long readPacked() throws IOException {
int b = readUint8();
if ((b & 0x80) == 0) {
return b & 0x7f;
} else {
int len = b & 0x7f;
long value = 0;
for (int i = 0; i < len; i++) {
b = readUint8();
value <<= 8;
value |= b;
}
return value;
}
}
// long readUint32() throws IOException {
// long value = readUint8();
// value <<= 8;
// value |= readUint8();
// value <<= 8;
// value |= readUint8();
// value <<= 8;
// value |= readUint8();
// return value;
// }
public byte[] readByteArray() throws IOException {
long length = readPacked();
if (length > MAX_BUFFER_SIZE) {
throw new IOException("Byte array too large");
}
byte[] buffer = new byte[(int) length];
ByteStreams.readFully(is, buffer, 0, (int) length);
return buffer;
}
// public String readString() throws IOException {
// return Utf8.toString(readByteArray());
// }
public BigInteger readInteger() throws IOException {
int tag = readTag();
if (tag != 0x02) {
throw new IOException("Expected INTEGER tag");
}
byte[] data = readByteArray();
if (data.length == 0) {
return BigInteger.ZERO;
}
return new BigInteger(data);
}
public int readTag() throws IOException {
int tag = readUint8();
return tag;
}
public int readSequenceTag() throws IOException {
int tag = readTag();
if (tag != 48) {
throw new IOException("Expected sequence tag");
}
return tag;
}
}
public static PublicKey readRsaPublicKey(byte[] data) throws IOException, InvalidKeySpecException {
Asn.AsnInputStream is = new Asn.AsnInputStream(new ByteArrayInputStream(data));
try {
is.readSequenceTag();
long length = is.readPacked();
final BigInteger modulus = is.readInteger();
final BigInteger publicExponent = is.readInteger();
final RSAPublicKeySpec rsaPubSpec = new RSAPublicKeySpec(modulus, publicExponent);
try {
KeyFactory rsaKeyFact = KeyFactory.getInstance("RSA");
return rsaKeyFact.generatePublic(rsaPubSpec);
} catch (NoSuchAlgorithmException e) {
throw new IllegalStateException("Error loading RSA provider", e);
}
} finally {
Closeables.closeQuietly(is);
}
}
}