You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@ws.apache.org by co...@apache.org on 2011/07/28 12:54:24 UTC

svn commit: r1151787 - in /webservices/wss4j/branches/1_5_x-fixes: keys/ src/org/apache/ws/security/components/crypto/ test/ test/wssec/

Author: coheigea
Date: Thu Jul 28 10:54:23 2011
New Revision: 1151787

URL: http://svn.apache.org/viewvc?rev=1151787&view=rev
Log:
[WSS-300] - SubjectKeyIidentifier (SKI) incorrectly calculated for 2048-bit RSA key
 - Backported to 1.5.x branch

Added:
    webservices/wss4j/branches/1_5_x-fixes/keys/rsa2048.crt
    webservices/wss4j/branches/1_5_x-fixes/keys/rsa2048.jks
    webservices/wss4j/branches/1_5_x-fixes/keys/rsa2048.pem
    webservices/wss4j/branches/1_5_x-fixes/src/org/apache/ws/security/components/crypto/DERDecoder.java
    webservices/wss4j/branches/1_5_x-fixes/src/org/apache/ws/security/components/crypto/X509SubjectPublicKeyInfo.java
    webservices/wss4j/branches/1_5_x-fixes/test/rsa2048.properties
    webservices/wss4j/branches/1_5_x-fixes/test/wssec/TestWSSecuritySKI.java
Modified:
    webservices/wss4j/branches/1_5_x-fixes/src/org/apache/ws/security/components/crypto/CryptoBase.java
    webservices/wss4j/branches/1_5_x-fixes/test/wssec/PackageTests.java

Added: webservices/wss4j/branches/1_5_x-fixes/keys/rsa2048.crt
URL: http://svn.apache.org/viewvc/webservices/wss4j/branches/1_5_x-fixes/keys/rsa2048.crt?rev=1151787&view=auto
==============================================================================
Files webservices/wss4j/branches/1_5_x-fixes/keys/rsa2048.crt (added) and webservices/wss4j/branches/1_5_x-fixes/keys/rsa2048.crt Thu Jul 28 10:54:23 2011 differ

Added: webservices/wss4j/branches/1_5_x-fixes/keys/rsa2048.jks
URL: http://svn.apache.org/viewvc/webservices/wss4j/branches/1_5_x-fixes/keys/rsa2048.jks?rev=1151787&view=auto
==============================================================================
Files webservices/wss4j/branches/1_5_x-fixes/keys/rsa2048.jks (added) and webservices/wss4j/branches/1_5_x-fixes/keys/rsa2048.jks Thu Jul 28 10:54:23 2011 differ

