Hi Niranda,

I've attached a sample Java class here that you can refer to get the
private key and public key. I've written this for signing a JWT but you can
use the same code segments for your scenario as well.

Thanks,
TharinduE

On Fri, Oct 7, 2016 at 8:54 AM, Danushka Fernando <danush...@wso2.com>
wrote:

> AFAIK SAML Token is signed using the tenant keystore. You can use the
> org.wso2.carbon.core.util.KeyStoreManager to achieve that. There are
> getDefaultPrivateKey and getDefaultPublicKey which will give you the
> default keys of the keystore.
>
> Thanks & Regards
> Danushka Fernando
> Senior Software Engineer
> WSO2 inc. http://wso2.com/
> Mobile : +94716332729
>
> On Thu, Oct 6, 2016 at 5:38 PM, Niranda Perera <nira...@wso2.com> wrote:
>
>> Hi,
>>
>> I am trying to create a SAML response manually. This is a special type of
>> SAML response named SAML NameIdResponse and I am trying to set a signature
>> in it.
>>
>> I am trying to create a signature element, as mentioned here [1].
>>
>> For me to do this, I need to access the private and public keys
>> programatically.
>>
>> Could you please point me to a place where I could extract these
>> information?
>>
>> Best
>>
>> [1] http://www.programcreek.com/java-api-examples/index.php?
>> source_dir=saml-generator-master/src/main/java/com/rackspace
>> /saml/SamlAssertionProducer.java
>>
>> --
>> *Niranda Perera*
>> Software Engineer, WSO2 Inc.
>> Mobile: +94-71-554-8430
>> Twitter: @n1r44 <https://twitter.com/N1R44>
>> https://pythagoreanscript.wordpress.com/
>>
>> _______________________________________________
>> Dev mailing list
>> Dev@wso2.org
>> http://wso2.org/cgi-bin/mailman/listinfo/dev
>>
>>
>
> _______________________________________________
> Dev mailing list
> Dev@wso2.org
> http://wso2.org/cgi-bin/mailman/listinfo/dev
>
>


-- 

Tharindu Edirisinghe
Senior Software Engineer | WSO2 Inc
Platform Security Team
Blog : tharindue.blogspot.com
mobile : +94 775181586
package org.wso2.carbon.jwt.helper;

import org.apache.axiom.util.base64.Base64Utils;
import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
import org.wso2.carbon.base.MultitenantConstants;
import org.wso2.carbon.core.util.KeyStoreManager;
import org.wso2.carbon.user.api.UserStoreException;
import org.wso2.carbon.user.core.service.RealmService;

import java.security.*;
import java.security.cert.Certificate;
import java.security.cert.CertificateEncodingException;
import java.util.Calendar;
import java.util.concurrent.ConcurrentHashMap;

public class JWTHelper {

    private static Log log = LogFactory.getLog(JWTHelper.class);

    private static final String SHA256_WITH_RSA = "SHA256withRSA";
    private static String signatureAlgorithm = SHA256_WITH_RSA;
    private static final String NONE = "NONE";
    private static volatile long ttl = -1L;

    private static ConcurrentHashMap<Integer, Key> privateKeys = new ConcurrentHashMap<Integer, Key>();
    private static ConcurrentHashMap<Integer, Certificate> publicCerts = new ConcurrentHashMap<Integer, Certificate>();


    public static String generateJWT(String tenantDomain) throws Exception {

        String jwt = buildJWT(tenantDomain);

        log.info("JWT  = " + jwt);

        return jwt;
    }


