blob: 896a8d87c3665e2e81f16a2c779aecd2c0853b85 [file] [log] [blame]
/**
* Licensed to the Apache Software Foundation (ASF) under one
* or more contributor license agreements. See the NOTICE file
* distributed with this work for additional information
* regarding copyright ownership. The ASF licenses this file
* to you under the Apache License, Version 2.0 (the
* "License"); you may not use this file except in compliance
* with the License. You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing,
* software distributed under the License is distributed on an
* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
* KIND, either express or implied. See the License for the
* specific language governing permissions and limitations
* under the License.
*/
package org.apache.wss4j.common.util;
import org.junit.jupiter.api.Assertions;
import org.junit.jupiter.api.Test;
import javax.security.auth.x500.X500Principal;
import java.io.File;
import java.io.FileInputStream;
import java.io.IOException;
import java.security.InvalidAlgorithmParameterException;
import java.security.KeyStore;
import java.security.KeyStoreException;
import java.security.NoSuchAlgorithmException;
import java.security.cert.CertificateException;
import java.security.cert.PKIXParameters;
import java.security.cert.TrustAnchor;
import java.security.cert.X509Certificate;
import static org.junit.jupiter.api.Assertions.assertEquals;
class CommaDelimiterRfc2253NameTest {
private static final String TYPICAL_CA ="CN=Entrust Certification Authority - L1K,OU=(c) 2012 Entrust\\, Inc. - for authorized use only,OU=See www.entrust.net/legal-terms,O=Entrust\\, Inc.,C=US";
private static final String QUOTES_TYPICAL_CA ="CN=Entrust Certification Authority - L1K, OU=\"(c) 2012 Entrust, Inc. - for authorized use only\", OU=See www.entrust.net/legal-terms, O=\"Entrust, Inc.\", C=US";
private CommaDelimiterRfc2253Name subject = new CommaDelimiterRfc2253Name();
@Test
void whenMultipleAttributesArePresentThenSpaceIsPlacedAfterComma() {
String actual = new CommaDelimiterRfc2253Name().execute("CN=EOIR,OU=Some Unit,DC=Another place");
assertEquals("CN=EOIR, OU=Some Unit, DC=Another place",actual);
}
@Test
void whenRdnContainsACommaThenTheRdnIsSurroundedByDoubleQuotes() {
String actual = new CommaDelimiterRfc2253Name().execute(TYPICAL_CA);
assertEquals(QUOTES_TYPICAL_CA,actual);
}
@Test
void whenRdnIsInvalidThenExpectException() {
Assertions.assertThrows(IllegalArgumentException.class, () -> {
subject.execute("invalid");
});
}
@Test
void whenCallingUnescapeWithStringNoEscapesThenNoChangesAreMade() throws Exception {
String input = "This is a string with (c) no escaped! sStrings $";
String actual = subject.unEscapeRfc2253RdnSubPart(input);
assertEquals(input,actual,"Expect that string is unchanged");
}
@Test
void whenCallingUnescapeWithStringThenItUnescapesAppropiateCharacters() throws Exception {
String input = "This is a string with escapes \\,\\; \\\\ and \\< then \\> \\\"Copyright Apache\\\" ";
String expected = "This is a string with escapes ,; \\ and < then > \"Copyright Apache\" ";
String actual = subject.unEscapeRfc2253RdnSubPart(input);
assertEquals(expected,actual,"Expect that string is unescaped");
}
@Test
void whenCallingUnescapeWithStringWithMultiValueRdnThenItUnescapesAppropriateCharacters() throws Exception {
String input = "OU=Sales\\+CN=J. Smith\\,O=Widget Inc.\\,C=US";
String expected = "OU=Sales+CN=J. Smith,O=Widget Inc.,C=US";
String actual = subject.unEscapeRfc2253RdnSubPart(input);
assertEquals(expected,actual,"Expect that string is unescaped");
}
@Test
public void testThatACommaDelimitedDnStringAndABackSlashEscapedDnProducesTheSameX509PrincipalUsingDefaultTruststore()
throws KeyStoreException, InvalidAlgorithmParameterException, CertificateException, NoSuchAlgorithmException, IOException {
KeyStore keystore = loadDefaultKeyStore();
PKIXParameters params = new PKIXParameters(keystore);
for (TrustAnchor ta : params.getTrustAnchors()) {
X509Certificate cert = ta.getTrustedCert();
assertThatTransformIsEquivalent(cert.getSubjectX500Principal().getName());
}
}
private void assertThatTransformIsEquivalent(String dnString) {
// The expected value below recreates what is done in the token class by recreating the X500Principal using getName()
// even though the calling methods already used a X500Principal.getName() to pass the value in the first place ,
// this seems wasteful but I believe there is a reason for this in the wss4j code ...
// Searching for different RFC 2253 parsers , this one :
// https://www.codeproject.com/Articles/9788/An-RFC-2253-Compliant-Distinguished-Name-Parser
// mentioned that its not possible to recreate the original binary because of the RFC allows multibyte characters using # encoding.
// Indeed w/o this additional calls to X500Principal.getName() this test will fail for one of the CA which indeed uses # encoding
// because the equals uses the X500Name.canonicalDn string for comparison which if used directly from the keystore would
// still contain the multibyte characters.
// Since wss4j does not send multibyte characters, this tests uses of new X500Principal(dnString)
// accurately reflects change usage.
X500Principal expected = new X500Principal(dnString);
X500Principal recreatedX509principal = new X500Principal(subject.execute(dnString));
assertEquals(expected, recreatedX509principal);
}
private KeyStore loadDefaultKeyStore() throws IOException, KeyStoreException, CertificateException, NoSuchAlgorithmException {
String filename = System.getProperty("java.home") + "/lib/security/cacerts".replace('/', File.separatorChar);
FileInputStream is = new FileInputStream(filename);
KeyStore keystore = KeyStore.getInstance(KeyStore.getDefaultType());
String password = "changeit";
keystore.load(is, password.toCharArray());
return keystore;
}
}