summaryrefslogtreecommitdiffstats
path: root/base/kra
diff options
context:
space:
mode:
authorChristina Fu <cfu@redhat.com>2012-04-05 13:37:01 -0700
committerChristina Fu <cfu@redhat.com>2012-04-05 13:37:01 -0700
commitdb4f081db1ea6eb38c185b34b118ed73c6a2b67d (patch)
treea94b99a921a1de8ab948fe8ddbaefd60a692a5ae /base/kra
parenta37d66662a859bd706f449edddc3ae715ee2d520 (diff)
downloadpki-db4f081db1ea6eb38c185b34b118ed73c6a2b67d.tar.gz
pki-db4f081db1ea6eb38c185b34b118ed73c6a2b67d.tar.xz
pki-db4f081db1ea6eb38c185b34b118ed73c6a2b67d.zip
Fix for Bug 745278 - [RFE] ECC encryption keys cannot be archived.
For the ECC plan and the different phases, please refer to http://pki.fedoraproject.org/wiki/ECC_in_Dogtag Design for each phase is on the same wiki page. Note: the designs beyond phase 2 were more like a brain dump. Although I said "Do Not Review," you are free to take a peak at what's intended down the road. I will go back and take a closer look and refine/adjust the designs when I begin implementation for each new phase. What you need to know: * Problem 1 - nethsm issue: On the server side, if you turn on FIPS mode, in addition to nethsm, you need to attach certicom as well to have ECC SSL working on the server side. This problem has already been reported to Thales last year and they said they'd look into putting the item on their next release. Recently through a different contact, we learned there might be a way to "turn it on" (still waiting for their further instruction) * Problem 2- Certicom issue: This is a show-stopper for deployment. Initially, on the client side, I used Kai's special version of Xulrunner/Firefox, attached to Certicom token, so that the CRMF requests can be generated with key archival option. However, I encountered (or, re-encountered) an issue with certicom token. Certicom generates ECC keys with the wrong format (not PKCS7 conforming), which makes ECC key archival impossible on the server side if you use non-certicom token with DRM (but we expect an HSM in most product deployment). I have contacted Certicom for this issue, and they confirmed that they indeed have such issue. We are hoping they will fix it. But then you might ask, "I thought I saw some ECC enrollment profiles/javascripts being checked in? How were the tests done?" The tests for those profiles were done against this ECC key archival/recovery DRM prototype I implemented last year (needs to be turned on manually in 8.1), where I "cheated" (yeah, that's why it's called a prototype) by decrypting the private key in the CRMF on DRM, and then manipulating the byte array to strip off the offending bytes before archival. In the real, non-prototype implementation, which is what's in this patch, for security reasons, private keys are unwrapped directly onto the token during key archival, so there is no way to manipulate the keys in memory and bypass the Certicom issue. A word about Kai's special version of Xulrunner/Firefox. It is not yet publicly available (due out in Firefox 10.0.4 on RHEL 5.8). * Problem 3- Firefox with nethsm issue: Another option was to connect Kai's special version firefox with an HSM to test my DRM/JSS code. However, for whatever reason, I could not get SSL going between such Firefox and ECC CA ( I did not try very hard though, as I have one other option -- writing my own ECC CRMF generation tool. I might come back to try the nethsm Firefox idea later) My solution (how I work on this official implementation): * I hacked up a ECC CRMF tool by taking the CRMFPopClient (existing in current releases), gutting out the RSA part of the code, and replacing it with ECC code. I call it CRMFPopClientEC. Two types of ECC key pairs could be generated: ECDSA or ECDH (That's another benefit of writing my own tool -- I don't know if you can select which type to generate in the Javascript... maybe you can, I just don't know). I'm in no way condoning archival of signing keys!! This is just a test tool. This tool takes a curve name as option (along with others), generates an ECC key pair, crafts up an CRMF request with key archival option, and sends request directly to the specified CA. You will see a "Deferred" message in the HTML response (see attachment for example) Once CA agent approves the request, the archival request goes to DRM and the user private key is archived. For recovery, DRM agent selects key recovery, etc, and you get your pkcs12. I did some sanity test with the pkcs12 recovered: * Import the recovered pkcs12 into a certicom library: pk12util -d . -h "Certicom FIPS Cert/Key Services" -i userEC.p12 I also tested by retrieving a p12, importing it into a browser, and adding the user as an agent and the user could act as agent via ssl client auth to the CA. Finally, much of the RSA-centric code had been cleared out of the way at the time when I worked on the DRM ECC prototype, so you don't see much of that in this round. How do you test? Well, unless you want to use my CRMFPopClientEC tool hooked up with a nethsm (like I did), or write your own tool, you can't really test it until Certicom fixes their issue. (BTW CRMFPopClientEC can also be changed to work with ceriticom, although you would run into the same issue I mentioned above)
Diffstat (limited to 'base/kra')
-rw-r--r--base/kra/src/com/netscape/kra/EncryptionUnit.java5
-rw-r--r--base/kra/src/com/netscape/kra/EnrollmentService.java152
-rw-r--r--base/kra/src/com/netscape/kra/RecoveryService.java7
3 files changed, 139 insertions, 25 deletions
diff --git a/base/kra/src/com/netscape/kra/EncryptionUnit.java b/base/kra/src/com/netscape/kra/EncryptionUnit.java
index 946f57613..1d06fd2d5 100644
--- a/base/kra/src/com/netscape/kra/EncryptionUnit.java
+++ b/base/kra/src/com/netscape/kra/EncryptionUnit.java
@@ -370,6 +370,7 @@ public abstract class EncryptionUnit implements IEncryptionUnit {
PrivateKey.Type keytype = null;
String alg = pubKey.getAlgorithm();
+ CMS.debug("EncryptionUnit.unwrap alg ="+ alg);
if (alg.equals("DSA")) {
keytype = PrivateKey.DSA;
} else if (alg.equals("EC")) {
@@ -384,18 +385,22 @@ public abstract class EncryptionUnit implements IEncryptionUnit {
} catch (TokenException e) {
CMS.getLogger().log(ILogger.EV_SYSTEM, null, ILogger.S_KRA, ILogger.LL_FAILURE, CMS.getLogMessage("CMSCORE_KRA_ENCRYPTION_UNWRAP", e.toString()));
Debug.trace("EncryptionUnit::unwrap " + e.toString());
+ CMS.debug("EncryptionUnit.unwrap "+ e.toString());
return null;
} catch (NoSuchAlgorithmException e) {
CMS.getLogger().log(ILogger.EV_SYSTEM, null, ILogger.S_KRA, ILogger.LL_FAILURE, CMS.getLogMessage("CMSCORE_KRA_ENCRYPTION_UNWRAP", e.toString()));
Debug.trace("EncryptionUnit::unwrap " + e.toString());
+ CMS.debug("EncryptionUnit.unwrap "+ e.toString());
return null;
} catch (InvalidAlgorithmParameterException e) {
CMS.getLogger().log(ILogger.EV_SYSTEM, null, ILogger.S_KRA, ILogger.LL_FAILURE, CMS.getLogMessage("CMSCORE_KRA_ENCRYPTION_UNWRAP", e.toString()));
Debug.trace("EncryptionUnit::unwrap " + e.toString());
+ CMS.debug("EncryptionUnit.unwrap "+ e.toString());
return null;
} catch (InvalidKeyException e) {
CMS.getLogger().log(ILogger.EV_SYSTEM, null, ILogger.S_KRA, ILogger.LL_FAILURE, CMS.getLogMessage("CMSCORE_KRA_ENCRYPTION_UNWRAP", e.toString()));
Debug.trace("EncryptionUnit::unwrap " + e.toString());
+ CMS.debug("EncryptionUnit.unwrap "+ e.toString());
return null;
} catch (Exception e) {
CMS.debug("EncryptionUnit.unwrap : Exception:"+e.toString());
diff --git a/base/kra/src/com/netscape/kra/EnrollmentService.java b/base/kra/src/com/netscape/kra/EnrollmentService.java
index 37d1aea53..c65a6ea62 100644
--- a/base/kra/src/com/netscape/kra/EnrollmentService.java
+++ b/base/kra/src/com/netscape/kra/EnrollmentService.java
@@ -22,6 +22,8 @@ import java.io.IOException;
import java.math.BigInteger;
import java.security.InvalidKeyException;
import java.security.cert.CertificateException;
+import java.security.PublicKey;
+import java.util.Arrays;
import java.util.StringTokenizer;
import java.util.Vector;
@@ -35,11 +37,15 @@ import netscape.security.x509.CertificateX509Key;
import netscape.security.x509.X509CertInfo;
import netscape.security.x509.X509Key;
+import org.mozilla.jss.CryptoManager;
import org.mozilla.jss.asn1.ASN1Util;
import org.mozilla.jss.asn1.ASN1Value;
import org.mozilla.jss.asn1.InvalidBERException;
import org.mozilla.jss.asn1.OBJECT_IDENTIFIER;
import org.mozilla.jss.asn1.SEQUENCE;
+import org.mozilla.jss.crypto.PrivateKey;
+import org.mozilla.jss.pkcs11.PK11ECPublicKey;
+import org.mozilla.jss.pkcs11.PK11ParameterSpec;
import org.mozilla.jss.pkix.crmf.CertReqMsg;
import org.mozilla.jss.pkix.crmf.CertRequest;
import org.mozilla.jss.pkix.crmf.PKIArchiveOptions;
@@ -48,8 +54,11 @@ import org.mozilla.jss.pkix.primitive.AVA;
import com.netscape.certsrv.apps.CMS;
import com.netscape.certsrv.authentication.AuthToken;
import com.netscape.certsrv.base.EBaseException;
+import com.netscape.certsrv.base.IConfigStore;
+import com.netscape.certsrv.base.MetaInfo;
import com.netscape.certsrv.base.SessionContext;
import com.netscape.certsrv.dbs.keydb.IKeyRepository;
+import com.netscape.certsrv.dbs.keydb.IKeyRecord;
import com.netscape.certsrv.kra.EKRAException;
import com.netscape.certsrv.kra.IKeyRecoveryAuthority;
import com.netscape.certsrv.kra.ProofOfArchival;
@@ -61,6 +70,7 @@ import com.netscape.certsrv.request.IService;
import com.netscape.certsrv.security.IStorageKeyUnit;
import com.netscape.certsrv.security.ITransportKeyUnit;
import com.netscape.certsrv.util.IStatsSubsystem;
+import com.netscape.cms.servlet.key.KeyRecordParser;
import com.netscape.cmscore.crmf.CRMFParser;
import com.netscape.cmscore.crmf.PKIArchiveOptionsContainer;
import com.netscape.kra.ArchiveOptions;
@@ -141,6 +151,17 @@ public class EnrollmentService implements IService {
*/
public boolean serviceRequest(IRequest request)
throws EBaseException {
+ CryptoManager cm = null;
+ IConfigStore config = null;
+ Boolean allowEncDecrypt_archival = false;
+
+ try {
+ cm = CryptoManager.getInstance();
+ config = CMS.getConfigStore();
+ allowEncDecrypt_archival = config.getBoolean("kra.allowEncDecrypt.archival", false);
+ } catch (Exception e) {
+ throw new EBaseException(CMS.getUserMessage("CMS_BASE_CERT_ERROR", e.toString()));
+ }
IStatsSubsystem statsSub = (IStatsSubsystem) CMS.getSubsystem("stats");
if (statsSub != null) {
@@ -167,6 +188,7 @@ public class EnrollmentService implements IService {
mKRA.log(ILogger.LL_INFO, "KRA services enrollment request");
// unwrap user key with transport
byte unwrapped[] = null;
+ byte tmp_unwrapped[] = null;
PKIArchiveOptionsContainer aOpts[] = null;
String profileId = request.getExtDataInString("profileId");
@@ -204,13 +226,14 @@ public class EnrollmentService implements IService {
for (int i = 0; i < aOpts.length; i++) {
ArchiveOptions opts = new ArchiveOptions(aOpts[i].mAO);
+ if (allowEncDecrypt_archival == true) {
if (statsSub != null) {
statsSub.startTiming("decrypt_user_key");
}
mKRA.log(ILogger.LL_INFO, "KRA decrypts external private");
if (CMS.debugOn())
CMS.debug("EnrollmentService::about to decryptExternalPrivate");
- unwrapped = mTransportUnit.decryptExternalPrivate(
+ tmp_unwrapped = mTransportUnit.decryptExternalPrivate(
opts.getEncSymmKey(),
opts.getSymmAlgOID(),
opts.getSymmAlgParams(),
@@ -220,7 +243,7 @@ public class EnrollmentService implements IService {
}
if (CMS.debugOn())
CMS.debug("EnrollmentService::finished decryptExternalPrivate");
- if (unwrapped == null) {
+ if (tmp_unwrapped == null) {
mKRA.log(ILogger.LL_FAILURE, CMS.getLogMessage("CMSCORE_KRA_UNWRAP_USER_KEY"));
auditMessage = CMS.getLogMessage(
@@ -235,6 +258,17 @@ public class EnrollmentService implements IService {
CMS.getUserMessage("CMS_KRA_INVALID_PRIVATE_KEY"));
}
+ /* making sure leading 0's are removed */
+ int first=0;
+ for (int j=0; (j< tmp_unwrapped.length) && (tmp_unwrapped[j]==0); j++) {
+ first++;
+ }
+ unwrapped = Arrays.copyOfRange(tmp_unwrapped, first, tmp_unwrapped.length);
+ } /*else { allowEncDecrypt_archival != true
+ this is done below with unwrap()
+ }
+ */
+
// retrieve pubic key
X509Key publicKey = getPublicKey(request, aOpts[i].mReqPos);
byte publicKeyData[] = publicKey.getEncoded();
@@ -255,28 +289,51 @@ public class EnrollmentService implements IService {
CMS.getUserMessage("CMS_KRA_INVALID_PUBLIC_KEY"));
}
- /* Bugscape #54948 - verify public and private key before archiving key */
+ String keyAlg = publicKey.getAlgorithm();
+ CMS.debug("EnrollmentService: algorithm of key to archive is: "+ keyAlg);
- if (statsSub != null) {
- statsSub.startTiming("verify_key");
- }
- if (verifyKeyPair(publicKeyData, unwrapped) == false) {
- mKRA.log(ILogger.LL_FAILURE,
+ PublicKey pubkey = null;
+ org.mozilla.jss.crypto.PrivateKey entityPrivKey = null;
+ if ( allowEncDecrypt_archival == false) {
+ try {
+ pubkey = X509Key.parsePublicKey (new DerValue(publicKeyData));
+ } catch (Exception e) {
+ CMS.debug("EnrollmentService: parsePublicKey:"+e.toString());
+ throw new EKRAException(
+ CMS.getUserMessage("CMS_KRA_INVALID_PUBLIC_KEY"));
+ }
+ entityPrivKey =
+ mTransportUnit.unwrap(
+ opts.getEncSymmKey(),
+ opts.getSymmAlgOID(),
+ opts.getSymmAlgParams(),
+ opts.getEncValue(),
+ (PublicKey) pubkey);
+ } // !allowEncDecrypt_archival
+
+ /* Bugscape #54948 - verify public and private key before archiving key */
+ if (keyAlg.equals("RSA") && (allowEncDecrypt_archival == true)) {
+ if (statsSub != null) {
+ statsSub.startTiming("verify_key");
+ }
+ if (verifyKeyPair(publicKeyData, unwrapped) == false) {
+ mKRA.log(ILogger.LL_FAILURE,
CMS.getLogMessage("CMSCORE_KRA_PUBLIC_NOT_FOUND"));
- auditMessage = CMS.getLogMessage(
+ auditMessage = CMS.getLogMessage(
LOGGING_SIGNED_AUDIT_PRIVATE_KEY_ARCHIVE_REQUEST,
auditSubjectID,
ILogger.FAILURE,
auditRequesterID,
auditArchiveID);
- audit(auditMessage);
- throw new EKRAException(
+ audit(auditMessage);
+ throw new EKRAException(
CMS.getUserMessage("CMS_KRA_INVALID_PUBLIC_KEY"));
- }
- if (statsSub != null) {
- statsSub.endTiming("verify_key");
+ }
+ if (statsSub != null) {
+ statsSub.endTiming("verify_key");
+ }
}
/**
@@ -309,8 +366,15 @@ public class EnrollmentService implements IService {
if (statsSub != null) {
statsSub.startTiming("encrypt_user_key");
}
- byte privateKeyData[] = mStorageUnit.encryptInternalPrivate(
+ byte privateKeyData[] = null;
+
+ if (allowEncDecrypt_archival == true) {
+ privateKeyData = mStorageUnit.encryptInternalPrivate(
unwrapped);
+ } else {
+ privateKeyData = mStorageUnit.wrap(entityPrivKey);
+ }
+
if (statsSub != null) {
statsSub.endTiming("encrypt_user_key");
}
@@ -335,12 +399,7 @@ public class EnrollmentService implements IService {
privateKeyData, owner,
publicKey.getAlgorithmId().getOID().toString(), agentId);
- // we deal with RSA key only
- try {
- RSAPublicKey rsaPublicKey = new RSAPublicKey(publicKeyData);
-
- rec.setKeySize(Integer.valueOf(rsaPublicKey.getKeySize()));
- } catch (InvalidKeyException e) {
+ if (rec == null) {
auditMessage = CMS.getLogMessage(
LOGGING_SIGNED_AUDIT_PRIVATE_KEY_ARCHIVE_REQUEST,
@@ -353,6 +412,57 @@ public class EnrollmentService implements IService {
throw new EKRAException(CMS.getUserMessage("CMS_KRA_INVALID_KEYRECORD"));
}
+ if (keyAlg.equals("RSA")) {
+ try {
+ RSAPublicKey rsaPublicKey = new RSAPublicKey(publicKeyData);
+
+ rec.setKeySize(Integer.valueOf(rsaPublicKey.getKeySize()));
+ } catch (InvalidKeyException e) {
+
+ auditMessage = CMS.getLogMessage(
+ LOGGING_SIGNED_AUDIT_PRIVATE_KEY_ARCHIVE_REQUEST,
+ auditSubjectID,
+ ILogger.FAILURE,
+ auditRequesterID,
+ auditArchiveID);
+
+ audit(auditMessage);
+ throw new EKRAException(CMS.getUserMessage("CMS_KRA_INVALID_KEYRECORD"));
+ }
+ } else if (keyAlg.equals("EC")) {
+
+ String oidDescription = "UNDETERMINED";
+ // for KeyRecordParser
+ MetaInfo metaInfo = new MetaInfo();
+
+ try {
+ byte curve[] =
+ ASN1Util.getECCurveBytesByX509PublicKeyBytes(publicKeyData,
+ false /* without tag and size */);
+ if (curve.length != 0) {
+ oidDescription = ASN1Util.getOIDdescription(curve);
+ } else {
+ /* this is to be used by derdump */
+ byte curveTS[] =
+ ASN1Util.getECCurveBytesByX509PublicKeyBytes(publicKeyData,
+ true /* with tag and size */);
+ if (curveTS.length != 0) {
+ oidDescription = CMS.BtoA(curveTS);
+ }
+ }
+ } catch (Exception e) {
+ CMS.debug("EnrollmentService: ASN1Util.getECCurveBytesByX509PublicKeyByte() throws exception: "+ e.toString());
+ CMS.debug("EnrollmentService: exception alowed. continue");
+ }
+
+ metaInfo.set(KeyRecordParser.OUT_KEY_EC_CURVE,
+ oidDescription);
+
+ rec.set(IKeyRecord.ATTR_META_INFO, metaInfo);
+ // key size does not apply to EC;
+ rec.setKeySize(-1);
+ }
+
// if record alreay has a serial number, yell out.
if (rec.getSerialNumber() != null) {
mKRA.log(ILogger.LL_FAILURE,
diff --git a/base/kra/src/com/netscape/kra/RecoveryService.java b/base/kra/src/com/netscape/kra/RecoveryService.java
index c8ecdcf5a..0cbe2009f 100644
--- a/base/kra/src/com/netscape/kra/RecoveryService.java
+++ b/base/kra/src/com/netscape/kra/RecoveryService.java
@@ -377,10 +377,9 @@ public class RecoveryService implements IService {
public synchronized PrivateKey recoverKey(Hashtable<String, Object> request, KeyRecord keyRecord, boolean isRSA)
throws EBaseException {
- if (!isRSA) {
- CMS.debug("RecoverService: recoverKey: currently, non-RSA keys are not supported when allowEncDecrypt_ is false");
- throw new EKRAException(CMS.getUserMessage("CMS_KRA_RECOVERY_FAILED_1", "key type not supported"));
- }
+ CMS.debug("RecoverService: recoverKey: key to recover is RSA? "+
+ isRSA);
+
try {
if (CMS.getConfigStore().getBoolean("kra.keySplitting")) {
Credential creds[] = (Credential[])