使用这个公钥是否能解密此加密信息?

2023-07-29 17:35:16 +08:00
 FaiChou

使用私钥 rsa 加密内容:

gl9E9ye5acVdn5MusJj0pay6y+i6qDna8DTSNHkkPjBsEE9DJ34cKhrCiNuRWIxY4+DQmrrQPMzibgCvP+3unWelxwmzMRzmSQQDTm9Z+oBiHGCcqWXtz47H/b1uoC4T6ZXkTrtuYAOVlRDn+2xKPcO9qpq5gS+cU6SPSRcRP/cAthqnLQ0FJJjY2vOqsoKda5IStxLjRSJPXs5zmJFVH/ii7J/u1vp8R6sUUNLhJmahFlODgOoEPDTW50k6O5LS8MYniZLvI/Czj9bt1z850rXGl3wHvfwmhIWH8umR5NqaPJ8/kDJXGk5ejHoljRN2GJ4iL48hvJawsoCNHpktrlqRtE6geQF74gB5hLzSvY2ZUcRo1fx7DxpPnRqfx7ctf6m7nC0vohax4NvdYJ7TWnK+oDGCJFFJRNg+L/DJlcckjWmL3nGoFYgHSfVwZXfTLOcKOtx8Pvd2Udjx+9t2/T4DqJNmhVzBvX/tEqBlv+deAbk8DaGx4FcBWm1b5j2qMUqA33OVIYLOzgFFzdMF8OqEvAPwqyg1pHjt9iPzWXWVjzFA2Z1yVou2TwZ6Z9QqrjaGyK6KfpT+h241FgCVrZgbg8g/JWU7wFHQjiEjJTSTZu6FeweQpRUuUy/A8LS0Wabh0eZQ0hcPcRBwf2gGOlmbfCojrfZNHbjRkh0Rz6YAq3T5q5VdfsKXBUMJcTilfUGRzIivVAFmM5Lx5WzgV7LsPSIuEq5sNFEypk5rrKplTirq3qE6maI3+KKbUccAs2ak6zGLtfFnJasXivwYPrivUnTDz9cV1dVFYKe72/aGnNby2TxWvNpbDu60T8BlX1CFwFj+JQyDqFu+EvgBTQ==

对应的公钥:

-----BEGIN PUBLIC KEY-----
MIGfMA0GCSqGSIb3DQEBAQUAA4GNADCBiQKBgQCGhpgMD1okxLnUMCDNLCJwP/P0
UHVlKQWLHPiPCbhgITZHcZim4mgxSWWb0SLDNZL9ta1HlErR6k02xrFyqtYzjDu2
rGInUC0BCZOsln0a7wDwyOA43i5NO8LsNory6fEKbx7aT3Ji8TZCDAfDMbhxvxOf
dPMBDjxP5X3zr7cWgwIDAQAB
-----END PUBLIC KEY-----

如果解密出的结果像是下面这样,应该是对的:

{"2658844581501957149":{"file_name":"wwdc2023-10117_hd.mp4","file_size":"100597894","pick_code":"b12x4iiejvkkq83a","url":{"url":"http:\/\/xxx.xxx.com\/xxx\/wwdc2023-10117_hd.mp4?t=1690607362&u=101248331&s=1048576&d=658578066-b12x4iiejvkkq83a-0&c=2&f=1&k=ddb3d920081771b2f40ee75164c5bb1b&us=52428800&uc=10&v=1","client":2,"desc":null,"isp":null,"oss_id":"fhnfile\/xxx","ooid":""}}}

注意由于加密内容数据比较大,需要分段解密。

不管用什么方法,python/go/java 还是网上的在线解密工具,只要能解密出来即可。

我用 swift 写了分段解密,结果解密失败,用网上在线的解密工具也是失败,不知道网上的工具是不是由于不支持分段解密才失败的。

下面是我用 Swift 写的方法:

