def getCertificate(self, certificateName, allowAny = False): """ Get a certificate from the identity storage. :param Name certificateName: The name of the requested certificate. :param bool allowAny: (optional) If False, only a valid certificate will be returned, otherwise validity is disregarded. If omitted, allowAny is False. :return: The requested certificate. If not found, return None. :rtype: Data """ chosenCert = None certificateUri = certificateName.toUri() cursor = self._database.cursor() #if not allowAny: # validityClause = " AND valid_flag=1" #else: validityClause = "" # use LIKE because key locators chop off timestamps # need to escape any percent signs in the certificate uri for sql's # sake, but still append % for LIKE escapedUri = certificateUri.replace('%', '\\%') full_statement = "SELECT certificate_data FROM Certificate WHERE cert_name LIKE ?"+validityClause+" ESCAPE '\\' ORDER BY cert_name DESC" #full_statement = "SELECT certificate_data FROM Certificate WHERE cert_name=?"+validityClause cursor.execute(full_statement, (escapedUri+'%', )) try: (certData, ) = cursor.fetchone() except TypeError: pass else: chosenCert = IdentityCertificate() chosenCert.wireDecode(bytearray(certData)) return chosenCert
def _generateCertificateForKey(self, keyName): # Let any raised SecurityExceptions bubble up. publicKeyBits = self._identityStorage.getKey(keyName) publicKey = PublicKey(publicKeyBits) timestamp = Common.getNowMilliseconds() # TODO: Specify where the 'KEY' component is inserted # to delegate responsibility for cert delivery. # cf: http://redmine.named-data.net/issues/1659 certificateName = keyName.getPrefix(-1).append('KEY').append( keyName.get(-1)) certificateName.append("ID-CERT").appendVersion(int(timestamp)) certificate = IdentityCertificate() certificate.setName(certificateName) certificate.setNotBefore(timestamp) certificate.setNotAfter( (timestamp + 2 * 365 * 24 * 3600 * 1000)) # about 2 years. certificate.setPublicKeyInfo(publicKey) # ndnsec likes to put the key name in a subject description. sd = CertificateSubjectDescription("2.5.4.41", keyName.toUri()) certificate.addSubjectDescription(sd) certificate.encode() return certificate
def test_prepare_unsigned_certificate(self): identityStorage = MemoryIdentityStorage() privateKeyStorage = MemoryPrivateKeyStorage() identityManager = IdentityManager(identityStorage, privateKeyStorage) keyName = Name("/test/ksk-1457560485494") identityStorage.addKey(keyName, KeyType.RSA, Blob(PUBLIC_KEY)) subjectDescriptions = [] subjectDescriptions.append( CertificateSubjectDescription(TEST_OID, "TEST NAME")) newCertificate = identityManager.prepareUnsignedIdentityCertificate( keyName, keyName.getPrefix(1), self.toyCertNotBefore, self.toyCertNotAfter, subjectDescriptions) # Update the generated certificate version to equal the one in toyCert. newCertificate.setName( Name(newCertificate.getName().getPrefix(-1).append( self.toyCert.getName().get(-1)))) # Make a copy to test encoding. certificateCopy = IdentityCertificate(newCertificate) self.assertEqual( str(self.toyCert), str(certificateCopy), "Prepared unsigned certificate dump does not have the expected format" )
def generateCertificateForKey(self, keyName): # let any raised SecurityExceptions bubble up publicKeyBits = self._identityStorage.getKey(keyName) publicKeyType = self._identityStorage.getKeyType(keyName) publicKey = PublicKey(publicKeyType, publicKeyBits) timestamp = Common.getNowMilliseconds() # TODO: specify where the 'KEY' component is inserted # to delegate responsibility for cert delivery certificateName = keyName.getPrefix(-1).append('KEY').append( keyName.get(-1)) certificateName.append("ID-CERT").append( Name.Component(struct.pack(">Q", timestamp))) certificate = IdentityCertificate(certificateName) certificate.setNotBefore(timestamp) certificate.setNotAfter( (timestamp + 30 * 86400 * 1000)) # about a month certificate.setPublicKeyInfo(publicKey) # ndnsec likes to put the key name in a subject description sd = CertificateSubjectDescription("2.5.4.41", keyName.toUri()) certificate.addSubjectDescription(sd) certificate.encode() return certificate
def test_refresh_10s(self): with open('policy_config/testData', 'r') as dataFile: encodedData = dataFile.read() data = Data() dataBlob = Blob(b64decode(encodedData)) data.wireDecode(dataBlob) # needed, since the KeyChain will express interests in unknown # certificates vr = doVerify(self.policyManager, data) self.assertTrue(vr.hasFurtherSteps, "ConfigPolicyManager did not create ValidationRequest for unknown certificate") self.assertEqual(vr.successCount, 0, "ConfigPolicyManager called success callback with pending ValidationRequest") self.assertEqual(vr.failureCount, 0, "ConfigPolicyManager called failure callback with pending ValidationRequest") # now save the cert data to our anchor directory, and wait # we have to sign it with the current identity or the # policy manager will create an interest for the signing certificate with open(self.testCertFile, 'w') as certFile: cert = IdentityCertificate() certData = b64decode(CERT_DUMP) cert.wireDecode(Blob(certData, False)) self.keyChain.signByIdentity(cert, self.identityName) encodedCert = b64encode(cert.wireEncode().toBytes()) certFile.write(Blob(encodedCert, False).toRawStr()) # still too early for refresh to pick it up vr = doVerify(self.policyManager, data) self.assertTrue(vr.hasFurtherSteps, "ConfigPolicyManager refresh occured sooner than specified") self.assertEqual(vr.successCount, 0, "ConfigPolicyManager called success callback with pending ValidationRequest") self.assertEqual(vr.failureCount, 0, "ConfigPolicyManager called failure callback with pending ValidationRequest") time.sleep(6) # now we should find it vr = doVerify(self.policyManager, data) self.assertFalse(vr.hasFurtherSteps, "ConfigPolicyManager did not refresh certificate store") self.assertEqual(vr.successCount, 1, "Verification success called {} times instead of 1".format( vr.successCount)) self.assertEqual(vr.failureCount, 0, "ConfigPolicyManager did not verify valid signed data")
def test_create_d_key_data(self): # Create the group manager. manager = GroupManager( Name("Alice"), Name("data_type"), Sqlite3GroupManagerDb(self.dKeyDatabaseFilePath), 2048, 1, self.keyChain) newCertificateBlob = self.certificate.wireEncode() newCertificate = IdentityCertificate() newCertificate.wireDecode(newCertificateBlob) # Encrypt the D-KEY. data = manager._createDKeyData( "20150825T000000", "20150827T000000", Name("/ndn/memberA/KEY"), self.decryptKeyBlob, newCertificate.getPublicKeyInfo().getKeyDer()) # Verify the encrypted D-KEY. dataContent = data.getContent() # Get the nonce key. # dataContent is a sequence of the two EncryptedContent. encryptedNonce = EncryptedContent() encryptedNonce.wireDecode(dataContent) self.assertEqual(0, encryptedNonce.getInitialVector().size()) self.assertEqual(EncryptAlgorithmType.RsaOaep, encryptedNonce.getAlgorithmType()) blobNonce = encryptedNonce.getPayload() decryptParams = EncryptParams(EncryptAlgorithmType.RsaOaep) nonce = RsaAlgorithm.decrypt(self.decryptKeyBlob, blobNonce, decryptParams) # Get the D-KEY. # Use the size of encryptedNonce to find the start of encryptedPayload. payloadContent = dataContent.buf()[encryptedNonce.wireEncode().size():] encryptedPayload = EncryptedContent() encryptedPayload.wireDecode(payloadContent) self.assertEqual(16, encryptedPayload.getInitialVector().size()) self.assertEqual(EncryptAlgorithmType.AesCbc, encryptedPayload.getAlgorithmType()) decryptParams.setAlgorithmType(EncryptAlgorithmType.AesCbc) decryptParams.setInitialVector(encryptedPayload.getInitialVector()) blobPayload = encryptedPayload.getPayload() largePayload = AesAlgorithm.decrypt(nonce, blobPayload, decryptParams) self.assertTrue(largePayload.equals(self.decryptKeyBlob))
def getCertificate(self, certificateName, allowAny=False): """ Get a certificate from the identity storage. :param Name certificateName: The name of the requested certificate. :param bool allowAny: (optional) If False, only a valid certificate will be returned, otherwise validity is disregarded. If omitted, allowAny is False. :return: The requested certificate. If not found, return None. :rtype: IdentityCertificate """ certificateNameUri = certificateName.toUri() if not (certificateNameUri in self._certificateStore): # Not found. Silently return None. return None certificate = IdentityCertificate() certificate.wireDecode(self._certificateStore[certificateNameUri]) return certificate
def onRootCertificateDownload(interest, data): try: # zhehao: the root cert is downloaded and installed without verifying; should the root cert be preconfigured? # Insert root certificate so that we can verify newCert self._policyManager._certificateCache.insertCertificate( data) # Set the root cert as default for root identity try: self._identityManager.addCertificateAsIdentityDefault( IdentityCertificate(data)) except SecurityException as e: print( "Error when addCertificateAsIdentityDefault for root: " + data.getName().toUri()) print(str(e)) self._rootCertificate = data try: # use the default configuration where possible # TODO: use environment variable for this, fall back to default fileName = os.path.expanduser('~/.ndn/.iot.root.cert') rootCertFile = open(fileName, "w") rootCertFile.write( Blob( b64encode(self._rootCertificate.wireEncode(). toBytes()), False).toRawStr()) rootCertFile.close() except IOError as e: self.log.error( "Cannot write to root certificate file: " + rootCertFile) print "Cannot write to root certificate file: " + rootCertFile except SecurityException as e: print(str(e)) # already exists, or got certificate in wrong format pass self._keyChain.verifyData(newCert, self._finalizeCertificateDownload, self._certificateValidationFailed)
def _processValidCertificate(self, data): # unpack the cert from the HMAC signed packet and verify try: newCert = IdentityCertificate() newCert.wireDecode(data.getContent()) self.log.info("Received certificate from controller") self.log.debug(str(newCert)) # NOTE: we download and install the root certificate without verifying it (!) # otherwise our policy manager will reject it. # we may need a static method on KeyChain to allow verifying before adding rootCertName = newCert.getSignature().getKeyLocator().getKeyName() # update trust rules so we trust the controller self._policyManager.setDeviceIdentity(self._configureIdentity) self._policyManager.updateTrustRules() def onRootCertificateDownload(interest, data): try: self._identityStorage.addCertificate(data) except SecurityException: # already exists pass self._keyChain.verifyData(newCert, self._finalizeCertificateDownload, self._certificateValidationFailed) def onRootCertificateTimeout(interest): # TODO: limit number of tries, then revert trust root + network prefix # reset salt, create new Hmac key self.face.expressInterest(rootCertName, onRootCertificateDownload, onRootCertificateTimeout) self.face.expressInterest(rootCertName, onRootCertificateDownload, onRootCertificateTimeout) except Exception as e: self.log.exception("Could not import new certificate", exc_info=True)
def addCertificate(self, certificate): """ Add a certificate to the identity storage. :param IdentityCertificate certificate: The certificate to be added. This makes a copy of the certificate. """ #TODO: actually check validity of certificate timestamp certificateName = certificate.getName() if self.doesCertificateExist(certificateName): raise SecurityException("Certificate has already been installed!") certCopy = IdentityCertificate(certificate) makeDefault = 0 keyName = certCopy.getPublicKeyName() keyInfo = certCopy.getPublicKeyInfo() if not self.doesKeyExist(keyName): self.addKey(keyName, keyInfo.getKeyType(), keyInfo.getKeyDer()) makeDefault = 1 else: # see if the key we already have matches this certificate keyBlob = self.getKey(keyName) if (keyBlob.isNull() or keyBlob.toBuffer() != keyInfo.getKeyDer().toBuffer()): raise SecurityException("Certificate does not match public key") keyId = keyName.get(-1).toEscapedString() identityUri = keyName.getPrefix(-1).toUri() certIssuer = certCopy.getSignature().getKeyLocator().getKeyName().toUri() encodedCert = buffer(bytearray(certCopy.wireEncode().buf())) notBefore = certCopy.getNotBefore() notAfter = certCopy.getNotAfter() cursor = self._database.cursor() cursor.execute("INSERT INTO Certificate VALUES(?,?,?,?,?,?,?,?,?)", (certificateName.toUri(), certIssuer, identityUri, keyId, notBefore, notAfter, encodedCert, 1, makeDefault)) self._database.commit() cursor.close()
def getCertificate(self, certificateName): """ Get a certificate from the identity storage. :param Name certificateName: The name of the requested certificate. :return: The requested certificate. :rtype: IdentityCertificate :raises SecurityException: if the certificate doesn't exist. """ certificateNameUri = certificateName.toUri() if not (certificateNameUri in self._certificateStore): raise SecurityException( "MemoryIdentityStorage.getCertificate: The certificate does not exist" ) certificate = IdentityCertificate() try: certificate.wireDecode(self._certificateStore[certificateNameUri]) except ValueError: raise SecurityException( "MemoryIdentityStorage.getCertificate: The certificate cannot be decoded" ) return certificate
def setUp(self): # Reuse the policy_config subdirectory for the temporary SQLite files. self.dKeyDatabaseFilePath = "policy_config/manager-d-key-test.db" try: os.remove(self.dKeyDatabaseFilePath) except OSError: # no such file pass self.eKeyDatabaseFilePath = "policy_config/manager-e-key-test.db" try: os.remove(self.eKeyDatabaseFilePath) except OSError: # no such file pass self.intervalDatabaseFilePath = "policy_config/manager-interval-test.db" try: os.remove(self.intervalDatabaseFilePath) except OSError: # no such file pass self.groupKeyDatabaseFilePath = "policy_config/manager-group-key-test.db" try: os.remove(self.groupKeyDatabaseFilePath) except OSError: # no such file pass params = RsaKeyParams() memberDecryptKey = RsaAlgorithm.generateKey(params) self.decryptKeyBlob = memberDecryptKey.getKeyBits() memberEncryptKey = RsaAlgorithm.deriveEncryptKey(self.decryptKeyBlob) self.encryptKeyBlob = memberEncryptKey.getKeyBits() # Generate the certificate. self.certificate = IdentityCertificate() self.certificate.setName(Name("/ndn/memberA/KEY/ksk-123/ID-CERT/123")) contentPublicKey = PublicKey(self.encryptKeyBlob) self.certificate.setPublicKeyInfo(contentPublicKey) self.certificate.setNotBefore(0) self.certificate.setNotAfter(0) self.certificate.encode() signatureInfoBlob = Blob(SIG_INFO, False) signatureValueBlob = Blob(SIG_VALUE, False) signature = TlvWireFormat.get().decodeSignatureInfoAndValue( signatureInfoBlob.buf(), signatureValueBlob.buf()) self.certificate.setSignature(signature) self.certificate.wireEncode() # Set up the keyChain. identityStorage = MemoryIdentityStorage() privateKeyStorage = MemoryPrivateKeyStorage() self.keyChain = KeyChain( IdentityManager(identityStorage, privateKeyStorage), NoVerifyPolicyManager()) identityName = Name("TestGroupManager") self.keyChain.createIdentityAndCertificate(identityName) self.keyChain.getIdentityManager().setDefaultIdentity(identityName)
def prepareUnsignedIdentityCertificate(self, keyName, publicKey, signingIdentity, notBefore, notAfter, subjectDescription=None, certPrefix=None): """ Prepare an unsigned identity certificate. :param Name keyName: The key name, e.g., `/{identity_name}/ksk-123456`. :param PublicKey publicKey: (optional) The public key to sign. If ommited, use the keyName to get the public key from the identity storage. :param Name signingIdentity: The signing identity. :param float notBefore: See IdentityCertificate. :param float notAfter: See IdentityCertificate. :param Array<CertificateSubjectDescription> subjectDescription: A list of CertificateSubjectDescription. See IdentityCertificate. If None or empty, this adds a an ATTRIBUTE_NAME based on the keyName. :param Name certPrefix: (optional) The prefix before the `KEY` component. If None, this infers the certificate name according to the relation between the signingIdentity and the subject identity. If the signingIdentity is a prefix of the subject identity, `KEY` will be inserted after the signingIdentity, otherwise `KEY` is inserted after subject identity (i.e., before `ksk-...`). :return: The unsigned IdentityCertificate, or None if the inputs are invalid. :rtype: IdentityCertificate """ if not isinstance(publicKey, PublicKey): # The publicKey was omitted. Shift arguments. certPrefix = subjectDescription subjectDescription = notAfter notAfter = notBefore notBefore = signingIdentity signingIdentity = publicKey publicKey = PublicKey(self._identityStorage.getKey(keyName)) if keyName.size() < 1: return None tempKeyIdPrefix = keyName.get(-1).toEscapedString() if len(tempKeyIdPrefix) < 4: return None keyIdPrefix = tempKeyIdPrefix[0:4] if keyIdPrefix != "ksk-" and keyIdPrefix != "dsk-": return None certificate = IdentityCertificate() certName = Name() if certPrefix == None: # No certificate prefix hint, so infer the prefix. if signingIdentity.match(keyName): certName.append(signingIdentity) \ .append("KEY") \ .append(keyName.getSubName(signingIdentity.size())) \ .append("ID-CERT") \ .appendVersion(int(Common.getNowMilliseconds())) else: certName.append(keyName.getPrefix(-1)) \ .append("KEY") \ .append(keyName.get(-1)) \ .append("ID-CERT") \ .appendVersion(int(Common.getNowMilliseconds())) else: # A cert prefix hint is supplied, so determine the cert name. if certPrefix.match(keyName) and not certPrefix.equals(keyName): certName.append(certPrefix) \ .append("KEY") \ .append(keyName.getSubName(certPrefix.size())) \ .append("ID-CERT") \ .appendVersion(int(Common.getNowMilliseconds())) else: return None certificate.setName(certName) certificate.setNotBefore(notBefore) certificate.setNotAfter(notAfter) certificate.setPublicKeyInfo(publicKey) if subjectDescription == None or len(subjectDescription) == 0: certificate.addSubjectDescription( CertificateSubjectDescription("2.5.4.41", keyName.getPrefix(-1).toUri())) else: for i in range(len(subjectDescription)): certificate.addSubjectDescription(subjectDescription[i]) try: certificate.encode() except Exception as ex: raise SecurityException("DerEncodingException: " + str(ex)) return certificate
def _processValidCertificate(self, data): # unpack the cert from the HMAC signed packet and verify try: newCert = IdentityCertificate() newCert.wireDecode(data.getContent()) self.log.info("Received certificate from controller") # NOTE: we download and install the root certificate without verifying it (!) # otherwise our policy manager will reject it. # we may need a static method on KeyChain to allow verifying before adding rootCertName = newCert.getSignature().getKeyLocator().getKeyName() # update trust rules so we trust the controller self._policyManager.setDeviceIdentity(self._configureIdentity) self._policyManager.updateTrustRules() def onRootCertificateDownload(interest, data): try: # zhehao: the root cert is downloaded and installed without verifying; should the root cert be preconfigured? # Insert root certificate so that we can verify newCert self._policyManager._certificateCache.insertCertificate( data) # Set the root cert as default for root identity try: self._identityManager.addCertificateAsIdentityDefault( IdentityCertificate(data)) except SecurityException as e: print( "Error when addCertificateAsIdentityDefault for root: " + data.getName().toUri()) print(str(e)) self._rootCertificate = data try: # use the default configuration where possible # TODO: use environment variable for this, fall back to default fileName = os.path.expanduser('~/.ndn/.iot.root.cert') rootCertFile = open(fileName, "w") rootCertFile.write( Blob( b64encode(self._rootCertificate.wireEncode(). toBytes()), False).toRawStr()) rootCertFile.close() except IOError as e: self.log.error( "Cannot write to root certificate file: " + rootCertFile) print "Cannot write to root certificate file: " + rootCertFile except SecurityException as e: print(str(e)) # already exists, or got certificate in wrong format pass self._keyChain.verifyData(newCert, self._finalizeCertificateDownload, self._certificateValidationFailed) def onRootCertificateTimeout(interest): # TODO: limit number of tries, then revert trust root + network prefix # reset salt, create new Hmac key self.face.expressInterest(rootCertName, onRootCertificateDownload, onRootCertificateTimeout) self.face.expressInterest(rootCertName, onRootCertificateDownload, onRootCertificateTimeout) except Exception as e: self.log.exception("Could not import new certificate", exc_info=True)