/* * eID Applet Project. * Copyright (C) 2008-2009 FedICT. * * This is free software; you can redistribute it and/or modify it * under the terms of the GNU Lesser General Public License version * 3.0 as published by the Free Software Foundation. * * This software is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public * License along with this software; if not, see * http://www.gnu.org/licenses/. */ package be.fedict.eid.applet.service.impl; import java.security.InvalidKeyException; import java.security.NoSuchAlgorithmException; import java.security.cert.X509Certificate; import javax.crypto.Mac; import javax.crypto.SecretKey; import javax.crypto.spec.SecretKeySpec; import javax.security.auth.x500.X500Principal; import org.apache.commons.codec.DecoderException; import org.apache.commons.codec.binary.Hex; import org.apache.commons.logging.Log; import org.apache.commons.logging.LogFactory; /** * Utility class for user identifier construction. * * @author Frank Cornelis * */ public class UserIdentifierUtil { private static final Log LOG = LogFactory.getLog(UserIdentifierUtil.class); private UserIdentifierUtil() { super(); } /** * Gives back a unique user identifier given an X509 certificate. * * @param signingCertificate * @return */ public static String getUserId(X509Certificate signingCertificate) { X500Principal userPrincipal = signingCertificate.getSubjectX500Principal(); String name = userPrincipal.toString(); int serialNumberBeginIdx = name.indexOf("SERIALNUMBER="); if (-1 == serialNumberBeginIdx) { throw new SecurityException("SERIALNUMBER not found in X509 CN"); } int serialNumberValueBeginIdx = serialNumberBeginIdx + "SERIALNUMBER=".length(); int serialNumberValueEndIdx = name.indexOf(",", serialNumberValueBeginIdx); if (-1 == serialNumberValueEndIdx) { serialNumberValueEndIdx = name.length(); } String userId = name.substring(serialNumberValueBeginIdx, serialNumberValueEndIdx); return userId; } public static final String HMAC_ALGO = "HmacSHA1"; /** * Gives back a non-reversible citizen identifier (NRCID). * * @param userId * the primary user identifier, i.e. the national registry * number. * @param orgId * the optional organization identifier. * @param appId * the optional application identifier. * @param secret * the application specific secret. Should be at least 128 bit * long. Encoded in hexadecimal format. * @return */ public static String getNonReversibleCitizenIdentifier(String userId, String orgId, String appId, String secret) { if (null == secret) { throw new IllegalArgumentException("secret key is null"); } /* * Avoid XML formatting issues introduced by some web.xml XML editors. */ secret = secret.trim(); if (null != orgId) { orgId = orgId.trim(); } else { LOG.warn("it is advised to use an orgId"); } if (null != appId) { appId = appId.trim(); } else { LOG.warn("it is advised to use an appId"); } /* * Decode the secret key. */ byte[] secretKey; try { secretKey = Hex.decodeHex(secret.toCharArray()); } catch (DecoderException e) { LOG.error("secret is not hexadecimal encoded: " + e.getMessage()); throw new IllegalArgumentException("secret is not hexadecimal encoded"); } if ((128 / 8) > secretKey.length) { /* * 128 bit is seen as secure these days. */ LOG.warn("secret key is too short"); throw new IllegalArgumentException("secret key is too short"); } /* * Construct the HMAC input sequence. */ String input = userId; if (null != appId) { input += appId; } if (null != orgId) { input += orgId; } byte[] inputData = input.getBytes(); SecretKey macKey = new SecretKeySpec(secretKey, HMAC_ALGO); Mac mac; try { mac = Mac.getInstance(macKey.getAlgorithm()); } catch (NoSuchAlgorithmException e) { throw new RuntimeException("HMAC algo not available: " + e.getMessage()); } try { mac.init(macKey); } catch (InvalidKeyException e) { LOG.error("invalid secret key: " + e.getMessage(), e); throw new RuntimeException("invalid secret"); } mac.update(inputData); byte[] resultHMac = mac.doFinal(); String resultHex = new String(Hex.encodeHex(resultHMac)).toUpperCase(); return resultHex; } }