【问题标题】:Write x509 certificate into PEM formatted string in java?在java中将x509证书写入PEM格式的字符串?
【发布时间】:2011-03-19 18:53:30
【问题描述】:

是否有一些高级方法可以将 X509Certificate 写入 PEM 格式的字符串? 目前我正在执行 x509cert.encode() 将其写入 DER 格式的字符串,然后对其进行 base 64 编码并附加页眉和页脚以创建 PEM 字符串,但这似乎很糟糕。特别是因为我也必须换行。

【问题讨论】:

    标签: java x509 pem der


    【解决方案1】:

    这还不错。 Java 不提供任何函数来编写 PEM 文件。你正在做的是正确的方法。甚至 KeyTool 也做同样的事情,

    BASE64Encoder encoder = new BASE64Encoder();
    out.println(X509Factory.BEGIN_CERT);
    encoder.encodeBuffer(cert.getEncoded(), out);
    out.println(X509Factory.END_CERT);
    

    如果你使用 BouncyCastle,你可以使用 PEMWriter 类在 PEM 中写出 X509 证书。

    【讨论】:

    • PEMWriter 现已弃用。 JcaPEMWriter 是替代它的较新类。
    • 你能包含out的声明吗?
    【解决方案2】:

    还没有看到有人提出 Java 8 的 Base64.getMimeEncoder 方法 - 实际上允许您像这样指定行长度 行分隔符:

    final Base64.Encoder encoder = Base64.getMimeEncoder(64, LINE_SEPARATOR.getBytes());
    

    我查看了这个 ^ 与标准编码器是否有任何区别,但我找不到任何东西。 javadoc 引用 RFC 2045 用于 BASIC 和 MIME 编码器,并添加了 RFC 4648 用于 BASIC。 AFAIK 这两个标准都使用相同的 Base64 字母表(表格看起来相同),因此如果您需要指定行长,则应该使用 MIME。

    这意味着在 Java 8 中,这可以通过以下方式完成:

    import java.security.cert.Certificate;
    import java.security.cert.CertificateEncodingException;
    import java.util.Base64;
    

    ...

    public static final String BEGIN_CERT = "-----BEGIN CERTIFICATE-----";
    public static final String END_CERT = "-----END CERTIFICATE-----";
    public final static String LINE_SEPARATOR = System.getProperty("line.separator");
    

    ...

    public static String formatCrtFileContents(final Certificate certificate) throws CertificateEncodingException {
        final Base64.Encoder encoder = Base64.getMimeEncoder(64, LINE_SEPARATOR.getBytes());
    
        final byte[] rawCrtText = certificate.getEncoded();
        final String encodedCertText = new String(encoder.encode(rawCrtText));
        final String prettified_cert = BEGIN_CERT + LINE_SEPARATOR + encodedCertText + LINE_SEPARATOR + END_CERT;
        return prettified_cert;
    }
    

    【讨论】:

    • 调用需要 API 级别 26。
    【解决方案3】:

    上一个答案给出了与 3de 方软件(如 PHP)的兼容性问题,因为 PEM 证书未正确分块。

    进口:

    import org.apache.commons.codec.binary.Base64;
    

    代码:

    protected static String convertToPem(X509Certificate cert) throws CertificateEncodingException {
     Base64 encoder = new Base64(64);
     String cert_begin = "-----BEGIN CERTIFICATE-----\n";
     String end_cert = "-----END CERTIFICATE-----";
    
     byte[] derCert = cert.getEncoded();
     String pemCertPre = new String(encoder.encode(derCert));
     String pemCert = cert_begin + pemCertPre + end_cert;
     return pemCert;
    }
    

    【讨论】:

    • OpenSSL一般需要64个字符的行。
    【解决方案4】:

    以下不使用大型外部库或可能版本不一致的 sun.* 库。它建立在 judoman 的答案之上,但它也按照 OpenSSL、Java 和其他人的要求将行分成 64 个字符。

    进口:

    import javax.xml.bind.DatatypeConverter;
    import java.security.cert.X509Certificate;
    import java.io.StringWriter;
    

    代码:

    public static String certToString(X509Certificate cert) {
        StringWriter sw = new StringWriter();
        try {
            sw.write("-----BEGIN CERTIFICATE-----\n");
            sw.write(DatatypeConverter.printBase64Binary(cert.getEncoded()).replaceAll("(.{64})", "$1\n"));
            sw.write("\n-----END CERTIFICATE-----\n");
        } catch (CertificateEncodingException e) {
            e.printStackTrace();
        }
        return sw.toString();
    }
    

    (我本来只是对 judoman 的回答发表评论,但我没有足够的声望点可以发表评论,并且我的简单编辑被拒绝,因为它应该是评论或答案,所以这里是答案。 )

    如果你想直接写入文件,也可以import java.io.FileWriter 和:

    FileWriter fw = new FileWriter(certFilePath);
    fw.write(certToString(myCert));
    fw.close();
    

    【讨论】:

    • 不幸的是 javax.xml.bind 已在 Java 11 中删除。现在最好的选择似乎是使用org.apache.commons.codec.binary.Base64 中的Base64.encodeBase64String 而不是printBase64Binary
    【解决方案5】:

    如果您有充气城堡的 PEMWriter,那么您可以执行以下操作:

    进口:

    import org.bouncycastle.openssl.PEMWriter;
    

    代码:

    /**
     * Converts a {@link X509Certificate} instance into a Base-64 encoded string (PEM format).
     *
     * @param x509Cert A X509 Certificate instance
     * @return PEM formatted String
     * @throws CertificateEncodingException
     */
    public String convertToBase64PEMString(Certificate x509Cert) throws IOException {
        StringWriter sw = new StringWriter();
        try (PEMWriter pw = new PEMWriter(sw)) {
            pw.writeObject(x509Cert);
        }
        return sw.toString();
    }
    

    【讨论】:

    • 它现在将返回空字符串,为了解决这个问题,在写入对象“pw.flush()”之后添加这个。
    【解决方案6】:

    要基于 ZZ Coder 的想法,但不使用不能保证在 JRE 版本之间保持一致的 sun.misc 类,请考虑这一点

    使用类:

    import javax.xml.bind.DatatypeConverter;
    

    代码:

    try {
        System.out.println("-----BEGIN CERTIFICATE-----");
        System.out.println(DatatypeConverter.printBase64Binary(x509cert.getEncoded()));
        System.out.println("-----END CERTIFICATE-----");
    } catch (CertificateEncodingException e) {
        e.printStackTrace();
    }
    

    【讨论】:

    【解决方案7】:

    几乎和@Andy Brown一样少了一行代码。

    StringWriter sw = new StringWriter();
    
    try (JcaPEMWriter jpw = new JcaPEMWriter(sw)) {
        jpw.writeObject(cert);
    }
    
    String pem = sw.toString();
    

    正如他所说,PEMWriter 已被弃用。

    【讨论】:

      【解决方案8】:

      使用Guava's BaseEncoding 进行编码的另一种选择:

      import com.google.common.io.BaseEncoding;
      
      public static final String LINE_SEPARATOR = System.getProperty("line.separator");
      public static final int LINE_LENGTH = 64;
      

      然后:

      String encodedCertText = BaseEncoding.base64()
                                           .withSeparator(LINE_SEPARATOR, LINE_LENGTH)
                                           .encode(cert.getEncoded());
      

      【讨论】:

      • 还需要添加-----BEGIN CERTIFICATE----------END CERTIFICATE----- 行。
      • 谢谢!经过 24 小时的搜索,你救了我的命
      【解决方案9】:

      在 BouncyCastle 1.60 中,PEMWriter 已被弃用,取而代之的是 PemWriter

      StringWriter sw = new StringWriter();
      
      try (PemWriter pw = new PemWriter(sw)) {
        PemObjectGenerator gen = new JcaMiscPEMGenerator(cert);
        pw.writeObject(gen);
      }
      
      return sw.toString();
      

      PemWriter 被缓冲,因此您需要在访问构造它的写入器之前刷新/关闭它。

      【讨论】:

        【解决方案10】:

        使用我在下面制作的一些微小的 Base64 不依赖于其他东西,如 base64 或 bouncycastle。

        import java.security.cert.CertificateEncodingException;
        import java.security.cert.X509Certificate;
        class Convert {
            private static byte[] convertToPem(X509Certificate cert)
                throws CertificateEncodingException {
                String cert_begin = "-----BEGIN CERTIFICATE-----\n";
                String end_cert = "-----END CERTIFICATE-----\n";
                String b64 = encode(cert.getEncoded()).replaceAll("(.{64})", "$1\n");
                if (b64.charAt(b64.length() - 1) != '\n') end_cert = "\n" + end_cert;
                String outpem = cert_begin + b64 + end_cert;
                return outpem.getBytes();
            }
        
            // Taken from https://gist.github.com/EmilHernvall/953733
            private static String encode(byte[] data) {
                String tbl = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/";
                StringBuilder buffer = new StringBuilder();
                int pad = 0;
                for (int i = 0; i < data.length; i += 3) {
                    int b = ((data[i] & 0xFF) << 16) & 0xFFFFFF;
                    if (i + 1 < data.length) b |= (data[i+1] & 0xFF) << 8;
                    else pad++;
                    if (i + 2 < data.length) b |= (data[i+2] & 0xFF);
                    else pad++;
                    for (int j = 0; j < 4 - pad; j++, b <<= 6) {
                        int c = (b & 0xFC0000) >> 18;
                        buffer.append(tbl.charAt(c));
                    }
                }
                for (int j = 0; j < pad; j++) buffer.append("=");
                return buffer.toString();
            }
        }
        

        【讨论】:

        • replaceAll("(.{64})", "$1\n"); +1!
        • @RobAu 我已经从this那里得到了这条线
        【解决方案11】:

        BouncyCastle 这是一种方法:

        // You may need to add BouncyCastle as provider:
        public static init() {
            Security.addProvider(new BouncyCastleProvider());
        }
        
        public static String encodePEM(Certificate certificate) {
            return encodePEM("CERTIFICATE", certificate.encoded);
        }
        public static String encodePEM(PrivateKey privateKey) {
            return encodePEM("PRIVATE KEY", privateKey.encoded);
        }
        public static String encodePEM(PublicKey publicKey) {
            return encodePEM("PUBLIC KEY", publicKey.encoded);
        }    
        /**
         * Converts byte array to PEM
         */
        protected static String toPEM(String type, byte[] data) {
          final PemObject pemObject = new PemObject(type, data);
          final StringWriter sw = new StringWriter();
          try (final PemWriter pw = new PemWriter(sw)) {
            pw.writeObject(pemObject);
          }
          return sw.toString();
        }
        

        【讨论】:

          猜你喜欢
          • 1970-01-01
          • 1970-01-01
          • 2017-06-13
          • 2021-03-28
          • 1970-01-01
          • 1970-01-01
          • 2012-11-13
          • 1970-01-01
          • 2013-05-11
          相关资源
          最近更新 更多