import Security
func convertPublicKey(pemString: String) -> SecKey? {
    // 1. Remove headers, newlines and spaces
    let keyString = pemString
        .replacingOccurrences(of: "-----BEGIN PUBLIC KEY-----", with: "")
        .replacingOccurrences(of: "-----END PUBLIC KEY-----", with: "")
        .replacingOccurrences(of: "\n", with: "")
        .replacingOccurrences(of: " ", with: "")

    // 2. Convert base64 string to Data
    guard let keyData = Data(base64Encoded: keyString) else {
        return nil
    }

    // 3. Create key dictionary
    let keyDict: [CFString: Any] = [
        kSecAttrKeyType: kSecAttrKeyTypeRSA,
        kSecAttrKeyClass: kSecAttrKeyClassPublic,
        kSecAttrKeySizeInBits: NSNumber(value: 2048),
        kSecReturnPersistentRef: true
    ]

    // 4. Create SecKey from Data
    var error: Unmanaged<CFError>?
    guard let key = SecKeyCreateWithData(keyData as CFData, keyDict as CFDictionary, &error) else {
        // Handle the error here
        return nil
    }

    return key
}
let rsaKeyInSwift = convertPublicKey(pemString: rsaPublicKey)
func rsaDecrypt(data: Data) -> Data? {
    let blockSize = SecKeyGetBlockSize(rsaKeyInSwift!)
    var decryptedData = Data()
    for i in stride(from: 0, to: data.count, by: blockSize) {
        let endIndex = min(i + blockSize, data.count)
        let chunk = data[i..<endIndex]
        print(chunk)
        var error: Unmanaged<CFError>?
        guard let decryptedChunk = SecKeyCreateDecryptedData(
            rsaKeyInSwift!,
            .rsaEncryptionPKCS1,
            chunk as CFData,
            &error
        ) else {
            print("Decryption error:", error.debugDescription)
            return nil
        }
        decryptedData.append(decryptedChunk as Data)
    }

    return decryptedData
}

报错:

Decryption error: Optional(Swift.Unmanaged<__C.CFErrorRef>(_value: Error Domain=NSOSStatusErrorDomain Code=-50 "RSAdecrypt wrong input (err -27)" UserInfo={numberOfErrorsDeep=0, NSDescription=RSAdecrypt wrong input (err -27)}))

在网上找到了一个开源的项目,它用 go 语言自己实现了 rsa 解密算法,不清楚为什么它不用标准库来解密。我用标准库实现的( Swift 版本)无法解密。所以不清楚是不是我的代码问题还是其他问题。

下面是 go 语言开源版本:

func rsaDecrypt(input []byte) []byte {
	output := make([]byte, 0)
	cipherSize, blockSize := len(input), rsaServerKey.Size()
	for offset := 0; offset < cipherSize; offset += blockSize {
		sliceSize := blockSize
		if offset+sliceSize > cipherSize {
			sliceSize = cipherSize - offset
		}

		n := big.NewInt(0).SetBytes(input[offset : offset+sliceSize])
		m := big.NewInt(0).Exp(n, big.NewInt(int64(rsaServerKey.E)), rsaServerKey.N)
		b := m.Bytes()
		index := bytes.IndexByte(b, '\x00')
		if index < 0 {
			return nil
		}
		output = append(output, b[index+1:]...)
	}
	return output
}

由于不熟悉 go 语言,大概看了下算法,也是计算 (n^e) mod N

所以请大家用这个公钥解密上面的内容,能否解密出来?

3258 次点击
所在节点    程序员
39 条回复
zbinlin
2023-07-29 17:39:16 +08:00
一般都是用公钥来加密的,你怎么想到用它来解密呢?
FaiChou
2023-07-29 17:43:47 +08:00
@zbinlin 公钥和私钥在数学上是对等的。反过来也能用。
maggch97
2023-07-29 18:02:00 +08:00
额额额,要是公钥能解密互联网安全就不存在了。一知半解的时候最好先把基础概念弄懂
ppxppx
2023-07-29 18:12:59 +08:00
@maggch97 #3 op 说的密文是私钥加密的。
v2yllhwa
2023-07-29 18:13:55 +08:00
@maggch97 你猜签名是怎么签的
FaiChou
2023-07-29 18:14:55 +08:00
@maggch97 一知半解的时候最好先把题目读懂。
Daniate
2023-07-29 18:19:50 +08:00
@FaiChou 对等不代表有对应的实现。苹果提供的 SDK 没实现公钥解密,即便填充方式、密钥长度什么的都是用的正确的,也会报 errSecUnimplemented
0o0O0o0O0o
2023-07-29 18:25:52 +08:00
https://go.dev/play/p/H_JAgXp8EOK