    /**
     * Method that generates the JWT.
     *
     * @return signed JWT token
     * @throws Exception
     */
    private static String buildJWT(String tenantDomain) throws Exception {

        //generating expiring timestamp
        long currentTime = Calendar.getInstance().getTimeInMillis();
        long expireIn = currentTime + 1000 * 60 * getTTL();

        String jwtBody;
        String issuer = "wso2.org/appserver";
        int tenantId = getTenantId(tenantDomain);

        //Sample JWT body
        //{"iss":"wso2.org/appserver","exp":1448299984841,"tenant_domain":"wso2.com","tenant_id":"1"}

        StringBuilder jwtBuilder = new StringBuilder();
        jwtBuilder.append("{");
        jwtBuilder.append("\"iss\":\"");
        jwtBuilder.append(issuer);
        jwtBuilder.append("\",");

        jwtBuilder.append("\"exp\":");
        jwtBuilder.append(String.valueOf(expireIn));
        jwtBuilder.append(",");

        jwtBuilder.append("\"tenant_domain\":\"");
        jwtBuilder.append(tenantDomain);
        jwtBuilder.append("\",");

        jwtBuilder.append("\"tenant_id\":\"");
        jwtBuilder.append(String.valueOf(tenantId));
        jwtBuilder.append("\"");

        jwtBuilder.append("}");
        jwtBody = jwtBuilder.toString();

        String jwtHeader = null;

        //if signature algo==NONE, header without cert
        if (signatureAlgorithm.equals(NONE)) {
            jwtHeader = "{\"typ\":\"JWT\"}";
        } else if (signatureAlgorithm.equals(SHA256_WITH_RSA)) {
            jwtHeader = addCertToHeader(tenantDomain);
        }

        String base64EncodedHeader = Base64Utils.encode(jwtHeader.getBytes());
        String base64EncodedBody = Base64Utils.encode(jwtBody.getBytes());
        if (signatureAlgorithm.equals(SHA256_WITH_RSA)) {
            String assertion = base64EncodedHeader + "." + base64EncodedBody;

            //get the assertion signed
            byte[] signedAssertion = signJWT(assertion, tenantDomain);


            log.info("Signed assertion value : " + new String(signedAssertion));
            String base64EncodedAssertion = Base64Utils.encode(signedAssertion);

            return base64EncodedHeader + "." + base64EncodedBody + "." + base64EncodedAssertion;
        } else {
            return base64EncodedHeader + "." + base64EncodedBody + ".";
        }
    }

    private static long getTTL() {
        if (ttl != -1) {
            return ttl;
        }

        synchronized (JWTHelper.class) {
            if (ttl != -1) {
                return ttl;
            }
            String ttlValue = "15"; //This can be read from a property
            if (ttlValue != null) {
                ttl = Long.parseLong(ttlValue);
            } else {
                ttl = 15L;
            }
            return ttl;
        }
    }


    private static byte[] signJWT(String assertion, String tenantDomain) throws Exception {

        try {
            //get tenantId
            int tenantId = getTenantId(tenantDomain);

            Key privateKey = null;

            if (!(privateKeys.containsKey(tenantId))) {
                //get tenant's key store manager
                KeyStoreManager tenantKSM = KeyStoreManager.getInstance(tenantId);

                if (!tenantDomain.equals(MultitenantConstants.SUPER_TENANT_DOMAIN_NAME)) {
                    //derive key store name
                    String ksName = tenantDomain.trim().replace(".", "-");
                    String jksName = ksName + ".jks";
                    //obtain private key
                    //TODO: maintain a hash map with tenants' private keys after first initialization
                    privateKey = tenantKSM.getPrivateKey(jksName, tenantDomain);
                } else {
                    try {
                        privateKey = tenantKSM.getDefaultPrivateKey();
                    } catch (Exception e) {
                        log.error("Error while obtaining private key for super tenant", e);
                    }
                }
                if (privateKey != null) {
                    privateKeys.put(tenantId, privateKey);
                }
            } else {
                privateKey = privateKeys.get(tenantId);
            }

            //initialize signature with private key and algorithm
            Signature signature = Signature.getInstance(signatureAlgorithm);
            signature.initSign((PrivateKey) privateKey);

            //update signature with data to be signed
            byte[] dataInBytes = assertion.getBytes();
            signature.update(dataInBytes);

            //sign the assertion and return the signature
            byte[] signedInfo = signature.sign();
            return signedInfo;

        } catch (NoSuchAlgorithmException e) {
            String error = "Signature algorithm not found.";
            //do not log
            throw new Exception(error);
        } catch (InvalidKeyException e) {
            String error = "Invalid private key provided for the signature";
            //do not log
            throw new Exception(error);
        } catch (SignatureException e) {
            String error = "Error in signature";
            //do not log
            throw new Exception(error);
        } catch (Exception e) {
            //do not log
            throw new Exception(e.getMessage());
        }
    }

