Skip to content

Commit 6412e50

Browse files
committed
saml2: Fixes #2548 SAML2 cert encoding and decoding
This fixes SAML2 certificate encoding/decoding issue due to refactoring regression introduced in 7ce54bf that did not account for base64 based encoding/decoding. The changes effectively restore the same logic as used in previous versions. Signed-off-by: Rohit Yadav <rohit.yadav@shapeblue.com>
1 parent 8533def commit 6412e50

4 files changed

Lines changed: 94 additions & 28 deletions

File tree

plugins/user-authenticators/saml2/src/org/apache/cloudstack/saml/SAML2AuthManagerImpl.java

Lines changed: 5 additions & 17 deletions
Original file line numberDiff line numberDiff line change
@@ -32,7 +32,6 @@
3232
import java.security.SignatureException;
3333
import java.security.cert.CertificateException;
3434
import java.security.cert.X509Certificate;
35-
import java.security.spec.InvalidKeySpecException;
3635
import java.util.ArrayList;
3736
import java.util.Collection;
3837
import java.util.HashMap;
@@ -147,11 +146,11 @@ protected boolean initSP() {
147146
try {
148147
KeyPair keyPair = CertUtils.generateRandomKeyPair(4096);
149148
_ksDao.save(SAMLPluginConstants.SAMLSP_KEYPAIR,
150-
CertUtils.privateKeyToPem(keyPair.getPrivate()),
151-
CertUtils.publicKeyToPem(keyPair.getPublic()), "samlsp-keypair");
149+
SAMLUtils.encodePrivateKey(keyPair.getPrivate()),
150+
SAMLUtils.encodePublicKey(keyPair.getPublic()), "samlsp-keypair");
152151
keyStoreVO = _ksDao.findByName(SAMLPluginConstants.SAMLSP_KEYPAIR);
153152
s_logger.info("No SAML keystore found, created and saved a new Service Provider keypair");
154-
} catch (final NoSuchProviderException | NoSuchAlgorithmException | IOException e) {
153+
} catch (final NoSuchProviderException | NoSuchAlgorithmException e) {
155154
s_logger.error("Unable to create and save SAML keypair, due to: ", e);
156155
}
157156
}
@@ -166,19 +165,8 @@ protected boolean initSP() {
166165
KeyPair spKeyPair = null;
167166
X509Certificate spX509Key = null;
168167
if (keyStoreVO != null) {
169-
170-
PrivateKey privateKey = null;
171-
try {
172-
privateKey = CertUtils.pemToPrivateKey(keyStoreVO.getCertificate());
173-
} catch (final InvalidKeySpecException | IOException e) {
174-
s_logger.error("Failed to read private key, due to error: ", e);
175-
}
176-
PublicKey publicKey = null;
177-
try {
178-
publicKey = CertUtils.pemToPublicKey(keyStoreVO.getKey());
179-
} catch (final InvalidKeySpecException | IOException e) {
180-
s_logger.error("Failed to read public key, due to error: ", e);
181-
}
168+
final PrivateKey privateKey = SAMLUtils.decodePrivateKey(keyStoreVO.getCertificate());
169+
final PublicKey publicKey = SAMLUtils.decodePublicKey(keyStoreVO.getKey());
182170
if (privateKey != null && publicKey != null) {
183171
spKeyPair = new KeyPair(publicKey, privateKey);
184172
KeystoreVO x509VO = _ksDao.findByName(SAMLPluginConstants.SAMLSP_X509CERT);

plugins/user-authenticators/saml2/src/org/apache/cloudstack/saml/SAMLUtils.java

Lines changed: 83 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -28,15 +28,20 @@
2828
import java.net.URLEncoder;
2929
import java.nio.charset.Charset;
3030
import java.security.InvalidKeyException;
31+
import java.security.KeyFactory;
3132
import java.security.KeyPair;
3233
import java.security.NoSuchAlgorithmException;
3334
import java.security.NoSuchProviderException;
3435
import java.security.PrivateKey;
36+
import java.security.PublicKey;
3537
import java.security.SecureRandom;
3638
import java.security.Signature;
3739
import java.security.SignatureException;
3840
import java.security.cert.CertificateException;
3941
import java.security.cert.X509Certificate;
42+
import java.security.spec.InvalidKeySpecException;
43+
import java.security.spec.PKCS8EncodedKeySpec;
44+
import java.security.spec.X509EncodedKeySpec;
4045
import java.util.List;
4146
import java.util.zip.Deflater;
4247
import java.util.zip.DeflaterOutputStream;
@@ -264,12 +269,6 @@ public static String generateSAMLRequestSignature(final String urlEncodedString,
264269
return url;
265270
}
266271

267-
public static X509Certificate generateRandomX509Certificate(KeyPair keyPair) throws NoSuchAlgorithmException, NoSuchProviderException, CertificateException, SignatureException, InvalidKeyException, OperatorCreationException {
268-
return CertUtils.generateV1Certificate(keyPair,
269-
"CN=ApacheCloudStack", "CN=ApacheCloudStack",
270-
3, "SHA256WithRSA");
271-
}
272-
273272
public static void setupSamlUserCookies(final LoginCmdResponse loginResponse, final HttpServletResponse resp) throws IOException {
274273
resp.addCookie(new Cookie("userid", URLEncoder.encode(loginResponse.getUserId(), HttpUtils.UTF_8)));
275274
resp.addCookie(new Cookie("domainid", URLEncoder.encode(loginResponse.getDomainId(), HttpUtils.UTF_8)));
@@ -284,4 +283,82 @@ public static void setupSamlUserCookies(final LoginCmdResponse loginResponse, fi
284283
resp.addHeader("SET-COOKIE", String.format("%s=%s;HttpOnly", ApiConstants.SESSIONKEY, loginResponse.getSessionKey()));
285284
}
286285

286+
/**
287+
* Returns base64 encoded PublicKey
288+
* @param key PublicKey
289+
* @return public key encoded string
290+
*/
291+
public static String encodePublicKey(PublicKey key) {
292+
try {
293+
KeyFactory keyFactory = CertUtils.getKeyFactory();
294+
if (keyFactory == null) return null;
295+
X509EncodedKeySpec spec = keyFactory.getKeySpec(key, X509EncodedKeySpec.class);
296+
return new String(org.bouncycastle.util.encoders.Base64.encode(spec.getEncoded()), Charset.forName("UTF-8"));
297+
} catch (InvalidKeySpecException e) {
298+
s_logger.error("Unable to create KeyFactory:" + e.getMessage());
299+
}
300+
return null;
301+
}
302+
303+
/**
304+
* Returns base64 encoded PrivateKey
305+
* @param key PrivateKey
306+
* @return privatekey encoded string
307+
*/
308+
public static String encodePrivateKey(PrivateKey key) {
309+
try {
310+
KeyFactory keyFactory = CertUtils.getKeyFactory();
311+
if (keyFactory == null) return null;
312+
PKCS8EncodedKeySpec spec = keyFactory.getKeySpec(key,
313+
PKCS8EncodedKeySpec.class);
314+
return new String(org.bouncycastle.util.encoders.Base64.encode(spec.getEncoded()), Charset.forName("UTF-8"));
315+
} catch (InvalidKeySpecException e) {
316+
s_logger.error("Unable to create KeyFactory:" + e.getMessage());
317+
}
318+
return null;
319+
}
320+
321+
/**
322+
* Decodes base64 encoded public key to PublicKey
323+
* @param publicKey encoded public key string
324+
* @return returns PublicKey
325+
*/
326+
public static PublicKey decodePublicKey(String publicKey) {
327+
byte[] sigBytes = org.bouncycastle.util.encoders.Base64.decode(publicKey);
328+
X509EncodedKeySpec x509KeySpec = new X509EncodedKeySpec(sigBytes);
329+
KeyFactory keyFactory = CertUtils.getKeyFactory();
330+
if (keyFactory == null)
331+
return null;
332+
try {
333+
return keyFactory.generatePublic(x509KeySpec);
334+
} catch (InvalidKeySpecException e) {
335+
s_logger.error("Unable to create PrivateKey from privateKey string:" + e.getMessage());
336+
}
337+
return null;
338+
}
339+
340+
/**
341+
* Decodes base64 encoded private key to PrivateKey
342+
* @param privateKey encoded private key string
343+
* @return returns PrivateKey
344+
*/
345+
public static PrivateKey decodePrivateKey(String privateKey) {
346+
byte[] sigBytes = org.bouncycastle.util.encoders.Base64.decode(privateKey);
347+
PKCS8EncodedKeySpec pkscs8KeySpec = new PKCS8EncodedKeySpec(sigBytes);
348+
KeyFactory keyFactory = CertUtils.getKeyFactory();
349+
if (keyFactory == null)
350+
return null;
351+
try {
352+
return keyFactory.generatePrivate(pkscs8KeySpec);
353+
} catch (InvalidKeySpecException e) {
354+
s_logger.error("Unable to create PrivateKey from privateKey string:" + e.getMessage());
355+
}
356+
return null;
357+
}
358+
359+
public static X509Certificate generateRandomX509Certificate(KeyPair keyPair) throws NoSuchAlgorithmException, NoSuchProviderException, CertificateException, SignatureException, InvalidKeyException, OperatorCreationException {
360+
return CertUtils.generateV1Certificate(keyPair,
361+
"CN=ApacheCloudStack", "CN=ApacheCloudStack",
362+
3, "SHA256WithRSA");
363+
}
287364
}

plugins/user-authenticators/saml2/test/org/apache/cloudstack/SAMLUtilsTest.java

Lines changed: 6 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -64,12 +64,14 @@ public void testBuildLogoutRequest() throws Exception {
6464
public void testX509Helpers() throws Exception {
6565
KeyPair keyPair = CertUtils.generateRandomKeyPair(4096);
6666

67-
String privateKeyString = CertUtils.privateKeyToPem(keyPair.getPrivate());
68-
String publicKeyString = CertUtils.publicKeyToPem(keyPair.getPublic());
67+
String privateKeyString = SAMLUtils.encodePrivateKey(keyPair.getPrivate());
68+
String publicKeyString = SAMLUtils.encodePublicKey(keyPair.getPublic());
6969

70-
PrivateKey privateKey = CertUtils.pemToPrivateKey(privateKeyString);
71-
PublicKey publicKey = CertUtils.pemToPublicKey(publicKeyString);
70+
PrivateKey privateKey = SAMLUtils.decodePrivateKey(privateKeyString);
71+
PublicKey publicKey = SAMLUtils.decodePublicKey(publicKeyString);
7272

73+
assertNotNull(privateKey);
74+
assertNotNull(publicKey);
7375
assertTrue(privateKey.equals(keyPair.getPrivate()));
7476
assertTrue(publicKey.equals(keyPair.getPublic()));
7577
}

ui/index.html

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1881,7 +1881,6 @@ <h3><translate key="label.set.up.zone.type"/></h3>
18811881
<script type="text/javascript" src="scripts/network.js"></script>
18821882
<script type="text/javascript" src="scripts/domains.js"></script>
18831883
<script type="text/javascript" src="scripts/docs.js"></script>
1884-
<script type="text/javascript" src="scripts/vm_snapshots.js"></script>
18851884
<script type="text/javascript" src="scripts/ui-custom/projectSelect.js"></script>
18861885
<script type="text/javascript" src="scripts/ui-custom/saml.js"></script>
18871886
<script type="text/javascript" src="scripts/ui-custom/ca.js"></script>

0 commit comments

Comments
 (0)