001/* 002 * nimbus-jose-jwt 003 * 004 * Copyright 2012-2016, Connect2id Ltd. 005 * 006 * Licensed under the Apache License, Version 2.0 (the "License"); you may not use 007 * this file except in compliance with the License. You may obtain a copy of the 008 * License at 009 * 010 * http://www.apache.org/licenses/LICENSE-2.0 011 * 012 * Unless required by applicable law or agreed to in writing, software distributed 013 * under the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR 014 * CONDITIONS OF ANY KIND, either express or implied. See the License for the 015 * specific language governing permissions and limitations under the License. 016 */ 017 018package com.nimbusds.jose.util; 019 020 021import java.io.ByteArrayInputStream; 022import java.security.MessageDigest; 023import java.security.NoSuchAlgorithmException; 024import java.security.cert.*; 025 026 027/** 028 * X.509 certificate utilities. 029 * 030 * @author Vladimir Dzhuvinov 031 * @version 2018-06-07 032 */ 033public class X509CertUtils { 034 035 036 /** 037 * The PEM start marker. 038 */ 039 private static final String PEM_BEGIN_MARKER = "-----BEGIN CERTIFICATE-----"; 040 041 042 /** 043 * The PEM end marker. 044 */ 045 private static final String PEM_END_MARKER = "-----END CERTIFICATE-----"; 046 047 048 /** 049 * Parses a DER-encoded X.509 certificate. 050 * 051 * @param derEncodedCert The DER-encoded X.509 certificate, as a byte 052 * array. May be {@code null}. 053 * 054 * @return The X.509 certificate, {@code null} if parsing failed. 055 */ 056 public static X509Certificate parse(final byte[] derEncodedCert) { 057 058 if (derEncodedCert == null || derEncodedCert.length == 0) { 059 return null; 060 } 061 062 final Certificate cert; 063 try { 064 CertificateFactory cf = CertificateFactory.getInstance("X.509"); 065 cert = cf.generateCertificate(new ByteArrayInputStream(derEncodedCert)); 066 } catch (CertificateException e) { 067 return null; 068 } 069 070 if (! (cert instanceof X509Certificate)) { 071 return null; 072 } 073 074 return (X509Certificate)cert; 075 } 076 077 078 /** 079 * Parses a PEM-encoded X.509 certificate. 080 * 081 * @param pemEncodedCert The PEM-encoded X.509 certificate, as a 082 * string. May be {@code null}. 083 * 084 * @return The X.509 certificate, {@code null} if parsing failed. 085 */ 086 public static X509Certificate parse(final String pemEncodedCert) { 087 088 if (pemEncodedCert == null || pemEncodedCert.isEmpty()) { 089 return null; 090 } 091 092 final int markerStart = pemEncodedCert.indexOf(PEM_BEGIN_MARKER); 093 094 if (markerStart < 0) { 095 return null; 096 } 097 098 String buf = pemEncodedCert.substring(markerStart + PEM_BEGIN_MARKER.length()); 099 100 final int markerEnd = buf.indexOf(PEM_END_MARKER); 101 102 if (markerEnd < 0) { 103 return null; 104 } 105 106 buf = buf.substring(0, markerEnd); 107 108 buf = buf.replaceAll("\\s", ""); 109 110 return parse(new Base64(buf).decode()); 111 } 112 113 114 /** 115 * Returns the specified X.509 certificate as PEM-encoded string. 116 * 117 * @param cert The X.509 certificate. Must not be {@code null}. 118 * 119 * @return The PEM-encoded X.509 certificate, {@code null} if encoding 120 * failed. 121 */ 122 public static String toPEMString(final X509Certificate cert) { 123 124 return toPEMString(cert, true); 125 } 126 127 128 /** 129 * Returns the specified X.509 certificate as PEM-encoded string. 130 * 131 * @param cert The X.509 certificate. Must not be 132 * {@code null}. 133 * @param withLineBreaks {@code false} to suppress line breaks. 134 * 135 * @return The PEM-encoded X.509 certificate, {@code null} if encoding 136 * failed. 137 */ 138 public static String toPEMString(final X509Certificate cert, final boolean withLineBreaks) { 139 140 StringBuilder sb = new StringBuilder(); 141 sb.append(PEM_BEGIN_MARKER); 142 143 if (withLineBreaks) 144 sb.append('\n'); 145 146 try { 147 sb.append(Base64.encode(cert.getEncoded()).toString()); 148 } catch (CertificateEncodingException e) { 149 return null; 150 } 151 152 if (withLineBreaks) 153 sb.append('\n'); 154 155 sb.append(PEM_END_MARKER); 156 return sb.toString(); 157 } 158 159 160 /** 161 * Computes the X.509 certificate SHA-256 thumbprint ({@code x5t#S256}). 162 * 163 * @param cert The X.509 certificate. Must not be {@code null}. 164 * 165 * @return The SHA-256 thumbprint, BASE64URL-encoded, {@code null} if 166 * a certificate encoding exception is encountered. 167 */ 168 public static Base64URL computeSHA256Thumbprint(final X509Certificate cert) { 169 170 try { 171 byte[] derEncodedCert = cert.getEncoded(); 172 MessageDigest sha256 = MessageDigest.getInstance("SHA-256"); 173 return Base64URL.encode(sha256.digest(derEncodedCert)); 174 } catch (NoSuchAlgorithmException | CertificateEncodingException e) { 175 return null; 176 } 177 } 178}