以前从 crypto/rsa 里抠出来改的私钥加密公钥解密 ECB ,这个能跑通,好像也跟 OpenSSL 测试了可以互相加解密

你这个用 PKCS1v15 或者无填充都解不了,或许可以用 openssl 试下 OAEP ?或者参考这个代码改一下 crypto/rsa 的 decryptOAEP
zbinlin
2023-07-29 18:28:26 +08:00
你给出来的公钥跟加密内容是对用上的吗?怎么解密出来的是 `vnAD3tym1s0ape6h` 开头的一段乱码。
FaiChou
2023-07-29 18:28:33 +08:00
@Daniate 谢谢。原来如此。怪不得要手撸 rsa 解密。我之前也猜到了,只不过不是很确定。
FaiChou
2023-07-29 18:30:46 +08:00
@zbinlin #9 上面那个结果就是用我主题中的 go 语言手撸的解密算法解密出来的。
ThirdFlame
2023-07-29 18:31:56 +08:00
这是 ctf 题目把

1 、非对称加密体系,一般使用公钥加密、私钥解密。
2 、本题中密文是使用公钥进行的加密,那么也是需要对应的私钥进行解密。
3 、由于公钥里面的存的一般是 n 和 e ,在某些特定情况下可以将 n 分解,得到 p q 。 也就从而能够算出来 d
4 、细化到本题,本题的 n 明显偏小,经过提取 n=94467199538421168685115018334776065898663751652520808966691769684389754194866868839785962914624862265689699980316658987338198288176273874160782292722912223482699621202960645813656296092078123617049558650961406540632832570073725203873545017737008711614000139573916153236215559489283800593547775766023112169091 理论上是可以分解开的。 但是通过 factordb 查询和 yafu 尝试分解都未能成功。
5 、如果只有分解成功得到了 p q ,方可计算 d 。 有了 d 就能组装成私钥,才能解密了(其实不用组装成私钥,直接用 d ,通过数学的方法就可以了。 )
0o0O0o0O0o
2023-07-29 18:32:27 +08:00
@zbinlin #9 那是以无填充的方式解密
ThirdFlame
2023-07-29 18:32:52 +08:00
哦 看差了一点,使用私钥进行的加密啊, 那正常情况下直接用公钥是可以解密的。
FaiChou
2023-07-29 18:37:37 +08:00
@ThirdFlame 不是题目。就是某厂的 API ,有一个接口,发送的数据需要经过一系列算法加密。返回的数据也要先用公钥解密。
ThirdFlame
2023-07-29 18:42:24 +08:00
@FaiChou #15 最好看一下加密代码怎么写的。 一般来说 RSA 只用来传递密钥,明文使用对称加密算法+密钥来进行加解密。
我尝试直接通过 n e ,去解密,得到的结果完全不对
其结果是 0x0270b638f77ac547acb17978f0a397f053d2fff2fecc8dd408587fcc174b187fd797536bb4c55f131ea3fdd092bd29c569c9798c4073e25afa73c3e0340d4f79724a18d47d450404838ac9f0518ea621f82fb8d6c871c13deed40de0cb4dd42e4137519c1d3563816ced8a6af7b10b8256b2f67cdd7496062190c998b382b980 128 字节的一个东西,应该是个对称加密算法的密钥
Daniate
2023-07-29 18:51:14 +08:00
@FaiChou 创建 key 的时候,试试设置下 kSecAttrCanDecrypt ,很多年前这样做过,当时是不行的,我猜现在应该还是不行🤔
FaiChou
2023-07-29 18:52:28 +08:00
@ThirdFlame #16 我先大概讲一下这个项目吧。
首先前端需要通过 A 算法加密数据,然后传给后台,后台进行解密;后台再加密返回数据,前端收到返回数据后再用 B 算法解密。

A 算法:
先生成一个 16 字节的随机 Key 数据,将要加密的数据拼接到后面。使用 xorKeySeed 进行将数据进行异或操作,然后再反转所有字节,最后再进行一次异或(两次异或也不一样)。最后将数据使用公钥 rsa 加密。

