SAML: Enhance tests to randomly select crypto algorithms (elastic/x-pack-elasticsearch#4217)

- Changes in CertUtils to add algorithm parameter to
  generateSignedCertificates
- Changes in Tests to randomly pick signature algorithms
- Changes in Tests to randomly pick encryption algorithms

relates elastic/x-pack-elasticsearch#3983

Original commit: elastic/x-pack-elasticsearch@d1b5f3a166
This commit is contained in:
Yogesh Gaikwad 2018-03-29 12:36:40 +11:00 committed by GitHub
parent 94b6f637a6
commit 01ab4782a1
3 changed files with 239 additions and 64 deletions

View File

@ -38,6 +38,7 @@ import org.bouncycastle.operator.jcajce.JcaContentSignerBuilder;
import org.bouncycastle.pkcs.PKCS10CertificationRequest;
import org.bouncycastle.pkcs.jcajce.JcaPKCS10CertificationRequestBuilder;
import org.elasticsearch.common.Nullable;
import org.elasticsearch.common.Strings;
import org.elasticsearch.common.SuppressForbidden;
import org.elasticsearch.common.io.PathUtils;
import org.elasticsearch.common.network.InetAddressHelper;
@ -417,33 +418,98 @@ public class CertUtils {
*/
public static X509Certificate generateCACertificate(X500Principal x500Principal, KeyPair keyPair, int days)
throws OperatorCreationException, CertificateException, CertIOException, NoSuchAlgorithmException {
return generateSignedCertificate(x500Principal, null, keyPair, null, null, true, days);
return generateSignedCertificate(x500Principal, null, keyPair, null, null, true, days, null);
}
/**
* Generates a signed certificate using the provided CA private key and information from the CA certificate
* Generates a signed certificate using the provided CA private key and
* information from the CA certificate
*
* @param principal
* the principal of the certificate; commonly referred to as the
* distinguished name (DN)
* @param subjectAltNames
* the subject alternative names that should be added to the
* certificate as an X509v3 extension. May be {@code null}
* @param keyPair
* the key pair that will be associated with the certificate
* @param caCert
* the CA certificate. If {@code null}, this results in a self signed
* certificate
* @param caPrivKey
* the CA private key. If {@code null}, this results in a self signed
* certificate
* @param days
* no of days certificate will be valid from now
* @return a signed {@link X509Certificate}
*/
public static X509Certificate generateSignedCertificate(X500Principal principal, GeneralNames subjectAltNames, KeyPair keyPair,
X509Certificate caCert, PrivateKey caPrivKey, int days)
throws OperatorCreationException, CertificateException, CertIOException, NoSuchAlgorithmException {
return generateSignedCertificate(principal, subjectAltNames, keyPair, caCert, caPrivKey, false, days);
return generateSignedCertificate(principal, subjectAltNames, keyPair, caCert, caPrivKey, false, days, null);
}
/**
* Generates a signed certificate using the provided CA private key and
* information from the CA certificate
*
* @param principal
* the principal of the certificate; commonly referred to as the
* distinguished name (DN)
* @param subjectAltNames
* the subject alternative names that should be added to the
* certificate as an X509v3 extension. May be {@code null}
* @param keyPair
* the key pair that will be associated with the certificate
* @param caCert
* the CA certificate. If {@code null}, this results in a self signed
* certificate
* @param caPrivKey
* the CA private key. If {@code null}, this results in a self signed
* certificate
* @param days
* no of days certificate will be valid from now
* @param signatureAlgorithm
* algorithm used for signing certificate. If {@code null} or
* empty, then use default algorithm {@link CertUtils#getDefaultSignatureAlgorithm(PrivateKey)}
* @return a signed {@link X509Certificate}
*/
public static X509Certificate generateSignedCertificate(X500Principal principal, GeneralNames subjectAltNames, KeyPair keyPair,
X509Certificate caCert, PrivateKey caPrivKey, int days, String signatureAlgorithm)
throws OperatorCreationException, CertificateException, CertIOException, NoSuchAlgorithmException {
return generateSignedCertificate(principal, subjectAltNames, keyPair, caCert, caPrivKey, false, days, signatureAlgorithm);
}
/**
* Generates a signed certificate
*
* @param principal the principal of the certificate; commonly referred to as the distinguished name (DN)
* @param subjectAltNames the subject alternative names that should be added to the certificate as an X509v3 extension. May be
* {@code null}
* @param keyPair the key pair that will be associated with the certificate
* @param caCert the CA certificate. If {@code null}, this results in a self signed certificate
* @param caPrivKey the CA private key. If {@code null}, this results in a self signed certificate
* @param isCa whether or not the generated certificate is a CA
* @param principal
* the principal of the certificate; commonly referred to as the
* distinguished name (DN)
* @param subjectAltNames
* the subject alternative names that should be added to the
* certificate as an X509v3 extension. May be {@code null}
* @param keyPair
* the key pair that will be associated with the certificate
* @param caCert
* the CA certificate. If {@code null}, this results in a self signed
* certificate
* @param caPrivKey
* the CA private key. If {@code null}, this results in a self signed
* certificate
* @param isCa
* whether or not the generated certificate is a CA
* @param days
* no of days certificate will be valid from now
* @param signatureAlgorithm
* algorithm used for signing certificate. If {@code null} or
* empty, then use default algorithm {@link CertUtils#getDefaultSignatureAlgorithm(PrivateKey)}
* @return a signed {@link X509Certificate}
*/
private static X509Certificate generateSignedCertificate(X500Principal principal, GeneralNames subjectAltNames, KeyPair keyPair,
X509Certificate caCert, PrivateKey caPrivKey, boolean isCa, int days)
X509Certificate caCert, PrivateKey caPrivKey, boolean isCa, int days, String signatureAlgorithm)
throws NoSuchAlgorithmException, CertificateException, CertIOException, OperatorCreationException {
Objects.requireNonNull(keyPair, "Key-Pair must not be null");
final DateTime notBefore = new DateTime(DateTimeZone.UTC);
if (days < 1) {
throw new IllegalArgumentException("the certificate must be valid for at least one day");
@ -478,11 +544,40 @@ public class CertUtils {
builder.addExtension(Extension.basicConstraints, isCa, new BasicConstraints(isCa));
PrivateKey signingKey = caPrivKey != null ? caPrivKey : keyPair.getPrivate();
ContentSigner signer = new JcaContentSignerBuilder("SHA256withRSA").build(signingKey);
ContentSigner signer = new JcaContentSignerBuilder(
(Strings.isNullOrEmpty(signatureAlgorithm)) ? getDefaultSignatureAlgorithm(signingKey) : signatureAlgorithm)
.setProvider(CertUtils.BC_PROV).build(signingKey);
X509CertificateHolder certificateHolder = builder.build(signer);
return new JcaX509CertificateConverter().getCertificate(certificateHolder);
}
/**
* Based on the private key algorithm {@link PrivateKey#getAlgorithm()}
* determines default signing algorithm used by CertUtils
*
* @param key
* {@link PrivateKey}
* @return algorithm
*/
private static String getDefaultSignatureAlgorithm(PrivateKey key) {
String signatureAlgorithm = null;
switch (key.getAlgorithm()) {
case "RSA":
signatureAlgorithm = "SHA256withRSA";
break;
case "DSA":
signatureAlgorithm = "SHA256withDSA";
break;
case "EC":
signatureAlgorithm = "SHA256withECDSA";
break;
default:
throw new IllegalArgumentException("Unsupported algorithm : " + key.getAlgorithm()
+ " for signature, allowed values for private key algorithm are [RSA, DSA, EC]");
}
return signatureAlgorithm;
}
/**
* Generates a certificate signing request
*

View File

@ -34,7 +34,6 @@ import org.opensaml.saml.saml2.core.NameID;
import org.opensaml.saml.saml2.core.Response;
import org.opensaml.saml.saml2.core.StatusCode;
import org.opensaml.security.credential.Credential;
import org.opensaml.security.x509.X509Credential;
import org.opensaml.xmlsec.encryption.support.DecryptionException;
import org.w3c.dom.Document;
import org.w3c.dom.Element;
@ -110,10 +109,12 @@ public class SamlAuthenticatorTests extends SamlTestCase {
private static final String IDP_ENTITY_ID = "https://idp.saml.elastic.test/";
private static final String SP_ACS_URL = SP_ENTITY_ID + "sso/post";
private static Tuple<X509Certificate, PrivateKey> idpCertificatePair;
private static Tuple<X509Certificate, PrivateKey> spCertificatePair;
private static Tuple<X509Certificate, PrivateKey> idpSigningCertificatePair;
private static Tuple<X509Certificate, PrivateKey> spSigningCertificatePair;
private static Tuple<X509Certificate, PrivateKey> spEncryptionCertificatePair;
private static List<Integer> supportedAesKeyLengths;
private static List<String> supportedAesTransformations;
private ClockMock clock;
private SamlAuthenticator authenticator;
@ -130,9 +131,17 @@ public class SamlAuthenticatorTests extends SamlTestCase {
@BeforeClass
public static void calculateAesLength() throws NoSuchAlgorithmException {
supportedAesKeyLengths = new ArrayList<>();
supportedAesTransformations = new ArrayList<>();
supportedAesKeyLengths.add(128);
if (Cipher.getMaxAllowedKeyLength("AES") >= 256) {
supportedAesTransformations.add(XMLCipher.AES_128);
supportedAesTransformations.add(XMLCipher.AES_128_GCM);
if (Cipher.getMaxAllowedKeyLength("AES") > 128) {
supportedAesKeyLengths.add(192);
supportedAesKeyLengths.add(256);
supportedAesTransformations.add(XMLCipher.AES_192);
supportedAesTransformations.add(XMLCipher.AES_192_GCM);
supportedAesTransformations.add(XMLCipher.AES_256);
supportedAesTransformations.add(XMLCipher.AES_256_GCM);
}
}
@ -141,22 +150,29 @@ public class SamlAuthenticatorTests extends SamlTestCase {
*/
@BeforeClass
public static void initCredentials() throws Exception {
idpCertificatePair = createKeyPair();
spCertificatePair = createKeyPair();
idpSigningCertificatePair = createKeyPair(randomSigningAlgorithm());
spSigningCertificatePair = createKeyPair(randomSigningAlgorithm());
spEncryptionCertificatePair = createKeyPair("RSA");
}
private static String randomSigningAlgorithm() {
return randomFrom("RSA", "DSA", "EC");
}
@AfterClass
public static void cleanup() {
idpCertificatePair = null;
spCertificatePair = null;
idpSigningCertificatePair = null;
spSigningCertificatePair = null;
spEncryptionCertificatePair = null;
supportedAesKeyLengths = null;
supportedAesTransformations = null;
}
@Before
public void setupAuthenticator() throws Exception {
this.clock = new ClockMock();
this.maxSkew = TimeValue.timeValueMinutes(1);
this.authenticator = buildAuthenticator(() -> singletonList(buildOpenSamlCredential(idpCertificatePair)));
this.authenticator = buildAuthenticator(() -> singletonList(buildOpenSamlCredential(idpSigningCertificatePair)));
this.requestId = randomId();
}
@ -165,9 +181,10 @@ public class SamlAuthenticatorTests extends SamlTestCase {
final Settings realmSettings = Settings.EMPTY;
final IdpConfiguration idp = new IdpConfiguration(IDP_ENTITY_ID, credentials);
final X509Credential spCredential = buildOpenSamlCredential(spCertificatePair);
final SigningConfiguration signingConfiguration = new SigningConfiguration(Collections.singleton("*"), spCredential);
final SpConfiguration sp = new SpConfiguration(SP_ENTITY_ID, SP_ACS_URL, null, signingConfiguration, spCredential);
final SigningConfiguration signingConfiguration =
new SigningConfiguration(Collections.singleton("*"), buildOpenSamlCredential(spSigningCertificatePair));
final SpConfiguration sp = new SpConfiguration(SP_ENTITY_ID, SP_ACS_URL, null, signingConfiguration,
buildOpenSamlCredential(spEncryptionCertificatePair));
final Environment env = TestEnvironment.newEnvironment(globalSettings);
return new SamlAuthenticator(
new RealmConfig("saml_test", realmSettings, globalSettings, env, new ThreadContext(globalSettings)),
@ -276,7 +293,7 @@ public class SamlAuthenticatorTests extends SamlTestCase {
final Instant now = clock.instant();
final String xml = getSimpleResponse(now);
final String encrypted = encryptAssertions(xml, spCertificatePair);
final String encrypted = encryptAssertions(xml, spEncryptionCertificatePair);
assertThat(encrypted, not(equalTo(xml)));
final String signed = signDoc(encrypted);
@ -306,13 +323,13 @@ public class SamlAuthenticatorTests extends SamlTestCase {
// This is the most reliable way to get a valid signature
final String str = SamlUtils.toString(element);
final Element clone = parseDocument(str).getDocumentElement();
signElement(clone, idpCertificatePair);
signElement(clone, idpSigningCertificatePair);
element.getOwnerDocument().adoptNode(clone);
element.getParentNode().replaceChild(clone, element);
});
assertThat(signed, not(equalTo(xml)));
final String encrypted = encryptAssertions(signed, spCertificatePair);
final String encrypted = encryptAssertions(signed, spEncryptionCertificatePair);
assertThat(encrypted, not(equalTo(signed)));
final SamlToken token = token(encrypted);
@ -331,10 +348,10 @@ public class SamlAuthenticatorTests extends SamlTestCase {
final Instant now = clock.instant();
final String xml = getSimpleResponse(now);
final String encrypted = encryptAttributes(xml, spCertificatePair);
final String encrypted = encryptAttributes(xml, spEncryptionCertificatePair);
assertThat(encrypted, not(equalTo(xml)));
final String signed = signer.transform(encrypted, idpCertificatePair);
final String signed = signer.transform(encrypted, idpSigningCertificatePair);
assertThat(signed, not(equalTo(encrypted)));
final SamlToken token = token(signed);
@ -352,8 +369,8 @@ public class SamlAuthenticatorTests extends SamlTestCase {
final Instant now = clock.instant();
final String xml = getSimpleResponse(now);
// Encrypting with idp cert instead of sp cert will mean that the SP cannot decrypt
final String encrypted = encryptAssertions(xml, idpCertificatePair);
// Encrypting with different cert instead of sp cert will mean that the SP cannot decrypt
final String encrypted = encryptAssertions(xml, createKeyPair("RSA"));
assertThat(encrypted, not(equalTo(xml)));
final String signed = signDoc(encrypted);
@ -369,8 +386,8 @@ public class SamlAuthenticatorTests extends SamlTestCase {
final Instant now = clock.instant();
final String xml = getSimpleResponse(now);
// Encrypting with idp cert instead of sp cert will mean that the SP cannot decrypt
final String encrypted = encryptAttributes(xml, idpCertificatePair);
// Encrypting with different cert instead of sp cert will mean that the SP cannot decrypt
final String encrypted = encryptAttributes(xml, createKeyPair("RSA"));
assertThat(encrypted, not(equalTo(xml)));
final String signed = signDoc(encrypted);
@ -913,10 +930,10 @@ public class SamlAuthenticatorTests extends SamlTestCase {
"</proto:Response>";
// check that the content is valid when signed by the correct key-pair
assertThat(authenticator.authenticate(token(signer.transform(xml, idpCertificatePair))), notNullValue());
assertThat(authenticator.authenticate(token(signer.transform(xml, idpSigningCertificatePair))), notNullValue());
// check is rejected when signed by a different key-pair
final Tuple<X509Certificate, PrivateKey> wrongKey = createKeyPair();
final Tuple<X509Certificate, PrivateKey> wrongKey = createKeyPair(randomSigningAlgorithm());
final ElasticsearchSecurityException exception = expectThrows(ElasticsearchSecurityException.class,
() -> authenticator.authenticate(token(signer.transform(xml, wrongKey))));
assertThat(exception.getMessage(), containsString("SAML Signature"));
@ -929,12 +946,12 @@ public class SamlAuthenticatorTests extends SamlTestCase {
final CryptoTransform signer = randomBoolean() ? this::signDoc : this::signAssertions;
final String xml = getSimpleResponse(Instant.now());
assertThat(authenticator.authenticate(token(signer.transform(xml, idpCertificatePair))), notNullValue());
assertThat(authenticator.authenticate(token(signer.transform(xml, idpSigningCertificatePair))), notNullValue());
final Tuple<X509Certificate, PrivateKey> oldKeyPair = idpCertificatePair;
idpCertificatePair = createKeyPair();
assertThat(idpCertificatePair.v2(), not(equalTo(oldKeyPair.v2())));
assertThat(authenticator.authenticate(token(signer.transform(xml, idpCertificatePair))), notNullValue());
final Tuple<X509Certificate, PrivateKey> oldKeyPair = idpSigningCertificatePair;
idpSigningCertificatePair = createKeyPair(randomSigningAlgorithm());
assertThat(idpSigningCertificatePair.v2(), not(equalTo(oldKeyPair.v2())));
assertThat(authenticator.authenticate(token(signer.transform(xml, idpSigningCertificatePair))), notNullValue());
}
public void testParsingRejectsTamperedContent() throws Exception {
@ -975,7 +992,7 @@ public class SamlAuthenticatorTests extends SamlTestCase {
"</proto:Response>";
// check that the original signed content is valid
final String signed = signer.transform(xml, idpCertificatePair);
final String signed = signer.transform(xml, idpSigningCertificatePair);
assertThat(authenticator.authenticate(token(signed)), notNullValue());
// but altered content is rejected
@ -992,7 +1009,7 @@ public class SamlAuthenticatorTests extends SamlTestCase {
final List<Tuple<X509Certificate, PrivateKey>> keys = new ArrayList<>(numberOfKeys);
final List<Credential> credentials = new ArrayList<>(numberOfKeys);
for (int i = 0; i < numberOfKeys; i++) {
final Tuple<X509Certificate, PrivateKey> key = createKeyPair();
final Tuple<X509Certificate, PrivateKey> key = createKeyPair(randomSigningAlgorithm());
keys.add(key);
credentials.add(buildOpenSamlCredential(key));
}
@ -1048,11 +1065,11 @@ public class SamlAuthenticatorTests extends SamlTestCase {
assertThat(unsigned.isSigned(), equalTo(false));
assertThat(unsigned.getAssertions().get(0).isSigned(), equalTo(false));
final Response signedDoc = toResponse(signDoc(xml, idpCertificatePair));
final Response signedDoc = toResponse(signDoc(xml, idpSigningCertificatePair));
assertThat(signedDoc.isSigned(), equalTo(true));
assertThat(signedDoc.getAssertions().get(0).isSigned(), equalTo(false));
final Response signedAssertions = toResponse(signAssertions(xml, idpCertificatePair));
final Response signedAssertions = toResponse(signAssertions(xml, idpSigningCertificatePair));
assertThat(signedAssertions.isSigned(), equalTo(false));
assertThat(signedAssertions.getAssertions().get(0).isSigned(), equalTo(true));
}
@ -1075,12 +1092,12 @@ public class SamlAuthenticatorTests extends SamlTestCase {
}
}
final Response encryptedAssertion = toResponse(encryptAssertions(xml, spCertificatePair));
final Response encryptedAssertion = toResponse(encryptAssertions(xml, spEncryptionCertificatePair));
// Expect EncryptedAssertion
assertThat(encryptedAssertion.getAssertions(), iterableWithSize(0));
assertThat(encryptedAssertion.getEncryptedAssertions(), iterableWithSize(1));
final Response encryptedAttributes = toResponse(encryptAttributes(xml, spCertificatePair));
final Response encryptedAttributes = toResponse(encryptAttributes(xml, spEncryptionCertificatePair));
// Expect Assertion > AttributeStatement (x2) > EncryptedAttribute
assertThat(encryptedAttributes.getAssertions(), iterableWithSize(1));
assertThat(encryptedAttributes.getEncryptedAssertions(), iterableWithSize(0));
@ -1188,7 +1205,7 @@ public class SamlAuthenticatorTests extends SamlTestCase {
public void testSignatureWrappingAttackOne() throws Exception {
final Instant now = clock.instant();
final String xml = getSimpleResponse(now);
final Document legitimateDocument = parseDocument(signDoc(xml, idpCertificatePair));
final Document legitimateDocument = parseDocument(signDoc(xml, idpSigningCertificatePair));
// First verify that the correct SAML Response can be consumed
final SamlToken legitimateToken = token(SamlUtils.toString(legitimateDocument.getDocumentElement()));
final SamlAttributes attributes = authenticator.authenticate(legitimateToken);
@ -1224,7 +1241,7 @@ public class SamlAuthenticatorTests extends SamlTestCase {
public void testSignatureWrappingAttackTwo() throws Exception {
final Instant now = clock.instant();
final String xml = getSimpleResponse(now);
final Document legitimateDocument = parseDocument(signDoc(xml, idpCertificatePair));
final Document legitimateDocument = parseDocument(signDoc(xml, idpSigningCertificatePair));
// First verify that the correct SAML Response can be consumed
final SamlToken legitimateToken = token(SamlUtils.toString(legitimateDocument.getDocumentElement()));
final SamlAttributes attributes = authenticator.authenticate(legitimateToken);
@ -1262,7 +1279,7 @@ public class SamlAuthenticatorTests extends SamlTestCase {
public void testSignatureWrappingAttackThree() throws Exception {
final Instant now = clock.instant();
final String xml = getSimpleResponse(now);
final Document legitimateDocument = parseDocument(signAssertions(xml, idpCertificatePair));
final Document legitimateDocument = parseDocument(signAssertions(xml, idpSigningCertificatePair));
// First verify that the correct SAML Response can be consumed
final SamlToken legitimateToken = token(SamlUtils.toString(legitimateDocument.getDocumentElement()));
final SamlAttributes attributes = authenticator.authenticate(legitimateToken);
@ -1301,7 +1318,7 @@ public class SamlAuthenticatorTests extends SamlTestCase {
public void testSignatureWrappingAttackFour() throws Exception {
final Instant now = clock.instant();
final String xml = getSimpleResponse(now);
final Document legitimateDocument = parseDocument(signAssertions(xml, idpCertificatePair));
final Document legitimateDocument = parseDocument(signAssertions(xml, idpSigningCertificatePair));
// First verify that the correct SAML Response can be consumed
final SamlToken legitimateToken = token(SamlUtils.toString(legitimateDocument.getDocumentElement()));
final SamlAttributes attributes = authenticator.authenticate(legitimateToken);
@ -1339,7 +1356,7 @@ public class SamlAuthenticatorTests extends SamlTestCase {
public void testSignatureWrappingAttackFive() throws Exception {
final Instant now = clock.instant();
final String xml = getSimpleResponse(now);
final Document legitimateDocument = parseDocument(signAssertions(xml, idpCertificatePair));
final Document legitimateDocument = parseDocument(signAssertions(xml, idpSigningCertificatePair));
// First verify that the correct SAML Response can be consumed
final SamlToken legitimateToken = token(SamlUtils.toString(legitimateDocument.getDocumentElement()));
final SamlAttributes attributes = authenticator.authenticate(legitimateToken);
@ -1377,7 +1394,7 @@ public class SamlAuthenticatorTests extends SamlTestCase {
public void testSignatureWrappingAttackSix() throws Exception {
final Instant now = clock.instant();
final String xml = getSimpleResponse(now);
final Document legitimateDocument = parseDocument(signAssertions(xml, idpCertificatePair));
final Document legitimateDocument = parseDocument(signAssertions(xml, idpSigningCertificatePair));
// First verify that the correct SAML Response can be consumed
final SamlToken legitimateToken = token(SamlUtils.toString(legitimateDocument.getDocumentElement()));
final SamlAttributes attributes = authenticator.authenticate(legitimateToken);
@ -1421,7 +1438,7 @@ public class SamlAuthenticatorTests extends SamlTestCase {
public void testSignatureWrappingAttackSeven() throws Exception {
final Instant now = clock.instant();
final String xml = getSimpleResponse(now);
final Document legitimateDocument = parseDocument(signAssertions(xml, idpCertificatePair));
final Document legitimateDocument = parseDocument(signAssertions(xml, idpSigningCertificatePair));
// First verify that the correct SAML Response can be consumed
final SamlToken legitimateToken = token(SamlUtils.toString(legitimateDocument.getDocumentElement()));
final SamlAttributes attributes = authenticator.authenticate(legitimateToken);
@ -1460,7 +1477,7 @@ public class SamlAuthenticatorTests extends SamlTestCase {
public void testSignatureWrappingAttackEight() throws Exception {
final Instant now = clock.instant();
final String xml = getSimpleResponse(now);
final Document legitimateDocument = parseDocument(signAssertions(xml, idpCertificatePair));
final Document legitimateDocument = parseDocument(signAssertions(xml, idpSigningCertificatePair));
// First verify that the correct SAML Response can be consumed
final SamlToken legitimateToken = token(SamlUtils.toString(legitimateDocument.getDocumentElement()));
final SamlAttributes attributes = authenticator.authenticate(legitimateToken);
@ -1571,7 +1588,7 @@ public class SamlAuthenticatorTests extends SamlTestCase {
assertThat(exception.getMessage(), containsString("SAML Signature"));
assertThat(exception.getMessage(), containsString("could not be validated"));
//Restore the authenticator with credentials for the rest of the test cases
authenticator = buildAuthenticator(() -> singletonList(buildOpenSamlCredential(idpCertificatePair)));
authenticator = buildAuthenticator(() -> singletonList(buildOpenSamlCredential(idpSigningCertificatePair)));
}
public void testFailureWhenIdPCredentialsAreNull() throws Exception {
@ -1583,7 +1600,7 @@ public class SamlAuthenticatorTests extends SamlTestCase {
assertThat(exception.getMessage(), containsString("SAML Signature"));
assertThat(exception.getMessage(), containsString("could not be validated"));
//Restore the authenticator with credentials for the rest of the test cases
authenticator = buildAuthenticator(() -> singletonList(buildOpenSamlCredential(idpCertificatePair)));
authenticator = buildAuthenticator(() -> singletonList(buildOpenSamlCredential(idpSigningCertificatePair)));
}
private interface CryptoTransform {
@ -1591,7 +1608,7 @@ public class SamlAuthenticatorTests extends SamlTestCase {
}
private String signDoc(String xml) throws Exception {
return signDoc(xml, EXCLUSIVE, SamlAuthenticatorTests.idpCertificatePair);
return signDoc(xml, EXCLUSIVE, SamlAuthenticatorTests.idpSigningCertificatePair);
}
private String signDoc(String xml, Tuple<X509Certificate, PrivateKey> keyPair) throws Exception {
@ -1599,7 +1616,7 @@ public class SamlAuthenticatorTests extends SamlTestCase {
}
private String signDoc(String xml, String c14nMethod) throws Exception {
return signDoc(xml, c14nMethod, SamlAuthenticatorTests.idpCertificatePair);
return signDoc(xml, c14nMethod, SamlAuthenticatorTests.idpSigningCertificatePair);
}
private String signDoc(String xml, String c14nMethod, Tuple<X509Certificate, PrivateKey> keyPair) throws Exception {
@ -1661,6 +1678,35 @@ public class SamlAuthenticatorTests extends SamlTestCase {
signElement(parent, keyPair, EXCLUSIVE);
}
/**
* Randomly selects digital signature algorithm URI for given private key
* algorithm ({@link PrivateKey#getAlgorithm()}).
*
* @param key
* {@link PrivateKey}
* @return algorithm URI
*/
private String getSignatureAlgorithmURI(PrivateKey key) {
String algoUri = null;
switch (key.getAlgorithm()) {
case "RSA":
algoUri = randomFrom("http://www.w3.org/2001/04/xmldsig-more#rsa-sha256",
"http://www.w3.org/2001/04/xmldsig-more#rsa-sha512");
break;
case "DSA":
algoUri = "http://www.w3.org/2009/xmldsig11#dsa-sha256";
break;
case "EC":
algoUri = randomFrom("http://www.w3.org/2001/04/xmldsig-more#ecdsa-sha256",
"http://www.w3.org/2001/04/xmldsig-more#ecdsa-sha512");
break;
default:
throw new IllegalArgumentException("Unsupported algorithm : " + key.getAlgorithm()
+ " for signature, allowed values for private key algorithm are [RSA, DSA, EC]");
}
return algoUri;
}
private void signElement(Element parent, Tuple<X509Certificate, PrivateKey> keyPair, String c14nMethod) throws Exception {
//We need to explicitly set the Id attribute, "ID" is just our convention
parent.setIdAttribute("ID", true);
@ -1668,12 +1714,12 @@ public class SamlAuthenticatorTests extends SamlTestCase {
final X509Certificate certificate = keyPair.v1();
final PrivateKey privateKey = keyPair.v2();
final XMLSignatureFactory fac = XMLSignatureFactory.getInstance("DOM");
final DigestMethod digestMethod = fac.newDigestMethod(DigestMethod.SHA256, null);
final DigestMethod digestMethod = fac.newDigestMethod(randomFrom(DigestMethod.SHA256, DigestMethod.SHA512), null);
final Transform transform = fac.newTransform(ENVELOPED, (TransformParameterSpec) null);
// We don't "have to" set the reference explicitly since we're using enveloped signatures, but it helps with
// creating the XSW test cases
final Reference reference = fac.newReference(refID, digestMethod, singletonList(transform), null, null);
final SignatureMethod signatureMethod = fac.newSignatureMethod("http://www.w3.org/2001/04/xmldsig-more#rsa-sha256", null);
final SignatureMethod signatureMethod = fac.newSignatureMethod(getSignatureAlgorithmURI(privateKey), null);
final CanonicalizationMethod canonicalizationMethod = fac.newCanonicalizationMethod(c14nMethod, (C14NMethodParameterSpec) null);
final SignedInfo signedInfo = fac.newSignedInfo(canonicalizationMethod, signatureMethod, singletonList(reference));
@ -1769,12 +1815,12 @@ public class SamlAuthenticatorTests extends SamlTestCase {
final Key aesKey = aesGenerator.generateKey();
// Encrypt the AES key with the public key of the recipient
final XMLCipher keyCipher = XMLCipher.getInstance(XMLCipher.RSA_OAEP);
final XMLCipher keyCipher = XMLCipher.getInstance(randomFrom(XMLCipher.RSA_OAEP, XMLCipher.RSA_OAEP_11));
keyCipher.init(XMLCipher.WRAP_MODE, certificate.getPublicKey());
final EncryptedKey encryptedKey = keyCipher.encryptKey(document, aesKey);
// Encryption context for actual content
final XMLCipher xmlCipher = XMLCipher.getInstance(XMLCipher.AES_128);
final XMLCipher xmlCipher = XMLCipher.getInstance(randomFrom(supportedAesTransformations));
xmlCipher.init(XMLCipher.ENCRYPT_MODE, aesKey);
final String keyElementId = randomId();

View File

@ -24,7 +24,9 @@ import java.security.KeyPairGenerator;
import java.security.PrivateKey;
import java.security.cert.Certificate;
import java.security.cert.X509Certificate;
import java.util.Arrays;
import java.util.Locale;
import java.util.stream.Collectors;
import static org.hamcrest.Matchers.is;
@ -57,9 +59,41 @@ public abstract class SamlTestCase extends ESTestCase {
}
}
/**
* Generates signed certificate and associates with generated key pair.
* @see #createKeyPair(String)
* @return X509Certificate a signed certificate, it's PrivateKey {@link Tuple}
* @throws Exception
*/
protected static Tuple<X509Certificate, PrivateKey> createKeyPair() throws Exception {
KeyPairGenerator keyPairGenerator = KeyPairGenerator.getInstance("RSA");
keyPairGenerator.initialize(2048);
return createKeyPair("RSA");
}
/**
* Generates key pair for given algorithm and then associates with a certificate.
* For testing, for "EC" algorithm 256 key size is used, others use 2048 as default.
* @param algorithm
* @return X509Certificate a signed certificate, it's PrivateKey {@link Tuple}
* @throws Exception
*/
protected static Tuple<X509Certificate, PrivateKey> createKeyPair(String algorithm) throws Exception {
KeyPairGenerator keyPairGenerator = KeyPairGenerator.getInstance(algorithm);
final boolean useBigKeySizes = rarely();
switch (algorithm) {
case "EC":
keyPairGenerator.initialize(randomFrom(256, 384));
break;
case "RSA":
keyPairGenerator.initialize(randomFrom(Arrays.stream(new int[] { 1024, 2048, 4096 }).boxed()
.filter((ksize) -> (ksize <= 2048 || useBigKeySizes)).collect(Collectors.toList())));
break;
case "DSA":
keyPairGenerator.initialize(randomFrom(Arrays.stream(new int[] { 1024, 2048, 3072 }).boxed()
.filter((ksize) -> (ksize <= 2048 || useBigKeySizes)).collect(Collectors.toList())));
break;
default:
keyPairGenerator.initialize(randomFrom(1024, 2048));
}
final KeyPair pair = keyPairGenerator.generateKeyPair();
final String name = randomAlphaOfLength(8);
final X509Certificate cert = CertUtils.generateSignedCertificate(new X500Principal("CN=test-" + name), null, pair, null, null, 30);