19
19
import java .net .Socket ;
20
20
import java .security .cert .CertificateException ;
21
21
import java .security .cert .X509Certificate ;
22
+ import java .util .ArrayList ;
23
+ import java .util .Collection ;
24
+ import java .util .List ;
22
25
import javax .naming .InvalidNameException ;
23
26
import javax .naming .ldap .LdapName ;
24
27
import javax .naming .ldap .Rdn ;
37
40
*/
38
41
class InstanceCheckingTrustManger extends X509ExtendedTrustManager {
39
42
private final X509ExtendedTrustManager tm ;
40
- private final CloudSqlInstanceName instanceName ;
43
+ private final InstanceMetadata instanceMetadata ;
41
44
42
45
public InstanceCheckingTrustManger (
43
- CloudSqlInstanceName instanceName , X509ExtendedTrustManager tm ) {
44
- this .instanceName = instanceName ;
46
+ InstanceMetadata instanceMetadata , X509ExtendedTrustManager tm ) {
47
+ this .instanceMetadata = instanceMetadata ;
45
48
this .tm = tm ;
46
49
}
47
50
@@ -92,6 +95,66 @@ private void checkCertificateChain(X509Certificate[] chain) throws CertificateEx
92
95
throw new CertificateException ("Subject is missing" );
93
96
}
94
97
98
+ if (instanceMetadata .isCasManagedCertificate () || instanceMetadata .isPscEnabled ()) {
99
+ checkSan (chain );
100
+ } else {
101
+ checkCn (chain );
102
+ }
103
+ }
104
+
105
+ private void checkSan (X509Certificate [] chain ) throws CertificateException {
106
+ List <String > sans = getSans (chain [0 ]);
107
+ String dns = instanceMetadata .getDnsName ();
108
+ if (dns == null || dns .isEmpty ()) {
109
+ throw new CertificateException (
110
+ "Instance metadata for " + instanceMetadata .getInstanceName () + " has an empty dnsName" );
111
+ }
112
+ for (String san : sans ) {
113
+ if (san .equalsIgnoreCase (dns )) {
114
+ return ;
115
+ }
116
+ }
117
+ throw new CertificateException (
118
+ "Server certificate does not contain expected name '"
119
+ + instanceMetadata .getDnsName ()
120
+ + "' for Cloud SQL instance "
121
+ + instanceMetadata .getInstanceName ());
122
+ }
123
+
124
+ private List <String > getSans (X509Certificate cert ) throws CertificateException {
125
+ ArrayList <String > names = new ArrayList <>();
126
+
127
+ Collection <List <?>> sanAsn1Field = cert .getSubjectAlternativeNames ();
128
+ if (sanAsn1Field == null ) {
129
+ return names ;
130
+ }
131
+
132
+ for (List item : sanAsn1Field ) {
133
+ Integer type = (Integer ) item .get (0 );
134
+ // RFC 5280 section 4.2.1.6. "Subject Alternative Name"
135
+ // describes the structure of subjectAlternativeName record.
136
+ // type == 0 means this contains an "otherName"
137
+ // type == 2 means this contains a "dNSName"
138
+ if (type == 0 || type == 2 ) {
139
+ Object value = item .get (1 );
140
+ if (value instanceof byte []) {
141
+ // This would only happen if the customer provided a non-standard JSSE encryption
142
+ // provider. The standard JSSE providers all return a list of Strings for the SAN.
143
+ // To handle this case, the project would need to add the BouncyCastle crypto library
144
+ // as a dependency, and follow the example to decode an ASN1 SAN data structure:
145
+ // https://stackoverflow.com/questions/30993879/retrieve-subject-alternative-names-of-x-509-certificate-in-java
146
+ throw new UnsupportedOperationException (
147
+ "Server certificate SAN field cannot be decoded." );
148
+ } else if (value instanceof String ) {
149
+ names .add ((String ) value );
150
+ }
151
+ }
152
+ }
153
+ return names ;
154
+ }
155
+
156
+ private void checkCn (X509Certificate [] chain ) throws CertificateException {
157
+
95
158
String cn = null ;
96
159
97
160
try {
@@ -111,7 +174,10 @@ private void checkCertificateChain(X509Certificate[] chain) throws CertificateEx
111
174
}
112
175
113
176
// parse CN from subject. CN always comes last in the list.
114
- String instName = this .instanceName .getProjectId () + ":" + this .instanceName .getInstanceId ();
177
+ String instName =
178
+ this .instanceMetadata .getInstanceName ().getProjectId ()
179
+ + ":"
180
+ + this .instanceMetadata .getInstanceName ().getInstanceId ();
115
181
if (!instName .equals (cn )) {
116
182
throw new CertificateException (
117
183
"Server certificate CN does not match instance name. Server certificate CN="
0 commit comments