1
1
/*
2
- * Copyright 2002-2019 the original author or authors.
2
+ * Copyright 2002-2021 the original author or authors.
3
3
*
4
4
* Licensed under the Apache License, Version 2.0 (the "License");
5
5
* you may not use this file except in compliance with the License.
17
17
package org .springframework .security .converter ;
18
18
19
19
import java .io .BufferedReader ;
20
+ import java .io .ByteArrayInputStream ;
21
+ import java .io .IOException ;
20
22
import java .io .InputStream ;
21
23
import java .io .InputStreamReader ;
22
24
import java .security .KeyFactory ;
23
25
import java .security .NoSuchAlgorithmException ;
26
+ import java .security .cert .CertificateException ;
27
+ import java .security .cert .CertificateFactory ;
28
+ import java .security .cert .X509Certificate ;
24
29
import java .security .interfaces .RSAPrivateKey ;
25
30
import java .security .interfaces .RSAPublicKey ;
26
31
import java .security .spec .PKCS8EncodedKeySpec ;
36
41
* Used for creating {@link java.security.Key} converter instances
37
42
*
38
43
* @author Josh Cummings
44
+ * @author Shazin Sadakath
39
45
* @since 5.2
40
46
*/
41
47
public final class RsaKeyConverters {
@@ -50,6 +56,10 @@ public final class RsaKeyConverters {
50
56
51
57
private static final String X509_PEM_FOOTER = DASHES + "END PUBLIC KEY" + DASHES ;
52
58
59
+ private static final String X509_CERT_HEADER = DASHES + "BEGIN CERTIFICATE" + DASHES ;
60
+
61
+ private static final String X509_CERT_FOOTER = DASHES + "END CERTIFICATE" + DASHES ;
62
+
53
63
private RsaKeyConverters () {
54
64
}
55
65
@@ -91,8 +101,8 @@ public static Converter<InputStream, RSAPrivateKey> pkcs8() {
91
101
}
92
102
93
103
/**
94
- * Construct a {@link Converter} for converting a PEM-encoded X.509 RSA Public Key
95
- * into a {@link RSAPublicKey}.
104
+ * Construct a {@link Converter} for converting a PEM-encoded X.509 RSA Public Key or
105
+ * X.509 Certificate into a {@link RSAPublicKey}.
96
106
*
97
107
* This converter does not close the {@link InputStream} in order to avoid making
98
108
* non-portable assumptions about the streams' origin and further use.
@@ -101,27 +111,52 @@ public static Converter<InputStream, RSAPrivateKey> pkcs8() {
101
111
*/
102
112
public static Converter <InputStream , RSAPublicKey > x509 () {
103
113
KeyFactory keyFactory = rsaFactory ();
114
+ CertificateFactory certificateFactory = x509CertificateFactory ();
104
115
return (source ) -> {
105
116
List <String > lines = readAllLines (source );
106
- Assert .isTrue (!lines .isEmpty () && lines .get (0 ).startsWith (X509_PEM_HEADER ),
107
- "Key is not in PEM-encoded X.509 format, please check that the header begins with -----"
108
- + X509_PEM_HEADER + "-----" );
117
+ Assert .isTrue (
118
+ !lines .isEmpty ()
119
+ && (lines .get (0 ).startsWith (X509_PEM_HEADER ) || lines .get (0 ).startsWith (X509_CERT_HEADER )),
120
+ "Key is not in PEM-encoded X.509 format or a valid X.509 certificate, please check that the header begins with "
121
+ + X509_PEM_HEADER + " or " + X509_CERT_HEADER );
109
122
StringBuilder base64Encoded = new StringBuilder ();
110
123
for (String line : lines ) {
111
- if (RsaKeyConverters .isNotX509Wrapper (line )) {
124
+ if (RsaKeyConverters .isNotX509PemWrapper ( line ) && isNotX509CertificateWrapper (line )) {
112
125
base64Encoded .append (line );
113
126
}
114
127
}
115
128
byte [] x509 = Base64 .getDecoder ().decode (base64Encoded .toString ());
116
- try {
117
- return (RSAPublicKey ) keyFactory .generatePublic (new X509EncodedKeySpec (x509 ));
129
+ if (lines .get (0 ).startsWith (X509_PEM_HEADER )) {
130
+ try {
131
+ return (RSAPublicKey ) keyFactory .generatePublic (new X509EncodedKeySpec (x509 ));
132
+ }
133
+ catch (Exception ex ) {
134
+ throw new IllegalArgumentException (ex );
135
+ }
118
136
}
119
- catch (Exception ex ) {
120
- throw new IllegalArgumentException (ex );
137
+ if (lines .get (0 ).startsWith (X509_CERT_HEADER )) {
138
+ try (InputStream x509CertStream = new ByteArrayInputStream (x509 )) {
139
+ X509Certificate certificate = (X509Certificate ) certificateFactory
140
+ .generateCertificate (x509CertStream );
141
+ return (RSAPublicKey ) certificate .getPublicKey ();
142
+ }
143
+ catch (CertificateException | IOException ex ) {
144
+ throw new IllegalArgumentException (ex );
145
+ }
121
146
}
147
+ return null ;
122
148
};
123
149
}
124
150
151
+ private static CertificateFactory x509CertificateFactory () {
152
+ try {
153
+ return CertificateFactory .getInstance ("X.509" );
154
+ }
155
+ catch (CertificateException ex ) {
156
+ throw new IllegalArgumentException (ex );
157
+ }
158
+ }
159
+
125
160
private static List <String > readAllLines (InputStream source ) {
126
161
BufferedReader reader = new BufferedReader (new InputStreamReader (source ));
127
162
return reader .lines ().collect (Collectors .toList ());
@@ -140,8 +175,12 @@ private static boolean isNotPkcs8Wrapper(String line) {
140
175
return !PKCS8_PEM_HEADER .equals (line ) && !PKCS8_PEM_FOOTER .equals (line );
141
176
}
142
177
143
- private static boolean isNotX509Wrapper (String line ) {
178
+ private static boolean isNotX509PemWrapper (String line ) {
144
179
return !X509_PEM_HEADER .equals (line ) && !X509_PEM_FOOTER .equals (line );
145
180
}
146
181
182
+ private static boolean isNotX509CertificateWrapper (String line ) {
183
+ return !X509_CERT_HEADER .equals (line ) && !X509_CERT_FOOTER .equals (line );
184
+ }
185
+
147
186
}
0 commit comments