Added: webservices/wss4j/branches/1_5_x-fixes/keys/rsa2048.pem
URL: http://svn.apache.org/viewvc/webservices/wss4j/branches/1_5_x-fixes/keys/rsa2048.pem?rev=1151787&view=auto
==============================================================================
--- webservices/wss4j/branches/1_5_x-fixes/keys/rsa2048.pem (added)
+++ webservices/wss4j/branches/1_5_x-fixes/keys/rsa2048.pem Thu Jul 28 10:54:23 2011
@@ -0,0 +1,24 @@
+-----BEGIN CERTIFICATE-----
+MIIEGzCCAwOgAwIBAgIJANl1lmfiF98pMA0GCSqGSIb3DQEBBQUAMGYxCzAJBgNV
+BAYTAklFMQ8wDQYDVQQIEwZEdWJsaW4xDzANBgNVBAcTBkR1YmxpbjEPMA0GA1UE
+ChMGVGFsZW5kMRUwEwYDVQQLEwxBcGFjaGUgV1NTNEoxDTALBgNVBAMTBENvbG0w
+HhcNMTEwNzI4MDk0OTQ5WhcNMjEwNzI1MDk0OTQ5WjBmMQswCQYDVQQGEwJJRTEP
+MA0GA1UECBMGRHVibGluMQ8wDQYDVQQHEwZEdWJsaW4xDzANBgNVBAoTBlRhbGVu
+ZDEVMBMGA1UECxMMQXBhY2hlIFdTUzRKMQ0wCwYDVQQDEwRDb2xtMIIBIjANBgkq
+hkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEA1UYfw24LPY4keBfH5PQewsZx0+uz8+A+
+WK4pP08l7lrmf295vmIY7XFjjh0MCXUkCqLzvYg0mkgMiNgLgB3jaaLrfhu8pyf5
+wUpaUBMeLaO39VZ7OtVVp83oExZ7QjKvFj8LIOGQFJcpGNgisG+ZS/Om3RsvwcmC
+YfHy79pudmDCekrzPSjzefLVYU76nbEhSY1gg7XwBrdcf9NxJ7kXaiksjusK0ghJ
+z1jeHTa3hqOx9PrEjZIbFg50OK4ryNea9cEXRkymQYwtWWYy1PIdsW5/vbvk0EY0
+c7rJyxwE8IwSDcq0qt5ycv4+IN0vy9u5W/F8ZL2FW6kOU96CgzvgjwIDAQABo4HL
+MIHIMB0GA1UdDgQWBBTf2sElFwaxKfu7TtX5rmGGzj7hXTCBmAYDVR0jBIGQMIGN
+gBTf2sElFwaxKfu7TtX5rmGGzj7hXaFqpGgwZjELMAkGA1UEBhMCSUUxDzANBgNV
+BAgTBkR1YmxpbjEPMA0GA1UEBxMGRHVibGluMQ8wDQYDVQQKEwZUYWxlbmQxFTAT
+BgNVBAsTDEFwYWNoZSBXU1M0SjENMAsGA1UEAxMEQ29sbYIJANl1lmfiF98pMAwG
+A1UdEwQFMAMBAf8wDQYJKoZIhvcNAQEFBQADggEBAJ2yaxEIxVSqmu+Q8E775cMM
+VVoQ2Z9wBoUK6L4h61Tq8EOQ7ixk9VzzNS2Ck4T7Y3ogSOtXInh6J27UDhtgnxlY
+LfZ1//SN52R/4llKRPFaS4cUYzEjI3eAul7c38zjqY0V0Zq/nSqF+3WKExSY5P3o
+D0JyuoalOKDUH0sNyK6uR5ItDYBLZvQ4La3fqx+UVEpndzyPeeYbBy13lxa0UYiM
+GXiyogX3FbNMfduJGpoD4nz/bNPjqR5DQOdNpLzsGarsA1QEGmXS5jMrdvRvyq37
+UjwzqpmuQY0VBBo6w9pBW2zYnJColy+LUKC90omFAml3PO1J9COEgRbODS+fjBI=
+-----END CERTIFICATE-----

