如何正确检测.p7m文件的内容类型、幻数及扩展名?
解决P7M文件类型识别错误的问题
首先明确:P7M本质是带原始内容的PKCS#7签名容器(SignedData类型),而P7S是仅包含签名的文件,Tika默认的检测逻辑容易混淆这两者,或者直接识别出P7M内部包裹的原始文件类型(比如你遇到的DBF)。
先排查代码优化点
你的代码本身没有语法错误,但缺少两个关键优化:
- 添加文件名元数据辅助识别
Tika会参考文件名扩展名来辅助判断类型,如果你知道目标文件的文件名(比如xxx.p7m),在创建Metadata时加上这行:
metadata.set(Metadata.RESOURCE_NAME_KEY, "xxx.p7m");
这样Tika会结合扩展名和文件内容来判断,避免只解析内部包裹的文件。
- 手动区分PKCS#7的两种类型
PKCS#7文件的开头通常是ASN.1 BER编码的0x30 0x82(SEQUENCE类型的长格式标记),你可以先检查字节流前两个字节:
if (content.length >= 2 && content[0] == 0x30 && content[1] == 0x82) { // 是PKCS#7结构,进一步区分P7M/P7S }
Tika默认把application/pkcs7-signature映射为.p7s,而application/pkcs7-mime对应.p7m,你可以手动判断媒体类型后调整扩展名:
MediaType mediaType = config.getMimeRepository().detect(stream, metadata); if (mediaType.toString().equals("application/pkcs7-signature")) { // 检查是否包含原始内容,是的话强制用.p7m extension = ".p7m"; }
用BouncyCastle实现精准识别
如果需要100%准确区分P7M和P7S,优先用BouncyCastle直接解析PKCS#7结构:
P7M是包含原始内容的SignedData,而P7S是仅签名的文件(无原始内容)。示例代码:
import org.bouncycastle.cms.CMSException; import org.bouncycastle.cms.CMSSignedData; import org.bouncycastle.cms.CMSTypedData; import java.io.ByteArrayInputStream; import java.io.IOException; public class P7MDetector { public static boolean isP7MFile(byte[] content) { try { CMSSignedData signedData = new CMSSignedData(new ByteArrayInputStream(content)); // 存在签名内容则为P7M,否则是P7S CMSTypedData signedContent = signedData.getSignedContent(); return signedContent != null; } catch (CMSException | IOException e) { return false; } } }
结合Tika使用:先用BouncyCastle判断是否是P7M,再用Tika解析内部文件类型;或者直接用BouncyCastle确定类型后返回.p7m扩展名。
总结
- 不需要更换库,优先通过添加文件名元数据+手动幻数检查优化Tika的识别逻辑;
- 追求精准性的话,结合BouncyCastle解析PKCS#7结构,直接判断是否为带内容的P7M容器。
内容的提问来源于stack exchange,提问作者Matt
相关产品推荐
相关产品推荐