    private static int getTenantId(String tenantDomain) throws Exception {

        try {
            RealmService realmService = JWTHelperDataHolder.getInstance().getRealmService();
            int tenantId = realmService.getTenantManager().getTenantId(tenantDomain);
            return tenantId;
        } catch (UserStoreException e) {
            String error = "Error in obtaining tenantId from Domain";
            //do not log
            throw new Exception(error);
        }
    }

    /**
     * Helper method to add public certificate to JWT_HEADER to signature verification.
     *
     * @param endUserName
     * @throws Exception
     */
    private static String addCertToHeader(String tenantDomain) throws Exception {

        try {
            //get tenantId
            int tenantId = getTenantId(tenantDomain);
            Certificate publicCert = null;

            if (!(publicCerts.containsKey(tenantId))) {
                //get tenant's key store manager
                KeyStoreManager tenantKSM = KeyStoreManager.getInstance(tenantId);

                KeyStore keyStore = null;
                if (!tenantDomain.equals(MultitenantConstants.SUPER_TENANT_DOMAIN_NAME)) {
                    //derive key store name
                    String ksName = tenantDomain.trim().replace(".", "-");
                    String jksName = ksName + ".jks";
                    keyStore = tenantKSM.getKeyStore(jksName);
                    publicCert = keyStore.getCertificate(tenantDomain);
                } else {
                    publicCert = tenantKSM.getDefaultPrimaryCertificate();
                }
                if (publicCert != null) {
                    publicCerts.put(tenantId, publicCert);
                }
            } else {
                publicCert = publicCerts.get(tenantId);
            }

            //generate the SHA-1 thumbprint of the certificate
            //TODO: maintain a hashmap with tenants' pubkey thumbprints after first initialization
            MessageDigest digestValue = MessageDigest.getInstance("SHA-1");
            byte[] der = publicCert.getEncoded();
            digestValue.update(der);
            byte[] digestInBytes = digestValue.digest();

            String publicCertThumbprint = hexify(digestInBytes);
            String base64EncodedThumbPrint = Base64Utils.encode(publicCertThumbprint.getBytes());

            StringBuilder jwtHeader = new StringBuilder();

            //Sample header
            //{"typ":"JWT", "alg":"SHA256withRSA", "x5t":"NmJmOGUxMzZlYjM2ZDRhNTZlYTA1YzdhZTRiOWE0NWI2M2JmOTc1ZA=="}

            jwtHeader.append("{\"typ\":\"JWT\",");
            jwtHeader.append("\"alg\":\"");
            jwtHeader.append(signatureAlgorithm);
            jwtHeader.append("\",");

            jwtHeader.append("\"x5t\":\"");
            jwtHeader.append(base64EncodedThumbPrint);
            jwtHeader.append("\"");

            jwtHeader.append("}");
            return jwtHeader.toString();

        } catch (KeyStoreException e) {
            String error = "Error in obtaining tenant's keystore";
            throw new Exception(error);
        } catch (CertificateEncodingException e) {
            String error = "Error in generating public cert thumbprint";
            throw new Exception(error);
        } catch (NoSuchAlgorithmException e) {
            String error = "Error in generating public cert thumbprint";
            throw new Exception(error);
        } catch (Exception e) {
            String error = "Error in obtaining tenant's keystore";
            throw new Exception(error);
        }
    }

    /**
     * Helper method to hexify a byte array.
     *
     * @param bytes
     * @return hexadecimal representation
     */
    private static String hexify(byte bytes[]) {

        char[] hexDigits = {'0', '1', '2', '3', '4', '5', '6', '7',
                '8', '9', 'a', 'b', 'c', 'd', 'e', 'f'};

        StringBuffer buf = new StringBuffer(bytes.length * 2);

        for (int i = 0; i < bytes.length; ++i) {
            buf.append(hexDigits[(bytes[i] & 0xf0) >> 4]);
            buf.append(hexDigits[bytes[i] & 0x0f]);
        }

        return buf.toString();
    }

}
_______________________________________________
Dev mailing list
Dev@wso2.org
http://wso2.org/cgi-bin/mailman/listinfo/dev

Reply via email to