Modified: webservices/wss4j/branches/1_5_x-fixes/src/org/apache/ws/security/components/crypto/CryptoBase.java
URL: http://svn.apache.org/viewvc/webservices/wss4j/branches/1_5_x-fixes/src/org/apache/ws/security/components/crypto/CryptoBase.java?rev=1151787&r1=1151786&r2=1151787&view=diff
==============================================================================
--- webservices/wss4j/branches/1_5_x-fixes/src/org/apache/ws/security/components/crypto/CryptoBase.java (original)
+++ webservices/wss4j/branches/1_5_x-fixes/src/org/apache/ws/security/components/crypto/CryptoBase.java Thu Jul 28 10:54:23 2011
@@ -597,17 +597,8 @@ public abstract class CryptoBase impleme
         byte[] derEncodedValue = cert.getExtensionValue(SKI_OID);
 
         if (cert.getVersion() < 3 || derEncodedValue == null) {
-            PublicKey key = cert.getPublicKey();
-            if (!(key instanceof RSAPublicKey)) {
-                throw new WSSecurityException(
-                        1,
-                        "noSKIHandling",
-                        new Object[]{"Support for RSA key only"});
-            }
-            byte[] encoded = key.getEncoded();
-            // remove 22-byte algorithm ID and header
-            byte[] value = new byte[encoded.length - 22];
-            System.arraycopy(encoded, 22, value, 0, value.length);
+            X509SubjectPublicKeyInfo spki = new X509SubjectPublicKeyInfo(cert.getPublicKey());
+            byte[] value = spki.getSubjectPublicKey();
             MessageDigest sha;
             try {
                 sha = MessageDigest.getInstance("SHA-1");
@@ -624,13 +615,15 @@ public abstract class CryptoBase impleme
         }
 
         //
-        // Strip away first four bytes from the DerValue (tag and length of
+        // Strip away first (four) bytes from the DerValue (tag and length of
         // ExtensionValue OCTET STRING and KeyIdentifier OCTET STRING)
         //
-        byte abyte0[] = new byte[derEncodedValue.length - 4];
-
-        System.arraycopy(derEncodedValue, 4, abyte0, 0, abyte0.length);
-        return abyte0;
+        DERDecoder extVal = new DERDecoder(derEncodedValue);
+        extVal.expect(DERDecoder.TYPE_OCTET_STRING);  // ExtensionValue OCTET STRING
+        extVal.getLength();
+        extVal.expect(DERDecoder.TYPE_OCTET_STRING);  // KeyIdentifier OCTET STRING
+        int keyIDLen = extVal.getLength();
+        return extVal.getBytes(keyIDLen);
     }
 
     public KeyStore getKeyStore() {

Added: webservices/wss4j/branches/1_5_x-fixes/src/org/apache/ws/security/components/crypto/DERDecoder.java
URL: http://svn.apache.org/viewvc/webservices/wss4j/branches/1_5_x-fixes/src/org/apache/ws/security/components/crypto/DERDecoder.java?rev=1151787&view=auto
==============================================================================
--- webservices/wss4j/branches/1_5_x-fixes/src/org/apache/ws/security/components/crypto/DERDecoder.java (added)
+++ webservices/wss4j/branches/1_5_x-fixes/src/org/apache/ws/security/components/crypto/DERDecoder.java Thu Jul 28 10:54:23 2011
@@ -0,0 +1,226 @@
+/**
+ * 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.ws.security.components.crypto;
+
+import java.math.BigInteger;
+import org.apache.ws.security.WSSecurityException;
+
+import org.apache.commons.logging.Log;
+import org.apache.commons.logging.LogFactory;
+
+/**
+ * Provides the means to navigate through a DER-encoded byte array, to help
+ * in decoding the contents.
+ * <p>
+ * It maintains a "current position" in the array that advances with each
+ * operation, providing a simple means to handle the type-length-value
+ * encoding of DER. For example
+ * <pre>
+ *   decoder.expect(TYPE);
+ *   int length = decoder.getLength();
+ *   byte[] value = decoder.getBytes(len);
+ * </pre>
+ */
+public class DERDecoder {
+    private static Log log = LogFactory.getLog(DERDecoder.class);
+
+    /** DER type identifier for a bit string value */
+    public static final byte TYPE_BIT_STRING = 0x03;
+    /** DER type identifier for a octet string value */
+    public static final byte TYPE_OCTET_STRING = 0x04;
+    /** DER type identifier for a sequence value */
+    public static final byte TYPE_SEQUENCE = 0x30;
+
+    private byte[] arr;
+    private int pos;
+
+    /**
+     * Construct a DERDecoder for the given byte array.
+     *
+     * @param derEncoded the DER-encoded array to decode.
+     * @throws WSSecurityException if the given array is null.
+     */
+    public DERDecoder(byte[] derEncoded) throws WSSecurityException {
+        if (derEncoded == null) {
+            throw new WSSecurityException(
+                    WSSecurityException.UNSUPPORTED_SECURITY_TOKEN,
+                    "noSKIHandling",
+                    new Object[] { "Invalid DER string" }
+            );
+        }
+        arr = derEncoded;
+        reset();
+    }
+
+
+    /**
+     * Reset the current position to the start of the array.
+     */
+    public void reset() {
+        pos = 0;
+    }
+
+    /**
+     * Advance the current position by the given number of bytes.
+     *
+     * @param length the number of bytes to skip.
+     * @throws WSSecurityException if length is negative.
+     */
+    public void skip(int length) throws WSSecurityException {
+        if (length < 0) {
+            throw new WSSecurityException(
+                    WSSecurityException.UNSUPPORTED_SECURITY_TOKEN,
+                    "noSKIHandling",
+                    new Object[] { "Unsupported DER format" }
+            );
+        }
+        pos += length;
+    }
+
+    /**
+     * Confirm that the byte at the current position matches the given value.
+     *
+     * @param val the expected next byte.
+     * @throws WSSecurityException
+     *         if the current position is at the end of the array, or if the
+     *         byte at the current position doesn't match the expected value.
+     */
+    public void expect(int val) throws WSSecurityException {
+        expect((byte)(val & 0xFF));
+    }
+
+    /**
+     * Confirm that the byte at the current position matches the given value.
+     *
+     * @param val the expected next byte.
+     * @throws WSSecurityException
+     *         if the current position is at the end of the array, or if the
+     *         byte at the current position doesn't match the expected value.
+     */
+    public void expect(byte val) throws WSSecurityException {
+        if (!test(val)) {
+            log.debug("DER mismatch: expected " + val + ", got " + arr[pos]);
+            throw new WSSecurityException(
+                    WSSecurityException.UNSUPPORTED_SECURITY_TOKEN,
+                    "noSKIHandling",
+                    new Object[] { "Invalid DER format" }
+            );
+        }
+        pos++;
+    }
+
+    /**
+     * Test if the byte at the current position matches the given value.
+     *
+     * @param val the value to test for a match with the current byte.
+     * @return true if the byte at the current position matches the given value.
+     * @throws WSSecurityException if the current position is at the end of
+     *                             the array.
+     */
+    public boolean test(byte val) throws WSSecurityException {
+        if (pos >= arr.length) {
+            throw new WSSecurityException(
+                    WSSecurityException.UNSUPPORTED_SECURITY_TOKEN,
+                    "noSKIHandling",
+                    new Object[] { "Invalid DER format" }
+            );
+        }
+        return (arr[pos] == val);
+    }
+
+    /**
+     * Get the DER length at the current position.
+     * <p>
+     * DER length is encoded as
+     * <ul>
+     * <li>If the first byte is 0x00 to 0x7F, it describes the actual length.
+     * <li>If the first byte is 0x80 + n with 0<n<0x7F, the actual length is
+     * described in the following 'n' bytes.
+     * <li>The length value 0x80, used only in constructed types, is
+     * defined as "indefinite length".
+     * </ul>
+     *
+     * @return the length, -1 for indefinite length.
+     * @throws WSSecurityException
+     *         if the current position is at the end of the array or there is
+     *         an incomplete length specification.
+     */
+    public int getLength() throws WSSecurityException {
+        if (pos >= arr.length) {
+            throw new WSSecurityException(
+                    WSSecurityException.UNSUPPORTED_SECURITY_TOKEN,
+                    "noSKIHandling",
+                    new Object[] { "Invalid DER format" }
+            );
+        }
+        int len;
+        if ((arr[pos] & 0xFF) <= 0x7F) {
+            len = arr[pos++];
+        } else if (arr[pos] == 0x80) {
+            len = -1;
+            pos++;
+        } else {
+            int nbytes = arr[pos++] & 0x7F;
+            if (pos + nbytes > arr.length) {
+                throw new WSSecurityException(
+                        WSSecurityException.UNSUPPORTED_SECURITY_TOKEN,
+                        "noSKIHandling",
+                        new Object[] { "Invalid DER format" }
+                );
+            }
+            byte[] lenBytes = new byte[nbytes];
+            System.arraycopy(arr, pos, lenBytes, 0, lenBytes.length);
+            len = new BigInteger(1, lenBytes).intValue();
+            pos += nbytes;
+        }
+        return len;
+    }
+
+    /**
+     * Return an array of bytes from the current position.
+     *
+     * @param length the number of bytes to return.
+     * @return an array of the requested number of bytes from the current
+     *         position.
+     * @throws WSSecurityException
+     *         if the current position is at the end of the array, or the
+     *         length is negative.
+     */
+    public byte[] getBytes(int length) throws WSSecurityException {
+        if (pos + length > arr.length) {
+            throw new WSSecurityException(
+                    WSSecurityException.UNSUPPORTED_SECURITY_TOKEN,
+                    "noSKIHandling",
+                    new Object[] { "Invalid DER format" }
+             );
+        } else if (length < 0) {
+            throw new WSSecurityException(
+                    WSSecurityException.UNSUPPORTED_SECURITY_TOKEN,
+                    "noSKIHandling",
+                    new Object[] { "Unsupported DER format" }
+            );
+        }
+        byte[] value = new byte[length];
+        System.arraycopy(arr, pos, value, 0, length);
+        pos += length;
+        return value;
+    }
+    
+}

Added: webservices/wss4j/branches/1_5_x-fixes/src/org/apache/ws/security/components/crypto/X509SubjectPublicKeyInfo.java
URL: http://svn.apache.org/viewvc/webservices/wss4j/branches/1_5_x-fixes/src/org/apache/ws/security/components/crypto/X509SubjectPublicKeyInfo.java?rev=1151787&view=auto
==============================================================================
--- webservices/wss4j/branches/1_5_x-fixes/src/org/apache/ws/security/components/crypto/X509SubjectPublicKeyInfo.java (added)
+++ webservices/wss4j/branches/1_5_x-fixes/src/org/apache/ws/security/components/crypto/X509SubjectPublicKeyInfo.java Thu Jul 28 10:54:23 2011
@@ -0,0 +1,107 @@
+/**
+ * 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.ws.security.components.crypto;
+
+import java.security.PublicKey;
+import org.apache.ws.security.WSSecurityException;
+
+/**
+ * Represents the X.509 SubjectPublicKeyInfo for a public key, as specified
+ * in RFC3280/5280:
+ * <pre>
+ * SubjectPublicKeyInfo  ::=  SEQUENCE  {
+ *       algorithm            AlgorithmIdentifier,
+ *       subjectPublicKey     BIT STRING  }
+ *
+ * AlgorithmIdentifier  ::=  SEQUENCE  {
+ *       algorithm               OBJECT IDENTIFIER,
+ *       parameters              ANY DEFINED BY algorithm OPTIONAL  }
+ * </pre>
+ */
+public class X509SubjectPublicKeyInfo extends DERDecoder {
+
+    /**
+     * Construct a SubjectPublicKeyInfo for the given public key.
+     *
+     * @param key the public key.
+     * @throws WSSecurityException if the public key encoding format is 
+     *                             not X.509 or the encoding is null.
+     */
+    public X509SubjectPublicKeyInfo(PublicKey key) throws WSSecurityException {
+        super(key.getEncoded());
+        if (!("X.509".equalsIgnoreCase(key.getFormat()) 
+                || "X509".equalsIgnoreCase(key.getFormat()))) {
+            throw new WSSecurityException(
+                WSSecurityException.UNSUPPORTED_SECURITY_TOKEN,
+                "noSKIHandling",
+                new Object[] { "Support for X.509-encoded public keys only" }
+            );
+        }
+    }
+
+    /**
+     * Construct a SubjectPublicKeyInfo for the given X.509-encoded public key.
+     *
+     * @param x509EncodedPublicKey the public key, in X.509 DER-encoding.
+     * @throws WSSecurityException if the encoded public key is null.
+     */
+    public X509SubjectPublicKeyInfo(byte[] x509EncodedPublicKey) throws WSSecurityException {
+        super(x509EncodedPublicKey);
+    }
+
+    /**
+     * Get the subjectPublicKey element of the SubjectPublicKeyInfo.
+     *
+     * @return the X.509-encoded subjectPublicKey bit string.
+     * @throws WSSecurityException the DER-encoding is invalid.
+     */
+    public byte[] getSubjectPublicKey() throws WSSecurityException {
+        reset();
+        expect(TYPE_SEQUENCE);    // SubjectPublicKeyInfo SEQUENCE
+        getLength();
+        // Could enforce the max length of this sequence, but not actually
+        // necessary for our purposes, so be forgiving and simply ignore.
+        expect(TYPE_SEQUENCE);    // algorithm AlgorithmIdentifier SEQUENCE
+        int algIDlen = getLength();
+        if (algIDlen < 0) {
+            // Unsupported indefinite-length
+            throw new WSSecurityException(
+                    WSSecurityException.UNSUPPORTED_SECURITY_TOKEN,
+                    "noSKIHandling",
+                    new Object[] { "Unsupported X.509 public key format" }
+            );
+        }
+        skip(algIDlen);           // AlgorithmIdentifier contents
+        expect(TYPE_BIT_STRING);  // subjectPublicKey BIT STRING
+        int keyLen = getLength()-1;
+        if (keyLen < 0) {
+            // Invalid BIT STRING length
+            throw new WSSecurityException(
+                    WSSecurityException.UNSUPPORTED_SECURITY_TOKEN,
+                    "noSKIHandling",
+                    new Object[] { "Invalid X.509 public key format" }
+            );
+        }
+        skip(1);   // number unused bits
+        // DER-encoding guarantees unused bits should be 0
+
+        return getBytes(keyLen);
+    }
+}

Added: webservices/wss4j/branches/1_5_x-fixes/test/rsa2048.properties
URL: http://svn.apache.org/viewvc/webservices/wss4j/branches/1_5_x-fixes/test/rsa2048.properties?rev=1151787&view=auto
==============================================================================
--- webservices/wss4j/branches/1_5_x-fixes/test/rsa2048.properties (added)
+++ webservices/wss4j/branches/1_5_x-fixes/test/rsa2048.properties Thu Jul 28 10:54:23 2011
@@ -0,0 +1,5 @@
+org.apache.ws.security.crypto.provider=org.apache.ws.security.components.crypto.Merlin
+org.apache.ws.security.crypto.merlin.keystore.type=jks
+org.apache.ws.security.crypto.merlin.keystore.password=security
+org.apache.ws.security.crypto.merlin.keystore.alias=rsa2048
+org.apache.ws.security.crypto.merlin.file=keys/rsa2048.jks

Modified: webservices/wss4j/branches/1_5_x-fixes/test/wssec/PackageTests.java
URL: http://svn.apache.org/viewvc/webservices/wss4j/branches/1_5_x-fixes/test/wssec/PackageTests.java?rev=1151787&r1=1151786&r2=1151787&view=diff
==============================================================================
--- webservices/wss4j/branches/1_5_x-fixes/test/wssec/PackageTests.java (original)
+++ webservices/wss4j/branches/1_5_x-fixes/test/wssec/PackageTests.java Thu Jul 28 10:54:23 2011
@@ -76,6 +76,7 @@ public class PackageTests extends TestCa
         suite.addTestSuite(TestWSSecurityWSS199.class);
         suite.addTestSuite(TestWSSecurityWSS234.class);
         suite.addTestSuite(TestWSSecurityWSS245.class);
+        suite.addTestSuite(TestWSSecuritySKI.class);
         
         return suite;
     }

