package io.netty.handler.ssl;
import java.math.BigInteger;
import java.security.Principal;
import java.security.PublicKey;
import java.security.cert.CertificateEncodingException;
import java.security.cert.X509Certificate;
import java.util.Arrays;
import java.util.Date;
import java.util.Set;
import io.netty.buffer.ByteBuf;
import io.netty.buffer.ByteBufAllocator;
import io.netty.buffer.Unpooled;
import io.netty.util.CharsetUtil;
import io.netty.util.IllegalReferenceCountException;
import io.netty.util.internal.ObjectUtil;
public final class PemX509Certificate extends X509Certificate implements PemEncoded {
private static final byte[] BEGIN_CERT = "-----BEGIN CERTIFICATE-----\n".getBytes(CharsetUtil.US_ASCII);
private static final byte[] END_CERT = "\n-----END CERTIFICATE-----\n".getBytes(CharsetUtil.US_ASCII);
static PemEncoded toPEM(ByteBufAllocator allocator, boolean useDirect,
X509Certificate... chain) throws CertificateEncodingException {
if (chain == null || chain.length == 0) {
throw new IllegalArgumentException("X.509 certificate chain can't be null or empty");
}
if (chain.length == 1) {
X509Certificate first = chain[0];
if (first instanceof PemEncoded) {
return ((PemEncoded) first).retain();
}
}
boolean success = false;
ByteBuf pem = null;
try {
for (X509Certificate cert : chain) {
if (cert == null) {
throw new IllegalArgumentException("Null element in chain: " + Arrays.toString(chain));
}
if (cert instanceof PemEncoded) {
pem = append(allocator, useDirect, (PemEncoded) cert, chain.length, pem);
} else {
pem = append(allocator, useDirect, cert, chain.length, pem);
}
}
PemValue value = new PemValue(pem, false);
success = true;
return value;
} finally {
if (!success && pem != null) {
pem.release();
}
}
}
private static ByteBuf append(ByteBufAllocator allocator, boolean useDirect,
PemEncoded encoded, int count, ByteBuf pem) {
ByteBuf content = encoded.content();
if (pem == null) {
pem = newBuffer(allocator, useDirect, content.readableBytes() * count);
}
pem.writeBytes(content.slice());
return pem;
}
private static ByteBuf append(ByteBufAllocator allocator, boolean useDirect,
X509Certificate cert, int count, ByteBuf pem) throws CertificateEncodingException {
ByteBuf encoded = Unpooled.wrappedBuffer(cert.getEncoded());
try {
ByteBuf base64 = SslUtils.toBase64(allocator, encoded);
try {
if (pem == null) {
pem = newBuffer(allocator, useDirect,
(BEGIN_CERT.length + base64.readableBytes() + END_CERT.length) * count);
}
pem.writeBytes(BEGIN_CERT);
pem.writeBytes(base64);
pem.writeBytes(END_CERT);
} finally {
base64.release();
}
} finally {
encoded.release();
}
return pem;
}
private static ByteBuf newBuffer(ByteBufAllocator allocator, boolean useDirect, int initialCapacity) {
return useDirect ? allocator.directBuffer(initialCapacity) : allocator.buffer(initialCapacity);
}
public static PemX509Certificate valueOf(byte[] key) {
return valueOf(Unpooled.wrappedBuffer(key));
}
public static PemX509Certificate valueOf(ByteBuf key) {
return new PemX509Certificate(key);
}
private final ByteBuf content;
private PemX509Certificate(ByteBuf content) {
this.content = ObjectUtil.checkNotNull(content, "content");
}
@Override
public boolean isSensitive() {
return false;
}
@Override
public int refCnt() {
return content.refCnt();
}
@Override
public ByteBuf content() {
int count = refCnt();
if (count <= 0) {
throw new IllegalReferenceCountException(count);
}
return content;
}
@Override
public PemX509Certificate copy() {
return replace(content.copy());
}
@Override
public PemX509Certificate duplicate() {
return replace(content.duplicate());
}
@Override
public PemX509Certificate retainedDuplicate() {
return replace(content.retainedDuplicate());
}
@Override
public PemX509Certificate replace(ByteBuf content) {
return new PemX509Certificate(content);
}
@Override
public PemX509Certificate retain() {
content.retain();
return this;
}
@Override
public PemX509Certificate retain(int increment) {
content.retain(increment);
return this;
}
@Override
public PemX509Certificate touch() {
content.touch();
return this;
}
@Override
public PemX509Certificate touch(Object hint) {
content.touch(hint);
return this;
}
@Override
public boolean release() {
return content.release();
}
@Override
public boolean release(int decrement) {
return content.release(decrement);
}
@Override
public byte[] getEncoded() {
throw new UnsupportedOperationException();
}
@Override
public boolean hasUnsupportedCriticalExtension() {
throw new UnsupportedOperationException();
}
@Override
public Set<String> getCriticalExtensionOIDs() {
throw new UnsupportedOperationException();
}
@Override
public Set<String> getNonCriticalExtensionOIDs() {
throw new UnsupportedOperationException();
}
@Override
public byte[] getExtensionValue(String oid) {
throw new UnsupportedOperationException();
}
@Override
public void checkValidity() {
throw new UnsupportedOperationException();
}
@Override
public void checkValidity(Date date) {
throw new UnsupportedOperationException();
}
@Override
public int getVersion() {
throw new UnsupportedOperationException();
}
@Override
public BigInteger getSerialNumber() {
throw new UnsupportedOperationException();
}
@Override
public Principal getIssuerDN() {
throw new UnsupportedOperationException();
}
@Override
public Principal getSubjectDN() {
throw new UnsupportedOperationException();
}
@Override
public Date getNotBefore() {
throw new UnsupportedOperationException();
}
@Override
public Date getNotAfter() {
throw new UnsupportedOperationException();
}
@Override
public byte[] getTBSCertificate() {
throw new UnsupportedOperationException();
}
@Override
public byte[] getSignature() {
throw new UnsupportedOperationException();
}
@Override
public String getSigAlgName() {
throw new UnsupportedOperationException();
}
@Override
public String getSigAlgOID() {
throw new UnsupportedOperationException();
}
@Override
public byte[] getSigAlgParams() {
throw new UnsupportedOperationException();
}
@Override
public boolean[] getIssuerUniqueID() {
throw new UnsupportedOperationException();
}
@Override
public boolean[] getSubjectUniqueID() {
throw new UnsupportedOperationException();
}
@Override
public boolean[] getKeyUsage() {
throw new UnsupportedOperationException();
}
@Override
public int getBasicConstraints() {
throw new UnsupportedOperationException();
}
@Override
public void verify(PublicKey key) {
throw new UnsupportedOperationException();
}
@Override
public void verify(PublicKey key, String sigProvider) {
throw new UnsupportedOperationException();
}
@Override
public PublicKey getPublicKey() {
throw new UnsupportedOperationException();
}
@Override
public boolean equals(Object o) {
if (o == this) {
return true;
} else if (!(o instanceof PemX509Certificate)) {
return false;
}
PemX509Certificate other = (PemX509Certificate) o;
return content.equals(other.content);
}
@Override
public int hashCode() {
return content.hashCode();
}
@Override
public String toString() {
return content.toString(CharsetUtil.UTF_8);
}
}