My guess is that the underlying code is behaving differently from how it might be expected to operate - instead of handling taxonomy items with custom properties, it is handling the properties themselves. This is consistent with the behavior of other ektron APIs in terms of its typing, but inconsistent in that it returns the associated taxonomy items themselves (though this is clearly the more desirable outcome). With respect to this design, it makes sense that you cannot retrieve a custom property that has name equal to p1 and p2.
The solution is to retrieve lists for each property you want to filter by independently, and then intersect these lists. Conveniently, ektron returns iQueryable lists, so you can do this trivially with linq:
CriteriaFilterGroup<TaxonomyCustomProperty> criteriaFilterGrp1= new CriteriaFilterGroup<TaxonomyCustomProperty>();
criteriaFilterGrp1.AddFilter(TaxonomyCustomProperty.Name,
CriteriaFilterOperator.EqualTo,"P1");
criteriaFilterGrp1.AddFilter(TaxonomyCustomProperty.Value,
CriteriaFilterOperator.EqualTo, "V1");
criteriaFilterGrp1.Condition = LogicalOperation.And;
custCtriteria.FilterGroups.Add(criteriaFilterGrp1);
var itemsWithProp1= taxManager.getList(custCtriteria);
custCtriteria = new criteria();
CriteriaFilterGroup<TaxonomyCustomProperty> criteriaFilterGrp2= new CriteriaFilterGroup<TaxonomyCustomProperty>();
criteriaFilterGrp2.AddFilter(TaxonomyCustomProperty.Name,
CriteriaFilterOperator.EqualTo, "P2";
criteriaFilterGrp2.AddFilter(TaxonomyCustomProperty.Value,
CriteriaFilterOperator.EqualTo, "V2");
custCtriteria.FilterGroups.Add(criteriaFilterGrp2);
var itemsWithProp2 = taxManager.getList(custCtriteria);
var itemsWithBoth = itemsWithProp1.Intersect(itemsWithProp2);
While this is not the most elegant or efficient solution, I'm not sure that there is a better way within Ektron's API, outside of crafting a custom SQL query.