B 算法:
先使用公钥解密后台发回来的数据。然后再进行异或和反转的操作。


所以我主题中的结果是给错了。rsa 解密出来应该是这样的 data:

```
[118 110 65 68 51 116 121 109 49 115 48 97 112 101 54 104 10 115 223 234 123 70 190 32 240 170 217 149 215 106 190 173 60 24 248 114 255 252 212 128 204 124 190 173 107 26 174 37 175 246 208 133 207 126 236 251 110 74 249 124 251 252 130 129 153 112 187 240 109 74 179 24 252 169 223 209 149 32 239 234 99 94 248 45 198 182 197 216 217 100 229 164 44 18 166 102 233 182 223 149 215 36 229 189 55 70 190 39 234 160 210 149 215 122 179 234 45 18 249 45 245 166 148 155 217 121 180 190 127 76 173 121 250 176 144 135 203 112 177 250 109 78 169 121 234 176 144 213 202 42 235 253 58 72 170 117 172 242 211 210 203 124 239 250 59 77 171 115 168 253 134 135 201 113 237 251 59 24 248 121 242 227 135 138 157 110 187 245 58 90 172 105 247 164 133 143 138 35 226 190 51 25 245 45 173 189 132 134 153 101 191 254 105 68 171 113 161 240 128 138 159 110 191 255 108 68 168 116 168 248 197 145 202 123 186 240 109 78 173 116 168 248 195 145 201 126 186 255 105 74 172 125 175 244 139 195 196 124 249 165 119 24 244 27 174 244 135 135 202 101 186 250 105 78 255 32 238 178 153 235 204 45 236 172 61 74 250 125 251 242 129 131 204 45 187 174 107 29 170 119 255 247 130 129 158 123 190 254 60 68 254 125 173 243 212 143 201 112 189 254 118 32 241 43 250 235 131 134 202 102 236 164 48 26 242 44 255 171 210 212 212 20 166 148 99 12 232 48 241 231 140 149 151 58 252 234 34 70 190 40 235 176 148 155 217 38 232 251 97 13 247 47 239 175 211 222 146 124 241 250 104 30 190 126 187 160 210 216 152 23 226 171 48 12 190 104 187 241 143 143 204 113 188 248 105 77 190 126 187 160 204 222 136 23 236 164 48 26 190 104 187 241 198 218 213 44 225 151 110 77 173 116 168 232 133 133 203 122 234 172 46 11 190 126 187 160 219 214 149 23 236 164 48 26 190 63 163 231 143 131 202 127 188 241 104 76 169 117 161 240 130 131 195 112 188 254 60 128 209]
```
0o0O0o0O0o
2023-07-29 18:53:49 +08:00
@ThirdFlame #16

用私钥加密就已经不是一般用法了, 无填充解密肯定是 128 字节,也就是 key 的 size 1024 bit ;如果是 PKCS1 ECB ,每段解密后是 128-11 字节

---
> 是某厂的 API ,有一个接口,发送的数据需要经过一系列算法加密

如果是无源码逆向,就去逆向找它加密的位置,看它的具体逻辑;
如果是有源码对接,就删掉多余的逻辑,删掉 ECB ,阅读代码,自己先拿 openssl 验证,尽量避免用各语言的核心加密库:虽然私钥加密公钥解密是可行的,但这是不被推荐的,而各语言的核心加密库通常是最佳实践,所以几乎不会支持这种操作。
0o0O0o0O0o
2023-07-29 18:55:42 +08:00
@FaiChou #18 那你这就是无填充,密文字节数组按照 rsa key size (在这个例子里就是 128 )逐个解密,然后拼接就行

这是一个专为移动设备优化的页面(即为了让你能够在 Google 搜索结果里秒开这个页面),如果你希望参与 V2EX 社区的讨论,你可以继续到 V2EX 上打开本讨论主题的完整版本。

https://www.v2ex.com/t/960808

V2EX 是创意工作者们的社区,是一个分享自己正在做的有趣事物、交流想法,可以遇见新朋友甚至新机会的地方。

V2EX is a community of developers, designers and creative people.

© 2021 V2EX