Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

feat(certificate): support PEM format #1070

Merged
merged 1 commit into from
Mar 15, 2021
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
@@ -0,0 +1,26 @@
/**
* Copyright (C) 2015 The Gravitee team (http://gravitee.io)
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package io.gravitee.am.certificate.api;

/**
* @author Titouan COMPIEGNE (titouan.compiegne at graviteesource.com)
* @author GraviteeSource Team
*/
public interface CertificateFormat {

String SSH_RSA = "SSH-RSA";
String PEM = "PEM";
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,66 @@
/**
* Copyright (C) 2015 The Gravitee team (http://gravitee.io)
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package io.gravitee.am.certificate.api;

import java.util.Map;

/**
* @author Titouan COMPIEGNE (titouan.compiegne at graviteesource.com)
* @author GraviteeSource Team
*/
public class CertificateKey {

private String fmt;
private String payload;
private Map<String, Object> metadata;

public CertificateKey() {
}

public CertificateKey(String fmt, String payload) {
this.fmt = fmt;
this.payload = payload;
}

public CertificateKey(String fmt, String payload, Map<String, Object> metadata) {
this(fmt, payload);
this.metadata = metadata;
}

public String getFmt() {
return fmt;
}

public void setFmt(String fmt) {
this.fmt = fmt;
}

public String getPayload() {
return payload;
}

public void setPayload(String payload) {
this.payload = payload;
}

public Map<String, Object> getMetadata() {
return metadata;
}

public void setMetadata(Map<String, Object> metadata) {
this.metadata = metadata;
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,9 @@
import io.reactivex.Flowable;
import io.reactivex.Single;

import java.util.Collections;
import java.util.List;

/**
* @author Titouan COMPIEGNE (titouan.compiegne at graviteesource.com)
* @author GraviteeSource Team
Expand All @@ -34,4 +37,8 @@ public interface CertificateProvider {
CertificateMetadata certificateMetadata();

String signatureAlgorithm();

default Single<List<CertificateKey>> publicKeys() {
return Single.just(Collections.emptyList());
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,57 @@
/**
* Copyright (C) 2015 The Gravitee team (http://gravitee.io)
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package io.gravitee.am.certificate.api;

import java.io.ByteArrayOutputStream;
import java.io.IOException;
import java.io.OutputStream;
import java.math.BigInteger;
import java.security.interfaces.RSAPublicKey;
import java.util.Base64;

/**
* @author Titouan COMPIEGNE (titouan.compiegne at graviteesource.com)
* @author GraviteeSource Team
*/
public final class RSAKeyUtils {

public static String toSSHRSAString(RSAPublicKey publicKey) throws IOException {
ByteArrayOutputStream out = new ByteArrayOutputStream();
/* encode the "ssh-rsa" string */
byte[] sshrsa = new byte[]{0, 0, 0, 7, 's', 's', 'h', '-', 'r', 's', 'a'};
out.write(sshrsa);
/* Encode the public exponent */
BigInteger e = publicKey.getPublicExponent();
byte[] data = e.toByteArray();
encodeUInt32(data.length, out);
out.write(data);
/* Encode the modulus */
BigInteger m = publicKey.getModulus();
data = m.toByteArray();
encodeUInt32(data.length, out);
out.write(data);
return Base64.getEncoder().encodeToString(out.toByteArray());
}

private static void encodeUInt32(int value, OutputStream out) throws IOException {
byte[] tmp = new byte[4];
tmp[0] = (byte)((value >>> 24) & 0xff);
tmp[1] = (byte)((value >>> 16) & 0xff);
tmp[2] = (byte)((value >>> 8) & 0xff);
tmp[3] = (byte)(value & 0xff);
out.write(tmp);
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,209 @@
/**
* Copyright (C) 2015 The Gravitee team (http://gravitee.io)
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package io.gravitee.am.certificate.api;

import java.io.ByteArrayInputStream;
import java.security.cert.CertificateEncodingException;
import java.security.cert.CertificateException;
import java.security.cert.CertificateFactory;
import java.security.cert.X509Certificate;
import java.util.Base64;

/**
* @author Titouan COMPIEGNE (titouan.compiegne at graviteesource.com)
* @author GraviteeSource Team
*/
public final class X509CertUtils {

private static final Base64.Encoder b64Enc = Base64.getEncoder();
private static final Base64.Decoder b64Dec = Base64.getDecoder();

/**
* The PEM start marker.
*/
public static final String PEM_BEGIN_MARKER = "-----BEGIN CERTIFICATE-----";

/**
* The PEM end marker.
*/
public static final String PEM_END_MARKER = "-----END CERTIFICATE-----";

/**
* Parses a DER-encoded X.509 certificate.
*
* @param derEncodedCert The DER-encoded X.509 certificate, as a byte
* array. May be {@code null}.
*
* @return The X.509 certificate, {@code null} if not specified or
* parsing failed.
*/
public static X509Certificate parse(final byte[] derEncodedCert) {

try {
return parseWithException(derEncodedCert);
} catch (CertificateException e) {
return null;
}
}


/**
* Parses a DER-encoded X.509 certificate with exception handling.
*
* @param derEncodedCert The DER-encoded X.509 certificate, as a byte
* array. Empty or {@code null} if not specified.
*
* @return The X.509 certificate, {@code null} if not specified.
*
* @throws CertificateException If parsing failed.
*/
public static X509Certificate parseWithException(final byte[] derEncodedCert)
throws CertificateException {

if (derEncodedCert == null || derEncodedCert.length == 0) {
return null;
}

CertificateFactory cf = CertificateFactory.getInstance("X.509");
final java.security.cert.Certificate cert = cf.generateCertificate(new ByteArrayInputStream(derEncodedCert));

if (! (cert instanceof X509Certificate)) {
throw new CertificateException("Not a X.509 certificate: " + cert.getType());
}

return (X509Certificate)cert;
}


/**
* Parses a PEM-encoded X.509 certificate.
*
* @param pemEncodedCert The PEM-encoded X.509 certificate, as a
* string. Empty or {@code null} if not
* specified.
*
* @return The X.509 certificate, {@code null} if parsing failed.
*/
public static X509Certificate parse(final String pemEncodedCert) {

if (pemEncodedCert == null || pemEncodedCert.isEmpty()) {
return null;
}

final int markerStart = pemEncodedCert.indexOf(PEM_BEGIN_MARKER);

if (markerStart < 0) {
return null;
}

String buf = pemEncodedCert.substring(markerStart + PEM_BEGIN_MARKER.length());

final int markerEnd = buf.indexOf(PEM_END_MARKER);

if (markerEnd < 0) {
return null;
}

buf = buf.substring(0, markerEnd);

buf = buf.replaceAll("\\s", "");

return parse(b64Dec.decode(buf));
}


/**
* Parses a PEM-encoded X.509 certificate with exception handling.
*
* @param pemEncodedCert The PEM-encoded X.509 certificate, as a
* string. Empty or {@code null} if not
* specified.
*
* @return The X.509 certificate, {@code null} if parsing failed.
*/
public static X509Certificate parseWithException(final String pemEncodedCert)
throws CertificateException {

if (pemEncodedCert == null || pemEncodedCert.isEmpty()) {
return null;
}

final int markerStart = pemEncodedCert.indexOf(PEM_BEGIN_MARKER);

if (markerStart < 0) {
throw new CertificateException("PEM begin marker not found");
}

String buf = pemEncodedCert.substring(markerStart + PEM_BEGIN_MARKER.length());

final int markerEnd = buf.indexOf(PEM_END_MARKER);

if (markerEnd < 0) {
throw new CertificateException("PEM end marker not found");
}

buf = buf.substring(0, markerEnd);

buf = buf.replaceAll("\\s", "");

return parseWithException(b64Dec.decode(buf));
}


/**
* Returns the specified X.509 certificate as PEM-encoded string.
*
* @param cert The X.509 certificate. Must not be {@code null}.
*
* @return The PEM-encoded X.509 certificate, {@code null} if encoding
* failed.
*/
public static String toPEMString(final X509Certificate cert) {
return toPEMString(cert, true);
}


/**
* Returns the specified X.509 certificate as PEM-encoded string.
*
* @param cert The X.509 certificate. Must not be
* {@code null}.
* @param withLineBreaks {@code false} to suppress line breaks.
*
* @return The PEM-encoded X.509 certificate, {@code null} if encoding
* failed.
*/
public static String toPEMString(final X509Certificate cert, final boolean withLineBreaks) {

StringBuilder sb = new StringBuilder();
sb.append(PEM_BEGIN_MARKER);

if (withLineBreaks)
sb.append('\n');

try {
sb.append(b64Enc.encodeToString(cert.getEncoded()));
} catch (CertificateEncodingException e) {
return null;
}

if (withLineBreaks)
sb.append('\n');

sb.append(PEM_END_MARKER);
return sb.toString();
}
}
Loading