Added: webservices/wss4j/branches/1_5_x-fixes/test/wssec/TestWSSecuritySKI.java
URL: http://svn.apache.org/viewvc/webservices/wss4j/branches/1_5_x-fixes/test/wssec/TestWSSecuritySKI.java?rev=1151787&view=auto
==============================================================================
--- webservices/wss4j/branches/1_5_x-fixes/test/wssec/TestWSSecuritySKI.java (added)
+++ webservices/wss4j/branches/1_5_x-fixes/test/wssec/TestWSSecuritySKI.java Thu Jul 28 10:54:23 2011
@@ -0,0 +1,74 @@
+/**
+ * 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 wssec;
+
+import junit.framework.Test;
+import junit.framework.TestCase;
+import junit.framework.TestSuite;
+
+import java.io.InputStream;
+import java.security.cert.X509Certificate;
+
+import org.apache.ws.security.components.crypto.Crypto;
+import org.apache.ws.security.components.crypto.CryptoFactory;
+import org.apache.ws.security.util.Base64;
+
+
+/**
+ * This is a test for WSS-300 - "SubjectKeyIdentifier (SKI) incorrectly calculated for 2048-bit RSA key".
+ * The SKI value WSS4J generates for various key sizes is tested against the output from openssl, e.g.:
+ * 
+ * openssl x509 -inform der -ocspid -in wss40_server.crt | grep 'Public key OCSP hash' 
+ * | perl -ne 'split; print pack("H*",$_[4])' | base64
+ */
+public class TestWSSecuritySKI extends TestCase {
+
+    /**
+     * TestWSSecurity constructor
+     * <p/>
+     * 
+     * @param name name of the test
+     */
+    public TestWSSecuritySKI(String name) {
+        super(name);
+    }
+
+    /**
+     * JUnit suite
+     * <p/>
+     * 
+     * @return a junit test suite
+     */
+    public static Test suite() {
+        return new TestSuite(TestWSSecuritySKI.class);
+    }
+    
+    public void testRSA2048() throws Exception {
+        // Load the keystore
+        Crypto crypto = CryptoFactory.getInstance("rsa2048.properties");
+        X509Certificate[] certs = crypto.getCertificates("rsa2048");
+        assertTrue(certs != null && certs.length > 0);
+        
+        byte[] skiBytes = crypto.getSKIBytesFromCert(certs[0]);
+        String knownBase64Encoding = "39rBJRcGsSn7u07V+a5hhs4+4V0=";
+        assertTrue(knownBase64Encoding.equals(Base64.encode(skiBytes)));
+    }
+    
+}